Index: /branches/symbreg-factors-2650/HeuristicLab 3.3.sln
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab 3.3.sln	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab 3.3.sln	(revision 14751)
@@ -420,5 +420,5 @@
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeuristicLab.Algorithms.CMAEvolutionStrategy-3.4", "HeuristicLab.Algorithms.CMAEvolutionStrategy\3.4\HeuristicLab.Algorithms.CMAEvolutionStrategy-3.4.csproj", "{291010E4-2F4E-4D29-A795-753CFF293FDB}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeuristicLab.Encodings.LinearLinkageEncoding-3.4", "HeuristicLab.Encodings.LinearLinkageEncoding\3.3\HeuristicLab.Encodings.LinearLinkageEncoding-3.4.csproj", "{BE698769-975A-429E-828C-72BB2B6182C8}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeuristicLab.Encodings.LinearLinkageEncoding-3.4", "HeuristicLab.Encodings.LinearLinkageEncoding\3.4\HeuristicLab.Encodings.LinearLinkageEncoding-3.4.csproj", "{BE698769-975A-429E-828C-72BB2B6182C8}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeuristicLab.Problems.Orienteering-3.3", "HeuristicLab.Problems.Orienteering\3.3\HeuristicLab.Problems.Orienteering-3.3.csproj", "{D1EFA4CC-909F-41D5-9C1F-C3AF1957A372}"
Index: /branches/symbreg-factors-2650/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/LinearDiscriminantAnalysis.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/LinearDiscriminantAnalysis.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/LinearDiscriminantAnalysis.cs	(revision 14751)
@@ -121,5 +121,5 @@
       }
 
-      var model = LinearDiscriminantAnalysis.CreateDiscriminantFunctionModel(tree, new SymbolicDataAnalysisExpressionTreeInterpreter(), problemData, rows);
+      var model = CreateDiscriminantFunctionModel(tree, new SymbolicDataAnalysisExpressionTreeLinearInterpreter(), problemData, rows);
       SymbolicDiscriminantFunctionClassificationSolution solution = new SymbolicDiscriminantFunctionClassificationSolution(model, (IClassificationProblemData)problemData.Clone());
 
Index: /branches/symbreg-factors-2650/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/LinearRegression.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/LinearRegression.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/LinearRegression.cs	(revision 14751)
@@ -129,5 +129,5 @@
       addition.AddSubtree(cNode);
 
-      SymbolicRegressionSolution solution = new SymbolicRegressionSolution(new SymbolicRegressionModel(problemData.TargetVariable, tree, new SymbolicDataAnalysisExpressionTreeInterpreter()), (IRegressionProblemData)problemData.Clone());
+      SymbolicRegressionSolution solution = new SymbolicRegressionSolution(new SymbolicRegressionModel(problemData.TargetVariable, tree, new SymbolicDataAnalysisExpressionTreeLinearInterpreter()), (IRegressionProblemData)problemData.Clone());
       solution.Model.Name = "Linear Regression Model";
       solution.Name = "Linear Regression Solution";
Index: /branches/symbreg-factors-2650/HeuristicLab.Analysis.Views/3.3/GanttDataView.Designer.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Analysis.Views/3.3/GanttDataView.Designer.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Analysis.Views/3.3/GanttDataView.Designer.cs	(revision 14751)
@@ -0,0 +1,75 @@
+﻿#region License Information
+
+/* HeuristicLab
+ * Copyright (C) 2002-2017 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
+
+namespace HeuristicLab.Analysis.Views {
+  partial class GanttDataView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.ganttChart = new HeuristicLab.Visualization.ChartControlsExtensions.GanttChart();
+      this.SuspendLayout();
+      // 
+      // ganttChart
+      // 
+      this.ganttChart.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.ganttChart.Location = new System.Drawing.Point(0, 0);
+      this.ganttChart.Name = "ganttChart";
+      this.ganttChart.Size = new System.Drawing.Size(367, 249);
+      this.ganttChart.TabIndex = 0;
+      // 
+      // GanttDataView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.ganttChart);
+      this.Name = "GanttDataView";
+      this.Size = new System.Drawing.Size(367, 249);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private Visualization.ChartControlsExtensions.GanttChart ganttChart;
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Analysis.Views/3.3/GanttDataView.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Analysis.Views/3.3/GanttDataView.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Analysis.Views/3.3/GanttDataView.cs	(revision 14751)
@@ -0,0 +1,177 @@
+﻿#region License Information
+
+/* HeuristicLab
+ * Copyright (C) 2002-2017 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.Drawing;
+using System.Linq;
+using HeuristicLab.Collections;
+using HeuristicLab.Core.Views;
+using HeuristicLab.MainForm;
+
+namespace HeuristicLab.Analysis.Views {
+  [View("Gantt Data View")]
+  [Content(typeof(GanttData), IsDefaultView = true)]
+  public partial class GanttDataView : ItemView {
+
+    public new GanttData Content {
+      get { return (GanttData)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public GanttDataView() {
+      InitializeComponent();
+    }
+
+    #region De/Register Content Events
+    protected override void RegisterContentEvents() {
+      base.RegisterContentEvents();
+      Content.Rows.ItemsAdded += GanttRowsOnChanged;
+      Content.Rows.ItemsRemoved += GanttRowsOnRemoved;
+      Content.Rows.ItemsReplaced += GanttRowsOnChanged;
+      Content.Rows.CollectionReset += GanttRowsOnChanged;
+      Content.CategoryColors.ItemsAdded += GanttCategoryColorsOnChanged;
+      Content.CategoryColors.ItemsRemoved += GanttCategoryColorsOnChanged;
+      Content.CategoryColors.ItemsReplaced += GanttCategoryColorsOnChanged;
+      Content.CategoryColors.CollectionReset += GanttCategoryColorsOnChanged;
+      foreach (var row in Content.Rows)
+        RegisterGanttRowEvents(row);
+    }
+
+    private void RegisterGanttRowEvents(GanttRow row) {
+      row.Items.ItemsAdded += GanttRowItemsOnChanged;
+      row.Items.ItemsReplaced += GanttRowItemsOnChanged;
+      row.Items.ItemsRemoved += GanttRowItemsOnRemoved;
+      row.Items.CollectionReset += GanttRowItemsOnChanged;
+      foreach (var item in row.Items)
+        RegisterGanttRowItemEvents(item);
+    }
+
+    private void RegisterGanttRowItemEvents(GanttItem item) {
+      item.PropertyChanged += GanttRowItemsItemOnChanged;
+    }
+
+    protected override void DeregisterContentEvents() {
+      base.DeregisterContentEvents();
+      Content.Rows.ItemsAdded -= GanttRowsOnChanged;
+      Content.Rows.ItemsRemoved -= GanttRowsOnRemoved;
+      Content.Rows.ItemsReplaced -= GanttRowsOnChanged;
+      Content.Rows.CollectionReset -= GanttRowsOnChanged;
+      Content.CategoryColors.ItemsAdded -= GanttCategoryColorsOnChanged;
+      Content.CategoryColors.ItemsRemoved -= GanttCategoryColorsOnChanged;
+      Content.CategoryColors.ItemsReplaced -= GanttCategoryColorsOnChanged;
+      Content.CategoryColors.CollectionReset -= GanttCategoryColorsOnChanged;
+      foreach (var row in Content.Rows)
+        DeregisterGanttRowEvents(row);
+    }
+
+    private void DeregisterGanttRowEvents(GanttRow row) {
+      row.Items.ItemsAdded -= GanttRowItemsOnChanged;
+      row.Items.ItemsReplaced -= GanttRowItemsOnChanged;
+      row.Items.ItemsRemoved -= GanttRowItemsOnRemoved;
+      row.Items.CollectionReset -= GanttRowItemsOnChanged;
+      foreach (var item in row.Items)
+        DeregisterGanttRowItemEvents(item);
+    }
+
+    private void DeregisterGanttRowItemEvents(GanttItem item) {
+      item.PropertyChanged -= GanttRowItemsItemOnChanged;
+    }
+    #endregion
+
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+
+      UpdateVisualization();
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      ganttChart.Enabled = !Locked && !ReadOnly && Content != null;
+    }
+
+    private void UpdateVisualization() {
+      ganttChart.Reset();
+
+      if (Content == null) return;
+
+      var colors = (KnownColor[])Enum.GetValues(typeof(KnownColor));
+
+      var categories = Content.Rows.SelectMany(x => x.Items.Select(y => y.Category ?? string.Empty)).Distinct();
+      foreach (var cat in categories) {
+        Color color;
+        if (!Content.CategoryColors.TryGetValue(cat, out color)) {
+          // if no color is defined, a random color is chosen for each category
+          var rand = new Random(cat.GetHashCode());
+          color = Color.FromKnownColor(colors[rand.Next(colors.Length)]);
+        }
+        ganttChart.AddCategory(cat, color);
+      }
+      
+      ganttChart.Hide();
+      ganttChart.Title = string.IsNullOrWhiteSpace(Content.Name) ? null : Content.Name;
+      foreach (var row in Content.Rows) {
+        foreach (var item in row.Items) {
+          ganttChart.AddData(row.Name, item.Category ?? string.Empty, item.StartDate, item.EndDate, item.ToolTip, item.ShowLabel);
+        }
+      }
+      ganttChart.Show();
+    }
+
+    #region Content Event Handlers
+    private void GanttRowsOnChanged(object sender, CollectionItemsChangedEventArgs<GanttRow> e) {
+      foreach (var row in e.Items)
+        RegisterGanttRowEvents(row);
+      foreach (var row in e.OldItems)
+        DeregisterGanttRowEvents(row);
+
+      UpdateVisualization();
+    }
+    private void GanttRowsOnRemoved(object sender, CollectionItemsChangedEventArgs<GanttRow> e) {
+      foreach (var row in e.Items)
+        DeregisterGanttRowEvents(row);
+
+      UpdateVisualization();
+    }
+    private void GanttCategoryColorsOnChanged(object sender, EventArgs e) {
+      UpdateVisualization();
+    }
+    private void GanttRowItemsOnChanged(object sender, CollectionItemsChangedEventArgs<IndexedItem<GanttItem>> e) {
+      foreach (var item in e.Items)
+        RegisterGanttRowItemEvents(item.Value);
+      foreach (var item in e.OldItems)
+        DeregisterGanttRowItemEvents(item.Value);
+
+      UpdateVisualization();
+    }
+    private void GanttRowItemsOnRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<GanttItem>> e) {
+      foreach (var item in e.Items)
+        DeregisterGanttRowItemEvents(item.Value);
+
+      UpdateVisualization();
+    }
+    private void GanttRowItemsItemOnChanged(object sender, EventArgs e) {
+      UpdateVisualization();
+    }
+    #endregion
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Analysis.Views/3.3/HeuristicLab.Analysis.Views-3.3.csproj
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Analysis.Views/3.3/HeuristicLab.Analysis.Views-3.3.csproj	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Analysis.Views/3.3/HeuristicLab.Analysis.Views-3.3.csproj	(revision 14751)
@@ -132,4 +132,10 @@
       <DependentUpon>DataTableControl.cs</DependentUpon>
     </Compile>
+    <Compile Include="GanttDataView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="GanttDataView.Designer.cs">
+      <DependentUpon>GanttDataView.cs</DependentUpon>
+    </Compile>
     <Compile Include="IndexedDataTableView.cs">
       <SubType>UserControl</SubType>
Index: /branches/symbreg-factors-2650/HeuristicLab.Analysis/3.3/DataVisualization/GanttData.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Analysis/3.3/DataVisualization/GanttData.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Analysis/3.3/DataVisualization/GanttData.cs	(revision 14751)
@@ -0,0 +1,188 @@
+﻿#region License Information
+
+/* HeuristicLab
+ * Copyright (C) 2002-2017 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.ComponentModel;
+using System.Drawing;
+using HeuristicLab.Collections;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Analysis {
+  [Item("Gantt Data", "Data of a Gantt visualization")]
+  [StorableClass]
+  public class GanttData : NamedItem {
+
+    [Storable]
+    private NamedItemCollection<GanttRow> rows;
+    public NamedItemCollection<GanttRow> Rows { get { return rows; } }
+
+    [Storable]
+    private ObservableDictionary<string, Color> categoryColors;
+    public ObservableDictionary<string, Color> CategoryColors { get { return categoryColors; } }
+
+    [StorableConstructor]
+    protected GanttData(bool deserializing) : base(deserializing) {}
+    protected GanttData(GanttData original, Cloner cloner)
+      : base(original, cloner) {
+      rows = cloner.Clone(original.rows);
+      categoryColors = new ObservableDictionary<string, Color>(original.categoryColors);
+    }
+    public GanttData() : this("Gantt Data", "Data of a Gantt visualization") { }
+    public GanttData(string name) : this(name, string.Empty) { }
+    public GanttData(string name, string description) : base(name, description) {
+      rows = new NamedItemCollection<GanttRow>();
+      categoryColors = new ObservableDictionary<string, Color>();
+    }
+    public GanttData(IEnumerable<GanttRow> rows) : this("Gantt Data", rows) { }
+    public GanttData(string name, IEnumerable<GanttRow> rows) : this(name, string.Empty, rows) { }
+    public GanttData(string name, string description, IEnumerable<GanttRow> rows) : base(name, description) {
+      this.rows = new NamedItemCollection<GanttRow>(rows);
+      categoryColors = new ObservableDictionary<string, Color>();
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new GanttData(this, cloner);
+    }
+  }
+
+  [Item("Gantt Row", "Row of a Gantt chart")]
+  [StorableClass]
+  public class GanttRow : NamedItem {
+    [Storable]
+    private ItemList<GanttItem> items;
+    public ItemList<GanttItem> Items { get { return items; } }
+
+    [StorableConstructor]
+    protected GanttRow(bool deserializing) : base(deserializing) {}
+    protected GanttRow(GanttRow original, Cloner cloner)
+      : base(original, cloner) {
+      items = cloner.Clone(original.items);
+    }
+    public GanttRow() : this("Gantt Row", "Row of a Gantt chart") { }
+    public GanttRow(string name) : this(name, string.Empty) { }
+    public GanttRow(string name, string description) : base(name, description) {
+      items = new ItemList<GanttItem>();
+    }
+    public GanttRow(IEnumerable<GanttItem> items) : this("Gantt Row", items) { }
+    public GanttRow(string name, IEnumerable<GanttItem> items) : this(name, string.Empty, items) { }
+    public GanttRow(string name, string description, IEnumerable<GanttItem> items) : base(name, description) {
+      this.items = new ItemList<GanttItem>(items);
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new GanttRow(this, cloner);
+    }
+  }
+
+  [Item("Gantt Item", "Item of a Gantt chart row")]
+  [StorableClass]
+  public class GanttItem : Item, INotifyPropertyChanged {
+
+    [Storable]
+    private DateTime startDate;
+    public DateTime StartDate {
+      get { return startDate; }
+      set {
+        if (startDate == value) return;
+        startDate = value;
+        OnPropertyChanged("StartDate");
+      }
+    }
+
+    [Storable]
+    private DateTime endDate;
+    public DateTime EndDate {
+      get { return endDate; }
+      set {
+        if (endDate == value) return;
+        endDate = value;
+        OnPropertyChanged("EndDate");
+      }
+    }
+
+    [Storable]
+    private string toolTip;
+    public string ToolTip {
+      get { return toolTip; }
+      set {
+        if (toolTip == value) return;
+        toolTip = value;
+        OnPropertyChanged("ToolTip");
+      }
+    }
+
+    [Storable]
+    private string category;
+    public string Category {
+      get { return category; }
+      set {
+        if (category == value) return;
+        category = value;
+        OnPropertyChanged("Category");
+      }
+    }
+
+    [Storable]
+    private bool showLabel;
+    public bool ShowLabel {
+      get { return showLabel; }
+      set {
+        if (showLabel == value) return;
+        showLabel = value;
+        OnPropertyChanged("ShowLabel");
+      }
+    }
+
+    [StorableConstructor]
+    protected GanttItem(bool deserializing) : base(deserializing) { }
+    protected GanttItem(GanttItem original, Cloner cloner)
+      : base(original, cloner) {
+      startDate = original.startDate;
+      endDate = original.endDate;
+      toolTip = original.toolTip;
+      category = original.category;
+      showLabel = original.showLabel;
+    }
+    public GanttItem() : this(DateTime.Now, DateTime.Now) { }
+    public GanttItem(DateTime start, DateTime end) {
+      startDate = start;
+      endDate = end;
+      toolTip = string.Empty;
+      category = string.Empty;
+      showLabel = false;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new GanttItem(this, cloner);
+    }
+
+    public event PropertyChangedEventHandler PropertyChanged;
+    protected virtual void OnPropertyChanged(string propertyName) {
+      var handler = PropertyChanged;
+      if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Analysis/3.3/HeuristicLab.Analysis-3.3.csproj
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Analysis/3.3/HeuristicLab.Analysis-3.3.csproj	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Analysis/3.3/HeuristicLab.Analysis-3.3.csproj	(revision 14751)
@@ -131,4 +131,5 @@
     <Compile Include="AlleleFrequencyAnalysis\AlleleFrequencyCollectionHistory.cs" />
     <Compile Include="BestScopeSolutionAnalyzer.cs" />
+    <Compile Include="DataVisualization\GanttData.cs" />
     <Compile Include="DataVisualization\IndexedDataRow.cs" />
     <Compile Include="DataVisualization\IndexedDataTable.cs" />
Index: /branches/symbreg-factors-2650/HeuristicLab.Clients.Access/3.3/ServiceClients/GenerateServiceClients.cmd
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Clients.Access/3.3/ServiceClients/GenerateServiceClients.cmd	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Clients.Access/3.3/ServiceClients/GenerateServiceClients.cmd	(revision 14751)
@@ -1,30 +1,52 @@
-echo off
+@ECHO OFF
 
-echo.
-echo *******************************************************************************************
-echo Generating AccessService client
-echo.
+SET HOST=
+SET GENERATECONFIG=
 
-svcutil.exe ^
-  http://localhost:81/AccessService?wsdl ^
-  /out:AccessServiceClient ^
-  /namespace:*,HeuristicLab.Clients.Access ^
-  /collectionType:System.Collections.Generic.List`1 ^
-  /targetClientVersion:Version35 ^
-  /serializable ^
-  /enableDataBinding ^
-  /config:..\app.config 
+ECHO.
+ECHO *******************************************************************************************
 
-echo.
-echo ---------------------------------------------------------------------------------------
-echo !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!!
-echo.
-echo Following modifications have to be done manually in generated data contracts:
-echo  * Remove method "protected void RaisePropertyChanged(string propertyName)" in AccessServiceItem
-echo.
-echo !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!!
-echo ---------------------------------------------------------------------------------------
-echo.
-echo Generation of AccessService client finished.
-echo *******************************************************************************************
+SET /P HOST=Which host should be used? [services.heuristiclab.com]: 
+IF "%HOST%"=="" SET HOST=services.heuristiclab.com
 
+SET /P GENERATECONFIG=Would you like to generate the configuration file? [y]: 
+IF "%GENERATECONFIG%"=="" SET GENERATECONFIG=y
+
+SET ARGS=http://%HOST%/AccessService-3.3/AccessService.svc?wsdl ^
+/out:AccessServiceClient ^
+/namespace:*,HeuristicLab.Clients.Access ^
+/collectionType:System.Collections.Generic.List`1 ^
+/targetClientVersion:Version35 ^
+/serializable ^
+/enableDataBinding ^
+/syncOnly
+
+IF "%GENERATECONFIG%"=="y" (
+  SET ARGS=%ARGS% /config:..\app.config /mergeConfig
+) ELSE (
+  SET ARGS=%ARGS% /noConfig
+)
+
+ECHO.
+ECHO Generating AccessService client
+ECHO.
+
+SETLOCAL ENABLEDELAYEDEXPANSION
+svcutil.exe %ARGS%
+ENDLOCAL
+
+ECHO.
+ECHO ---------------------------------------------------------------------------------------
+ECHO !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!!
+ECHO.
+ECHO Following modifications have to be done manually in generated data contracts:
+ECHO  * Remove method "protected void RaisePropertyChanged(string propertyName)" in generated AccessItem
+ECHO.
+ECHO !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!!
+ECHO ---------------------------------------------------------------------------------------
+ECHO.
+ECHO Generation of AccessService client finished.
+ECHO *******************************************************************************************
+ECHO.
+
+PAUSE
Index: /branches/symbreg-factors-2650/HeuristicLab.Clients.Hive/3.3/ServiceClients/GenerateServiceClients.cmd
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Clients.Hive/3.3/ServiceClients/GenerateServiceClients.cmd	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Clients.Hive/3.3/ServiceClients/GenerateServiceClients.cmd	(revision 14751)
@@ -1,30 +1,52 @@
-echo off
+@ECHO OFF
 
-echo.
-echo *******************************************************************************************
-echo Generating HiveService client
-echo.
+SET HOST=
+SET GENERATECONFIG=
 
-svcutil.exe ^
-  http://localhost/Hive-3.3/HiveService.svc?wsdl ^
-  /out:HiveServiceClient ^
-  /namespace:*,HeuristicLab.Clients.Hive ^
-  /collectionType:System.Collections.Generic.List`1 ^
-  /targetClientVersion:Version35 ^
-  /serializable ^
-  /enableDataBinding ^
-  /config:..\app.config 
+ECHO.
+ECHO *******************************************************************************************
 
-echo.
-echo ---------------------------------------------------------------------------------------
-echo !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!!
-echo.
-echo Following modifications have to be done manually in generated data contracts:
-echo  * Remove method "protected void RaisePropertyChanged(string propertyName)" in HiveItem
-echo.
-echo !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!!
-echo ---------------------------------------------------------------------------------------
-echo.
-echo Generation of HiveService client finished.
-echo *******************************************************************************************
+SET /P HOST=Which host should be used? [services.heuristiclab.com]: 
+IF "%HOST%"=="" SET HOST=services.heuristiclab.com
 
+SET /P GENERATECONFIG=Would you like to generate the configuration file? [y]: 
+IF "%GENERATECONFIG%"=="" SET GENERATECONFIG=y
+
+SET ARGS=http://%HOST%/Hive-3.3/HiveService.svc?wsdl ^
+/out:HiveServiceClient ^
+/namespace:*,HeuristicLab.Clients.Hive ^
+/collectionType:System.Collections.Generic.List`1 ^
+/targetClientVersion:Version35 ^
+/serializable ^
+/enableDataBinding ^
+/syncOnly
+
+IF "%GENERATECONFIG%"=="y" (
+  SET ARGS=%ARGS% /config:..\app.config /mergeConfig
+) ELSE (
+  SET ARGS=%ARGS% /noConfig
+)
+
+ECHO.
+ECHO Generating HiveService client
+ECHO.
+
+SETLOCAL ENABLEDELAYEDEXPANSION
+svcutil.exe %ARGS%
+ENDLOCAL
+
+ECHO.
+ECHO ---------------------------------------------------------------------------------------
+ECHO !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!!
+ECHO.
+ECHO Following modifications have to be done manually in generated data contracts:
+ECHO  * Remove method "protected void RaisePropertyChanged(string propertyName)" in generated HiveItem
+ECHO.
+ECHO !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!!
+ECHO ---------------------------------------------------------------------------------------
+ECHO.
+ECHO Generation of HiveService client finished.
+ECHO *******************************************************************************************
+ECHO.
+
+PAUSE
Index: /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/Administration/ServiceClient/GenerateServiceClient.cmd
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/Administration/ServiceClient/GenerateServiceClient.cmd	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/Administration/ServiceClient/GenerateServiceClient.cmd	(revision 14751)
@@ -1,33 +1,51 @@
-echo off
+@ECHO OFF
 
-echo.
-echo *******************************************************************************************
-echo Generating OKB administration service client
-echo.
+SET HOST=
+SET GENERATECONFIG=
 
-REM If app.config should be generated, use option "/config:..\..\app.config" and optionally "/mergeConfig" instead of "/noConfig".
+ECHO.
+ECHO *******************************************************************************************
 
-svcutil.exe ^
-  http://localhost:8732/Design_Time_Addresses/OKB-3.3/AdministrationService/mex ^
-  /out:AdministrationServiceClient ^
-  /namespace:*,HeuristicLab.Clients.OKB.Administration ^
-  /collectionType:System.Collections.Generic.List`1 ^
-  /targetClientVersion:Version35 ^
-  /enableDataBinding ^
-  /noConfig
+SET /P HOST=Which host should be used? [services.heuristiclab.com]: 
+IF "%HOST%"=="" SET HOST=services.heuristiclab.com
 
-echo.
-echo ---------------------------------------------------------------------------------------
-echo !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!!
-echo.
-echo Following modifications have to be done manually in generated data contracts:
-echo  * Remove method "protected void RaisePropertyChanged(string propertyName)" in OKBItem
-echo.
-echo !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!!
-echo ---------------------------------------------------------------------------------------
-echo.
-echo Generation of OKB administration service client finished.
-echo *******************************************************************************************
-echo.
+SET /P GENERATECONFIG=Would you like to generate the configuration file? [y]: 
+IF "%GENERATECONFIG%"=="" SET GENERATECONFIG=y
 
-pause
+SET ARGS=http://%HOST%/OKB-3.3/AdministrationService.svc?wsdl ^
+/out:AdministrationServiceClient ^
+/namespace:*,HeuristicLab.Clients.OKB.Administration ^
+/collectionType:System.Collections.Generic.List`1 ^
+/targetClientVersion:Version35 ^
+/enableDataBinding ^
+/syncOnly
+
+IF "%GENERATECONFIG%"=="y" (
+  SET ARGS=%ARGS% /config:..\..\app.config /mergeConfig
+) ELSE (
+  SET ARGS=%ARGS% /noConfig
+)
+
+ECHO.
+ECHO Generating AdministrationService client
+ECHO.
+
+SETLOCAL ENABLEDELAYEDEXPANSION
+svcutil.exe %ARGS%
+ENDLOCAL
+
+ECHO.
+ECHO ---------------------------------------------------------------------------------------
+ECHO !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!!
+ECHO.
+ECHO Following modifications have to be done manually in generated data contracts:
+ECHO  * Remove method "protected void RaisePropertyChanged(string propertyName)" in generated OKBItem
+ECHO.
+ECHO !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!! ATTENTION !!!
+ECHO ---------------------------------------------------------------------------------------
+ECHO.
+ECHO Generation of AdministrationService client finished.
+ECHO *******************************************************************************************
+ECHO.
+
+PAUSE
Index: /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/HeuristicLab.Clients.OKB-3.3.csproj
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/HeuristicLab.Clients.OKB-3.3.csproj	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/HeuristicLab.Clients.OKB-3.3.csproj	(revision 14751)
@@ -114,4 +114,9 @@
     <Compile Include="Administration\ServiceClient\ProblemClass.cs" />
     <Compile Include="OKBRoles.cs" />
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+      <DependentUpon>Settings.settings</DependentUpon>
+    </Compile>
     <Compile Include="RunCreation\OKBSolution.cs" />
     <Compile Include="Plugin.cs" />
@@ -177,4 +182,8 @@
     <None Include="Properties\AssemblyInfo.cs.frame" />
     <None Include="Administration\ServiceClient\GenerateServiceClient.cmd" />
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
     <None Include="Query\ServiceClient\GenerateServiceClient.cmd" />
     <None Include="RunCreation\ServiceClient\GenerateServiceClient.cmd" />
Index: /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/Properties/Settings.Designer.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/Properties/Settings.Designer.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/Properties/Settings.Designer.cs	(revision 14751)
@@ -0,0 +1,46 @@
+﻿//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace HeuristicLab.Clients.OKB.Properties {
+    
+    
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+        
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+        
+        public static Settings Default {
+            get {
+                return defaultInstance;
+            }
+        }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n<ArrayOfString xmlns:xsi=\"http://www.w3." +
+            "org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\r\n  <s" +
+            "tring>WSHttpBinding_IQueryService</string>\r\n</ArrayOfString>")]
+        public global::System.Collections.Specialized.StringCollection EndpointConfigurationPriorities {
+            get {
+                return ((global::System.Collections.Specialized.StringCollection)(this["EndpointConfigurationPriorities"]));
+            }
+        }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("3")]
+        public int MaxEndpointRetries {
+            get {
+                return ((int)(this["MaxEndpointRetries"]));
+            }
+        }
+    }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/Properties/Settings.settings
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/Properties/Settings.settings	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/Properties/Settings.settings	(revision 14751)
@@ -0,0 +1,15 @@
+﻿<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="HeuristicLab.Clients.OKB.Properties" GeneratedClassName="Settings">
+  <Profiles />
+  <Settings>
+    <Setting Name="EndpointConfigurationPriorities" Type="System.Collections.Specialized.StringCollection" Scope="Application">
+      <Value Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
+&lt;ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
+  &lt;string&gt;WSHttpBinding_IQueryService&lt;/string&gt;
+&lt;/ArrayOfString&gt;</Value>
+    </Setting>
+    <Setting Name="MaxEndpointRetries" Type="System.Int32" Scope="Application">
+      <Value Profile="(Default)">3</Value>
+    </Setting>
+  </Settings>
+</SettingsFile>
Index: /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/Query/QueryClient.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/Query/QueryClient.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/Query/QueryClient.cs	(revision 14751)
@@ -24,4 +24,5 @@
 using System.IO;
 using System.Linq;
+using System.ServiceModel;
 using HeuristicLab.Clients.Common;
 using HeuristicLab.Common;
@@ -39,4 +40,7 @@
       }
     }
+
+    private int endpointRetries;
+    private string workingEndpoint;
 
     #region Properties
@@ -68,5 +72,5 @@
     }
     public void RefreshAsync(Action<Exception> exceptionCallback) {
-      var call = new Func<Exception>(delegate() {
+      var call = new Func<Exception>(delegate () {
         try {
           Refresh();
@@ -76,5 +80,5 @@
         return null;
       });
-      call.BeginInvoke(delegate(IAsyncResult result) {
+      call.BeginInvoke(delegate (IAsyncResult result) {
         Exception ex = call.EndInvoke(result);
         if (ex != null) exceptionCallback(ex);
@@ -156,6 +160,34 @@
 
     #region Helpers
+    private QueryServiceClient NewServiceClient() {
+      if (endpointRetries >= Properties.Settings.Default.MaxEndpointRetries)
+        return CreateClient(workingEndpoint);
+
+      var configurations = Properties.Settings.Default.EndpointConfigurationPriorities;
+      Exception exception = null;
+
+      foreach (var endpointConfigurationName in configurations) {
+        try {
+          var cl = CreateClient(endpointConfigurationName);
+          cl.Open();
+          workingEndpoint = endpointConfigurationName;
+          return cl;
+        } catch (EndpointNotFoundException e) {
+          exception = e;
+          ++endpointRetries;
+        }
+      }
+
+      throw exception ?? new EndpointNotFoundException("No endpoint for Query service found.");
+    }
+
+    private QueryServiceClient CreateClient(string endpointConfigurationName) {
+      var cl = ClientFactory.CreateClient<QueryServiceClient, IQueryService>(endpointConfigurationName);
+      return cl;
+    }
+
     private T CallQueryService<T>(Func<IQueryService, T> call) {
-      QueryServiceClient client = ClientFactory.CreateClient<QueryServiceClient, IQueryService>();
+      QueryServiceClient client = NewServiceClient();
+
       try {
         return call(client);
Index: /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/Query/ServiceClient/GenerateServiceClient.cmd
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/Query/ServiceClient/GenerateServiceClient.cmd	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/Query/ServiceClient/GenerateServiceClient.cmd	(revision 14751)
@@ -1,24 +1,42 @@
-echo off
+@ECHO OFF
 
-echo.
-echo *******************************************************************************************
-echo Generating OKB query service client
-echo.
+SET HOST=
+SET GENERATECONFIG=
 
-REM If app.config should be generated, use option "/config:..\..\app.config" and optionally "/mergeConfig" instead of "/noConfig".
+ECHO.
+ECHO *******************************************************************************************
 
-svcutil.exe ^
-  http://localhost:8732/Design_Time_Addresses/OKB-3.3/QueryService/mex ^
-  /out:QueryServiceClient ^
-  /namespace:*,HeuristicLab.Clients.OKB.Query ^
-  /collectionType:System.Collections.Generic.List`1 ^
-  /targetClientVersion:Version35 ^
-  /enableDataBinding ^
-  /noConfig
+SET /P HOST=Which host should be used? [services.heuristiclab.com]: 
+IF "%HOST%"=="" SET HOST=services.heuristiclab.com
 
-echo.
-echo Generation of OKB query service client finished.
-echo *******************************************************************************************
-echo.
+SET /P GENERATECONFIG=Would you like to generate the configuration file? [y]: 
+IF "%GENERATECONFIG%"=="" SET GENERATECONFIG=y
 
-pause
+SET ARGS=http://%HOST%/OKB-3.3/QueryService.svc ^
+/out:QueryServiceClient ^
+/namespace:*,HeuristicLab.Clients.OKB.Query ^
+/collectionType:System.Collections.Generic.List`1 ^
+/targetClientVersion:Version35 ^
+/enableDataBinding ^
+/syncOnly
+
+IF "%GENERATECONFIG%"=="y" (
+  SET ARGS=%ARGS% /config:..\..\app.config /mergeConfig
+) ELSE (
+  SET ARGS=%ARGS% /noConfig
+)
+
+ECHO.
+ECHO Generating QueryService client
+ECHO.
+
+SETLOCAL ENABLEDELAYEDEXPANSION
+svcutil.exe %ARGS%
+ENDLOCAL
+
+ECHO.
+ECHO Generation of QueryService client finished.
+ECHO *******************************************************************************************
+ECHO.
+
+PAUSE
Index: /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/RunCreation/ServiceClient/GenerateServiceClient.cmd
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/RunCreation/ServiceClient/GenerateServiceClient.cmd	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/RunCreation/ServiceClient/GenerateServiceClient.cmd	(revision 14751)
@@ -1,24 +1,42 @@
-echo off
+@ECHO OFF
 
-echo.
-echo *******************************************************************************************
-echo Generating OKB run creation service client
-echo.
+SET HOST=
+SET GENERATECONFIG=
 
-REM If app.config should be generated, use option "/config:..\..\app.config" and optionally "/mergeConfig" instead of "/noConfig".
+ECHO.
+ECHO *******************************************************************************************
 
-svcutil.exe ^
-  http://localhost:8732/Design_Time_Addresses/OKB-3.3/RunCreationService/mex ^
-  /out:RunCreationServiceClient ^
-  /namespace:*,HeuristicLab.Clients.OKB.RunCreation ^
-  /collectionType:System.Collections.Generic.List`1 ^
-  /targetClientVersion:Version35 ^
-  /enableDataBinding ^
-  /noConfig
+SET /P HOST=Which host should be used? [services.heuristiclab.com]: 
+IF "%HOST%"=="" SET HOST=services.heuristiclab.com
 
-echo.
-echo Generation of OKB run creation service client finished.
-echo *******************************************************************************************
-echo.
+SET /P GENERATECONFIG=Would you like to generate the configuration file? [y]: 
+IF "%GENERATECONFIG%"=="" SET GENERATECONFIG=y
 
-pause
+SET ARGS=http://%HOST%/OKB-3.3/RunCreationService.svc?wsdl ^
+/out:RunCreationServiceClient ^
+/namespace:*,HeuristicLab.Clients.OKB.RunCreation ^
+/collectionType:System.Collections.Generic.List`1 ^
+/targetClientVersion:Version35 ^
+/enableDataBinding ^
+/syncOnly
+
+IF "%GENERATECONFIG%"=="y" (
+  SET ARGS=%ARGS% /config:..\..\app.config /mergeConfig
+) ELSE (
+  SET ARGS=%ARGS% /noConfig
+)
+
+ECHO.
+ECHO Generating RunCreationService client
+ECHO.
+
+SETLOCAL ENABLEDELAYEDEXPANSION
+svcutil.exe %ARGS%
+ENDLOCAL
+
+ECHO.
+ECHO Generation of RunCreationService client finished.
+ECHO *******************************************************************************************
+ECHO.
+
+PAUSE
Index: /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/app.config
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/app.config	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Clients.OKB/3.3/app.config	(revision 14751)
@@ -1,4 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <configuration>
+  <configSections>
+    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
+      <section name="HeuristicLab.Clients.OKB.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
+    </sectionGroup>
+  </configSections>
   <system.serviceModel>
     <behaviors>
@@ -63,3 +68,18 @@
     </client>
   </system.serviceModel>
-<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>
+<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup><applicationSettings>
+    <HeuristicLab.Clients.OKB.Properties.Settings>
+      <setting name="EndpointConfigurationPriorities" serializeAs="Xml">
+        <value>
+          <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+            <string>WSHttpBinding_IQueryService</string>
+          </ArrayOfString>
+        </value>
+      </setting>
+      <setting name="MaxEndpointRetries" serializeAs="String">
+        <value>3</value>
+      </setting>
+    </HeuristicLab.Clients.OKB.Properties.Settings>
+  </applicationSettings>
+</configuration>
Index: /branches/symbreg-factors-2650/HeuristicLab.Core/3.3/Attributes/ItemAttribute.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Core/3.3/Attributes/ItemAttribute.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Core/3.3/Attributes/ItemAttribute.cs	(revision 14751)
@@ -39,12 +39,12 @@
       set { description = value == null ? string.Empty : value; }
     }
+    public bool ExcludeGenericTypeInfo { get; set; }
 
-    public ItemAttribute() {
-      Name = string.Empty;
-      Description = string.Empty;
-    }
-    public ItemAttribute(string name, string description) {
+    public ItemAttribute() : this(string.Empty, string.Empty, false) { }
+    public ItemAttribute(string name, string description) : this(name, description, false) { }
+    public ItemAttribute(string name, string description, bool excludeGenericTypeInfo) {
       Name = name;
       Description = description;
+      ExcludeGenericTypeInfo = excludeGenericTypeInfo;
     }
 
@@ -52,6 +52,7 @@
       object[] attribs = type.GetCustomAttributes(typeof(ItemAttribute), false);
       if (attribs.Length > 0) {
-        string name = ((ItemAttribute)attribs[0]).Name;
-        if (type.IsGenericType) {
+        var attribute = (ItemAttribute)attribs[0];
+        string name = attribute.Name;
+        if (!attribute.ExcludeGenericTypeInfo && type.IsGenericType) {
           name += "<";
           Type[] typeParams = type.GetGenericArguments();
Index: /branches/symbreg-factors-2650/HeuristicLab.Data.Views/3.3/EnumValueView.Designer.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Data.Views/3.3/EnumValueView.Designer.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Data.Views/3.3/EnumValueView.Designer.cs	(revision 14751)
@@ -47,4 +47,6 @@
       this.valueLabel = new System.Windows.Forms.Label();
       this.valueComboBox = new System.Windows.Forms.ComboBox();
+      this.flagsListView = new System.Windows.Forms.ListView();
+      this.columnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
       this.SuspendLayout();
       // 
@@ -54,5 +56,5 @@
       this.valueLabel.Location = new System.Drawing.Point(3, 3);
       this.valueLabel.Name = "valueLabel";
-      this.valueLabel.Size = new System.Drawing.Size(56, 13);
+      this.valueLabel.Size = new System.Drawing.Size(37, 13);
       this.valueLabel.TabIndex = 0;
       this.valueLabel.Text = "&Value:";
@@ -60,5 +62,5 @@
       // valueComboBox
       // 
-      this.valueComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+      this.valueComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.valueComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
@@ -71,8 +73,29 @@
       this.valueComboBox.SelectedIndexChanged += new System.EventHandler(this.valueComboBox_SelectedIndexChanged);
       // 
+      // flagsListView
+      // 
+      this.flagsListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.flagsListView.CheckBoxes = true;
+      this.flagsListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+            this.columnHeader});
+      this.flagsListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None;
+      this.flagsListView.Location = new System.Drawing.Point(65, 0);
+      this.flagsListView.Name = "flagsListView";
+      this.flagsListView.Size = new System.Drawing.Size(212, 21);
+      this.flagsListView.TabIndex = 2;
+      this.flagsListView.UseCompatibleStateImageBehavior = false;
+      this.flagsListView.View = System.Windows.Forms.View.Details;
+      this.flagsListView.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.flagsListView_ItemChecked);
+      // 
+      // columnHeader
+      // 
+      this.columnHeader.Width = 100;
+      // 
       // EnumValueView
       // 
-      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
+      this.Controls.Add(this.flagsListView);
       this.Controls.Add(this.valueComboBox);
       this.Controls.Add(this.valueLabel);
@@ -81,4 +104,5 @@
       this.ResumeLayout(false);
       this.PerformLayout();
+
     }
 
@@ -87,4 +111,6 @@
     private System.Windows.Forms.Label valueLabel;
     private System.Windows.Forms.ComboBox valueComboBox;
+    private System.Windows.Forms.ListView flagsListView;
+    private System.Windows.Forms.ColumnHeader columnHeader;
   }
 }
Index: /branches/symbreg-factors-2650/HeuristicLab.Data.Views/3.3/EnumValueView.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Data.Views/3.3/EnumValueView.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Data.Views/3.3/EnumValueView.cs	(revision 14751)
@@ -21,4 +21,5 @@
 
 using System;
+using System.Windows.Forms;
 using HeuristicLab.Core.Views;
 using HeuristicLab.MainForm;
@@ -44,6 +45,15 @@
     public EnumValueView() {
       InitializeComponent();
+      this.Name = typeof(T).Name + "EnumView";
+
       valueComboBox.DataSource = Enum.GetValues(typeof(T));
-      this.Name = typeof(T).Name + "EnumView";
+      foreach (T flag in Enum.GetValues(typeof(T)))
+        flagsListView.Items.Add(new ListViewItem(flag.ToString()) { Tag = flag });
+      columnHeader.Width = -1;
+
+      bool isFlags = Attribute.IsDefined(typeof(T), typeof(FlagsAttribute));
+      if (isFlags) valueLabel.Text = "Flags:";
+      valueComboBox.Visible = !isFlags;
+      flagsListView.Visible = isFlags;
     }
     public EnumValueView(EnumValue<T> content)
@@ -64,14 +74,26 @@
     protected override void OnContentChanged() {
       base.OnContentChanged();
-      if (Content == null)
-        valueComboBox.Enabled = false;
-      else
+      if (Content == null) {
+        valueComboBox.SelectedIndex = -1;
+        foreach (ListViewItem item in flagsListView.Items)
+          item.Checked = false;
+      } else {
         valueComboBox.SelectedItem = Content.Value;
+        foreach (ListViewItem item in flagsListView.Items) {
+          var flag = (Enum)item.Tag;
+          item.Checked = ((Enum)(object)Content.Value).HasFlag(flag);
+        }
+      }
     }
 
     protected override void SetEnabledStateOfControls() {
       base.SetEnabledStateOfControls();
-      if (Content == null) valueComboBox.Enabled = false;
-      else valueComboBox.Enabled = !ReadOnly;
+      if (Content == null) {
+        valueComboBox.Enabled = false;
+        flagsListView.Enabled = false;
+      } else {
+        valueComboBox.Enabled = !ReadOnly;
+        flagsListView.Enabled = !ReadOnly;
+      }
     }
 
@@ -79,6 +101,11 @@
       if (InvokeRequired)
         Invoke(new EventHandler(Content_ValueChanged), sender, e);
-      else
+      else {
         valueComboBox.SelectedItem = Content.Value;
+        foreach (ListViewItem item in flagsListView.Items) {
+          var flag = (Enum)item.Tag;
+          item.Checked = ((Enum)(object)Content.Value).HasFlag(flag);
+        }
+      }
     }
 
@@ -87,4 +114,10 @@
         Content.Value = (T)valueComboBox.SelectedItem;
     }
+
+    private void flagsListView_ItemChecked(object sender, ItemCheckedEventArgs e) {
+      var flag = (T)e.Item.Tag;
+      if ((Content != null) && !Content.ReadOnly)
+        Content.Value = ((Enum)(object)Content.Value).SetFlag(flag, e.Item.Checked);
+    }
   }
 }
Index: /branches/symbreg-factors-2650/HeuristicLab.Data/3.3/EnumValue.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Data/3.3/EnumValue.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Data/3.3/EnumValue.cs	(revision 14751)
@@ -52,6 +52,4 @@
     private EnumValue(EnumValue<T> original, Cloner cloner)
       : base(original, cloner) {
-      this.value = original.value;
-      this.readOnly = original.readOnly;
     }
 
@@ -64,3 +62,16 @@
     }
   }
+
+  public static class EnumHelper {
+    public static T SetFlag<T>(this Enum value, T flag, bool set) {
+      var baseType = Enum.GetUnderlyingType(value.GetType());
+      dynamic valueAsBase = Convert.ChangeType(value, baseType);
+      dynamic flagAsBase = Convert.ChangeType(flag, baseType);
+      if (set)
+        valueAsBase |= flagAsBase;
+      else
+        valueAsBase &= ~flagAsBase;
+      return (T)valueAsBase;
+    }
+  }
 }
Index: /branches/symbreg-factors-2650/HeuristicLab.Data/3.3/HeuristicLab.Data-3.3.csproj
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Data/3.3/HeuristicLab.Data-3.3.csproj	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Data/3.3/HeuristicLab.Data-3.3.csproj	(revision 14751)
@@ -108,4 +108,5 @@
   </PropertyGroup>
   <ItemGroup>
+    <Reference Include="Microsoft.CSharp" />
     <Reference Include="System" />
     <Reference Include="System.Core">
Index: /branches/symbreg-factors-2650/HeuristicLab.Data/3.3/ValueTypeMatrix.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Data/3.3/ValueTypeMatrix.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Data/3.3/ValueTypeMatrix.cs	(revision 14751)
@@ -201,4 +201,16 @@
     }
 
+    public virtual IEnumerable<T> GetRow(int row) {
+      for (var col = 0; col < Columns; col++) {
+        yield return matrix[row, col];
+      }
+    }
+
+    public virtual IEnumerable<T> GetColumn(int col) {
+      for (var row = 0; row < Rows; row++) {
+        yield return matrix[row, col];
+      }
+    }
+
     public override string ToString() {
       if (matrix.Length == 0) return "[]";
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/BinaryVectorEqualityComparer.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/BinaryVectorEqualityComparer.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/BinaryVectorEqualityComparer.cs	(revision 14751)
@@ -0,0 +1,40 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 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.Collections.Generic;
+using System.Linq;
+
+namespace HeuristicLab.Encodings.BinaryVectorEncoding {
+  public class BinaryVectorEqualityComparer : EqualityComparer<BinaryVector> {
+    public override bool Equals(BinaryVector first, BinaryVector second) {
+      return first.SequenceEqual(second);
+    }
+    public override int GetHashCode(BinaryVector obj) {
+      unchecked {
+        int hash = 17;
+        foreach (var bit in obj) {
+          hash = hash * 29 + (bit ? 1231 : 1237);
+        }
+        return hash;
+      }
+    }
+  }
+}
Index: anches/symbreg-factors-2650/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/HammingSimilarityCalculator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/HammingSimilarityCalculator.cs	(revision 14750)
+++ 	(revision )
@@ -1,66 +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 HeuristicLab.Common;
-using HeuristicLab.Core;
-using HeuristicLab.Optimization.Operators;
-using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
-
-namespace HeuristicLab.Encodings.BinaryVectorEncoding {
-  [Item("Hamming Similarity Calculator for BinaryVector", "Calculates the solution similarity based on the Hamming distance between two binary vectors.")]
-  [StorableClass]
-  public sealed class HammingSimilarityCalculator : SingleObjectiveSolutionSimilarityCalculator {
-    protected override bool IsCommutative {
-      get { return true; }
-    }
-
-    [StorableConstructor]
-    private HammingSimilarityCalculator(bool deserializing) : base(deserializing) { }
-    private HammingSimilarityCalculator(HammingSimilarityCalculator original, Cloner cloner) : base(original, cloner) { }
-    public HammingSimilarityCalculator() { }
-
-    public override IDeepCloneable Clone(Cloner cloner) {
-      return new HammingSimilarityCalculator(this, cloner);
-    }
-
-    public static double CalculateSimilarity(BinaryVector left, BinaryVector right) {
-      if (left == null || right == null)
-        throw new ArgumentException("Cannot calculate similarity because one or both of the provided scopes is null.");
-      if (left.Length != right.Length)
-        throw new ArgumentException("Cannot calculate similarity because the provided solutions have different lengths.");
-      if (left == right) return 1.0;
-
-      double similarity = 0.0;
-      for (int i = 0; i < left.Length; i++)
-        if (left[i] == right[i]) similarity++;
-      return similarity / left.Length;
-
-    }
-
-    public override double CalculateSolutionSimilarity(IScope leftSolution, IScope rightSolution) {
-      var left = leftSolution.Variables[SolutionVariableName].Value as BinaryVector;
-      var right = rightSolution.Variables[SolutionVariableName].Value as BinaryVector;
-
-      return CalculateSimilarity(left, right);
-    }
-  }
-}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/HeuristicLab.Encodings.BinaryVectorEncoding-3.3.csproj
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/HeuristicLab.Encodings.BinaryVectorEncoding-3.3.csproj	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/HeuristicLab.Encodings.BinaryVectorEncoding-3.3.csproj	(revision 14751)
@@ -120,4 +120,5 @@
   <ItemGroup>
     <Compile Include="BinaryVectorEncoding.cs" />
+    <Compile Include="BinaryVectorEqualityComparer.cs" />
     <Compile Include="Creators\RandomBinaryVectorCreator.cs" />
     <Compile Include="Crossovers\MultiBinaryVectorCrossover.cs" />
@@ -126,5 +127,5 @@
     <Compile Include="Crossovers\NPointCrossover.cs" />
     <Compile Include="BinaryVector.cs" />
-    <Compile Include="HammingSimilarityCalculator.cs" />
+    <Compile Include="SimilarityCalculators\HammingSimilarityCalculator.cs" />
     <Compile Include="Interfaces\IBinaryVectorMultiNeighborhoodShakingOperator.cs" />
     <Compile Include="Interfaces\IOneBitflipMoveOperator.cs" />
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/SimilarityCalculators/HammingSimilarityCalculator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/SimilarityCalculators/HammingSimilarityCalculator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/SimilarityCalculators/HammingSimilarityCalculator.cs	(revision 14751)
@@ -0,0 +1,66 @@
+﻿#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 HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Optimization.Operators;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.BinaryVectorEncoding {
+  [Item("Hamming Similarity Calculator for BinaryVector", "Calculates the solution similarity based on the Hamming distance between two binary vectors.")]
+  [StorableClass]
+  public sealed class HammingSimilarityCalculator : SingleObjectiveSolutionSimilarityCalculator {
+    protected override bool IsCommutative {
+      get { return true; }
+    }
+
+    [StorableConstructor]
+    private HammingSimilarityCalculator(bool deserializing) : base(deserializing) { }
+    private HammingSimilarityCalculator(HammingSimilarityCalculator original, Cloner cloner) : base(original, cloner) { }
+    public HammingSimilarityCalculator() { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new HammingSimilarityCalculator(this, cloner);
+    }
+
+    public static double CalculateSimilarity(BinaryVector left, BinaryVector right) {
+      if (left == null || right == null)
+        throw new ArgumentException("Cannot calculate similarity because one or both of the provided scopes is null.");
+      if (left.Length != right.Length)
+        throw new ArgumentException("Cannot calculate similarity because the provided solutions have different lengths.");
+      if (left == right) return 1.0;
+
+      double similarity = 0.0;
+      for (int i = 0; i < left.Length; i++)
+        if (left[i] == right[i]) similarity++;
+      return similarity / left.Length;
+
+    }
+
+    public override double CalculateSolutionSimilarity(IScope leftSolution, IScope rightSolution) {
+      var left = leftSolution.Variables[SolutionVariableName].Value as BinaryVector;
+      var right = rightSolution.Variables[SolutionVariableName].Value as BinaryVector;
+
+      return CalculateSimilarity(left, right);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.IntegerVectorEncoding/3.3/HeuristicLab.Encodings.IntegerVectorEncoding-3.3.csproj
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.IntegerVectorEncoding/3.3/HeuristicLab.Encodings.IntegerVectorEncoding-3.3.csproj	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.IntegerVectorEncoding/3.3/HeuristicLab.Encodings.IntegerVectorEncoding-3.3.csproj	(revision 14751)
@@ -130,4 +130,5 @@
     <Compile Include="Crossovers\RoundedUniformArithmeticCrossover.cs" />
     <Compile Include="IntegerVectorEncoding.cs" />
+    <Compile Include="IntegerVectorEqualityComparer.cs" />
     <Compile Include="IntegerVectorOperator.cs" />
     <Compile Include="Interfaces\IBoundedIntegerVectorOperator.cs" />
@@ -146,4 +147,5 @@
     <Compile Include="Manipulators\RoundedNormalAllPositionsManipulator.cs" />
     <Compile Include="Manipulators\SelfAdaptiveRoundedNormalAllPositionsManipulator.cs" />
+    <Compile Include="SimilarityCalculators\HammingSimilarityCalculator.cs" />
     <Compile Include="StrategyParameters\StdDevStrategyVectorCreator.cs" />
     <Compile Include="StrategyParameters\StdDevStrategyVectorCrossover.cs" />
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.IntegerVectorEncoding/3.3/IntegerVectorEqualityComparer.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.IntegerVectorEncoding/3.3/IntegerVectorEqualityComparer.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.IntegerVectorEncoding/3.3/IntegerVectorEqualityComparer.cs	(revision 14751)
@@ -0,0 +1,44 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 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.Collections.Generic;
+
+namespace HeuristicLab.Encodings.IntegerVectorEncoding {
+  public class IntegerVectorEqualityComparer : EqualityComparer<IntegerVector> {
+    public override bool Equals(IntegerVector x, IntegerVector y) {
+      if (x == null && y == null) return true;
+      if (x == null || y == null) return false;
+      if (x.Length != y.Length) return false;
+      for (var i = 0; i < x.Length; i++)
+        if (x[i] != y[i]) return false;
+      return true;
+    }
+
+    public override int GetHashCode(IntegerVector obj) {
+      unchecked {
+        int hash = 17;
+        foreach (var o in obj)
+          hash = hash * 31 + o.GetHashCode();
+        return hash;
+      }
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.IntegerVectorEncoding/3.3/SimilarityCalculators/HammingSimilarityCalculator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.IntegerVectorEncoding/3.3/SimilarityCalculators/HammingSimilarityCalculator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.IntegerVectorEncoding/3.3/SimilarityCalculators/HammingSimilarityCalculator.cs	(revision 14751)
@@ -0,0 +1,66 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 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 HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Optimization.Operators;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.IntegerVectorEncoding {
+  [Item("Hamming Similarity Calculator for IntegerVector", "Calculates the solution similarity based on the Hamming distance between two integer vectors.")]
+  [StorableClass]
+  public sealed class HammingSimilarityCalculator : SingleObjectiveSolutionSimilarityCalculator {
+    protected override bool IsCommutative {
+      get { return true; }
+    }
+
+    [StorableConstructor]
+    private HammingSimilarityCalculator(bool deserializing) : base(deserializing) { }
+    private HammingSimilarityCalculator(HammingSimilarityCalculator original, Cloner cloner) : base(original, cloner) { }
+    public HammingSimilarityCalculator() { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new HammingSimilarityCalculator(this, cloner);
+    }
+
+    public static double CalculateSimilarity(IntegerVector left, IntegerVector right) {
+      if (left == null || right == null)
+        throw new ArgumentException("Cannot calculate similarity because one or both of the provided scopes is null.");
+      if (left.Length != right.Length)
+        throw new ArgumentException("Cannot calculate similarity because the provided solutions have different lengths.");
+      if (left == right) return 1.0;
+
+      double similarity = 0.0;
+      for (int i = 0; i < left.Length; i++)
+        if (left[i] == right[i]) similarity++;
+      return similarity / left.Length;
+
+    }
+
+    public override double CalculateSolutionSimilarity(IScope leftSolution, IScope rightSolution) {
+      var left = leftSolution.Variables[SolutionVariableName].Value as IntegerVector;
+      var right = rightSolution.Variables[SolutionVariableName].Value as IntegerVector;
+
+      return CalculateSimilarity(left, right);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Creators/ExactGroupsLinearLinkageCreator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Creators/ExactGroupsLinearLinkageCreator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Creators/ExactGroupsLinearLinkageCreator.cs	(revision 14751)
@@ -0,0 +1,72 @@
+﻿#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.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Exactgroups Linear Linkage Creator", "Creates a random linear linkage LLE encoded solution with a given number of equal-sized groups.")]
+  [StorableClass]
+  public sealed class ExactGroupsLinearLinkageCreator : LinearLinkageCreator {
+
+    public IValueLookupParameter<IntValue> ExactGroupsParameter {
+      get { return (IValueLookupParameter<IntValue>)Parameters["ExactGroups"]; }
+    }
+
+    public IntValue ExactGroups {
+      get { return ExactGroupsParameter.Value; }
+      set { ExactGroupsParameter.Value = value; }
+    }
+
+    [StorableConstructor]
+    private ExactGroupsLinearLinkageCreator(bool deserializing) : base(deserializing) { }
+    private ExactGroupsLinearLinkageCreator(ExactGroupsLinearLinkageCreator original, Cloner cloner) : base(original, cloner) { }
+
+    public ExactGroupsLinearLinkageCreator() {
+      Parameters.Add(new ValueLookupParameter<IntValue>("ExactGroups", "The exact number of equal-sized groups to create.", new IntValue(3)));
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new ExactGroupsLinearLinkageCreator(this, cloner);
+    }
+
+    public static LinearLinkage Apply(IRandom random, int length, int groups) {
+      var groupNumbers = Enumerable.Range(0, length).Select(x => x % groups).Shuffle(random);
+      var grouping = Enumerable.Range(0, groups).Select(x => new List<int>()).ToList();
+      var idx = 0;
+      foreach (var g in groupNumbers)
+        grouping[g].Add(idx++);
+
+      return LinearLinkage.FromGroups(length, grouping);
+    }
+
+    protected override LinearLinkage Create(IRandom random, int length) {
+      var groups = ExactGroupsParameter.ActualValue.Value;
+      return Apply(random, length, groups);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Creators/MaxGroupSizeLinearLinkageCreator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Creators/MaxGroupSizeLinearLinkageCreator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Creators/MaxGroupSizeLinearLinkageCreator.cs	(revision 14751)
@@ -0,0 +1,52 @@
+﻿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.Encodings.LinearLinkageEncoding.Creators {
+  [Item("Max Group-size Linear Linkage Creator", "Creates a random linear linkage LLE encoded solution with a given maximum number of items per group.")]
+  [StorableClass]
+  public class MaxGroupSizeLinearLinkageCreator: LinearLinkageCreator {
+
+    public IValueLookupParameter<IntValue> MaxGroupSizeParameter {
+      get { return (IValueLookupParameter<IntValue>)Parameters["MaxGroupSize"]; }
+    }
+
+    public IntValue MaxGroupSize {
+      get { return MaxGroupSizeParameter.Value; }
+      set { MaxGroupSizeParameter.Value = value; }
+    }
+
+    [StorableConstructor]
+    protected MaxGroupSizeLinearLinkageCreator(bool deserializing) : base(deserializing) { }
+    protected MaxGroupSizeLinearLinkageCreator(MaxGroupSizeLinearLinkageCreator original, Cloner cloner) : base(original, cloner) { }
+
+    public MaxGroupSizeLinearLinkageCreator() {
+      Parameters.Add(new ValueLookupParameter<IntValue>("MaxGroupSize", "The maximum number items in every group.", new IntValue(4)));
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new MaxGroupSizeLinearLinkageCreator(this, cloner);
+    }
+
+    public static LinearLinkage Apply(IRandom random, int length, int maxGroupSize) {
+      var groups = Enumerable.Range(0, length).Select(x => new { Item = x, Group = random.Next(length)})
+        .GroupBy(x => x.Group)
+        .SelectMany(x => 
+           x.Select((y, i) => new {SubGroup = i%maxGroupSize, y.Item})
+            .GroupBy(y=>y.SubGroup)
+            .Select(y=>
+              y.Select(z=>z.Item).ToList()));
+      return LinearLinkage.FromGroups(length, groups);
+    }
+
+    protected override LinearLinkage Create(IRandom random, int length) {
+      var maxGroupSize = MaxGroupSizeParameter.ActualValue.Value;
+      return Apply(random, length, maxGroupSize);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Creators/MaxGroupsLinearLinkageCreator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Creators/MaxGroupsLinearLinkageCreator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Creators/MaxGroupsLinearLinkageCreator.cs	(revision 14751)
@@ -0,0 +1,68 @@
+﻿#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.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Maxgroups Linear Linkage Creator", "Creates a random linear linkage LLE encoded solution with a given maximum number of groups.")]
+  [StorableClass]
+  public sealed class MaxGroupsLinearLinkageCreator : LinearLinkageCreator {
+
+    public IValueLookupParameter<IntValue> MaxGroupsParameter {
+      get { return (IValueLookupParameter<IntValue>)Parameters["MaxGroups"]; }
+    }
+
+    public IntValue MaxGroups {
+      get { return MaxGroupsParameter.Value; }
+      set { MaxGroupsParameter.Value = value; }
+    }
+
+    [StorableConstructor]
+    private MaxGroupsLinearLinkageCreator(bool deserializing) : base(deserializing) { }
+    private MaxGroupsLinearLinkageCreator(MaxGroupsLinearLinkageCreator original, Cloner cloner) : base(original, cloner) { }
+
+    public MaxGroupsLinearLinkageCreator() {
+      Parameters.Add(new ValueLookupParameter<IntValue>("MaxGroups", "The maximum number of groups to create.", new IntValue(3)));
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new MaxGroupsLinearLinkageCreator(this, cloner);
+    }
+
+    public static LinearLinkage Apply(IRandom random, int length, int maxGroups) {
+      var groups = Enumerable.Range(0, length).Select(x => Tuple.Create(x, random.Next(maxGroups)))
+                                              .GroupBy(x => x.Item2)
+                                              .Select(x => x.Select(y => y.Item1).ToList());
+      return LinearLinkage.FromGroups(length, groups);
+    }
+
+    protected override LinearLinkage Create(IRandom random, int length) {
+      var maxGroups = MaxGroupsParameter.ActualValue.Value;
+      return Apply(random, length, maxGroups);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Creators/RandomLinearLinkageCreator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Creators/RandomLinearLinkageCreator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Creators/RandomLinearLinkageCreator.cs	(revision 14751)
@@ -0,0 +1,53 @@
+﻿#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.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Random Linear Linkage Creator", "Creates a random linear linkage LLE encoded solution (similar to MaxGroups set to N).")]
+  [StorableClass]
+  public sealed class RandomLinearLinkageCreator : LinearLinkageCreator {
+
+    [StorableConstructor]
+    private RandomLinearLinkageCreator(bool deserializing) : base(deserializing) { }
+    private RandomLinearLinkageCreator(RandomLinearLinkageCreator original, Cloner cloner) : base(original, cloner) { }
+    public RandomLinearLinkageCreator() { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new RandomLinearLinkageCreator(this, cloner);
+    }
+
+    public static LinearLinkage Apply(IRandom random, int length) {
+      var groups = Enumerable.Range(0, length).Select(x => Tuple.Create(x, random.Next(length)))
+                                              .GroupBy(x => x.Item2)
+                                              .Select(x => x.Select(y => y.Item1).ToList());
+      return LinearLinkage.FromGroups(length, groups);
+    }
+
+    protected override LinearLinkage Create(IRandom random, int length) {
+      return Apply(random, length);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/GreedyPartitionCrossover.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/GreedyPartitionCrossover.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/GreedyPartitionCrossover.cs	(revision 14751)
@@ -0,0 +1,76 @@
+﻿#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.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Greedy Partition Crossover", "The Greedy Partition Crossover (GPX) is implemented as described in Ülker, Ö., Özcan, E., Korkmaz, E. E. 2007. Linear linkage encoding in grouping problems: applications on graph coloring and timetabling. In Practice and Theory of Automated Timetabling VI, pp. 347-363. Springer Berlin Heidelberg.")]
+  [StorableClass]
+  public sealed class GreedyPartitionCrossover : LinearLinkageCrossover {
+
+    [StorableConstructor]
+    private GreedyPartitionCrossover(bool deserializing) : base(deserializing) { }
+    private GreedyPartitionCrossover(GreedyPartitionCrossover original, Cloner cloner) : base(original, cloner) { }
+    public GreedyPartitionCrossover() { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new GreedyPartitionCrossover(this, cloner);
+    }
+
+    public static LinearLinkage Apply(IRandom random, ItemArray<LinearLinkage> parents) {
+      var len = parents[0].Length;
+      var childGroup = new List<HashSet<int>>();
+      var currentParent = random.Next(parents.Length);
+      var groups = parents.Select(x => x.GetGroups().Select(y => new HashSet<int>(y)).ToList()).ToList();
+      bool remaining;
+      do {
+        var maxGroup = groups[currentParent].Select((v, i) => Tuple.Create(i, v))
+          .MaxItems(x => x.Item2.Count)
+          .SampleRandom(random).Item1;
+        var group = groups[currentParent][maxGroup];
+        groups[currentParent].RemoveAt(maxGroup);
+        childGroup.Add(group);
+
+        remaining = false;
+        for (var p = 0; p < groups.Count; p++) {
+          for (var j = 0; j < groups[p].Count; j++) {
+            foreach (var elem in group) groups[p][j].Remove(elem);
+            if (!remaining && groups[p][j].Count > 0) remaining = true;
+          }
+        }
+
+        currentParent = (currentParent + 1) % parents.Length;
+      } while (remaining);
+
+      return LinearLinkage.FromGroups(len, childGroup);
+    }
+
+    protected override LinearLinkage Cross(IRandom random, ItemArray<LinearLinkage> parents) {
+      return Apply(random, parents);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/GroupCrossover.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/GroupCrossover.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/GroupCrossover.cs	(revision 14751)
@@ -0,0 +1,85 @@
+﻿#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 HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Group Crossover", "The Group Crossover is implemented as described in Korkmaz, E.E. 2010. Multi-objective Genetic Algorithms for grouping problems. Applied Intelligence 33(2), pp. 179-192.")]
+  [StorableClass]
+  public sealed class GroupCrossover : LinearLinkageCrossover {
+
+    [StorableConstructor]
+    private GroupCrossover(bool deserializing) : base(deserializing) { }
+    private GroupCrossover(GroupCrossover original, Cloner cloner) : base(original, cloner) { }
+    public GroupCrossover() { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new GroupCrossover(this, cloner);
+    }
+    
+    public static LinearLinkage Apply(IRandom random, LinearLinkage p1, LinearLinkage p2) {
+      var length = p1.Length;
+      var lleeP1 = p1.ToEndLinks();
+      var lleeP2 = p2.ToEndLinks();
+      var lleeChild = new int[length];
+      var isTransfered = new bool[length];
+
+      for (var i = p1.Length - 1; i >= 0; i--) {
+        lleeChild[i] = i;
+
+        // Step 1
+        var isEndP1 = p1[i] == i;
+        var isEndP2 = p2[i] == i;
+        if (isEndP1 & isEndP2 || (isEndP1 | isEndP2 && random.NextDouble() < 0.5)) {
+          isTransfered[i] = true;
+          continue;
+        }
+
+        // Step 2
+        var end1 = lleeP1[i];
+        var end2 = lleeP2[i];
+
+        if (isTransfered[end1] & isTransfered[end2]) {
+          var end = random.NextDouble() < 0.5 ? end1 : end2;
+          lleeChild[i] = end;
+        } else if (isTransfered[end1]) {
+          lleeChild[i] = end1;
+        } else if (isTransfered[end2]) {
+          lleeChild[i] = end2;
+        } else {
+          var next = random.NextDouble() < 0.5 ? p1[i] : p2[i];
+          var end = lleeChild[next];
+          lleeChild[i] = end;
+        }
+      }
+      return LinearLinkage.FromEndLinks(lleeChild);
+    }
+
+    protected override LinearLinkage Cross(IRandom random, ItemArray<LinearLinkage> parents) {
+      if (parents.Length != 2) throw new InvalidOperationException(Name + ": Can only cross exactly two parents.");
+      return Apply(random, parents[0], parents[1]);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/LowestIndexFirstCrossover.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/LowestIndexFirstCrossover.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/LowestIndexFirstCrossover.cs	(revision 14751)
@@ -0,0 +1,68 @@
+﻿#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.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Lowest Index First Crossover", "The Lowest Index First Crossover (LIFX) is implemented as described in Ülker, Ö., Özcan, E., Korkmaz, E. E. 2007. Linear linkage encoding in grouping problems: applications on graph coloring and timetabling. In Practice and Theory of Automated Timetabling VI, pp. 347-363. Springer Berlin Heidelberg.")]
+  [StorableClass]
+  public sealed class LowestIndexFirstCrossover : LinearLinkageCrossover {
+
+    [StorableConstructor]
+    private LowestIndexFirstCrossover(bool deserializing) : base(deserializing) { }
+    private LowestIndexFirstCrossover(LowestIndexFirstCrossover original, Cloner cloner) : base(original, cloner) { }
+    public LowestIndexFirstCrossover() { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new LowestIndexFirstCrossover(this, cloner);
+    }
+
+    public static LinearLinkage Apply(IRandom random, ItemArray<LinearLinkage> parents) {
+      var len = parents[0].Length;
+      var p = random.Next(parents.Length);
+      var child = LinearLinkage.SingleElementGroups(len);
+      var remaining = new SortedSet<int>(Enumerable.Range(0, len));
+      do {
+        var i = remaining.Min;
+        foreach (var g in parents[p].GetGroupForward(i)) {
+          if (!remaining.Contains(g)) continue;
+          child[i] = g;
+          i = g;
+          remaining.Remove(g);
+        }
+        child[i] = i;
+        remaining.Remove(i);
+
+        p = (p + 1) % parents.Length;
+      } while (remaining.Count > 0);
+
+      return child;
+    }
+
+    protected override LinearLinkage Cross(IRandom random, ItemArray<LinearLinkage> parents) {
+      return Apply(random, parents);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/LowestIndexMaxCrossover.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/LowestIndexMaxCrossover.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/LowestIndexMaxCrossover.cs	(revision 14751)
@@ -0,0 +1,68 @@
+﻿#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.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Lowest Index Max Crossover", "The Lowest Index Max Crossover (LIMX) is implemented as described in Ülker, Ö., Özcan, E., Korkmaz, E. E. 2007. Linear linkage encoding in grouping problems: applications on graph coloring and timetabling. In Practice and Theory of Automated Timetabling VI, pp. 347-363. Springer Berlin Heidelberg.")]
+  [StorableClass]
+  public sealed class LowestIndexMaxCrossover : LinearLinkageCrossover {
+
+    [StorableConstructor]
+    private LowestIndexMaxCrossover(bool deserializing) : base(deserializing) { }
+    private LowestIndexMaxCrossover(LowestIndexMaxCrossover original, Cloner cloner) : base(original, cloner) { }
+    public LowestIndexMaxCrossover() { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new LowestIndexMaxCrossover(this, cloner);
+    }
+
+    public static LinearLinkage Apply(IRandom random, ItemArray<LinearLinkage> parents) {
+      var len = parents[0].Length;
+      var child = LinearLinkage.SingleElementGroups(len);
+      var remaining = new SortedSet<int>(Enumerable.Range(0, len));
+      do {
+        var groups = parents.Select(x => x.GetGroupForward(remaining.Min).Where(y => remaining.Contains(y)).ToList()).ToList();
+        var max = groups.Select((v, idx) => Tuple.Create(idx, v.Count)).MaxItems(x => x.Item2).SampleRandom(random).Item1;
+        var i = groups[max][0];
+        for (var k = 1; k < groups[max].Count; k++) {
+          child[i] = groups[max][k];
+          remaining.Remove(i);
+          i = child[i];
+        }
+        child[i] = i;
+        remaining.Remove(i);
+      } while (remaining.Count > 0);
+
+      return child;
+    }
+
+    protected override LinearLinkage Cross(IRandom random, ItemArray<LinearLinkage> parents) {
+      return Apply(random, parents);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/MultiLLECrossover.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/MultiLLECrossover.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/MultiLLECrossover.cs	(revision 14751)
@@ -0,0 +1,100 @@
+﻿#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.Linq;
+using System.Reflection;
+using HeuristicLab.Collections;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Operators;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.PluginInfrastructure;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Multi LLE Crossover", "Randomly selects and applies one of its crossovers every time it is called.")]
+  [StorableClass]
+  public class MultiLinearLinkageCrossover : StochasticMultiBranch<ILinearLinkageCrossover>, ILinearLinkageCrossover, IStochasticOperator {
+    public override bool CanChangeName {
+      get { return false; }
+    }
+    protected override bool CreateChildOperation {
+      get { return true; }
+    }
+
+    public IScopeTreeLookupParameter<LinearLinkage> ParentsParameter {
+      get { return (IScopeTreeLookupParameter<LinearLinkage>)Parameters["Parents"]; }
+    }
+
+    public ILookupParameter<LinearLinkage> ChildParameter {
+      get { return (ILookupParameter<LinearLinkage>)Parameters["Child"]; }
+    }
+
+    [StorableConstructor]
+    protected MultiLinearLinkageCrossover(bool deserializing) : base(deserializing) { }
+    protected MultiLinearLinkageCrossover(MultiLinearLinkageCrossover original, Cloner cloner) : base(original, cloner) { }
+    public MultiLinearLinkageCrossover()
+      : base() {
+      Parameters.Add(new ScopeTreeLookupParameter<LinearLinkage>("Parents", "The parent LLE which should be crossed."));
+      ParentsParameter.ActualName = "LLE";
+      Parameters.Add(new LookupParameter<LinearLinkage>("Child", "The child LLE resulting from the crossover."));
+      ChildParameter.ActualName = "LLE";
+
+      foreach (Type type in ApplicationManager.Manager.GetTypes(typeof(ILinearLinkageCrossover), typeof(LinearLinkageEncoding).Assembly)) {
+        if (!typeof(MultiOperator<ILinearLinkageCrossover>).IsAssignableFrom(type))
+          Operators.Add((ILinearLinkageCrossover)Activator.CreateInstance(type), true);
+      }
+
+      SelectedOperatorParameter.ActualName = "SelectedCrossoverOperator";
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new MultiLinearLinkageCrossover(this, cloner);
+    }
+
+    protected override void Operators_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<ILinearLinkageCrossover>> e) {
+      base.Operators_ItemsReplaced(sender, e);
+      ParameterizeCrossovers(e.Items);
+    }
+
+    protected override void Operators_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<ILinearLinkageCrossover>> e) {
+      base.Operators_ItemsAdded(sender, e);
+      ParameterizeCrossovers(e.Items);
+    }
+
+    private void ParameterizeCrossovers(IEnumerable<IndexedItem<ILinearLinkageCrossover>> crossovers) {
+      foreach (var c in crossovers.Select(x => x.Value)) {
+        c.ChildParameter.ActualName = ChildParameter.Name;
+        c.ParentsParameter.ActualName = ParentsParameter.Name;
+        var stOp = c as IStochasticOperator;
+        if (stOp != null) stOp.RandomParameter.ActualName = RandomParameter.Name;
+      }
+    }
+
+    public override IOperation InstrumentedApply() {
+      if (Operators.Count == 0) throw new InvalidOperationException(Name + ": Please add at least one LLE crossover to choose from.");
+      return base.InstrumentedApply();
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/SinglePointCrossover.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/SinglePointCrossover.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Crossovers/SinglePointCrossover.cs	(revision 14751)
@@ -0,0 +1,56 @@
+﻿#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 HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Single Point Crossover", "The single point crossover concatenates two linkage strings at a randomly chosen point.")]
+  [StorableClass]
+  public sealed class SinglePointCrossover : LinearLinkageCrossover {
+
+    [StorableConstructor]
+    private SinglePointCrossover(bool deserializing) : base(deserializing) { }
+    private SinglePointCrossover(SinglePointCrossover original, Cloner cloner) : base(original, cloner) { }
+    public SinglePointCrossover() { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new SinglePointCrossover(this, cloner);
+    }
+
+    public static LinearLinkage Apply(IRandom random, LinearLinkage p1, LinearLinkage p2) {
+      var length = p1.Length;
+      var child = LinearLinkage.SingleElementGroups(length);
+      var bp = random.Next(length - 1);
+      for (var i = 0; i <= bp; i++) child[i] = p1[i];
+      for (var i = bp + 1; i < length; i++) child[i] = p2[i];
+      child.LinearizeTreeStructures();
+      return child;
+    }
+
+    protected override LinearLinkage Cross(IRandom random, ItemArray<LinearLinkage> parents) {
+      if (parents.Length != 2) throw new InvalidOperationException(Name + ": Can only cross exactly two parents.");
+      return Apply(random, parents[0], parents[1]);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/HeuristicLab.Encodings.LinearLinkageEncoding-3.4.csproj
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/HeuristicLab.Encodings.LinearLinkageEncoding-3.4.csproj	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/HeuristicLab.Encodings.LinearLinkageEncoding-3.4.csproj	(revision 14751)
@@ -0,0 +1,229 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{BE698769-975A-429E-828C-72BB2B6182C8}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>HeuristicLab.Encodings.LinearLinkageEncoding</RootNamespace>
+    <AssemblyName>HeuristicLab.Encodings.LinearLinkageEncoding-3.4</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
+    <DocumentationFile>
+    </DocumentationFile>
+    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <DocumentationFile>
+    </DocumentationFile>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
+    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <DocumentationFile>
+    </DocumentationFile>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup>
+    <SignAssembly>true</SignAssembly>
+  </PropertyGroup>
+  <PropertyGroup>
+    <AssemblyOriginatorKeyFile>HeuristicLab.snk</AssemblyOriginatorKeyFile>
+  </PropertyGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\HeuristicLab.Collections\3.3\HeuristicLab.Collections-3.3.csproj">
+      <Project>{958B43BC-CC5C-4FA2-8628-2B3B01D890B6}</Project>
+      <Name>HeuristicLab.Collections-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.Common\3.3\HeuristicLab.Common-3.3.csproj">
+      <Project>{A9AD58B9-3EF9-4CC1-97E5-8D909039FF5C}</Project>
+      <Name>HeuristicLab.Common-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.Core\3.3\HeuristicLab.Core-3.3.csproj">
+      <Project>{C36BD924-A541-4A00-AFA8-41701378DDC5}</Project>
+      <Name>HeuristicLab.Core-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.Data\3.3\HeuristicLab.Data-3.3.csproj">
+      <Project>{BBAB9DF5-5EF3-4BA8-ADE9-B36E82114937}</Project>
+      <Name>HeuristicLab.Data-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.Operators\3.3\HeuristicLab.Operators-3.3.csproj">
+      <Project>{23DA7FF4-D5B8-41B6-AA96-F0561D24F3EE}</Project>
+      <Name>HeuristicLab.Operators-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.Optimization.Operators\3.3\HeuristicLab.Optimization.Operators-3.3.csproj">
+      <Project>{25087811-F74C-4128-BC86-8324271DA13E}</Project>
+      <Name>HeuristicLab.Optimization.Operators-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.Optimization\3.3\HeuristicLab.Optimization-3.3.csproj">
+      <Project>{14AB8D24-25BC-400C-A846-4627AA945192}</Project>
+      <Name>HeuristicLab.Optimization-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.Parameters\3.3\HeuristicLab.Parameters-3.3.csproj">
+      <Project>{56F9106A-079F-4C61-92F6-86A84C2D84B7}</Project>
+      <Name>HeuristicLab.Parameters-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.Persistence\3.3\HeuristicLab.Persistence-3.3.csproj">
+      <Project>{102BC7D3-0EF9-439C-8F6D-96FF0FDB8E1B}</Project>
+      <Name>HeuristicLab.Persistence-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.PluginInfrastructure\3.3\HeuristicLab.PluginInfrastructure-3.3.csproj">
+      <Project>{94186A6A-5176-4402-AE83-886557B53CCA}</Project>
+      <Name>HeuristicLab.PluginInfrastructure-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.Random\3.3\HeuristicLab.Random-3.3.csproj">
+      <Project>{f4539fb6-4708-40c9-be64-0a1390aea197}</Project>
+      <Name>HeuristicLab.Random-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Creators\ExactGroupsLinearLinkageCreator.cs" />
+    <Compile Include="Creators\MaxGroupSizeLinearLinkageCreator.cs" />
+    <Compile Include="Creators\MaxGroupsLinearLinkageCreator.cs" />
+    <Compile Include="Creators\RandomLinearLinkageCreator.cs" />
+    <Compile Include="Crossovers\GreedyPartitionCrossover.cs" />
+    <Compile Include="Crossovers\GroupCrossover.cs" />
+    <Compile Include="Crossovers\LowestIndexMaxCrossover.cs" />
+    <Compile Include="Crossovers\MultiLLECrossover.cs" />
+    <Compile Include="Crossovers\SinglePointCrossover.cs" />
+    <Compile Include="Interfaces\ILinearLinkageMoveOperator.cs" />
+    <Compile Include="Interfaces\ILinearLinkageSwap2MoveOperator.cs" />
+    <Compile Include="Interfaces\ILinearLinkageCreator.cs" />
+    <Compile Include="LinearLinkage.cs" />
+    <Compile Include="LinearLinkageCreator.cs" />
+    <Compile Include="LinearLinkageEncoding.cs" />
+    <Compile Include="LinearLinkageEqualityComparer.cs" />
+    <Compile Include="LinearLinkageManipulator.cs" />
+    <Compile Include="LinearLinkageCrossover.cs" />
+    <Compile Include="Manipulators\GraftManipulator.cs" />
+    <Compile Include="Manipulators\MoveItemManipulator.cs" />
+    <Compile Include="Manipulators\MergeGroupManipulator.cs" />
+    <Compile Include="Manipulators\MultiLLEManipulator.cs" />
+    <Compile Include="Moves\ExtractMove.cs" />
+    <Compile Include="Moves\MergeMove.cs" />
+    <Compile Include="Moves\Move.cs" />
+    <Compile Include="Moves\ShiftMove.cs" />
+    <Compile Include="Moves\SplitMove.cs" />
+    <Compile Include="Moves\StaticAPI\MoveGenerator.cs" />
+    <Compile Include="Moves\Swap\ExhaustiveSwap2MoveGenerator.cs" />
+    <Compile Include="Moves\Swap\StochasticSwap2MultiMoveGenerator.cs" />
+    <Compile Include="Moves\Swap\StochasticSwap2SingleMoveGenerator.cs" />
+    <Compile Include="Moves\Swap\Swap2Move.cs" />
+    <Compile Include="Moves\Swap\Swap2MoveGenerator.cs" />
+    <Compile Include="Moves\Swap\Swap2MoveMaker.cs" />
+    <Compile Include="ShakingOperators\LLEShakingOperator.cs" />
+    <Compile Include="SimilarityCalculators\HammingSimilarityCalculator.cs" />
+    <None Include="HeuristicLab.snk" />
+    <None Include="Plugin.cs.frame" />
+    <Compile Include="Crossovers\LowestIndexFirstCrossover.cs" />
+    <Compile Include="Interfaces\ILinearLinkageCrossover.cs" />
+    <Compile Include="Interfaces\ILinearLinkageManipulator.cs" />
+    <Compile Include="Interfaces\ILinearLinkageOperator.cs" />
+    <Compile Include="Interfaces\ILinearLinkageShakingOperator.cs" />
+    <Compile Include="Manipulators\SplitGroupManipulator.cs" />
+    <Compile Include="Manipulators\SwapItemManipulator.cs" />
+    <Compile Include="Plugin.cs" />
+    <None Include="Properties\AssemblyInfo.cs.frame" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <PropertyGroup>
+    <PreBuildEvent Condition=" '$(OS)' == 'Windows_NT' ">set Path=%25Path%25;$(ProjectDir);$(SolutionDir)
+set ProjectDir=$(ProjectDir)
+set SolutionDir=$(SolutionDir)
+set Outdir=$(Outdir)
+
+call PreBuildEvent.cmd
+    </PreBuildEvent>
+    <PreBuildEvent Condition=" '$(OS)' != 'Windows_NT' ">
+export ProjectDir=$(ProjectDir)
+export SolutionDir=$(SolutionDir)
+
+$SolutionDir/PreBuildEvent.sh
+    </PreBuildEvent>
+  </PropertyGroup>
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageCreator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageCreator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageCreator.cs	(revision 14751)
@@ -0,0 +1,31 @@
+﻿#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 HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Optimization;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  public interface ILinearLinkageCreator : ISolutionCreator, ILinearLinkageOperator {
+    IValueLookupParameter<IntValue> LengthParameter { get; }
+    ILookupParameter<LinearLinkage> LLEParameter { get; }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageCrossover.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageCrossover.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageCrossover.cs	(revision 14751)
@@ -0,0 +1,30 @@
+﻿#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 HeuristicLab.Core;
+using HeuristicLab.Optimization;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  public interface ILinearLinkageCrossover : ICrossover, ILinearLinkageOperator {
+    ILookupParameter<LinearLinkage> ChildParameter { get; }
+    IScopeTreeLookupParameter<LinearLinkage> ParentsParameter { get; }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageManipulator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageManipulator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageManipulator.cs	(revision 14751)
@@ -0,0 +1,29 @@
+﻿#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 HeuristicLab.Core;
+using HeuristicLab.Optimization;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  public interface ILinearLinkageManipulator : IManipulator, ILinearLinkageOperator {
+    ILookupParameter<LinearLinkage> LLEParameter { get; }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageMoveOperator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageMoveOperator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageMoveOperator.cs	(revision 14751)
@@ -0,0 +1,36 @@
+﻿#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 HeuristicLab.Core;
+using HeuristicLab.Optimization;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  /// <summary>
+  /// Move operators can be extended by deriving a new interface from this interface
+  /// and implementing that derived interface in all move operators that belong to
+  /// the same group of move operators.
+  /// 
+  /// See <see cref="ILinearLinkageSwap2MoveOperator"/> for a reference implementation.
+  /// </summary>
+  public interface ILinearLinkageMoveOperator : IMoveOperator, ILinearLinkageOperator {
+    ILookupParameter<LinearLinkage> LLEParameter { get; }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageOperator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageOperator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageOperator.cs	(revision 14751)
@@ -0,0 +1,27 @@
+﻿#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 HeuristicLab.Core;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  public interface ILinearLinkageOperator : IOperator {
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageShakingOperator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageShakingOperator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageShakingOperator.cs	(revision 14751)
@@ -0,0 +1,29 @@
+﻿#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 HeuristicLab.Core;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  public interface ILinearLinkageShakingOperator : ILinearLinkageOperator {
+    ILookupParameter<LinearLinkage> LLEParameter { get; }
+    ICheckedItemList<ILinearLinkageManipulator> Shakers { get; }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageSwap2MoveOperator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageSwap2MoveOperator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Interfaces/ILinearLinkageSwap2MoveOperator.cs	(revision 14751)
@@ -0,0 +1,28 @@
+﻿#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 HeuristicLab.Core;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  public interface ILinearLinkageSwap2MoveOperator : ILinearLinkageMoveOperator {
+    ILookupParameter<Swap2Move> Swap2MoveParameter { get; }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkage.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkage.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkage.cs	(revision 14751)
@@ -0,0 +1,409 @@
+﻿#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.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("LinearLinkage", "Represents an LLE grouping of items.")]
+  [StorableClass]
+  public sealed class LinearLinkage : IntArray {
+
+    [StorableConstructor]
+    private LinearLinkage(bool deserializing) : base(deserializing) { }
+    private LinearLinkage(LinearLinkage original, Cloner cloner) : base(original, cloner) { }
+    public LinearLinkage() { }
+
+    private LinearLinkage(int length) : base(length) { }
+    private LinearLinkage(int[] elements) : base(elements) { }
+
+    /// <summary>
+    /// Create a new LinearLinkage object where every element is in a seperate group.
+    /// </summary>
+    public static LinearLinkage SingleElementGroups(int length) {
+      var elements = new int[length];
+      for (var i = 0; i < length; i++) {
+        elements[i] = i;
+      }
+      return new LinearLinkage(elements);
+    }
+
+    /// <summary>
+    /// Create a new LinearLinkage object from an int[] in LLE
+    /// </summary>
+    /// <remarks>
+    /// This operation checks if the argument is a well formed LLE
+    /// and throws an ArgumentException otherwise.
+    /// </remarks>
+    /// <exception cref="ArgumentException">If <paramref name="lle"/> does not represent a valid LLE array.</exception>
+    /// <param name="lle">The LLE representation</param>
+    /// <returns>The linear linkage encoding in LLE format (with forward-links).</returns>
+    public static LinearLinkage FromForwardLinks(int[] lle) {
+      if (!Validate(lle))
+        throw new ArgumentException("Array is malformed and does not represent a valid LLE forward encoding.", "elements");
+      return new LinearLinkage(lle);
+    }
+
+    /// <summary>
+    /// Create a new LinearLinkage object by parsing a LLE-b representation
+    /// and modifing the underlying array so that it is in LLE representation.
+    /// </summary>
+    /// <remarks>
+    /// This operation runs in O(n) time, the parameter <paramref name="lleb"/> is not modified.
+    /// </remarks>
+    /// <exception cref="ArgumentException">If <paramref name="lleb"/> does not represent a valid LLE-b array.</exception>
+    /// <param name="lleb">The LLE-b representation (LLE with back-links)</param>
+    /// <returns>The linear linkage encoding in LLE format (with forward-links).</returns>
+    public static LinearLinkage FromBackLinks(int[] lleb) {
+      var result = new LinearLinkage(lleb.Length);
+      for (var i = lleb.Length - 1; i > 0; i--) {
+        if (lleb[i] == i) {
+          if (result[i] == 0) result[i] = i;
+          continue;
+        }
+        result[lleb[i]] = i;
+        if (result[i] == 0) result[i] = i;
+      }
+      if (!Validate(result.array))
+        throw new ArgumentException("Array is malformed and does not represent a valid LLE-b encoding (with back-links).", "lleb");
+      return result;
+    }
+
+    /// <summary>
+    /// Create a new LinearLinkage object by parsing an LLE-e representation
+    /// and modifing the underlying array so that it is in LLE representation.
+    /// </summary>
+    /// <remarks>
+    /// This operation runs in O(n) time, but requires additional memory
+    /// in form of a int[].
+    /// </remarks>
+    /// <param name="llee">The LLE-e representation</param>
+    /// <returns>The linear linkage encoding in LLE format (with forward-links).</returns>
+    public static LinearLinkage FromEndLinks(int[] llee) {
+      var result = new LinearLinkage(llee.Length);
+      result.SetEndLinks(llee);
+      return result;
+    }
+
+    /// <summary>
+    /// Create a new LinearLinkage object by translating
+    /// an enumeration of groups into the underlying array representation.
+    /// </summary>
+    /// <remarks>
+    /// Throws an ArgumentException when there is an element assigned to
+    /// multiple groups or elements that are not assigned to any group.
+    /// </remarks>
+    /// <param name="grouping">The grouping of the elements, each element must
+    /// be part of exactly one group.</param>
+    public static LinearLinkage FromGroups(int length, IEnumerable<IEnumerable<int>> grouping) {
+      var result = new LinearLinkage(length);
+      result.SetGroups(grouping);
+      return result;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new LinearLinkage(this, cloner);
+    }
+
+    /// <summary>
+    /// This method parses the encoded array and calculates the membership of
+    /// each element to the groups. It starts at the lowest element.
+    /// </summary>
+    /// <remarks>
+    /// Runtime complexity of this method is O(n) where n is the length of the
+    /// array.
+    /// </remarks>
+    /// <exception cref="InvalidOperationException">If this object is not vaild LLE.</exception>
+    /// <returns>An enumeration of all groups.</returns>
+    public IEnumerable<List<int>> GetGroups() {
+      var len = array.Length;
+      var used = new bool[len];
+      for (var i = 0; i < len; i++) {
+        if (used[i]) continue;
+        var curr = i;
+        var next = array[curr];
+        var group = new List<int> { curr };
+        while (next > curr && next < len && !used[next]) {
+          used[curr] = true;
+          curr = next;
+          next = array[next];
+          group.Add(curr);
+        }
+        if (curr != next) throw new InvalidOperationException("Array is malformed and does not represent a valid LLE forward encoding.");
+        used[curr] = true;
+        yield return group;
+      }
+    }
+
+    /// <summary>
+    /// This method parses the encoded array and gathers all elements
+    /// that belong to the same group as element <paramref name="index"/>.
+    /// </summary>
+    /// <param name="index">The element whose group should be returned.
+    /// </param>
+    /// <returns>The element at <paramref name="index"/> and all other
+    /// elements in the same group.</returns>
+    public IEnumerable<int> GetGroup(int index) {
+      foreach (var n in GetGroupForward(index))
+        yield return n;
+      // the element index has already been yielded
+      foreach (var n in GetGroupBackward(index).Skip(1))
+        yield return n;
+    }
+
+    /// <summary>
+    /// This method parses the encoded array and gathers the element
+    /// <paramref name="index"/> as well as subsequent elements that
+    /// belong to the same group.
+    /// </summary>
+    /// <param name="index">The element from which subsequent (having a
+    /// larger number) elements in the group should be returned.
+    /// </param>
+    /// <returns>The element <paramref name="index"/> and all subsequent
+    /// elements in the same group.</returns>
+    public IEnumerable<int> GetGroupForward(int index) {
+      yield return index;
+      var next = array[index];
+      if (next == index) yield break;
+      int prev;
+      do {
+        yield return next;
+        prev = next;
+        next = array[next];
+      } while (next != prev);
+    }
+
+    /// <summary>
+    /// This method parses the encoded array and gathers the element
+    /// given <paramref name="index"/> as well as preceeding elements that
+    /// belong to the same group.
+    /// </summary>
+    /// <remarks>
+    /// Warning, this code has performance O(index) as the array has to
+    /// be fully traversed backwards from the given index.
+    /// </remarks>
+    /// <param name="index">The element from which preceeding (having a
+    /// smaller number) elements in the group should be returned.
+    /// </param>
+    /// <returns>The element <paramref name="index"/> and all preceeding
+    /// elements in the same group.</returns>
+    public IEnumerable<int> GetGroupBackward(int index) {
+      yield return index;
+      var next = array[index];
+      // return preceding elements in group
+      for (var prev = index - 1; prev >= 0; prev--) {
+        if (array[prev] != next) continue;
+        next = prev;
+        yield return next;
+      }
+    }
+
+    /// <summary>
+    /// This method translates an enumeration of groups into the underlying
+    /// array representation.
+    /// </summary>
+    /// <remarks>
+    /// Throws an ArgumentException when there is an element assigned to
+    /// multiple groups or elements that are not assigned to any group.
+    /// </remarks>
+    /// <param name="grouping">The grouping of the elements, each element must
+    /// be part of exactly one group.</param>
+    /// <exception cref="ArgumentException">If <paramref name="grouping"/> cannot be converted
+    /// to a valid LLE representation. For instance, because elements are too big or too small,
+    /// or they're contained in multiple groups, or there are elements not assigned to any group.
+    /// </exception>
+    public void SetGroups(IEnumerable<IEnumerable<int>> grouping) {
+      var len = array.Length;
+      var used = new bool[len];
+      foreach (var group in grouping) {
+        var prev = -1;
+        foreach (var g in group.OrderBy(x => x)) {
+          if (g < prev || g >= len) throw new ArgumentException(string.Format("Element {0} is bigger than {1} or smaller than 0.", g, len - 1), "grouping");
+          if (prev >= 0) array[prev] = g;
+          prev = g;
+          if (used[prev]) {
+            throw new ArgumentException(string.Format("Element {0} is contained at least twice.", prev), "grouping");
+          }
+          used[prev] = true;
+        }
+        array[prev] = prev;
+      }
+      if (!used.All(x => x))
+        throw new ArgumentException(string.Format("Elements are not assigned a group: {0}", string.Join(", ", used.Select((x, i) => new { x, i }).Where(x => !x.x).Select(x => x.i))));
+    }
+
+    /// <summary>
+    /// Performs a check whether the array represents a valid LLE encoding.
+    /// </summary>
+    /// <remarks>
+    /// The runtime complexity of this method is O(n) where n is the length of
+    /// the array.
+    /// </remarks>
+    /// <returns>True if the encoding is valid.</returns>
+    public bool Validate() {
+      return Validate(array);
+    }
+
+    private static bool Validate(int[] array) {
+      var len = array.Length;
+      var used = new bool[len];
+      for (var i = 0; i < len; i++) {
+        if (used[i]) continue;
+        var curr = i;
+        var next = array[curr];
+        while (next > curr && next < len && !used[next]) {
+          used[curr] = true;
+          curr = next;
+          next = array[next];
+        }
+        if (curr != next) return false;
+        used[curr] = true;
+      }
+      return true;
+    }
+
+    /// <summary>
+    /// This method flattens tree structures that may be present in groups.
+    /// These tree structures may be created by e.g. merging two groups by
+    /// linking one end node to the end node of another.
+    /// Consider following array: 5, 5, 6, 4, 4, 7, 7, 7, 8.
+    /// This results in the following tree structure for group 7:
+    ///      7
+    ///    /   \
+    ///   5     6
+    ///  / \    |
+    /// 0   1   2
+    /// After this operation the array will be 1, 2, 5, 4, 4, 6, 7, 7, 8.
+    /// Representing a tree with one branch: 0 -> 1 -> 2 -> 5 -> 6 -> 7.
+    /// </summary>
+    /// <remarks>
+    /// The method first converts the array to LLE-e format and then
+    /// linearizes the links. This requires two passes of the whole array
+    /// as well as another copy of the underlying array.
+    /// The runtime complexity is O(n).
+    /// 
+    /// The method assumes that there are no back links present.
+    /// </remarks>
+    public void LinearizeTreeStructures() {
+      // Step 1: Convert the array into LLE-e
+      ToEndLinksInplace(array);
+      // Step 2: For all groups linearize the links
+      SetEndLinks(array);
+    }
+
+    /// <summary>
+    /// Creates a copy of the underlying array and turns it into LLE-e.
+    /// </summary>
+    /// <remarks>
+    /// LLE-e is a special format where each element points to the
+    /// ending item of a group.
+    /// The LLE representation 1, 2, 4, 5, 4, 6, 7, 7 would become 
+    /// 4, 4, 4, 7, 4, 7, 7, 7 in LLE-e.
+    /// 
+    /// This operation runs in O(n) time.
+    /// </remarks>
+    /// <exception cref="ArgumentException">In case, this object does not
+    /// represent a valid LLE encoding.
+    /// </exception>
+    /// <returns>An integer array in LLE-e representation</returns>
+    public int[] ToEndLinks() {
+      var result = (int[])array.Clone();
+      ToEndLinksInplace(result);
+      return result;
+    }
+
+    private static void ToEndLinksInplace(int[] array) {
+      var length = array.Length;
+      for (var i = length - 1; i >= 0; i--) {
+        var next = array[i];
+        if (next > i) {
+          array[i] = array[next];
+        } else if (next < i) {
+          throw new ArgumentException("Array is malformed and does not represent a valid LLE encoding.", "array");
+        }
+      }
+    }
+
+    /// <summary>
+    /// Parses an LLE-e representation and modifies the underlying array
+    /// so that it is in LLE representation.
+    /// </summary>
+    /// <remarks>
+    /// This operation runs in O(n) time, but requires additional memory
+    /// in form of a int[].
+    /// </remarks>
+    /// <param name="llee">The LLE-e representation</param>
+    /// <exception cref="ArgumentException">
+    /// If <paramref name="llee"/> does not contain a valid LLE-e representation or
+    /// has a different length to the given instance.
+    /// </exception>
+    public void SetEndLinks(int[] llee) {
+      var length = array.Length;
+      if (length != llee.Length) {
+        throw new ArgumentException(string.Format("Expected length {0} but length was {1}", length, llee.Length), "llee");
+      }
+      // If we are ok with mutating llee we can avoid this clone
+      var lookup = (int[])llee.Clone();
+      for (var i = length - 1; i >= 0; i--) {
+        var end = llee[i];
+        if (end == i) {
+          array[i] = end;
+        } else if (end > i && end < length) {
+          array[i] = lookup[end];
+          lookup[end] = i;
+        } else {
+          throw new ArgumentException("Array is malformed and does not represent a valid LLE-e end encoding.", "llee");
+        }
+      }
+    }
+
+    /// <summary>
+    /// Creates a copy of the underlying array and turns it into LLE-b.
+    /// </summary>
+    /// <remarks>
+    /// LLE-b is a special format where each element points to the
+    /// predecessor instead of the successor.
+    /// The LLE representation 1, 2, 4, 5, 4, 6, 7, 7
+    /// would become           0, 0, 1, 3, 2, 3, 5, 6 in LLE-b.
+    /// 
+    /// This operation runs in O(n) time.
+    /// </remarks>
+    /// <returns>An integer array in LLE-b representation</returns>
+    public int[] ToBackLinks() {
+      var result = new int[array.Length];
+      var zeroLink = array[0];
+      for (var i = 0; i < array.Length; i++) {
+        if (array[i] == i) {
+          if (result[i] == 0 && i != zeroLink) result[i] = i;
+          continue;
+        }
+        result[array[i]] = i;
+        if (result[i] == 0 && i != zeroLink) result[i] = i;
+      }
+      return result;
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageCreator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageCreator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageCreator.cs	(revision 14751)
@@ -0,0 +1,64 @@
+﻿#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 HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Operators;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Linear Linkage Creator", "Base class for linear linkage creators.")]
+  [StorableClass]
+  public abstract class LinearLinkageCreator : InstrumentedOperator, ILinearLinkageCreator, IStochasticOperator {
+
+    public ILookupParameter<IRandom> RandomParameter {
+      get { return (ILookupParameter<IRandom>)Parameters["Random"]; }
+    }
+
+    public ILookupParameter<LinearLinkage> LLEParameter {
+      get { return (ILookupParameter<LinearLinkage>)Parameters["LLE"]; }
+    }
+
+    public IValueLookupParameter<IntValue> LengthParameter {
+      get { return (IValueLookupParameter<IntValue>)Parameters["Length"]; }
+    }
+
+    [StorableConstructor]
+    protected LinearLinkageCreator(bool deserializing) : base(deserializing) { }
+    protected LinearLinkageCreator(LinearLinkageCreator original, Cloner cloner) : base(original, cloner) { }
+    protected LinearLinkageCreator() {
+      Parameters.Add(new LookupParameter<IRandom>("Random", "The random number generator to use."));
+      Parameters.Add(new LookupParameter<LinearLinkage>("LLE", "The encoding vector that is to be created.") { Hidden = false });
+      Parameters.Add(new ValueLookupParameter<IntValue>("Length", "The length of the vector that is to be created."));
+    }
+
+    public override IOperation InstrumentedApply() {
+      var random = RandomParameter.ActualValue;
+      LLEParameter.ActualValue = Create(random, LengthParameter.ActualValue.Value);
+      return base.InstrumentedApply();
+    }
+
+    protected abstract LinearLinkage Create(IRandom random, int length);
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageCrossover.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageCrossover.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageCrossover.cs	(revision 14751)
@@ -0,0 +1,67 @@
+﻿#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 HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Operators;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Linear Linkage Crossover", "Base class for linear linkage crossovers.")]
+  [StorableClass]
+  public abstract class LinearLinkageCrossover : InstrumentedOperator, ILinearLinkageCrossover, IStochasticOperator {
+
+    public ILookupParameter<IRandom> RandomParameter {
+      get { return (ILookupParameter<IRandom>)Parameters["Random"]; }
+    }
+
+    public ILookupParameter<LinearLinkage> ChildParameter {
+      get { return (ILookupParameter<LinearLinkage>)Parameters["Child"]; }
+    }
+
+    public IScopeTreeLookupParameter<LinearLinkage> ParentsParameter {
+      get { return (IScopeTreeLookupParameter<LinearLinkage>)Parameters["Parents"]; }
+    }
+
+    [StorableConstructor]
+    protected LinearLinkageCrossover(bool deserializing) : base(deserializing) { }
+    protected LinearLinkageCrossover(LinearLinkageCrossover original, Cloner cloner) : base(original, cloner) { }
+    protected LinearLinkageCrossover() {
+      Parameters.Add(new LookupParameter<IRandom>("Random", "The random number generator to use."));
+      Parameters.Add(new LookupParameter<LinearLinkage>("Child", "The child that is to be created."));
+      Parameters.Add(new ScopeTreeLookupParameter<LinearLinkage>("Parents", "The parents that are to be crossed."));
+    }
+
+    public override IOperation InstrumentedApply() {
+      var random = RandomParameter.ActualValue;
+      var parents = ParentsParameter.ActualValue;
+      if (parents == null) throw new InvalidOperationException(Name + ": Parents could not be found.");
+      var child = Cross(random, parents);
+      ChildParameter.ActualValue = child;
+      return base.InstrumentedApply();
+    }
+
+    protected abstract LinearLinkage Cross(IRandom random, ItemArray<LinearLinkage> parents);
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageEncoding.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageEncoding.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageEncoding.cs	(revision 14751)
@@ -0,0 +1,179 @@
+﻿#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.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.PluginInfrastructure;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Linear Linkage Encoding", "Describes a linear linkage (LLE) encoding.")]
+  [StorableClass]
+  public sealed class LinearLinkageEncoding : Encoding<ILinearLinkageCreator> {
+    #region encoding parameters
+    [Storable]
+    private IFixedValueParameter<IntValue> lengthParameter;
+    public IFixedValueParameter<IntValue> LengthParameter {
+      get { return lengthParameter; }
+      set {
+        if (value == null) throw new ArgumentNullException("Length parameter must not be null.");
+        if (value.Value == null) throw new ArgumentNullException("Length parameter value must not be null.");
+        if (lengthParameter == value) return;
+
+        if (lengthParameter != null) Parameters.Remove(lengthParameter);
+        lengthParameter = value;
+        Parameters.Add(lengthParameter);
+        OnLengthParameterChanged();
+      }
+    }
+    #endregion
+
+    public int Length {
+      get { return LengthParameter.Value.Value; }
+      set { LengthParameter.Value.Value = value; }
+    }
+
+    [StorableConstructor]
+    private LinearLinkageEncoding(bool deserializing) : base(deserializing) { }
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      RegisterParameterEvents();
+      DiscoverOperators();
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) { return new LinearLinkageEncoding(this, cloner); }
+    private LinearLinkageEncoding(LinearLinkageEncoding original, Cloner cloner)
+      : base(original, cloner) {
+      lengthParameter = cloner.Clone(original.lengthParameter);
+      RegisterParameterEvents();
+    }
+
+
+    public LinearLinkageEncoding() : this("LLE", 10) { }
+    public LinearLinkageEncoding(string name) : this(name, 10) { }
+    public LinearLinkageEncoding(int length) : this("LLE", length) { }
+    public LinearLinkageEncoding(string name, int length)
+      : base(name) {
+      lengthParameter = new FixedValueParameter<IntValue>(Name + ".Length", new IntValue(length));
+      Parameters.Add(lengthParameter);
+
+      SolutionCreator = new RandomLinearLinkageCreator();
+      RegisterParameterEvents();
+      DiscoverOperators();
+    }
+
+    private void OnLengthParameterChanged() {
+      RegisterLengthParameterEvents();
+      ConfigureOperators(Operators);
+    }
+
+    private void RegisterParameterEvents() {
+      RegisterLengthParameterEvents();
+    }
+    private void RegisterLengthParameterEvents() {
+      LengthParameter.Value.ValueChanged += (o, s) => ConfigureOperators(Operators);
+    }
+
+    #region Operator Discovery
+    private static readonly IEnumerable<Type> encodingSpecificOperatorTypes;
+    static LinearLinkageEncoding() {
+      encodingSpecificOperatorTypes = new List<Type>() {
+          typeof (ILinearLinkageOperator),
+          typeof (ILinearLinkageCreator),
+          typeof (ILinearLinkageCrossover),
+          typeof (ILinearLinkageManipulator),
+          typeof (ILinearLinkageShakingOperator),
+          typeof (ILinearLinkageMoveOperator)
+      };
+    }
+    private void DiscoverOperators() {
+      var assembly = typeof(ILinearLinkageOperator).Assembly;
+      var discoveredTypes = ApplicationManager.Manager.GetTypes(encodingSpecificOperatorTypes, assembly, true, false, false);
+      var operators = discoveredTypes.Select(t => (IOperator)Activator.CreateInstance(t));
+      var newOperators = operators.Except(Operators, new TypeEqualityComparer<IOperator>()).ToList();
+
+      ConfigureOperators(newOperators);
+      foreach (var @operator in newOperators)
+        AddOperator(@operator);
+    }
+    #endregion
+
+    public override void ConfigureOperators(IEnumerable<IOperator> operators) {
+      ConfigureCreators(operators.OfType<ILinearLinkageCreator>());
+      ConfigureCrossovers(operators.OfType<ILinearLinkageCrossover>());
+      ConfigureManipulators(operators.OfType<ILinearLinkageManipulator>());
+      ConfigureShakingOperators(operators.OfType<ILinearLinkageShakingOperator>());
+      ConfigureMoveOperators(operators.OfType<ILinearLinkageMoveOperator>());
+      ConfigureSwap2MoveOperators(operators.OfType<ILinearLinkageSwap2MoveOperator>());
+    }
+
+    #region specific operator wiring
+    private void ConfigureCreators(IEnumerable<ILinearLinkageCreator> creators) {
+      foreach (var creator in creators) {
+        creator.LengthParameter.ActualName = LengthParameter.Name;
+        creator.LLEParameter.ActualName = Name;
+      }
+    }
+    private void ConfigureCrossovers(IEnumerable<ILinearLinkageCrossover> crossovers) {
+      foreach (var crossover in crossovers) {
+        crossover.ChildParameter.ActualName = Name;
+        crossover.ParentsParameter.ActualName = Name;
+      }
+    }
+    private void ConfigureManipulators(IEnumerable<ILinearLinkageManipulator> manipulators) {
+      foreach (var manipulator in manipulators) {
+        manipulator.LLEParameter.ActualName = Name;
+      }
+    }
+    private void ConfigureShakingOperators(IEnumerable<ILinearLinkageShakingOperator> shakingOperators) {
+      foreach (var shakingOperator in shakingOperators) {
+        shakingOperator.LLEParameter.ActualName = Name;
+      }
+    }
+    private void ConfigureMoveOperators(IEnumerable<ILinearLinkageMoveOperator> moveOperators) {
+      foreach (var moveOperator in moveOperators) {
+        moveOperator.LLEParameter.ActualName = Name;
+      }
+    }
+    private void ConfigureSwap2MoveOperators(IEnumerable<ILinearLinkageSwap2MoveOperator> swap2MoveOperators) {
+      foreach (var swap2MoveOperator in swap2MoveOperators) {
+        swap2MoveOperator.Swap2MoveParameter.ActualName = Name + ".Swap2Move";
+      }
+    }
+    #endregion
+  }
+
+  public static class IndividualExtensionMethods {
+    public static LinearLinkage LinearLinkage(this Individual individual) {
+      var encoding = individual.GetEncoding<LinearLinkageEncoding>();
+      return individual.LinearLinkage(encoding.Name);
+    }
+    public static LinearLinkage LinearLinkage(this Individual individual, string name) {
+      return (LinearLinkage)individual[name];
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageEqualityComparer.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageEqualityComparer.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageEqualityComparer.cs	(revision 14751)
@@ -0,0 +1,44 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 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.Collections.Generic;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  public class LinearLinkageEqualityComparer : EqualityComparer<LinearLinkage> {
+    public override bool Equals(LinearLinkage x, LinearLinkage y) {
+      if (x == null && y == null) return true;
+      if (x == null || y == null) return false;
+      if (x.Length != y.Length) return false;
+      for (var i = 0; i < x.Length; i++)
+        if (x[i] != y[i]) return false;
+      return true;
+    }
+
+    public override int GetHashCode(LinearLinkage obj) {
+      unchecked {
+        int hash = 17;
+        foreach (var o in obj)
+          hash = hash * 31 + o.GetHashCode();
+        return hash;
+      }
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageManipulator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageManipulator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageManipulator.cs	(revision 14751)
@@ -0,0 +1,58 @@
+﻿#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 HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Operators;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Linear Linkage Manipulator", "Base class for linear linkage manipulators.")]
+  [StorableClass]
+  public abstract class LinearLinkageManipulator : InstrumentedOperator, ILinearLinkageManipulator, IStochasticOperator {
+    public ILookupParameter<IRandom> RandomParameter {
+      get { return (ILookupParameter<IRandom>)Parameters["Random"]; }
+    }
+
+    public ILookupParameter<LinearLinkage> LLEParameter {
+      get { return (ILookupParameter<LinearLinkage>)Parameters["LLE"]; }
+    }
+
+    [StorableConstructor]
+    protected LinearLinkageManipulator(bool deserializing) : base(deserializing) { }
+    protected LinearLinkageManipulator(LinearLinkageManipulator original, Cloner cloner) : base(original, cloner) { }
+    protected LinearLinkageManipulator() {
+      Parameters.Add(new LookupParameter<IRandom>("Random", "The random number generator to use."));
+      Parameters.Add(new LookupParameter<LinearLinkage>("LLE", "The encoding vector that is to be manipulated."));
+    }
+
+    public override IOperation InstrumentedApply() {
+      var random = RandomParameter.ActualValue;
+      var lle = LLEParameter.ActualValue;
+      Manipulate(random, lle);
+      return base.InstrumentedApply();
+    }
+
+    protected abstract void Manipulate(IRandom random, LinearLinkage lle);
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/GraftManipulator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/GraftManipulator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/GraftManipulator.cs	(revision 14751)
@@ -0,0 +1,77 @@
+﻿#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.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Graft Manipulator", "Performs graft mutation as described in Du, J., Korkmaz, E.E., Alhajj, R., and Barker, K. 2004. Novel Clustering Approach that employs Genetic Algorithm with New Representation Scheme and Multiple Objectives. Data Warehousing and Knowledge Discovery, pp. 219-228. Springer Berlin Heidelberg.")]
+  [StorableClass]
+  public sealed class GraftManipulator : LinearLinkageManipulator {
+
+    [StorableConstructor]
+    private GraftManipulator(bool deserializing) : base(deserializing) { }
+    private GraftManipulator(GraftManipulator original, Cloner cloner) : base(original, cloner) { }
+    public GraftManipulator() { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new GraftManipulator(this, cloner);
+    }
+
+    public static void Apply(IRandom random, LinearLinkage lle) {
+      int tries = lle.Length;
+      var index = random.Next(lle.Length);
+      while (tries > 0 && lle[index] == index) {
+        index = random.Next(lle.Length);
+        tries--;
+      }
+      if (lle[index] != index) Apply(random, lle, index);
+    }
+
+    public static void Apply(IRandom random, LinearLinkage lle, int index) {
+      var groups = lle.Select((val, idx) => Tuple.Create(idx, val))
+                      .Where(x => x.Item1 == x.Item2)
+                      .Select(x => x.Item2).ToList();
+      var z = groups.Count;
+
+      if (random.NextDouble() < 0.5)
+        lle[index] = index; // divide the cluster into two
+      else {
+        var c = random.Next(z);
+        if (groups[c] > index)
+          lle[index] = groups[c]; // combine the portion with another class
+        else {
+          // combine the other class here
+          lle[groups[c]] = lle[index];
+          lle[index] = index;
+        }
+        lle.LinearizeTreeStructures();
+      }
+    }
+
+    protected override void Manipulate(IRandom random, LinearLinkage lle) {
+      Apply(random, lle);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/MergeGroupManipulator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/MergeGroupManipulator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/MergeGroupManipulator.cs	(revision 14751)
@@ -0,0 +1,74 @@
+﻿#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.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Merge Group Manipulator", "Performs a maximum of N merge operations on the groups. An already merged group may be merged again.")]
+  [StorableClass]
+  public sealed class MergeGroupManipulator : LinearLinkageManipulator {
+
+    public IValueLookupParameter<IntValue> NParameter {
+      get { return (IValueLookupParameter<IntValue>)Parameters["N"]; }
+    }
+
+    [StorableConstructor]
+    private MergeGroupManipulator(bool deserializing) : base(deserializing) { }
+    private MergeGroupManipulator(MergeGroupManipulator original, Cloner cloner) : base(original, cloner) { }
+    public MergeGroupManipulator() {
+      Parameters.Add(new ValueLookupParameter<IntValue>("N", "The number of groups to merge.", new IntValue(1)));
+    }
+    public MergeGroupManipulator(int n)
+      : this() {
+      NParameter.Value = new IntValue(n);
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new MergeGroupManipulator(this, cloner);
+    }
+
+    public static void Apply(IRandom random, LinearLinkage lle, int n) {
+      var grouping = lle.GetGroups().ToList();
+      if (grouping.Count == 1) return; // nothing to merge
+
+      for (var i = 0; i < n; i++) {
+        var g1 = random.Next(grouping.Count);
+        var g2 = random.Next(grouping.Count);
+        while (g1 == g2) g2 = random.Next(grouping.Count);
+        grouping[g1].AddRange(grouping[g2]);
+        grouping.RemoveAt(g2);
+        if (grouping.Count == 1) break;
+      }
+
+      lle.SetGroups(grouping);
+    }
+
+    protected override void Manipulate(IRandom random, LinearLinkage lle) {
+      var N = NParameter.ActualValue.Value;
+      Apply(random, lle, N);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/MoveItemManipulator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/MoveItemManipulator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/MoveItemManipulator.cs	(revision 14751)
@@ -0,0 +1,82 @@
+﻿#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.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.Encodings.LinearLinkageEncoding {
+  [Item("Move Item Manipulator", "Performs a maximum of N move operations between groups or to new groups. An already moved item may be moved again.")]
+  [StorableClass]
+  public sealed class MoveItemManipulator : LinearLinkageManipulator {
+
+    public IValueLookupParameter<IntValue> NParameter {
+      get { return (IValueLookupParameter<IntValue>)Parameters["N"]; }
+    }
+
+    [StorableConstructor]
+    private MoveItemManipulator(bool deserializing) : base(deserializing) { }
+    private MoveItemManipulator(MoveItemManipulator original, Cloner cloner) : base(original, cloner) { }
+    public MoveItemManipulator() {
+      Parameters.Add(new ValueLookupParameter<IntValue>("N", "The number of items to move.", new IntValue(1)));
+    }
+    public MoveItemManipulator(int n)
+      : this() {
+      NParameter.Value = new IntValue(n);
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new MoveItemManipulator(this, cloner);
+    }
+
+    public static void Apply(IRandom random, LinearLinkage lle, int n) {
+      var grouping = lle.GetGroups().ToList();
+      grouping.Add(new List<int>()); // add an empty group
+
+      for (var i = 0; i < n; i++) {
+        var src = random.Next(grouping.Count - 1); // only select from non-empty groups
+        var dest = random.Next(grouping.Count);
+        while (src == dest || grouping[src].Count == 1 && dest == grouping.Count - 1) {
+          src = random.Next(grouping.Count - 1);
+          dest = random.Next(grouping.Count);
+        }
+        if (dest == grouping.Count - 1) grouping.Add(new List<int>());
+
+        var e = random.Next(grouping[src].Count);
+        grouping[dest].Add(grouping[src][e]);
+        grouping[src].RemoveAt(e);
+        if (grouping[src].Count == 0)
+          grouping.RemoveAt(src);
+      }
+
+      lle.SetGroups(grouping.Where(x => x.Count > 0));
+    }
+
+    protected override void Manipulate(IRandom random, LinearLinkage lle) {
+      var N = NParameter.ActualValue.Value;
+      Apply(random, lle, N);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/MultiLLEManipulator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/MultiLLEManipulator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/MultiLLEManipulator.cs	(revision 14751)
@@ -0,0 +1,90 @@
+﻿#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.Linq;
+using System.Reflection;
+using HeuristicLab.Collections;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Operators;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.PluginInfrastructure;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Multi LLE Manipulator", "Randomly selects and applies one of its manipulators every time it is called.")]
+  [StorableClass]
+  public class MultiLinearLinkageManipulator : StochasticMultiBranch<ILinearLinkageManipulator>, ILinearLinkageManipulator, IStochasticOperator {
+    public override bool CanChangeName {
+      get { return false; }
+    }
+    protected override bool CreateChildOperation {
+      get { return true; }
+    }
+
+    public ILookupParameter<LinearLinkage> LLEParameter {
+      get { return (ILookupParameter<LinearLinkage>)Parameters["LLE"]; }
+    }
+
+    [StorableConstructor]
+    protected MultiLinearLinkageManipulator(bool deserializing) : base(deserializing) { }
+    protected MultiLinearLinkageManipulator(MultiLinearLinkageManipulator original, Cloner cloner) : base(original, cloner) { }
+    public MultiLinearLinkageManipulator()
+      : base() {
+      Parameters.Add(new LookupParameter<LinearLinkage>("LLE", "The encoding vector that is to be manipulated."));
+      foreach (Type type in ApplicationManager.Manager.GetTypes(typeof(ILinearLinkageManipulator), typeof(LinearLinkageEncoding).Assembly)) {
+        if (!typeof(MultiOperator<ILinearLinkageManipulator>).IsAssignableFrom(type))
+          Operators.Add((ILinearLinkageManipulator)Activator.CreateInstance(type), true);
+      }
+      SelectedOperatorParameter.ActualName = "SelectedManipulationOperator";
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new MultiLinearLinkageManipulator(this, cloner);
+    }
+
+    protected override void Operators_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<ILinearLinkageManipulator>> e) {
+      base.Operators_ItemsReplaced(sender, e);
+      ParameterizeManipulators(e.Items);
+    }
+
+    protected override void Operators_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<ILinearLinkageManipulator>> e) {
+      base.Operators_ItemsAdded(sender, e);
+      ParameterizeManipulators(e.Items);
+    }
+
+    private void ParameterizeManipulators(IEnumerable<IndexedItem<ILinearLinkageManipulator>> manipulators) {
+      foreach (var m in manipulators.Select(x => x.Value)) {
+        m.LLEParameter.ActualName = LLEParameter.Name;
+        var stOp = m as IStochasticOperator;
+        if (stOp != null) stOp.RandomParameter.ActualName = RandomParameter.Name;
+      }
+    }
+
+    public override IOperation InstrumentedApply() {
+      if (Operators.Count == 0) throw new InvalidOperationException(Name + ": Please add at least one LLE manipulator to choose from.");
+      return base.InstrumentedApply();
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/SplitGroupManipulator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/SplitGroupManipulator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/SplitGroupManipulator.cs	(revision 14751)
@@ -0,0 +1,95 @@
+﻿#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.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Split Group Manipulator", "Performs a maximum of N split operations on the groups. An already split group may be split again.")]
+  [StorableClass]
+  public sealed class SplitGroupManipulator : LinearLinkageManipulator {
+
+    public IValueLookupParameter<IntValue> NParameter {
+      get { return (IValueLookupParameter<IntValue>)Parameters["N"]; }
+    }
+
+    [StorableConstructor]
+    private SplitGroupManipulator(bool deserializing) : base(deserializing) { }
+    private SplitGroupManipulator(SplitGroupManipulator original, Cloner cloner) : base(original, cloner) { }
+    public SplitGroupManipulator() {
+      Parameters.Add(new ValueLookupParameter<IntValue>("N", "The number of groups to split.", new IntValue(1)));
+    }
+    public SplitGroupManipulator(int n)
+      : this() {
+      NParameter.Value = new IntValue(n);
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new SplitGroupManipulator(this, cloner);
+    }
+
+    public static void Apply(IRandom random, LinearLinkage lle, int n) {
+      var grouping = lle.GetGroups().ToList();
+      var groupsLargerOne = grouping.Select((v, i) => Tuple.Create(i, v))
+                                    .Where(x => x.Item2.Count > 1)
+                                    .ToDictionary(x => x.Item1, x => x.Item2);
+      if (groupsLargerOne.Count == 0) return;
+      var toRemove = new List<int>();
+
+      for (var i = 0; i < n; i++) {
+        var g = groupsLargerOne.Keys.SampleRandom(random);
+        var idx = random.Next(1, groupsLargerOne[g].Count);
+        // shuffle here to avoid a potential bias of grouping smaller and larger numbers together
+        var tmp = groupsLargerOne[g].Shuffle(random);
+        var before = new List<int>();
+        var after = new List<int>();
+        foreach (var t in tmp) {
+          if (idx > 0) before.Add(t);
+          else after.Add(t);
+          idx--;
+        }
+        if (before.Count > 1) groupsLargerOne[grouping.Count] = before;
+        grouping.Add(before);
+        if (after.Count > 1) groupsLargerOne[grouping.Count] = after;
+        grouping.Add(after);
+        toRemove.Add(g);
+        groupsLargerOne.Remove(g);
+        if (groupsLargerOne.Count == 0) break;
+      }
+      foreach (var r in toRemove.OrderByDescending(x => x))
+        grouping.RemoveAt(r);
+
+      lle.SetGroups(grouping);
+    }
+
+    protected override void Manipulate(IRandom random, LinearLinkage lle) {
+      var N = NParameter.ActualValue.Value;
+      Apply(random, lle, N);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/SwapItemManipulator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/SwapItemManipulator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Manipulators/SwapItemManipulator.cs	(revision 14751)
@@ -0,0 +1,80 @@
+﻿#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.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Swap Item Manipulator", "Performs N swaps operations of two items each. The same items may be swapped multiple times, at least two groups need to be present.")]
+  [StorableClass]
+  public sealed class SwapItemManipulator : LinearLinkageManipulator {
+
+    public IValueLookupParameter<IntValue> NParameter {
+      get { return (IValueLookupParameter<IntValue>)Parameters["N"]; }
+    }
+
+    [StorableConstructor]
+    private SwapItemManipulator(bool deserializing) : base(deserializing) { }
+    private SwapItemManipulator(SwapItemManipulator original, Cloner cloner) : base(original, cloner) { }
+    public SwapItemManipulator() {
+      Parameters.Add(new ValueLookupParameter<IntValue>("N", "The number of swaps to perform.", new IntValue(1)));
+    }
+    public SwapItemManipulator(int n)
+      : this() {
+      NParameter.Value = new IntValue(n);
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new SwapItemManipulator(this, cloner);
+    }
+
+    public static void Apply(IRandom random, LinearLinkage lle, int n) {
+      var grouping = lle.GetGroups().Select(x => x.ToList()).ToList();
+      if (grouping.Count == 1) return; // nothing can be changed
+
+      var prevGroup = random.Next(grouping.Count);
+      var prevItem = random.Next(grouping[prevGroup].Count);
+      for (var i = 0; i < n; i++) {
+        int nextGroup, nextItem;
+        do {
+          nextGroup = random.Next(grouping.Count);
+          nextItem = random.Next(grouping[nextGroup].Count);
+        } while (nextGroup == prevGroup);
+        var h = grouping[nextGroup][nextItem];
+        grouping[nextGroup][nextItem] = grouping[prevGroup][prevItem];
+        grouping[prevGroup][prevItem] = h;
+        prevGroup = nextGroup;
+        prevItem = nextItem;
+      }
+
+      lle.SetGroups(grouping);
+    }
+
+    protected override void Manipulate(IRandom random, LinearLinkage lle) {
+      var N = NParameter.ActualValue.Value;
+      Apply(random, lle, N);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/ExtractMove.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/ExtractMove.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/ExtractMove.cs	(revision 14751)
@@ -0,0 +1,60 @@
+﻿#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;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  public class ExtractMove : Move {
+    public int PreviousItem { get; private set; }
+    public int NextItem { get; private set; }
+
+    private bool IsFirst { get { return PreviousItem == Item; } }
+    private bool IsLast { get { return NextItem == Item; } }
+
+    public ExtractMove(int item, int prev, int next) {
+      Item = item;
+      PreviousItem = prev;
+      NextItem = next;
+    }
+
+    public override void Apply(LinearLinkage lle) {
+      if ((!IsFirst && lle[PreviousItem] != Item)
+        || lle[Item] != NextItem)
+        throw new ArgumentException("Move conditions have changed!");
+      if (!IsFirst) lle[PreviousItem] = IsLast ? PreviousItem : NextItem;
+      lle[Item] = Item;
+    }
+
+    public override void Undo(LinearLinkage lle) {
+      if (!IsFirst && lle[PreviousItem] != (IsLast ? PreviousItem : NextItem)
+        || lle[Item] != Item)
+        throw new ArgumentException("Move conditions have changed, cannot undo move.");
+
+      if (!IsFirst) lle[PreviousItem] = Item;
+      lle[Item] = NextItem;
+    }
+
+    public override void ApplyToLLEb(int[] lleb) {
+      if (!IsLast) lleb[NextItem] = IsFirst ? NextItem : PreviousItem;
+      lleb[Item] = Item;
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/MergeMove.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/MergeMove.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/MergeMove.cs	(revision 14751)
@@ -0,0 +1,48 @@
+﻿#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;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  public class MergeMove : Move {
+
+    public int LastItemOfOtherGroup { get; private set; }
+
+    public MergeMove(int item, int lastOfOther) {
+      Item = item;
+      LastItemOfOtherGroup = lastOfOther;
+    }
+
+    public override void Apply(LinearLinkage lle) {
+      if (lle[LastItemOfOtherGroup] != LastItemOfOtherGroup) throw new ArgumentException("Move conditions have changed, group does not terminate at " + LastItemOfOtherGroup);
+      lle[LastItemOfOtherGroup] = Item;
+    }
+
+    public override void Undo(LinearLinkage lle) {
+      if (lle[LastItemOfOtherGroup] != Item) throw new ArgumentException("Move conditions have changed, groups are no longer linked between " + LastItemOfOtherGroup + " and " + Item);
+      lle[LastItemOfOtherGroup] = LastItemOfOtherGroup;
+    }
+
+    public override void ApplyToLLEb(int[] lleb) {
+      lleb[Item] = LastItemOfOtherGroup;
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Move.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Move.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Move.cs	(revision 14751)
@@ -0,0 +1,31 @@
+﻿#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
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  public abstract class Move {
+    public int Item { get; protected set; }
+
+    public abstract void Apply(LinearLinkage lle);
+    public abstract void Undo(LinearLinkage lle);
+
+    public abstract void ApplyToLLEb(int[] lleb);
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/ShiftMove.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/ShiftMove.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/ShiftMove.cs	(revision 14751)
@@ -0,0 +1,74 @@
+﻿#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;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  public class ShiftMove : Move {
+    public int PreviousItemOfOldGroup { get; private set; }
+    public int PreviousItemOfNewGroup { get; private set; }
+    public int NextItemOfOldGroup { get; private set; }
+    public int NextItemOfNewGroup { get; private set; }
+
+    private bool IsFirst { get { return PreviousItemOfOldGroup == Item; } }
+    private bool IsLast { get { return NextItemOfOldGroup == Item; } }
+    private bool NewGroupClosed { get { return PreviousItemOfNewGroup == NextItemOfNewGroup; } }
+
+    public ShiftMove(int item, int prevOld, int prevNew, int nextOld, int nextNew) {
+      Item = item;
+      PreviousItemOfOldGroup = prevOld;
+      PreviousItemOfNewGroup = prevNew;
+      NextItemOfOldGroup = nextOld;
+      NextItemOfNewGroup = nextNew;
+    }
+
+    public override void Apply(LinearLinkage lle) {
+      if ((!IsFirst && lle[PreviousItemOfOldGroup] != Item)
+        || lle[Item] != NextItemOfOldGroup
+        || lle[PreviousItemOfNewGroup] != NextItemOfNewGroup)
+        throw new ArgumentException("Move conditions have changed!");
+      if (!IsFirst)
+        lle[PreviousItemOfOldGroup] = IsLast ? PreviousItemOfOldGroup : NextItemOfOldGroup;
+      lle[PreviousItemOfNewGroup] = Item;
+      lle[Item] = NewGroupClosed ? Item : NextItemOfNewGroup;
+    }
+
+    public override void Undo(LinearLinkage lle) {
+      if (!IsFirst && lle[PreviousItemOfOldGroup] != (IsLast ? PreviousItemOfOldGroup : NextItemOfOldGroup)
+        || lle[PreviousItemOfNewGroup] != Item
+        || lle[Item] != (NewGroupClosed ? Item : NextItemOfNewGroup))
+        throw new ArgumentException("Move conditions have changed, cannot undo move.");
+
+      if (!IsFirst)
+        lle[PreviousItemOfOldGroup] = Item;
+      lle[PreviousItemOfNewGroup] = NextItemOfNewGroup;
+      lle[Item] = NextItemOfOldGroup;
+    }
+
+    public override void ApplyToLLEb(int[] lleb) {
+      if (!IsLast)
+        lleb[NextItemOfOldGroup] = IsFirst ? NextItemOfOldGroup : PreviousItemOfOldGroup;
+      lleb[Item] = PreviousItemOfNewGroup;
+      if (!NewGroupClosed)
+        lleb[NextItemOfNewGroup] = Item;
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/SplitMove.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/SplitMove.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/SplitMove.cs	(revision 14751)
@@ -0,0 +1,51 @@
+﻿#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;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  public class SplitMove : Move {
+
+    private int nextItem;
+
+    public SplitMove(int item) {
+      Item = item;
+      nextItem = -1;
+    }
+
+    public override void Apply(LinearLinkage lle) {
+      if (lle[Item] == Item) throw new ArgumentException("Move conditions have changed, group is already split at " + Item);
+      nextItem = lle[Item];
+      lle[Item] = Item;
+    }
+
+    public override void Undo(LinearLinkage lle) {
+      if (lle[Item] != Item) throw new ArgumentException("Move conditions have changed, group no longer terminates at " + Item);
+      if (nextItem < 0) throw new InvalidOperationException("Cannot undo move that has not been applied first.");
+      lle[Item] = nextItem;
+    }
+
+    public override void ApplyToLLEb(int[] lleb) {
+      if (nextItem < 0) throw new InvalidOperationException("Cannot undo move that has not been applied first to LLE.");
+      lleb[nextItem] = nextItem;
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/StaticAPI/MoveGenerator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/StaticAPI/MoveGenerator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/StaticAPI/MoveGenerator.cs	(revision 14751)
@@ -0,0 +1,68 @@
+﻿#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.Collections.Generic;
+using System.Linq;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  public static class MoveGenerator {
+    public static IEnumerable<Move> GenerateForItem(int i, List<int> groupItems, LinearLinkage lle, int[] lleb) {
+      var pred = lleb[i];
+      var next = lle[i];
+      var isFirst = pred == i;
+      var isLast = next == i;
+
+      // First: shift i into each previous group
+      foreach (var m in groupItems.Where(x => lle[x] != i)) {
+        yield return new ShiftMove(i, pred, m, next, lle[m]);
+      }
+
+      if (!isLast) {
+        // Second: split group at i
+        yield return new SplitMove(i);
+
+        if (isFirst) {
+          // Third: merge with closed groups
+          foreach (var m in groupItems.Where(x => lle[x] == x)) {
+            yield return new MergeMove(i, m);
+          }
+        } else {
+          // Fourth: extract i into group of its own (exclude first and last, because of SplitMove)
+          yield return new ExtractMove(i, pred, next);
+        }
+      }
+        
+
+    }
+
+    public static IEnumerable<Move> Generate(LinearLinkage lle) {
+      var groupItems = new List<int>();
+      var lleb = lle.ToBackLinks();
+      for (var i = 0; i < lle.Length; i++) {
+        foreach (var move in GenerateForItem(i, groupItems, lle, lleb))
+          yield return move;
+        if (lleb[i] != i)
+          groupItems.Remove(lleb[i]);
+        groupItems.Add(i);
+      }
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/ExhaustiveSwap2MoveGenerator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/ExhaustiveSwap2MoveGenerator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/ExhaustiveSwap2MoveGenerator.cs	(revision 14751)
@@ -0,0 +1,62 @@
+﻿#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.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Optimization;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("ExhaustiveSwap2MoveGenerator", "Generates all possible swap-2 moves from a given permutation.")]
+  [StorableClass]
+  public class ExhaustiveSwap2MoveGenerator : Swap2MoveGenerator, IExhaustiveMoveGenerator {
+    [StorableConstructor]
+    protected ExhaustiveSwap2MoveGenerator(bool deserializing) : base(deserializing) { }
+    protected ExhaustiveSwap2MoveGenerator(ExhaustiveSwap2MoveGenerator original, Cloner cloner) : base(original, cloner) { }
+    public ExhaustiveSwap2MoveGenerator() : base() { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new ExhaustiveSwap2MoveGenerator(this, cloner);
+    }
+
+    public static IEnumerable<Swap2Move> Generate(LinearLinkage lle) {
+      int length = lle.Length;
+      if (length == 1) throw new ArgumentException("ExhaustiveSwap2MoveGenerator: There cannot be an Swap move given only one item.", "lle");
+
+      var groups = lle.GetGroups().ToList();
+      if (groups.Count == 1) throw new InvalidOperationException("ExhaustiveSwap2MoveGenerator: Swap moves cannot be applied when there is only one group.");
+
+      for (int i = 0; i < groups.Count; i++)
+        for (int j = i + 1; j < groups.Count; j++)
+          for (var k = 0; k < groups[i].Count; k++)
+            for (var m = 0; m < groups[j].Count; m++) {
+              yield return new Swap2Move(groups[i][k], groups[j][m]);
+            }
+    }
+
+    protected override Swap2Move[] GenerateMoves(LinearLinkage lle) {
+      return Generate(lle).ToArray();
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/StochasticSwap2MultiMoveGenerator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/StochasticSwap2MultiMoveGenerator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/StochasticSwap2MultiMoveGenerator.cs	(revision 14751)
@@ -0,0 +1,72 @@
+﻿#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 HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("StochasticSwap2MultiMoveGenerator", "Randomly samples n from all possible swap-2 moves from a given lle grouping.")]
+  [StorableClass]
+  public class StochasticSwap2MultiMoveGenerator : Swap2MoveGenerator, IMultiMoveGenerator, IStochasticOperator {
+    public ILookupParameter<IRandom> RandomParameter {
+      get { return (ILookupParameter<IRandom>)Parameters["Random"]; }
+    }
+    public IValueLookupParameter<IntValue> SampleSizeParameter {
+      get { return (IValueLookupParameter<IntValue>)Parameters["SampleSize"]; }
+    }
+
+    public IntValue SampleSize {
+      get { return SampleSizeParameter.Value; }
+      set { SampleSizeParameter.Value = value; }
+    }
+
+    [StorableConstructor]
+    protected StochasticSwap2MultiMoveGenerator(bool deserializing) : base(deserializing) { }
+    protected StochasticSwap2MultiMoveGenerator(StochasticSwap2MultiMoveGenerator original, Cloner cloner) : base(original, cloner) { }
+    public StochasticSwap2MultiMoveGenerator()
+      : base() {
+      Parameters.Add(new LookupParameter<IRandom>("Random", "The random number generator."));
+      Parameters.Add(new ValueLookupParameter<IntValue>("SampleSize", "The number of moves to generate."));
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new StochasticSwap2MultiMoveGenerator(this, cloner);
+    }
+
+    public static Swap2Move[] Apply(LinearLinkage lle, IRandom random, int sampleSize) {
+      int length = lle.Length;
+      Swap2Move[] moves = new Swap2Move[sampleSize];
+      for (int i = 0; i < sampleSize; i++) {
+        moves[i] = StochasticSwap2SingleMoveGenerator.Apply(lle, random);
+      }
+      return moves;
+    }
+
+    protected override Swap2Move[] GenerateMoves(LinearLinkage lle) {
+      IRandom random = RandomParameter.ActualValue;
+      return Apply(lle, random, SampleSizeParameter.ActualValue.Value);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/StochasticSwap2SingleMoveGenerator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/StochasticSwap2SingleMoveGenerator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/StochasticSwap2SingleMoveGenerator.cs	(revision 14751)
@@ -0,0 +1,73 @@
+﻿#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.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("StochasticSwap2SingleMoveGenerator", "Randomly samples a single from all possible swap-2 moves from a given grouping.")]
+  [StorableClass]
+  public class StochasticSwap2SingleMoveGenerator : Swap2MoveGenerator, IStochasticOperator, ISingleMoveGenerator {
+    public ILookupParameter<IRandom> RandomParameter {
+      get { return (ILookupParameter<IRandom>)Parameters["Random"]; }
+    }
+
+    [StorableConstructor]
+    protected StochasticSwap2SingleMoveGenerator(bool deserializing) : base(deserializing) { }
+    protected StochasticSwap2SingleMoveGenerator(StochasticSwap2SingleMoveGenerator original, Cloner cloner) : base(original, cloner) { }
+    public StochasticSwap2SingleMoveGenerator()
+      : base() {
+      Parameters.Add(new LookupParameter<IRandom>("Random", "The random number generator."));
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new StochasticSwap2SingleMoveGenerator(this, cloner);
+    }
+
+    public static Swap2Move Apply(LinearLinkage lle, IRandom random) {
+      int length = lle.Length;
+      if (length < 2) throw new ArgumentException("StochasticSwap2SingleMoveGenerator: There cannot be a swap-2 move given only one item.", "lle");
+
+      var groups = lle.GetGroups().ToList();
+      if (groups.Count == 1) throw new InvalidOperationException("StochasticSwap2SingleMoveGenerator: Swap moves cannot be applied when there is only one group.");
+
+      int index1 = random.Next(groups.Count), index2 = 0;
+      do {
+        index2 = random.Next(length);
+      } while (index1 == index2);
+
+      var item1 = random.Next(groups[index1].Count);
+      var item2 = random.Next(groups[index2].Count);
+
+      return new Swap2Move(groups[index1][item1], groups[index2][item2]);
+    }
+
+    protected override Swap2Move[] GenerateMoves(LinearLinkage lle) {
+      IRandom random = RandomParameter.ActualValue;
+      return new[] { Apply(lle, random) };
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/Swap2Move.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/Swap2Move.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/Swap2Move.cs	(revision 14751)
@@ -0,0 +1,53 @@
+﻿#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 HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Swap2Move", "Item that describes a swap-2 move.")]
+  [StorableClass]
+  public class Swap2Move : Item {
+    [Storable]
+    public int Item1 { get; protected set; }
+    [Storable]
+    public int Item2 { get; protected set; }
+    [Storable]
+    public LinearLinkage LLE { get; protected set; }
+
+    [StorableConstructor]
+    protected Swap2Move(bool deserializing) : base(deserializing) { }
+    protected Swap2Move(Swap2Move original, Cloner cloner) : base(original, cloner) { }
+    public Swap2Move() : this(-1, -1, null) { }
+    public Swap2Move(int item1, int item2) : this(item1, item2, null) { }
+    public Swap2Move(int item1, int item2, LinearLinkage lle)
+      : base() {
+      Item1 = item1;
+      Item2 = item2;
+      LLE = lle;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new Swap2Move(this, cloner);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/Swap2MoveGenerator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/Swap2MoveGenerator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/Swap2MoveGenerator.cs	(revision 14751)
@@ -0,0 +1,66 @@
+﻿#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 HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Operators;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Swap2MoveGenerator", "Base class for all swap-2 move generators.")]
+  [StorableClass]
+  public abstract class Swap2MoveGenerator : SingleSuccessorOperator, ILinearLinkageSwap2MoveOperator, IMoveGenerator {
+    public override bool CanChangeName {
+      get { return false; }
+    }
+    public ILookupParameter<LinearLinkage> LLEParameter {
+      get { return (ILookupParameter<LinearLinkage>)Parameters["LLE"]; }
+    }
+    public ILookupParameter<Swap2Move> Swap2MoveParameter {
+      get { return (LookupParameter<Swap2Move>)Parameters["Swap2Move"]; }
+    }
+
+    [StorableConstructor]
+    protected Swap2MoveGenerator(bool deserializing) : base(deserializing) { }
+    protected Swap2MoveGenerator(Swap2MoveGenerator original, Cloner cloner) : base(original, cloner) { }
+    public Swap2MoveGenerator()
+      : base() {
+      Parameters.Add(new LookupParameter<LinearLinkage>("LLE", "The LLE solution for which moves should be generated."));
+      Parameters.Add(new LookupParameter<Swap2Move>("Swap2Move", "The moves that should be generated in subscopes."));
+    }
+
+    public override IOperation Apply() {
+      var lle = LLEParameter.ActualValue;
+      var moves = GenerateMoves(lle);
+      var moveScopes = new Scope[moves.Length];
+      for (var i = 0; i < moveScopes.Length; i++) {
+        moveScopes[i] = new Scope(i.ToString());
+        moveScopes[i].Variables.Add(new Variable(Swap2MoveParameter.ActualName, moves[i]));
+      }
+      ExecutionContext.Scope.SubScopes.AddRange(moveScopes);
+      return base.Apply();
+    }
+
+    protected abstract Swap2Move[] GenerateMoves(LinearLinkage lle);
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/Swap2MoveMaker.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/Swap2MoveMaker.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Swap/Swap2MoveMaker.cs	(revision 14751)
@@ -0,0 +1,106 @@
+﻿#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.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Operators;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Swap2MoveMaker", "Peforms a swap-2 move on a given grouping and updates the quality.")]
+  [StorableClass]
+  public class Swap2MoveMaker : SingleSuccessorOperator, ILinearLinkageSwap2MoveOperator, IMoveMaker, ISingleObjectiveOperator {
+    public override bool CanChangeName {
+      get { return false; }
+    }
+    public ILookupParameter<DoubleValue> QualityParameter {
+      get { return (ILookupParameter<DoubleValue>)Parameters["Quality"]; }
+    }
+    public ILookupParameter<DoubleValue> MoveQualityParameter {
+      get { return (ILookupParameter<DoubleValue>)Parameters["MoveQuality"]; }
+    }
+    public ILookupParameter<Swap2Move> Swap2MoveParameter {
+      get { return (ILookupParameter<Swap2Move>)Parameters["Swap2Move"]; }
+    }
+    public ILookupParameter<LinearLinkage> LLEParameter {
+      get { return (ILookupParameter<LinearLinkage>)Parameters["LLE"]; }
+    }
+
+    [StorableConstructor]
+    protected Swap2MoveMaker(bool deserializing) : base(deserializing) { }
+    protected Swap2MoveMaker(Swap2MoveMaker original, Cloner cloner) : base(original, cloner) { }
+    public Swap2MoveMaker()
+      : base() {
+      Parameters.Add(new LookupParameter<DoubleValue>("Quality", "The quality of the solution."));
+      Parameters.Add(new LookupParameter<Swap2Move>("Swap2Move", "The move to apply."));
+      Parameters.Add(new LookupParameter<DoubleValue>("MoveQuality", "The relative quality of the move."));
+      Parameters.Add(new LookupParameter<LinearLinkage>("LLE", "The linear linkage encoded solution to which the move should be applied."));
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new Swap2MoveMaker(this, cloner);
+    }
+
+    public static void Apply(LinearLinkage lle, Swap2Move move) {
+      var groups = lle.GetGroups().ToList();
+      int g1 = -1, g2 = -1, g1Idx = -1, g2Idx = -1;
+      for (var i = 0; i < groups.Count; i++) {
+        if (g1 < 0) {
+          g1Idx = groups[i].IndexOf(move.Item1);
+          if (g1Idx >= 0) {
+            g1 = i;
+            continue;
+          }
+        }
+        if (g2 < 0) {
+          g2Idx = groups[i].IndexOf(move.Item2);
+          if (g2Idx >= 0) g2 = i;
+        }
+      }
+
+      // can happen if (for some reason) the items belong to the same group
+      if (g1 < 0 || g2 < 0) throw new InvalidOperationException("Swap2MoveMaker: Cannot apply swap move, items are not found in different groups.");
+
+      var h = groups[g1][g1Idx];
+      groups[g1][g1Idx] = groups[g2][g2Idx];
+      groups[g2][g2Idx] = h;
+      lle.SetGroups(groups);
+    }
+
+    public override IOperation Apply() {
+      var move = Swap2MoveParameter.ActualValue;
+      var lle = LLEParameter.ActualValue;
+      var moveQuality = MoveQualityParameter.ActualValue;
+      var quality = QualityParameter.ActualValue;
+
+      Apply(lle, move);
+
+      quality.Value = moveQuality.Value;
+
+      return base.Apply();
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Plugin.cs.frame
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Plugin.cs.frame	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Plugin.cs.frame	(revision 14751)
@@ -0,0 +1,38 @@
+﻿#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 HeuristicLab.PluginInfrastructure;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Plugin("HeuristicLab.Encodings.LinearLinkageEncoding", "Provides data structures and operators for the linear linkage encoding (LLE).", "3.4.14.$WCREV$")]
+  [PluginFile("HeuristicLab.Encodings.LinearLinkageEncoding-3.4.dll", PluginFileType.Assembly)]
+  [PluginDependency("HeuristicLab.Collections", "3.3")]
+  [PluginDependency("HeuristicLab.Common", "3.3")]
+  [PluginDependency("HeuristicLab.Core", "3.3")]
+  [PluginDependency("HeuristicLab.Data", "3.3")]
+  [PluginDependency("HeuristicLab.Operators", "3.3")]
+  [PluginDependency("HeuristicLab.Optimization", "3.3")]
+  [PluginDependency("HeuristicLab.Optimization.Operators", "3.3")]
+  [PluginDependency("HeuristicLab.Parameters", "3.3")]
+  [PluginDependency("HeuristicLab.Persistence", "3.3")]
+  [PluginDependency("HeuristicLab.Random", "3.3")]
+  public class HeuristicLabEncodingsLinearLinkageEncodingPlugin : PluginBase { }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Properties/AssemblyInfo.cs.frame
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Properties/AssemblyInfo.cs.frame	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Properties/AssemblyInfo.cs.frame	(revision 14751)
@@ -0,0 +1,56 @@
+﻿#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.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("HeuristicLab.Encodings.LinearLinkage")]
+[assembly: AssemblyDescription("Linear linkage encoding (LLE) for HeuristicLab")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("HEAL")]
+[assembly: AssemblyProduct("HeuristicLab")]
+[assembly: AssemblyCopyright("(c) 2002-2016 HEAL")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("1fb5c9e2-42ac-441a-ae79-bfdf261a7262")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("3.4.0.0")]
+[assembly: AssemblyFileVersion("3.4.14.$WCREV$")]
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/ShakingOperators/LLEShakingOperator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/ShakingOperators/LLEShakingOperator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/ShakingOperators/LLEShakingOperator.cs	(revision 14751)
@@ -0,0 +1,88 @@
+﻿#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.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Collections;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Optimization;
+using HeuristicLab.Optimization.Operators;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("LLE Shaking Operator", "A shaking operator for VNS which uses LLE manipulators to perform the shaking.")]
+  [StorableClass]
+  public class LLEShakingOperator : ShakingOperator<ILinearLinkageManipulator>, IStochasticOperator, ILinearLinkageShakingOperator {
+
+    public ILookupParameter<IRandom> RandomParameter {
+      get { return (LookupParameter<IRandom>)Parameters["Random"]; }
+    }
+
+    public ILookupParameter<LinearLinkage> LLEParameter {
+      get { return (ILookupParameter<LinearLinkage>)Parameters["LLE"]; }
+    }
+
+    public ICheckedItemList<ILinearLinkageManipulator> Shakers {
+      get { return Operators; }
+    }
+
+    [StorableConstructor]
+    protected LLEShakingOperator(bool deserializing) : base(deserializing) { }
+    protected LLEShakingOperator(LLEShakingOperator original, Cloner cloner) : base(original, cloner) { }
+    public LLEShakingOperator()
+      : base() {
+      Parameters.Add(new LookupParameter<LinearLinkage>("LLE", "The encoding to shake."));
+      Parameters.Add(new LookupParameter<IRandom>("Random", "The random number generator that will be used for stochastic shaking operators."));
+      for (var i = 1; i < 4; i++) {
+        Operators.Add(new MoveItemManipulator(i * 2));
+        Operators.Add(new SwapItemManipulator(i * 2));
+        Operators.Add(new SplitGroupManipulator(i * 2));
+        Operators.Add(new MergeGroupManipulator(i * 2));
+      }
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new LLEShakingOperator(this, cloner);
+    }
+
+    #region Wiring of some parameters
+    protected override void Operators_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<ILinearLinkageManipulator>> e) {
+      base.Operators_ItemsAdded(sender, e);
+      ParameterizeOperators(e.Items);
+    }
+
+    protected override void Operators_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<ILinearLinkageManipulator>> e) {
+      base.Operators_ItemsReplaced(sender, e);
+      ParameterizeOperators(e.Items);
+    }
+
+    private void ParameterizeOperators(IEnumerable<IndexedItem<ILinearLinkageManipulator>> items) {
+      if (!items.Any()) return;
+      foreach (var op in items.Select(x => x.Value).OfType<IStochasticOperator>())
+        op.RandomParameter.ActualName = RandomParameter.Name;
+      foreach (var op in items.Select(x => x.Value))
+        op.LLEParameter.ActualName = LLEParameter.Name;
+    }
+    #endregion
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/SimilarityCalculators/HammingSimilarityCalculator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/SimilarityCalculators/HammingSimilarityCalculator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/SimilarityCalculators/HammingSimilarityCalculator.cs	(revision 14751)
@@ -0,0 +1,62 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 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 HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Optimization.Operators;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  [Item("Hamming Similarity Calculator for LinearLinkage", "Calculates the hamming similarity for two linear linkage encoded solutions.")]
+  [StorableClass]
+  public sealed class HammingSimilarityCalculator : SingleObjectiveSolutionSimilarityCalculator {
+
+    protected override bool IsCommutative {
+      get { return true; }
+    }
+
+    [StorableConstructor]
+    private HammingSimilarityCalculator(bool deserializing) : base(deserializing) { }
+    private HammingSimilarityCalculator(HammingSimilarityCalculator original, Cloner cloner) : base(original, cloner) { }
+    public HammingSimilarityCalculator() { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new HammingSimilarityCalculator(this, cloner);
+    }
+
+    public static double CalculateSimilarity(LinearLinkage left, LinearLinkage right) {
+      if (left.Length != right.Length) throw new ArgumentException("Comparing encodings of unequal length");
+      var similarity = 0;
+      for (var i = 0; i < left.Length; i++) {
+        if (left[i] == right[i]) similarity++;
+      }
+      return similarity / (double)left.Length;
+    }
+
+    public override double CalculateSolutionSimilarity(IScope leftSolution, IScope rightSolution) {
+      var left = leftSolution.Variables[SolutionVariableName].Value as LinearLinkage;
+      var right = rightSolution.Variables[SolutionVariableName].Value as LinearLinkage;
+
+      return CalculateSimilarity(left, right);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/HeuristicLab.Encodings.PermutationEncoding-3.3.csproj
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/HeuristicLab.Encodings.PermutationEncoding-3.3.csproj	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/HeuristicLab.Encodings.PermutationEncoding-3.3.csproj	(revision 14751)
@@ -203,4 +203,5 @@
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="ShakingOperators\PermutationShakingOperator.cs" />
+    <Compile Include="SimilarityCalculators\HammingSimilarityCalculator.cs" />
   </ItemGroup>
   <ItemGroup>
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/InsertionManipulator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/InsertionManipulator.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/InsertionManipulator.cs	(revision 14751)
@@ -50,27 +50,10 @@
     /// <param name="permutation">The permutation to manipulate.</param>
     public static void Apply(IRandom random, Permutation permutation) {
-      Permutation original = (Permutation)permutation.Clone();
-      int cutIndex, insertIndex, number;
+      var cutIndex = random.Next(permutation.Length);
+      var insertIndex = random.Next(permutation.Length);
 
-      cutIndex = random.Next(original.Length);
-      insertIndex = random.Next(original.Length);
-      number = original[cutIndex];
+      if (cutIndex == insertIndex) return;
 
-      int i = 0;  // index in new permutation
-      int j = 0;  // index in old permutation
-      while (i < original.Length) {
-        if (j == cutIndex) {
-          j++;
-        }
-        if (i == insertIndex) {
-          permutation[i] = number;
-          i++;
-        }
-        if ((i < original.Length) && (j < original.Length)) {
-          permutation[i] = original[j];
-          i++;
-          j++;
-        }
-      }
+      permutation.Move(cutIndex, cutIndex, insertIndex);
     }
 
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/InversionManipulator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/InversionManipulator.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/InversionManipulator.cs	(revision 14751)
@@ -60,9 +60,5 @@
 
     public static void Apply(Permutation permutation, int breakPoint1, int breakPoint2) {
-      for (int i = 0; i <= (breakPoint2 - breakPoint1) / 2; i++) {  // invert permutation between breakpoints
-        int temp = permutation[breakPoint1 + i];
-        permutation[breakPoint1 + i] = permutation[breakPoint2 - i];
-        permutation[breakPoint2 - i] = temp;
-      }
+      permutation.Reverse(breakPoint1, breakPoint2 - breakPoint1 + 1);
     }
 
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/ScrambleManipulator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/ScrambleManipulator.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/ScrambleManipulator.cs	(revision 14751)
@@ -20,4 +20,5 @@
 #endregion
 
+using System.Linq;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -57,4 +58,10 @@
       breakPoint2 = random.Next(breakPoint1 + 1, permutation.Length);
 
+      // TODO: Use Fisher-Yates-Shuffle rather than complicated code below
+      // scrambledIndices = Enumerable.Range(0, breakPoint2 - breakPoint1 + 1).Shuffle(random).ToArray();
+      // Also, it would be more memory-efficient to change here and Apply(Permutation, int, int[]) below to interpret scrambleArray as values, not indices
+      // Don't forget the move generator
+      // BackwardsCompatibility3.3
+      #region This whole code should be replaced by above line when going for 3.4
       scrambledIndices = new int[breakPoint2 - breakPoint1 + 1];
       remainingIndices = new int[breakPoint2 - breakPoint1 + 1];
@@ -77,4 +84,5 @@
         }
       }
+      #endregion
 
       Apply(permutation, breakPoint1, scrambledIndices);
@@ -82,8 +90,5 @@
 
     public static void Apply(Permutation permutation, int startIndex, int[] scrambleArray) {
-      Permutation original = (Permutation)permutation.Clone();
-      for (int i = 0; i < scrambleArray.Length; i++) {  // scramble permutation between breakpoints
-        permutation[startIndex + i] = original[startIndex + scrambleArray[i]];
-      }
+      permutation.Replace(startIndex, scrambleArray.Select(x => permutation[startIndex + x]).ToArray());
     }
 
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/Swap2Manipulator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/Swap2Manipulator.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/Swap2Manipulator.cs	(revision 14751)
@@ -58,7 +58,5 @@
 
     public static void Apply(Permutation permutation, int index1, int index2) {
-      int temp = permutation[index1];
-      permutation[index1] = permutation[index2];
-      permutation[index2] = temp;
+      permutation.Swap(index1, index2);
     }
 
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/Swap3Manipulator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/Swap3Manipulator.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/Swap3Manipulator.cs	(revision 14751)
@@ -57,5 +57,5 @@
     public static void Apply(IRandom random, Permutation permutation) {
       if (permutation.Length < 3) throw new ArgumentException("Swap3Manipulator: The permutation must be at least of size 3.", "permutation");
-      int index1, index2, index3, temp;
+      int index1, index2, index3;
 
       do {
@@ -65,12 +65,6 @@
       } while (index1 == index2 || index2 == index3 || index1 == index3);
 
-      // swap positions 1 and 2
-      temp = permutation[index1];
-      permutation[index1] = permutation[index2];
-      permutation[index2] = temp;
-      // swap positions 2 and 3
-      temp = permutation[index2];
-      permutation[index2] = permutation[index3];
-      permutation[index3] = temp;
+      permutation.Swap(index1, index2);
+      permutation.Swap(index2, index3);
     }
 
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/TranslocationManipulator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/TranslocationManipulator.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Manipulators/TranslocationManipulator.cs	(revision 14751)
@@ -65,24 +65,5 @@
 
     public static void Apply(Permutation permutation, int breakPoint1, int breakPoint2, int insertPoint) {
-      Permutation original = (Permutation)permutation.Clone();
-
-      int i = 0;  // index in new permutation
-      int j = 0;  // index in old permutation
-      while (i < original.Length) {
-        if (i == insertPoint) {  // copy translocated area
-          for (int k = breakPoint1; k <= breakPoint2; k++) {
-            permutation[i] = original[k];
-            i++;
-          }
-        }
-        if (j == breakPoint1) {  // skip area between breakpoints
-          j = breakPoint2 + 1;
-        }
-        if ((i < original.Length) && (j < original.Length)) {
-          permutation[i] = original[j];
-          i++;
-          j++;
-        }
-      }
+      permutation.Move(breakPoint1, breakPoint2, insertPoint);
     }
 
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Permutation.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Permutation.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/Permutation.cs	(revision 14751)
@@ -121,4 +121,38 @@
     }
 
+    public virtual void Swap(int i, int j) {
+      var h = array[i];
+      array[i] = array[j];
+      array[j] = h;
+      OnReset();
+    }
+
+    public virtual void Reverse(int startIndex, int length) {
+      Array.Reverse(array, startIndex, length);
+      if (length > 1) OnReset();
+    }
+
+    public virtual void Move(int startIndex, int endIndex, int insertIndex) {
+      if (insertIndex == startIndex) return;
+      if (insertIndex > startIndex && insertIndex <= endIndex) {
+        var start = endIndex + 1;
+        var end = endIndex + insertIndex - startIndex;
+        insertIndex = startIndex;
+        startIndex = start;
+        endIndex = end;
+      }
+      var original = (int[])array.Clone();
+      Array.Copy(original, startIndex, array, insertIndex, endIndex - startIndex + 1);
+      if (insertIndex > endIndex)
+        Array.Copy(original, endIndex + 1, array, startIndex, insertIndex - startIndex);
+      else Array.Copy(original, insertIndex, array, insertIndex + endIndex - startIndex + 1, startIndex - insertIndex);
+      OnReset();
+    }
+
+    public virtual void Replace(int startIndex, int[] replacement) {
+      Array.Copy(replacement, 0, array, startIndex, replacement.Length);
+      OnReset();
+    }
+
     public event EventHandler PermutationTypeChanged;
 
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/SimilarityCalculators/HammingSimilarityCalculator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/SimilarityCalculators/HammingSimilarityCalculator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.PermutationEncoding/3.3/SimilarityCalculators/HammingSimilarityCalculator.cs	(revision 14751)
@@ -0,0 +1,110 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 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 HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Optimization.Operators;
+
+namespace HeuristicLab.Encodings.PermutationEncoding {
+  [Item("Hamming Similarity Calculator for Permutation", "An operator that performs similarity calculation between two permutation-encoded solutions.")]
+  public sealed class HammingSimilarityCalculator : SingleObjectiveSolutionSimilarityCalculator {
+    protected override bool IsCommutative { get { return true; } }
+
+    private HammingSimilarityCalculator(bool deserializing) : base(deserializing) { }
+    private HammingSimilarityCalculator(HammingSimilarityCalculator original, Cloner cloner) : base(original, cloner) { }
+    public HammingSimilarityCalculator() : base() { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new HammingSimilarityCalculator(this, cloner);
+    }
+
+    public static double CalculateSimilarity(Permutation left, Permutation right) {
+      if (left == null || right == null)
+        throw new ArgumentException("Cannot calculate similarity because one of the provided solutions or both are null.");
+      if (left.PermutationType != right.PermutationType)
+        throw new ArgumentException("Cannot calculate similarity because the provided solutions have different types.");
+      if (left.Length != right.Length)
+        throw new ArgumentException("Cannot calculate similarity because the provided solutions have different lengths.");
+      if (object.ReferenceEquals(left, right)) return 1.0;
+
+      switch (left.PermutationType) {
+        case PermutationTypes.Absolute:
+          return CalculateAbsolute(left, right);
+        case PermutationTypes.RelativeDirected:
+          return CalculateRelativeDirected(left, right);
+        case PermutationTypes.RelativeUndirected:
+          return CalculateRelativeUndirected(left, right);
+        default:
+          throw new InvalidOperationException("unknown permutation type");
+      }
+    }
+
+    private static double CalculateAbsolute(Permutation left, Permutation right) {
+      double similarity = 0.0;
+      for (int i = 0; i < left.Length; i++)
+        if (left[i] == right[i]) similarity++;
+
+      return similarity / left.Length;
+    }
+
+    private static double CalculateRelativeDirected(Permutation left, Permutation right) {
+      int[] edgesR = CalculateEdgesVector(right);
+      int[] edgesL = CalculateEdgesVector(left);
+
+      double similarity = 0.0;
+      for (int i = 0; i < left.Length; i++) {
+        if (edgesL[i] == edgesR[i]) similarity++;
+      }
+
+      return similarity / left.Length;
+    }
+
+    private static double CalculateRelativeUndirected(Permutation left, Permutation right) {
+      int[] edgesR = CalculateEdgesVector(right);
+      int[] edgesL = CalculateEdgesVector(left);
+
+      double similarity = 0.0;
+      for (int i = 0; i < left.Length; i++) {
+        if ((edgesL[i] == edgesR[i]) || (edgesL[edgesR[i]] == i))
+          similarity++;
+      }
+
+      return similarity / left.Length;
+    }
+
+    private static int[] CalculateEdgesVector(Permutation permutation) {
+      // transform path representation into adjacency representation
+      int[] edgesVector = new int[permutation.Length];
+      for (int i = 0; i < permutation.Length - 1; i++)
+        edgesVector[permutation[i]] = permutation[i + 1];
+      edgesVector[permutation[permutation.Length - 1]] = permutation[0];
+      return edgesVector;
+    }
+
+    public override double CalculateSolutionSimilarity(IScope leftSolution, IScope rightSolution) {
+      var sol1 = leftSolution.Variables[SolutionVariableName].Value as Permutation;
+      var sol2 = rightSolution.Variables[SolutionVariableName].Value as Permutation;
+
+      return CalculateSimilarity(sol1, sol2);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.RealVectorEncoding/3.3/HeuristicLab.Encodings.RealVectorEncoding-3.3.csproj
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.RealVectorEncoding/3.3/HeuristicLab.Encodings.RealVectorEncoding-3.3.csproj	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.RealVectorEncoding/3.3/HeuristicLab.Encodings.RealVectorEncoding-3.3.csproj	(revision 14751)
@@ -137,4 +137,5 @@
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="RealVectorEncoding.cs" />
+    <Compile Include="RealVectorEqualityComparer.cs" />
     <Compile Include="RealVectorManipulator.cs" />
   </ItemGroup>
@@ -182,4 +183,5 @@
     <Compile Include="ReflectiveBoundsChecker.cs" />
     <Compile Include="ShakingOperators\RealVectorShakingOperator.cs" />
+    <Compile Include="SimilarityCalculators\HammingSimilarityCalculator.cs" />
     <Compile Include="StrategyParameters\StdDevStrategyVectorCreator.cs" />
     <Compile Include="StrategyParameters\StdDevStrategyVectorCrossover.cs" />
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.RealVectorEncoding/3.3/RealVectorEqualityComparer.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.RealVectorEncoding/3.3/RealVectorEqualityComparer.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.RealVectorEncoding/3.3/RealVectorEqualityComparer.cs	(revision 14751)
@@ -0,0 +1,44 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 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.Collections.Generic;
+
+namespace HeuristicLab.Encodings.RealVectorEncoding {
+  public class RealVectorEqualityComparer : EqualityComparer<RealVector> {
+    public override bool Equals(RealVector x, RealVector y) {
+      if (x == null && y == null) return true;
+      if (x == null || y == null) return false;
+      if (x.Length != y.Length) return false;
+      for (var i = 0; i < x.Length; i++)
+        if (x[i] == y[i]) return false;
+      return true;
+    }
+
+    public override int GetHashCode(RealVector obj) {
+      unchecked {
+        int hash = 17;
+        foreach (var o in obj)
+          hash = hash * 31 + o.GetHashCode();
+        return hash;
+      }
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Encodings.RealVectorEncoding/3.3/SimilarityCalculators/HammingSimilarityCalculator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Encodings.RealVectorEncoding/3.3/SimilarityCalculators/HammingSimilarityCalculator.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Encodings.RealVectorEncoding/3.3/SimilarityCalculators/HammingSimilarityCalculator.cs	(revision 14751)
@@ -0,0 +1,66 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 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 HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Optimization.Operators;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Encodings.RealVectorEncoding {
+  [Item("Hamming Similarity Calculator for RealVector", "Calculates the solution similarity based on the Hamming distance between two real vectors.")]
+  [StorableClass]
+  public sealed class HammingSimilarityCalculator : SingleObjectiveSolutionSimilarityCalculator {
+    protected override bool IsCommutative {
+      get { return true; }
+    }
+
+    [StorableConstructor]
+    private HammingSimilarityCalculator(bool deserializing) : base(deserializing) { }
+    private HammingSimilarityCalculator(HammingSimilarityCalculator original, Cloner cloner) : base(original, cloner) { }
+    public HammingSimilarityCalculator() { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new HammingSimilarityCalculator(this, cloner);
+    }
+
+    public static double CalculateSimilarity(RealVector left, RealVector right) {
+      if (left == null || right == null)
+        throw new ArgumentException("Cannot calculate similarity because one or both of the provided scopes is null.");
+      if (left.Length != right.Length)
+        throw new ArgumentException("Cannot calculate similarity because the provided solutions have different lengths.");
+      if (left == right) return 1.0;
+
+      double similarity = 0.0;
+      for (int i = 0; i < left.Length; i++)
+        if (left[i] == right[i]) similarity++;
+      return similarity / left.Length;
+
+    }
+
+    public override double CalculateSolutionSimilarity(IScope leftSolution, IScope rightSolution) {
+      var left = leftSolution.Variables[SolutionVariableName].Value as RealVector;
+      var right = rightSolution.Variables[SolutionVariableName].Value as RealVector;
+
+      return CalculateSimilarity(left, right);
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.ExtLibs/HeuristicLab.Netron/3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446.csproj
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.ExtLibs/HeuristicLab.Netron/3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446.csproj	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.ExtLibs/HeuristicLab.Netron/3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446.csproj	(revision 14751)
@@ -69,5 +69,5 @@
     <DebugType>full</DebugType>
     <ErrorReport>prompt</ErrorReport>
-    <GenerateSerializationAssemblies>On</GenerateSerializationAssemblies>
+    <GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
     <RunCodeAnalysis>false</RunCodeAnalysis>
     <CodeAnalysisRules>
@@ -145,5 +145,5 @@
     <CodeAnalysisRules>
     </CodeAnalysisRules>
-    <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
+    <GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
     <ErrorReport>prompt</ErrorReport>
     <CodeAnalysisRuleSet>Migrated rules for Netron.Diagramming.Core-3.0.2672.12446.ruleset</CodeAnalysisRuleSet>
Index: /branches/symbreg-factors-2650/HeuristicLab.Optimization.Views/3.3/HeuristicLab.Optimization.Views-3.3.csproj
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Optimization.Views/3.3/HeuristicLab.Optimization.Views-3.3.csproj	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Optimization.Views/3.3/HeuristicLab.Optimization.Views-3.3.csproj	(revision 14751)
@@ -269,4 +269,10 @@
       <DependentUpon>RunCollectionEqualityConstraintView.cs</DependentUpon>
     </Compile>
+    <Compile Include="RunCollectionViews\RunCollectionParameterAnalysisView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="RunCollectionViews\RunCollectionParameterAnalysisView.Designer.cs">
+      <DependentUpon>RunCollectionParameterAnalysisView.cs</DependentUpon>
+    </Compile>
     <Compile Include="RunCollectionViews\RunCollectionRLDView.cs">
       <SubType>UserControl</SubType>
Index: /branches/symbreg-factors-2650/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionParameterAnalysisView.Designer.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionParameterAnalysisView.Designer.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionParameterAnalysisView.Designer.cs	(revision 14751)
@@ -0,0 +1,400 @@
+﻿#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
+
+namespace HeuristicLab.Optimization.Views {
+  partial class RunCollectionParameterAnalysisView {
+    /// <summary>
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary>
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Windows Form Designer generated code
+
+    /// <summary>
+    /// Required method for Designer support - do not modify
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
+      System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend();
+      System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series();
+      System.Windows.Forms.DataVisualization.Charting.Title title1 = new System.Windows.Forms.DataVisualization.Charting.Title();
+      this.chart = new HeuristicLab.Visualization.ChartControlsExtensions.EnhancedChart();
+      this.stepSizeLabel = new System.Windows.Forms.Label();
+      this.stepSizeTextBox = new System.Windows.Forms.TextBox();
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
+      this.logScalingCheckBox = new System.Windows.Forms.CheckBox();
+      this.errorProvider = new System.Windows.Forms.ErrorProvider(this.components);
+      this.groupsTreeView = new System.Windows.Forms.TreeView();
+      this.splitContainer = new System.Windows.Forms.SplitContainer();
+      this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+      this.parametersGroupBox = new System.Windows.Forms.GroupBox();
+      this.parametersTreeView = new System.Windows.Forms.TreeView();
+      this.groupsGroupBox = new System.Windows.Forms.GroupBox();
+      this.removeGroupButton = new System.Windows.Forms.Button();
+      this.addGroupButton = new System.Windows.Forms.Button();
+      this.dataRowsGroupBox = new System.Windows.Forms.GroupBox();
+      this.minMaxCheckBox = new System.Windows.Forms.CheckBox();
+      this.medianCheckBox = new System.Windows.Forms.CheckBox();
+      this.quartilesCheckBox = new System.Windows.Forms.CheckBox();
+      this.averageCheckBox = new System.Windows.Forms.CheckBox();
+      ((System.ComponentModel.ISupportInitialize)(this.chart)).BeginInit();
+      ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).BeginInit();
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
+      this.splitContainer.Panel1.SuspendLayout();
+      this.splitContainer.Panel2.SuspendLayout();
+      this.splitContainer.SuspendLayout();
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
+      this.splitContainer1.Panel1.SuspendLayout();
+      this.splitContainer1.Panel2.SuspendLayout();
+      this.splitContainer1.SuspendLayout();
+      this.parametersGroupBox.SuspendLayout();
+      this.groupsGroupBox.SuspendLayout();
+      this.dataRowsGroupBox.SuspendLayout();
+      this.SuspendLayout();
+      // 
+      // chart
+      // 
+      this.chart.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      chartArea1.AxisX.IsStartedFromZero = false;
+      chartArea1.AxisX.LabelStyle.Format = "N0";
+      chartArea1.AxisX.MinorGrid.Enabled = true;
+      chartArea1.AxisX.MinorGrid.LineColor = System.Drawing.Color.WhiteSmoke;
+      chartArea1.AxisX.MinorGrid.LineDashStyle = System.Windows.Forms.DataVisualization.Charting.ChartDashStyle.Dash;
+      chartArea1.AxisX.ScaleBreakStyle.Enabled = true;
+      chartArea1.AxisX.Title = "Evaluated Solutions";
+      chartArea1.AxisY.LabelStyle.Format = "P0";
+      chartArea1.AxisY.MinorGrid.Enabled = true;
+      chartArea1.AxisY.MinorGrid.LineColor = System.Drawing.Color.WhiteSmoke;
+      chartArea1.AxisY.MinorGrid.LineDashStyle = System.Windows.Forms.DataVisualization.Charting.ChartDashStyle.Dash;
+      chartArea1.AxisY.Title = "Relative Distance to Best Known Quality";
+      chartArea1.CursorX.Interval = 0D;
+      chartArea1.CursorX.IsUserSelectionEnabled = true;
+      chartArea1.CursorY.Interval = 0D;
+      chartArea1.CursorY.IsUserSelectionEnabled = true;
+      chartArea1.Name = "chartArea";
+      this.chart.ChartAreas.Add(chartArea1);
+      this.chart.Cursor = System.Windows.Forms.Cursors.Default;
+      legend1.Alignment = System.Drawing.StringAlignment.Center;
+      legend1.Docking = System.Windows.Forms.DataVisualization.Charting.Docking.Top;
+      legend1.Name = "legend";
+      this.chart.Legends.Add(legend1);
+      this.chart.Location = new System.Drawing.Point(3, 0);
+      this.chart.Name = "chart";
+      series1.ChartArea = "chartArea";
+      series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.FastLine;
+      series1.Legend = "legend";
+      series1.Name = "Series1";
+      this.chart.Series.Add(series1);
+      this.chart.Size = new System.Drawing.Size(735, 852);
+      this.chart.TabIndex = 0;
+      this.chart.Text = "chart";
+      title1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+      title1.Name = "title";
+      this.chart.Titles.Add(title1);
+      this.chart.CustomizeLegend += new System.EventHandler<System.Windows.Forms.DataVisualization.Charting.CustomizeLegendEventArgs>(this.chart_CustomizeLegend);
+      this.chart.MouseDown += new System.Windows.Forms.MouseEventHandler(this.chart_MouseDown);
+      this.chart.MouseMove += new System.Windows.Forms.MouseEventHandler(this.chart_MouseMove);
+      // 
+      // stepSizeLabel
+      // 
+      this.stepSizeLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+      this.stepSizeLabel.AutoSize = true;
+      this.stepSizeLabel.Location = new System.Drawing.Point(3, 831);
+      this.stepSizeLabel.Name = "stepSizeLabel";
+      this.stepSizeLabel.Size = new System.Drawing.Size(55, 13);
+      this.stepSizeLabel.TabIndex = 1;
+      this.stepSizeLabel.Text = "&Step Size:";
+      // 
+      // stepSizeTextBox
+      // 
+      this.stepSizeTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+      this.stepSizeTextBox.Location = new System.Drawing.Point(82, 828);
+      this.stepSizeTextBox.Name = "stepSizeTextBox";
+      this.stepSizeTextBox.Size = new System.Drawing.Size(100, 20);
+      this.stepSizeTextBox.TabIndex = 2;
+      this.toolTip.SetToolTip(this.stepSizeTextBox, "The step size of evaluated solutions on the x-axis.");
+      this.stepSizeTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.stepSizeTextBox_KeyDown);
+      this.stepSizeTextBox.Validating += new System.ComponentModel.CancelEventHandler(this.stepSizeTextBox_Validating);
+      this.stepSizeTextBox.Validated += new System.EventHandler(this.stepSizeTextBox_Validated);
+      // 
+      // logScalingCheckBox
+      // 
+      this.logScalingCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+      this.logScalingCheckBox.AutoSize = true;
+      this.logScalingCheckBox.Location = new System.Drawing.Point(206, 830);
+      this.logScalingCheckBox.Name = "logScalingCheckBox";
+      this.logScalingCheckBox.Size = new System.Drawing.Size(118, 17);
+      this.logScalingCheckBox.TabIndex = 3;
+      this.logScalingCheckBox.Text = "&Logarithmic Scaling";
+      this.toolTip.SetToolTip(this.logScalingCheckBox, "Logarithmic scaling on x-axis.");
+      this.logScalingCheckBox.UseVisualStyleBackColor = true;
+      this.logScalingCheckBox.CheckedChanged += new System.EventHandler(this.logScalingCheckBox_CheckedChanged);
+      // 
+      // errorProvider
+      // 
+      this.errorProvider.BlinkStyle = System.Windows.Forms.ErrorBlinkStyle.NeverBlink;
+      this.errorProvider.ContainerControl = this;
+      // 
+      // groupsTreeView
+      // 
+      this.groupsTreeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.groupsTreeView.CheckBoxes = true;
+      this.groupsTreeView.HideSelection = false;
+      this.groupsTreeView.Location = new System.Drawing.Point(6, 49);
+      this.groupsTreeView.Name = "groupsTreeView";
+      this.groupsTreeView.ShowRootLines = false;
+      this.groupsTreeView.Size = new System.Drawing.Size(255, 524);
+      this.groupsTreeView.TabIndex = 2;
+      this.groupsTreeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.groupsTreeView_AfterCheck);
+      this.groupsTreeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.groupsTreeView_AfterSelect);
+      // 
+      // splitContainer
+      // 
+      this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.splitContainer.Location = new System.Drawing.Point(0, 0);
+      this.splitContainer.Name = "splitContainer";
+      // 
+      // splitContainer.Panel1
+      // 
+      this.splitContainer.Panel1.Controls.Add(this.splitContainer1);
+      // 
+      // splitContainer.Panel2
+      // 
+      this.splitContainer.Panel2.Controls.Add(this.dataRowsGroupBox);
+      this.splitContainer.Panel2.Controls.Add(this.stepSizeTextBox);
+      this.splitContainer.Panel2.Controls.Add(this.stepSizeLabel);
+      this.splitContainer.Panel2.Controls.Add(this.logScalingCheckBox);
+      this.splitContainer.Panel2.Controls.Add(this.chart);
+      this.splitContainer.Size = new System.Drawing.Size(1101, 851);
+      this.splitContainer.SplitterDistance = 273;
+      this.splitContainer.TabIndex = 0;
+      // 
+      // splitContainer1
+      // 
+      this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.splitContainer1.Location = new System.Drawing.Point(0, 0);
+      this.splitContainer1.Name = "splitContainer1";
+      this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
+      // 
+      // splitContainer1.Panel1
+      // 
+      this.splitContainer1.Panel1.Controls.Add(this.parametersGroupBox);
+      // 
+      // splitContainer1.Panel2
+      // 
+      this.splitContainer1.Panel2.Controls.Add(this.groupsGroupBox);
+      this.splitContainer1.Size = new System.Drawing.Size(273, 851);
+      this.splitContainer1.SplitterDistance = 261;
+      this.splitContainer1.TabIndex = 0;
+      // 
+      // parametersGroupBox
+      // 
+      this.parametersGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.parametersGroupBox.Controls.Add(this.parametersTreeView);
+      this.parametersGroupBox.Location = new System.Drawing.Point(3, 3);
+      this.parametersGroupBox.Name = "parametersGroupBox";
+      this.parametersGroupBox.Size = new System.Drawing.Size(267, 255);
+      this.parametersGroupBox.TabIndex = 0;
+      this.parametersGroupBox.TabStop = false;
+      this.parametersGroupBox.Text = "&Parameters";
+      // 
+      // parametersTreeView
+      // 
+      this.parametersTreeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.parametersTreeView.CheckBoxes = true;
+      this.parametersTreeView.HideSelection = false;
+      this.parametersTreeView.Location = new System.Drawing.Point(6, 19);
+      this.parametersTreeView.Name = "parametersTreeView";
+      this.parametersTreeView.ShowRootLines = false;
+      this.parametersTreeView.Size = new System.Drawing.Size(255, 230);
+      this.parametersTreeView.TabIndex = 0;
+      this.parametersTreeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.parametersTreeView_AfterCheck);
+      this.parametersTreeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.parametersTreeView_AfterSelect);
+      // 
+      // groupsGroupBox
+      // 
+      this.groupsGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.groupsGroupBox.Controls.Add(this.removeGroupButton);
+      this.groupsGroupBox.Controls.Add(this.groupsTreeView);
+      this.groupsGroupBox.Controls.Add(this.addGroupButton);
+      this.groupsGroupBox.Location = new System.Drawing.Point(3, 4);
+      this.groupsGroupBox.Name = "groupsGroupBox";
+      this.groupsGroupBox.Size = new System.Drawing.Size(267, 579);
+      this.groupsGroupBox.TabIndex = 0;
+      this.groupsGroupBox.TabStop = false;
+      this.groupsGroupBox.Text = "&Groups";
+      // 
+      // removeGroupButton
+      // 
+      this.removeGroupButton.Enabled = false;
+      this.removeGroupButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Remove;
+      this.removeGroupButton.Location = new System.Drawing.Point(36, 19);
+      this.removeGroupButton.Name = "removeGroupButton";
+      this.removeGroupButton.Size = new System.Drawing.Size(24, 24);
+      this.removeGroupButton.TabIndex = 1;
+      this.removeGroupButton.UseVisualStyleBackColor = true;
+      this.removeGroupButton.Click += new System.EventHandler(this.removeGroupButton_Click);
+      // 
+      // addGroupButton
+      // 
+      this.addGroupButton.Enabled = false;
+      this.addGroupButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Add;
+      this.addGroupButton.Location = new System.Drawing.Point(6, 19);
+      this.addGroupButton.Name = "addGroupButton";
+      this.addGroupButton.Size = new System.Drawing.Size(24, 24);
+      this.addGroupButton.TabIndex = 0;
+      this.addGroupButton.UseVisualStyleBackColor = true;
+      this.addGroupButton.Click += new System.EventHandler(this.addGroupButton_Click);
+      // 
+      // dataRowsGroupBox
+      // 
+      this.dataRowsGroupBox.Anchor = System.Windows.Forms.AnchorStyles.Right;
+      this.dataRowsGroupBox.Controls.Add(this.minMaxCheckBox);
+      this.dataRowsGroupBox.Controls.Add(this.medianCheckBox);
+      this.dataRowsGroupBox.Controls.Add(this.quartilesCheckBox);
+      this.dataRowsGroupBox.Controls.Add(this.averageCheckBox);
+      this.dataRowsGroupBox.Location = new System.Drawing.Point(744, 395);
+      this.dataRowsGroupBox.Name = "dataRowsGroupBox";
+      this.dataRowsGroupBox.Size = new System.Drawing.Size(77, 111);
+      this.dataRowsGroupBox.TabIndex = 4;
+      this.dataRowsGroupBox.TabStop = false;
+      this.dataRowsGroupBox.Text = "&Data Rows";
+      // 
+      // minMaxCheckBox
+      // 
+      this.minMaxCheckBox.AutoSize = true;
+      this.minMaxCheckBox.Location = new System.Drawing.Point(6, 19);
+      this.minMaxCheckBox.Name = "minMaxCheckBox";
+      this.minMaxCheckBox.Size = new System.Drawing.Size(68, 17);
+      this.minMaxCheckBox.TabIndex = 0;
+      this.minMaxCheckBox.Text = "&Min/Max";
+      this.minMaxCheckBox.UseVisualStyleBackColor = true;
+      this.minMaxCheckBox.CheckedChanged += new System.EventHandler(this.dataRowCheckBox_CheckedChanged);
+      // 
+      // medianCheckBox
+      // 
+      this.medianCheckBox.AutoSize = true;
+      this.medianCheckBox.Checked = true;
+      this.medianCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
+      this.medianCheckBox.Location = new System.Drawing.Point(6, 88);
+      this.medianCheckBox.Name = "medianCheckBox";
+      this.medianCheckBox.Size = new System.Drawing.Size(61, 17);
+      this.medianCheckBox.TabIndex = 3;
+      this.medianCheckBox.Text = "M&edian";
+      this.medianCheckBox.UseVisualStyleBackColor = true;
+      this.medianCheckBox.CheckedChanged += new System.EventHandler(this.dataRowCheckBox_CheckedChanged);
+      // 
+      // quartilesCheckBox
+      // 
+      this.quartilesCheckBox.AutoSize = true;
+      this.quartilesCheckBox.Checked = true;
+      this.quartilesCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
+      this.quartilesCheckBox.Location = new System.Drawing.Point(6, 42);
+      this.quartilesCheckBox.Name = "quartilesCheckBox";
+      this.quartilesCheckBox.Size = new System.Drawing.Size(59, 17);
+      this.quartilesCheckBox.TabIndex = 1;
+      this.quartilesCheckBox.Text = "&Q1/Q3";
+      this.quartilesCheckBox.UseVisualStyleBackColor = true;
+      this.quartilesCheckBox.CheckedChanged += new System.EventHandler(this.dataRowCheckBox_CheckedChanged);
+      // 
+      // averageCheckBox
+      // 
+      this.averageCheckBox.AutoSize = true;
+      this.averageCheckBox.Location = new System.Drawing.Point(6, 65);
+      this.averageCheckBox.Name = "averageCheckBox";
+      this.averageCheckBox.Size = new System.Drawing.Size(66, 17);
+      this.averageCheckBox.TabIndex = 2;
+      this.averageCheckBox.Text = "&Average";
+      this.averageCheckBox.UseVisualStyleBackColor = true;
+      this.averageCheckBox.CheckedChanged += new System.EventHandler(this.dataRowCheckBox_CheckedChanged);
+      // 
+      // RunCollectionParameterAnalysisView
+      // 
+      this.BackColor = System.Drawing.SystemColors.Window;
+      this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+      this.Controls.Add(this.splitContainer);
+      this.Name = "RunCollectionParameterAnalysisView";
+      this.Size = new System.Drawing.Size(1101, 851);
+      ((System.ComponentModel.ISupportInitialize)(this.chart)).EndInit();
+      ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).EndInit();
+      this.splitContainer.Panel1.ResumeLayout(false);
+      this.splitContainer.Panel2.ResumeLayout(false);
+      this.splitContainer.Panel2.PerformLayout();
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
+      this.splitContainer.ResumeLayout(false);
+      this.splitContainer1.Panel1.ResumeLayout(false);
+      this.splitContainer1.Panel2.ResumeLayout(false);
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
+      this.splitContainer1.ResumeLayout(false);
+      this.parametersGroupBox.ResumeLayout(false);
+      this.groupsGroupBox.ResumeLayout(false);
+      this.dataRowsGroupBox.ResumeLayout(false);
+      this.dataRowsGroupBox.PerformLayout();
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private Visualization.ChartControlsExtensions.EnhancedChart chart;
+    private System.Windows.Forms.Label stepSizeLabel;
+    private System.Windows.Forms.TextBox stepSizeTextBox;
+    private System.Windows.Forms.ToolTip toolTip;
+    private System.Windows.Forms.ErrorProvider errorProvider;
+    private System.Windows.Forms.CheckBox logScalingCheckBox;
+    private System.Windows.Forms.SplitContainer splitContainer;
+    private System.Windows.Forms.TreeView groupsTreeView;
+    private System.Windows.Forms.GroupBox parametersGroupBox;
+    private System.Windows.Forms.TreeView parametersTreeView;
+    private System.Windows.Forms.CheckBox medianCheckBox;
+    private System.Windows.Forms.CheckBox averageCheckBox;
+    private System.Windows.Forms.CheckBox quartilesCheckBox;
+    private System.Windows.Forms.CheckBox minMaxCheckBox;
+    private System.Windows.Forms.GroupBox dataRowsGroupBox;
+    private System.Windows.Forms.GroupBox groupsGroupBox;
+    private System.Windows.Forms.Button removeGroupButton;
+    private System.Windows.Forms.Button addGroupButton;
+    private System.Windows.Forms.SplitContainer splitContainer1;
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionParameterAnalysisView.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionParameterAnalysisView.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionParameterAnalysisView.cs	(revision 14751)
@@ -0,0 +1,636 @@
+﻿#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.ComponentModel;
+using System.Data;
+using System.Linq;
+using System.Windows.Forms;
+using HeuristicLab.Analysis;
+using HeuristicLab.Collections;
+using HeuristicLab.Common;
+using HeuristicLab.Core.Views;
+using HeuristicLab.MainForm;
+using System.Windows.Forms.DataVisualization.Charting;
+using System.Text;
+using HeuristicLab.Data;
+using System.Drawing;
+using HeuristicLab.Core;
+
+namespace HeuristicLab.Optimization.Views {
+  [View("Parameter Analysis")]
+  [Content(typeof(RunCollection), false)]
+  public sealed partial class RunCollectionParameterAnalysisView : ItemView {
+    #region Colors
+    private static readonly Color[] colors = new[] {
+      Color.FromArgb(0x40, 0x6A, 0xB7),
+      Color.FromArgb(0xB1, 0x6D, 0x01),
+      Color.FromArgb(0x4E, 0x8A, 0x06),
+      Color.FromArgb(0x75, 0x50, 0x7B),
+      Color.FromArgb(0x72, 0x9F, 0xCF),
+      Color.FromArgb(0xA4, 0x00, 0x00),
+      Color.FromArgb(0xAD, 0x7F, 0xA8),
+      Color.FromArgb(0x29, 0x50, 0xCF),
+      Color.FromArgb(0x90, 0xB0, 0x60),
+      Color.FromArgb(0xF5, 0x89, 0x30),
+      Color.FromArgb(0x55, 0x57, 0x53),
+      Color.FromArgb(0xEF, 0x59, 0x59),
+      Color.FromArgb(0xED, 0xD4, 0x30),
+      Color.FromArgb(0x63, 0xC2, 0x16),
+    };
+    #endregion
+
+    private bool suppressUpdates = false;
+    private int stepSize = 1000;
+    private Dictionary<IRun, List<Tuple<int, double>>> runData;
+    private Dictionary<string, ParameterInfo> paramInfos;
+
+    public new RunCollection Content {
+      get { return (RunCollection)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public RunCollectionParameterAnalysisView() {
+      InitializeComponent();
+      chart.CustomizeAllChartAreas();
+      stepSizeTextBox.Text = stepSize.ToString();
+      errorProvider.SetIconAlignment(stepSizeTextBox, ErrorIconAlignment.MiddleLeft);
+      errorProvider.SetIconPadding(stepSizeTextBox, 2);
+    }
+
+    #region Content Events
+    protected override void DeregisterContentEvents() {
+      Content.ItemsAdded -= Content_ItemsAdded;
+      Content.ItemsRemoved -= Content_ItemsRemoved;
+      Content.CollectionReset -= Content_CollectionReset;
+      Content.UpdateOfRunsInProgressChanged -= Content_UpdateOfRunsInProgressChanged;
+      DeregisterRunEvents(Content);
+      base.DeregisterContentEvents();
+    }
+    protected override void RegisterContentEvents() {
+      base.RegisterContentEvents();
+      Content.ItemsAdded += Content_ItemsAdded;
+      Content.ItemsRemoved += Content_ItemsRemoved;
+      Content.CollectionReset += Content_CollectionReset;
+      Content.UpdateOfRunsInProgressChanged += Content_UpdateOfRunsInProgressChanged;
+      RegisterRunEvents(Content);
+    }
+    private void DeregisterRunEvents(IEnumerable<IRun> runs) {
+      foreach (var run in runs)
+        run.PropertyChanged -= Run_PropertyChanged;
+    }
+    private void RegisterRunEvents(IEnumerable<IRun> runs) {
+      foreach (var run in runs)
+        run.PropertyChanged += Run_PropertyChanged;
+    }
+
+    private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
+      RegisterRunEvents(e.Items);
+    }
+    private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
+      DeregisterRunEvents(e.Items);
+    }
+    private void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
+      DeregisterRunEvents(e.OldItems);
+      RegisterRunEvents(e.Items);
+    }
+    private void Content_UpdateOfRunsInProgressChanged(object sender, EventArgs e) {
+      if (InvokeRequired)
+        Invoke((Action<object, EventArgs>)Content_UpdateOfRunsInProgressChanged, sender, e);
+      else {
+        suppressUpdates = Content.UpdateOfRunsInProgress;
+        if (suppressUpdates) return;
+        CollectRunData();
+        AnalyzeParameters();
+        UpdateChart();
+      }
+    }
+    private void Run_PropertyChanged(object sender, PropertyChangedEventArgs e) {
+      if (suppressUpdates) return;
+      if (InvokeRequired)
+        Invoke((Action<object, PropertyChangedEventArgs>)Run_PropertyChanged, sender, e);
+      else {
+        CollectRunData();
+        AnalyzeParameters();
+        UpdateChart();
+      }
+    }
+    #endregion
+
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      CollectRunData();
+      AnalyzeParameters();
+      UpdateChart();
+    }
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      parametersGroupBox.Enabled = Content != null;
+      groupsGroupBox.Enabled = Content != null;
+      stepSizeTextBox.Enabled = Content != null;
+      logScalingCheckBox.Enabled = Content != null;
+      dataRowsGroupBox.Enabled = Content != null;
+    }
+
+    private void AnalyzeParameters() {
+      paramInfos = new Dictionary<string, ParameterInfo>();
+      if (Content == null) return;
+
+      foreach (var run in runData.Keys) {
+        foreach (var param in run.Parameters) {
+          ParameterInfo info;
+          if (!paramInfos.TryGetValue(param.Key, out info))
+            paramInfos.Add(param.Key, new ParameterInfo(param.Key, param.Value.ToString(), run));
+          else
+            info.AddValue(param.Value.ToString(), run);
+        }
+      }
+
+      // remove irrelevant parameters
+      if (paramInfos.ContainsKey("Seed"))
+        paramInfos.Remove("Seed");
+
+      // remove all parameters which only have a single value
+      var singles = new List<string>();
+      foreach (var paramInfo in paramInfos.Values) {
+        if (paramInfo.Values.Count == 1)
+          singles.Add(paramInfo.Name);
+      }
+      foreach (var single in singles)
+        paramInfos.Remove(single);
+
+      // set color of parameter values
+      int i = 0;
+      foreach (var valueInfo in paramInfos.Values.SelectMany(x => x.Values.Values)) {
+        valueInfo.Color = colors[i];
+        i = (i + 1) % colors.Length;
+      }
+
+      // populate parametersTreeView
+      parametersTreeView.Nodes.Clear();
+      var paramsRoot = new TreeNode("All Runs (" + runData.Keys.Count + ")");
+      paramsRoot.Tag = runData.Keys.ToList();
+      foreach (var paramInfo in paramInfos.Values.OrderBy(x => x.Name)) {
+        var node = new TreeNode(paramInfo.Name + " (" + paramInfo.RunCount + ")");
+        foreach (var value in paramInfo.Values) {
+          var child = new TreeNode(value.Key + " (" + value.Value.RunCount + ")");
+          child.Tag = value.Value;
+          node.Nodes.Add(child);
+        }
+        node.Tag = paramInfo;
+        paramsRoot.Nodes.Add(node);
+      }
+      parametersTreeView.Nodes.Add(paramsRoot);
+      paramsRoot.Expand();
+      paramsRoot.Checked = true;
+      parametersTreeView.SelectedNode = paramsRoot;
+
+      // populate groupsTreeView
+      groupsTreeView.Nodes.Clear();
+      var groupsRoot = new TreeNode("All Runs (" + runData.Keys.Count + ")");
+      groupsRoot.Tag = runData.Keys.ToList();
+      groupsTreeView.Nodes.Add(groupsRoot);
+      groupsRoot.Expand();
+      groupsTreeView.SelectedNode = groupsRoot;
+    }
+    private void CollectRunData() {
+      runData = new Dictionary<IRun, List<Tuple<int, double>>>();
+      if (Content == null) return;
+
+      foreach (var run in Content) {
+        try {
+          IList<Tuple<double, double>> values = (run.Results["QualityPerEvaluations"] as IndexedDataTable<double>).Rows["First-hit Graph"].Values;
+          var bestKnown = (run.Results["BestKnownQuality"] as DoubleValue).Value;
+
+          var data = new List<Tuple<int, double>>();
+          int i = 0;
+          double qual = double.NaN;
+          foreach (var val in values) {
+            while (i * stepSize < val.Item1) {
+              if (!double.IsNaN(qual)) data.Add(Tuple.Create(i * stepSize, qual));
+              i++;
+            }
+            var diff = Math.Abs(bestKnown - val.Item2);
+            qual = bestKnown == 0 ? diff : diff / bestKnown;
+          }
+          runData.Add(run, data);
+        }
+        catch {
+        }
+      }
+    }
+
+    private void UpdateChart() {
+      if (suppressUpdates) return;
+
+      chart.Series.Clear();
+      chart.Legends[0].CustomItems.Clear();
+      chart.Titles[0].Text = string.Empty;
+
+      var checkedNodes = TraverseTreeNodes(parametersTreeView.Nodes)
+                           .Where(x => x.Checked)
+                           .Concat(
+                             TraverseTreeNodes(groupsTreeView.Nodes)
+                               .Where(x => x.Checked)
+                           );
+
+      foreach (var node in checkedNodes) {
+        if (node.Parent == null) {
+          var series = BuildSeries(node.Tag as IEnumerable<IRun>, colors[0]);
+          chart.Titles[0].Text = node.Text;
+          foreach (var s in series)
+            chart.Series.Add(s);
+          var legendItem = new LegendItem();
+          var legendItemInfo = new LegendItemInfo(colors[0], series);
+          legendItem.Color = legendItemInfo.Color;
+          legendItem.BorderColor = Color.Transparent;
+          legendItem.Name = node.Text;
+          legendItem.Tag = legendItemInfo;
+          chart.Legends[0].CustomItems.Add(legendItem);
+        } else if (node.Tag is ParameterInfo) {
+          var paramInfo = node.Tag as ParameterInfo;
+          chart.Titles[0].Text = paramInfo.Name + " (" + paramInfo.RunCount + ")";
+          foreach (var value in paramInfo.Values) {
+            var series = BuildSeries(value.Value.Runs, value.Value.Color);
+            foreach (var s in series)
+              chart.Series.Add(s);
+            var legendItem = new LegendItem();
+            var legendItemInfo = new LegendItemInfo(value.Value.Color, series);
+            legendItem.Color = legendItemInfo.Color;
+            legendItem.BorderColor = Color.Transparent;
+            legendItem.Name = value.Key + " (" + value.Value.RunCount + ")";
+            legendItem.Tag = legendItemInfo;
+            chart.Legends[0].CustomItems.Add(legendItem);
+          }
+        } else if (node.Tag is ParameterValueInfo) {
+          var valueInfo = node.Tag as ParameterValueInfo;
+          var series = BuildSeries(valueInfo.Runs, valueInfo.Color);
+          chart.Titles[0].Text = node.Parent.Text;
+          foreach (var s in series)
+            chart.Series.Add(s);
+          var legendItem = new LegendItem();
+          var legendItemInfo = new LegendItemInfo(valueInfo.Color, series);
+          legendItem.Color = legendItemInfo.Color;
+          legendItem.BorderColor = Color.Transparent;
+          legendItem.Name = node.Text;
+          legendItem.Tag = legendItemInfo;
+          chart.Legends[0].CustomItems.Add(legendItem);
+        } else if (node.Tag is GroupInfo) {
+          var groupInfo = node.Tag as GroupInfo;
+          if (groupInfo.IsParameter) {
+            chart.Titles[0].Text = groupInfo.Text + " (" + groupInfo.Runs.Count + ")";
+            foreach (TreeNode child in node.Nodes) {
+              var childInfo = child.Tag as GroupInfo;
+              var series = BuildSeries(childInfo.Runs, childInfo.Color);
+              foreach (var s in series)
+                chart.Series.Add(s);
+              var legendItem = new LegendItem();
+              var legendItemInfo = new LegendItemInfo(childInfo.Color, series);
+              legendItem.Color = legendItemInfo.Color;
+              legendItem.BorderColor = Color.Transparent;
+              legendItem.Name = childInfo.Text + " (" + childInfo.Runs.Count + ")";
+              legendItem.Tag = legendItemInfo;
+              chart.Legends[0].CustomItems.Add(legendItem);
+            }
+          } else {
+            var parentInfo = node.Parent.Tag as GroupInfo;
+            chart.Titles[0].Text = parentInfo.Text + " (" + parentInfo.Runs.Count + ")";
+            var series = BuildSeries(groupInfo.Runs, groupInfo.Color);
+            foreach (var s in series)
+              chart.Series.Add(s);
+            var legendItem = new LegendItem();
+            var legendItemInfo = new LegendItemInfo(groupInfo.Color, series);
+            legendItem.Color = legendItemInfo.Color;
+            legendItem.BorderColor = Color.Transparent;
+            legendItem.Name = groupInfo.Text + " (" + groupInfo.Runs.Count + ")";
+            legendItem.Tag = legendItemInfo;
+            chart.Legends[0].CustomItems.Add(legendItem);
+          }
+        }
+      }
+    }
+    private void UpdateSeriesVisibility() {
+      foreach (var legendItem in chart.Legends[0].CustomItems) {
+        var legendItemInfo = legendItem.Tag as LegendItemInfo;
+        foreach (var s in legendItemInfo.Series) {
+          if (legendItemInfo.SeriesVisible) {
+            var seriesInfo = s.Tag as SeriesInfo;
+            switch (seriesInfo.Type) {
+              case SeriesTypes.MinMax:
+                s.Color = minMaxCheckBox.Checked ? seriesInfo.Color : Color.Transparent;
+                break;
+              case SeriesTypes.Quartiles:
+                s.Color = quartilesCheckBox.Checked ? seriesInfo.Color : Color.Transparent;
+                break;
+              case SeriesTypes.Median:
+                s.Color = medianCheckBox.Checked ? seriesInfo.Color : Color.Transparent;
+                break;
+              case SeriesTypes.Average:
+                s.Color = averageCheckBox.Checked ? seriesInfo.Color : Color.Transparent;
+                break;
+            }
+          } else {
+            s.Color = Color.Transparent;
+          }
+        }
+      }
+    }
+
+    private IEnumerable<Series> BuildSeries(IEnumerable<IRun> runs, Color color) {
+      var values = new Dictionary<int, List<double>>();
+      foreach (var run in runs) {
+        foreach (var step in runData[run]) {
+          List<double> vals;
+          if (!values.TryGetValue(step.Item1, out vals)) {
+            vals = new List<double>();
+            values.Add(step.Item1, vals);
+          }
+          vals.Add(step.Item2);
+        }
+      }
+
+      List<Series> series = new List<Series>();
+      var minMaxSeries = new Series();
+      var minMaxSeriesInfo = new SeriesInfo(Color.FromArgb(25, color), SeriesTypes.MinMax);
+      minMaxSeries.ChartType = SeriesChartType.Range;
+      minMaxSeries.Color = minMaxCheckBox.Checked ? minMaxSeriesInfo.Color : Color.Transparent;
+      minMaxSeries.IsVisibleInLegend = false;
+      minMaxSeries.Tag = minMaxSeriesInfo;
+      series.Add(minMaxSeries);
+
+      var quartilesSeries = new Series();
+      var quartilesSeriesInfo = new SeriesInfo(Color.FromArgb(50, color), SeriesTypes.Quartiles);
+      quartilesSeries.ChartType = SeriesChartType.Range;
+      quartilesSeries.Color = quartilesCheckBox.Checked ? quartilesSeriesInfo.Color : Color.Transparent;
+      quartilesSeries.IsVisibleInLegend = false;
+      quartilesSeries.Tag = quartilesSeriesInfo;
+      series.Add(quartilesSeries);
+
+      var medianSeries = new Series();
+      var medianSeriesInfo = new SeriesInfo(color, SeriesTypes.Median);
+      medianSeries.ChartType = SeriesChartType.FastLine;
+      medianSeries.Color = medianCheckBox.Checked ? medianSeriesInfo.Color : Color.Transparent;
+      medianSeries.BorderWidth = 3;
+      medianSeries.BorderDashStyle = ChartDashStyle.Solid;
+      medianSeries.IsVisibleInLegend = false;
+      medianSeries.Tag = medianSeriesInfo;
+      series.Add(medianSeries);
+
+      var averageSeries = new Series();
+      var averageSeriesInfo = new SeriesInfo(color, SeriesTypes.Average);
+      averageSeries.ChartType = SeriesChartType.FastLine;
+      averageSeries.Color = averageCheckBox.Checked ? averageSeriesInfo.Color : Color.Transparent;
+      averageSeries.BorderWidth = 3;
+      averageSeries.BorderDashStyle = ChartDashStyle.Dash;
+      averageSeries.IsVisibleInLegend = false;
+      averageSeries.Tag = averageSeriesInfo;
+      series.Add(averageSeries);
+
+      foreach (var point in values.OrderBy(x => x.Key)) {
+        if (point.Value.Count > 0) {
+          minMaxSeries.Points.Add(new DataPoint(point.Key, new double[] { point.Value.Min(), point.Value.Max() }));
+          quartilesSeries.Points.Add(new DataPoint(point.Key, new double[] { point.Value.Quantile(0.25), point.Value.Quantile(0.75) }));
+          medianSeries.Points.Add(new DataPoint(point.Key, point.Value.Median()));
+          averageSeries.Points.Add(new DataPoint(point.Key, point.Value.Average()));
+        }
+      }
+      return series;
+    }
+
+    #region Control Events
+    #region stepSizeTextBox
+    private void stepSizeTextBox_KeyDown(object sender, KeyEventArgs e) {
+      if ((e.KeyCode == Keys.Enter) || (e.KeyCode == Keys.Return))
+        stepSizeLabel.Select();  // select label to validate data
+
+      if (e.KeyCode == Keys.Escape) {
+        stepSizeTextBox.Text = stepSize.ToString();
+        stepSizeLabel.Select();  // select label to validate data
+      }
+    }
+    private void stepSizeTextBox_Validating(object sender, CancelEventArgs e) {
+      int val;
+      if (!int.TryParse(stepSizeTextBox.Text, out val) || val <= 0) {
+        e.Cancel = true;
+        errorProvider.SetError(stepSizeTextBox, "Invalid Value (Valid Value Format: \"[+]digits\")");
+        stepSizeTextBox.SelectAll();
+      }
+    }
+    private void stepSizeTextBox_Validated(object sender, EventArgs e) {
+      int val = int.Parse(stepSizeTextBox.Text);
+      errorProvider.SetError(stepSizeTextBox, string.Empty);
+      stepSizeTextBox.Text = val.ToString();
+      if (stepSize != val) {
+        stepSize = val;
+        CollectRunData();
+        UpdateChart();
+      }
+    }
+    #endregion
+    #region logScalingCheckBox
+    private void logScalingCheckBox_CheckedChanged(object sender, EventArgs e) {
+      chart.ChartAreas[0].AxisX.IsLogarithmic = logScalingCheckBox.Checked;
+    }
+    #endregion
+    #region chart
+    private void chart_MouseDown(object sender, MouseEventArgs e) {
+      HitTestResult result = chart.HitTest(e.X, e.Y);
+      if (result.ChartElementType == ChartElementType.LegendItem) {
+        var legendItemInfo = (result.Object as LegendItem).Tag as LegendItemInfo;
+        legendItemInfo.SeriesVisible = !legendItemInfo.SeriesVisible;
+        UpdateSeriesVisibility();
+      }
+    }
+    private void chart_MouseMove(object sender, MouseEventArgs e) {
+      HitTestResult result = chart.HitTest(e.X, e.Y);
+      switch (result.ChartElementType) {
+        case ChartElementType.LegendItem:
+          Cursor = Cursors.Hand;
+          break;
+        default:
+          Cursor = Cursors.Default;
+          break;
+      }
+    }
+    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
+      foreach (LegendItem legendItem in e.LegendItems) {
+        var legendItemInfo = legendItem.Tag as LegendItemInfo;
+        legendItem.Color = legendItemInfo.SeriesVisible ? legendItemInfo.Color : Color.Transparent;
+        foreach (LegendCell cell in legendItem.Cells) {
+          cell.ForeColor = legendItemInfo.SeriesVisible ? Color.Black : Color.Gray;
+        }
+      }
+    }
+    #endregion
+    #region parametersTreeView
+    private void parametersTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
+      addGroupButton.Enabled = (parametersTreeView.SelectedNode != null) && (parametersTreeView.SelectedNode.Parent != null);
+    }
+    private void parametersTreeView_AfterCheck(object sender, TreeViewEventArgs e) {
+      UpdateChart();
+    }
+    #endregion
+    #region groupsTreeView
+    private void groupsTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
+      removeGroupButton.Enabled = (groupsTreeView.SelectedNode != null) && (groupsTreeView.SelectedNode.Parent != null);
+    }
+    private void groupsTreeView_AfterCheck(object sender, TreeViewEventArgs e) {
+      UpdateChart();
+    }
+    #endregion
+    #region addGroupButton, removeGroupButton
+    private void addGroupButton_Click(object sender, EventArgs e) {
+      var group = groupsTreeView.SelectedNode;
+      var param = parametersTreeView.SelectedNode;
+      var groupRuns = group.Parent == null ? group.Tag as IEnumerable<IRun> : (group.Tag as GroupInfo).Runs;
+
+      if (param.Tag is ParameterInfo) {
+        var paramInfo = param.Tag as ParameterInfo;
+        var paramNode = new TreeNode();
+        foreach (var valueInfo in paramInfo.Values.Values) {
+          var valueRuns = groupRuns.Intersect(valueInfo.Runs);
+          var valueNode = new TreeNode(valueInfo.Value + " (" + valueRuns.Count() + ")");
+          valueNode.Tag = new GroupInfo(valueInfo.Value, valueInfo.Color, valueRuns, false);
+          paramNode.Nodes.Add(valueNode);
+        }
+        var paramRuns = groupRuns.Intersect(paramInfo.Runs);
+        paramNode.Text = paramInfo.Name + " (" + paramRuns.Count() + ")";
+        paramNode.Tag = new GroupInfo(paramInfo.Name, Color.Empty, paramRuns, true);
+        group.Nodes.Add(paramNode);
+      } else if (param.Tag is ParameterValueInfo) {
+        var paramInfo = param.Parent.Tag as ParameterInfo;
+        var valueInfo = param.Tag as ParameterValueInfo;
+        var valueRuns = groupRuns.Intersect(valueInfo.Runs);
+        var paramRuns = groupRuns.Intersect(paramInfo.Runs);
+        var paramNode = new TreeNode(paramInfo.Name + " (" + paramRuns.Count() + ")");
+        var valueNode = new TreeNode(valueInfo.Value + " (" + valueRuns.Count() + ")");
+        valueNode.Tag = new GroupInfo(valueInfo.Value, valueInfo.Color, valueRuns, false);
+        paramNode.Tag = new GroupInfo(paramInfo.Name, Color.Empty, paramRuns, true);
+        paramNode.Nodes.Add(valueNode);
+        group.Nodes.Add(paramNode);
+      }
+      groupsTreeView.Nodes[0].Expand();
+    }
+    private void removeGroupButton_Click(object sender, EventArgs e) {
+      if (groupsTreeView.SelectedNode != null)
+        groupsTreeView.SelectedNode.Remove();
+      UpdateChart();
+    }
+    #endregion
+    #region minMaxCheckBox, quartilesCheckBox, averageCheckBox, medianCheckBox
+    private void dataRowCheckBox_CheckedChanged(object sender, EventArgs e) {
+      UpdateSeriesVisibility();
+    }
+    #endregion
+    #endregion
+
+    #region Helpers
+    private IEnumerable<TreeNode> TraverseTreeNodes(TreeNodeCollection nodes) {
+      foreach (var node in nodes.OfType<TreeNode>()) {
+        yield return node;
+        foreach (var child in TraverseTreeNodes(node.Nodes))
+          yield return child;
+      }
+    }
+    #endregion
+
+    #region Inner Types
+    class ParameterInfo {
+      public string Name { get; private set; }
+      public Dictionary<string, ParameterValueInfo> Values { get; private set; }
+      public IEnumerable<IRun> Runs {
+        get { return Values.Values.SelectMany(x => x.Runs); }
+      }
+      public int RunCount {
+        get { return Values.Values.Select(x => x.RunCount).Sum(); }
+      }
+
+      public ParameterInfo(string name, string value, IRun run) {
+        Name = name;
+        Values = new Dictionary<string, ParameterValueInfo>();
+        AddValue(value, run);
+      }
+      public void AddValue(string value, IRun run) {
+        ParameterValueInfo valueInfo;
+        if (!Values.TryGetValue(value, out valueInfo)) {
+          Values.Add(value, new ParameterValueInfo(value, run));
+        } else {
+          valueInfo.Runs.Add(run);
+        }
+      }
+    }
+    class ParameterValueInfo {
+      public string Value { get; private set; }
+      public Color Color { get; set; }
+      public IList<IRun> Runs { get; private set; }
+      public int RunCount {
+        get { return Runs.Count; }
+      }
+
+      public ParameterValueInfo(string value, IRun run) {
+        Value = value;
+        Runs = new List<IRun>();
+        Runs.Add(run);
+      }
+      public ParameterValueInfo(string value, Color color, IRun run) : this(value, run) {
+        Color = color;
+      }
+    }
+    class GroupInfo {
+      public string Text { get; private set; }
+      public Color Color { get; private set; }
+      public IList<IRun> Runs { get; private set; }
+      public bool IsParameter { get; private set; }
+      public GroupInfo(string text, Color color, IEnumerable<IRun> runs, bool isParamter) {
+        Text = text;
+        Color = color;
+        Runs = runs.ToList();
+        IsParameter = isParamter;
+      }
+    }
+    enum SeriesTypes {
+      MinMax,
+      Quartiles,
+      Average,
+      Median
+    }
+    class SeriesInfo {
+      public Color Color { get; private set; }
+      public SeriesTypes Type { get; private set; }
+      public SeriesInfo(Color color, SeriesTypes type) {
+        Color = color;
+        Type = type;
+      }
+    }
+    class LegendItemInfo {
+      public Color Color { get; private set; }
+      public bool SeriesVisible { get; set; }
+      public IEnumerable<Series> Series { get; private set; }
+      public LegendItemInfo(Color color, IEnumerable<Series> series) {
+        Color = color;
+        SeriesVisible = true;
+        Series = series;
+      }
+    }
+    #endregion
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionRLDView.Designer.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionRLDView.Designer.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionRLDView.Designer.cs	(revision 14751)
@@ -64,10 +64,12 @@
       this.tabControl = new System.Windows.Forms.TabControl();
       this.byTargetTabPage = new System.Windows.Forms.TabPage();
+      this.relativeOrAbsoluteComboBox = new System.Windows.Forms.ComboBox();
       this.targetChart = new HeuristicLab.Visualization.ChartControlsExtensions.EnhancedChart();
+      this.markerCheckBox = new System.Windows.Forms.CheckBox();
       this.boundShadingCheckBox = new System.Windows.Forms.CheckBox();
       this.byCostTabPage = new System.Windows.Forms.TabPage();
       this.byCostViewHost = new HeuristicLab.MainForm.WindowsForms.ViewHost();
       this.budgetLogScalingCheckBox = new System.Windows.Forms.CheckBox();
-      this.eachOrAllBudgetsCheckBox = new System.Windows.Forms.CheckBox();
+      this.aggregateBudgetsCheckBox = new System.Windows.Forms.CheckBox();
       this.generateBudgetsButton = new System.Windows.Forms.Button();
       this.byTableTabPage = new System.Windows.Forms.TabPage();
@@ -143,5 +145,5 @@
       this.targetsTextBox.Location = new System.Drawing.Point(59, 8);
       this.targetsTextBox.Name = "targetsTextBox";
-      this.targetsTextBox.Size = new System.Drawing.Size(287, 20);
+      this.targetsTextBox.Size = new System.Drawing.Size(204, 20);
       this.targetsTextBox.TabIndex = 1;
       this.toolTip.SetToolTip(this.targetsTextBox, "The order of the targets is important, first to-hit targets\r\nshould be given firs" +
@@ -200,5 +202,5 @@
       this.budgetsTextBox.Location = new System.Drawing.Point(59, 8);
       this.budgetsTextBox.Name = "budgetsTextBox";
-      this.budgetsTextBox.Size = new System.Drawing.Size(326, 20);
+      this.budgetsTextBox.Size = new System.Drawing.Size(309, 20);
       this.budgetsTextBox.TabIndex = 6;
       this.budgetsTextBox.Validating += new System.ComponentModel.CancelEventHandler(this.budgetsTextBox_Validating);
@@ -221,5 +223,5 @@
       this.aggregateTargetsCheckBox.Checked = true;
       this.aggregateTargetsCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
-      this.aggregateTargetsCheckBox.Location = new System.Drawing.Point(352, 10);
+      this.aggregateTargetsCheckBox.Location = new System.Drawing.Point(374, 10);
       this.aggregateTargetsCheckBox.Name = "aggregateTargetsCheckBox";
       this.aggregateTargetsCheckBox.Size = new System.Drawing.Size(74, 17);
@@ -245,5 +247,7 @@
       // byTargetTabPage
       // 
+      this.byTargetTabPage.Controls.Add(this.relativeOrAbsoluteComboBox);
       this.byTargetTabPage.Controls.Add(this.targetChart);
+      this.byTargetTabPage.Controls.Add(this.markerCheckBox);
       this.byTargetTabPage.Controls.Add(this.boundShadingCheckBox);
       this.byTargetTabPage.Controls.Add(this.targetLogScalingCheckBox);
@@ -260,4 +264,18 @@
       this.byTargetTabPage.Text = "Performance by Target";
       this.byTargetTabPage.UseVisualStyleBackColor = true;
+      // 
+      // relativeOrAbsoluteComboBox
+      // 
+      this.relativeOrAbsoluteComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+      this.relativeOrAbsoluteComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+      this.relativeOrAbsoluteComboBox.FormattingEnabled = true;
+      this.relativeOrAbsoluteComboBox.Items.AddRange(new object[] {
+            "relative",
+            "absolute"});
+      this.relativeOrAbsoluteComboBox.Location = new System.Drawing.Point(269, 8);
+      this.relativeOrAbsoluteComboBox.Name = "relativeOrAbsoluteComboBox";
+      this.relativeOrAbsoluteComboBox.Size = new System.Drawing.Size(99, 21);
+      this.relativeOrAbsoluteComboBox.TabIndex = 8;
+      this.relativeOrAbsoluteComboBox.SelectedIndexChanged += new System.EventHandler(this.relativeOrAbsoluteComboBox_SelectedIndexChanged);
       // 
       // targetChart
@@ -291,4 +309,16 @@
       this.targetChart.MouseMove += new System.Windows.Forms.MouseEventHandler(this.chart_MouseMove);
       // 
+      // markerCheckBox
+      // 
+      this.markerCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+      this.markerCheckBox.AutoSize = true;
+      this.markerCheckBox.Location = new System.Drawing.Point(229, 364);
+      this.markerCheckBox.Name = "markerCheckBox";
+      this.markerCheckBox.Size = new System.Drawing.Size(91, 17);
+      this.markerCheckBox.TabIndex = 6;
+      this.markerCheckBox.Text = "show markers";
+      this.markerCheckBox.UseVisualStyleBackColor = true;
+      this.markerCheckBox.CheckedChanged += new System.EventHandler(this.markerCheckBox_CheckedChanged);
+      // 
       // boundShadingCheckBox
       // 
@@ -309,5 +339,5 @@
       this.byCostTabPage.Controls.Add(this.byCostViewHost);
       this.byCostTabPage.Controls.Add(this.budgetLogScalingCheckBox);
-      this.byCostTabPage.Controls.Add(this.eachOrAllBudgetsCheckBox);
+      this.byCostTabPage.Controls.Add(this.aggregateBudgetsCheckBox);
       this.byCostTabPage.Controls.Add(this.generateBudgetsButton);
       this.byCostTabPage.Controls.Add(this.budgetsLabel);
@@ -350,15 +380,17 @@
       this.budgetLogScalingCheckBox.CheckedChanged += new System.EventHandler(this.logScalingCheckBox_CheckedChanged);
       // 
-      // eachOrAllBudgetsCheckBox
-      // 
-      this.eachOrAllBudgetsCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-      this.eachOrAllBudgetsCheckBox.AutoSize = true;
-      this.eachOrAllBudgetsCheckBox.Location = new System.Drawing.Point(414, 10);
-      this.eachOrAllBudgetsCheckBox.Name = "eachOrAllBudgetsCheckBox";
-      this.eachOrAllBudgetsCheckBox.Size = new System.Drawing.Size(36, 17);
-      this.eachOrAllBudgetsCheckBox.TabIndex = 10;
-      this.eachOrAllBudgetsCheckBox.Text = "all";
-      this.eachOrAllBudgetsCheckBox.UseVisualStyleBackColor = true;
-      this.eachOrAllBudgetsCheckBox.CheckedChanged += new System.EventHandler(this.eachOrAllBudgetsCheckBox_CheckedChanged);
+      // aggregateBudgetsCheckBox
+      // 
+      this.aggregateBudgetsCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+      this.aggregateBudgetsCheckBox.AutoSize = true;
+      this.aggregateBudgetsCheckBox.Checked = true;
+      this.aggregateBudgetsCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
+      this.aggregateBudgetsCheckBox.Location = new System.Drawing.Point(374, 10);
+      this.aggregateBudgetsCheckBox.Name = "aggregateBudgetsCheckBox";
+      this.aggregateBudgetsCheckBox.Size = new System.Drawing.Size(74, 17);
+      this.aggregateBudgetsCheckBox.TabIndex = 10;
+      this.aggregateBudgetsCheckBox.Text = "aggregate";
+      this.aggregateBudgetsCheckBox.UseVisualStyleBackColor = true;
+      this.aggregateBudgetsCheckBox.CheckedChanged += new System.EventHandler(this.aggregateBudgetsCheckBox_CheckedChanged);
       // 
       // generateBudgetsButton
@@ -464,5 +496,5 @@
     private System.Windows.Forms.TabPage byCostTabPage;
     private System.Windows.Forms.TabPage byTableTabPage;
-    private System.Windows.Forms.CheckBox eachOrAllBudgetsCheckBox;
+    private System.Windows.Forms.CheckBox aggregateBudgetsCheckBox;
     private System.Windows.Forms.Button generateBudgetsButton;
     private System.Windows.Forms.CheckBox budgetLogScalingCheckBox;
@@ -473,4 +505,6 @@
     private MainForm.WindowsForms.ViewHost byCostViewHost;
     private System.Windows.Forms.CheckBox boundShadingCheckBox;
+    private System.Windows.Forms.CheckBox markerCheckBox;
+    private System.Windows.Forms.ComboBox relativeOrAbsoluteComboBox;
   }
 }
Index: /branches/symbreg-factors-2650/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionRLDView.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionRLDView.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionRLDView.cs	(revision 14751)
@@ -30,4 +30,5 @@
 using HeuristicLab.Analysis;
 using HeuristicLab.Collections;
+using HeuristicLab.Core;
 using HeuristicLab.Core.Views;
 using HeuristicLab.Data;
@@ -79,4 +80,6 @@
     private double[] targets;
     private double[] budgets;
+    private bool targetsAreRelative = true;
+    private readonly BindingList<ProblemInstance> problems;
 
     private bool suppressUpdates;
@@ -103,4 +106,9 @@
       };
       byCostViewHost.Content = byCostDataTable;
+      suppressUpdates = true;
+      relativeOrAbsoluteComboBox.SelectedItem = targetsAreRelative ? "relative" : "absolute";
+      problems = new BindingList<ProblemInstance>();
+      problemComboBox.DataSource = new BindingSource() { DataSource = problems };
+      problemComboBox.DataBindings.DefaultDataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;
       suppressUpdates = false;
     }
@@ -223,27 +231,25 @@
       }
 
-      var problems = new HashSet<ProblemDescription>();
-      foreach (var run in Content) {
-        problems.Add(new ProblemDescription(run));
-      }
-
-      var problemTypesDifferent = problems.Select(x => x.ProblemType).Where(x => !string.IsNullOrEmpty(x)).Distinct().Count() > 1;
-      var problemNamesDifferent = problems.Select(x => x.ProblemName).Where(x => !string.IsNullOrEmpty(x)).Distinct().Count() > 1;
-      var evaluatorDifferent = problems.Select(x => x.Evaluator).Where(x => !string.IsNullOrEmpty(x)).Distinct().Count() > 1;
-      var maximizationDifferent = problems.Select(x => x.Maximization).Distinct().Count() > 1;
+      var table = (string)dataTableComboBox.SelectedItem;
+      var problemDict = CalculateBestTargetPerProblemInstance(table);
+
+      var problemTypesDifferent = problemDict.Keys.Select(x => x.ProblemType).Where(x => !string.IsNullOrEmpty(x)).Distinct().Count() > 1;
+      var problemNamesDifferent = problemDict.Keys.Select(x => x.ProblemName).Where(x => !string.IsNullOrEmpty(x)).Distinct().Count() > 1;
+      var evaluatorDifferent = problemDict.Keys.Select(x => x.Evaluator).Where(x => !string.IsNullOrEmpty(x)).Distinct().Count() > 1;
+      var maximizationDifferent = problemDict.Keys.Select(x => x.Maximization).Distinct().Count() > 1;
       var allEqual = !problemTypesDifferent && !problemNamesDifferent && !evaluatorDifferent && !maximizationDifferent;
 
-      var selectedProblemItem = (ProblemDescription)problemComboBox.SelectedItem;
-      problemComboBox.Items.Clear();
-      problemComboBox.Items.Add(ProblemDescription.MatchAll);
-      if (selectedProblemItem == null || selectedProblemItem == ProblemDescription.MatchAll)
-        problemComboBox.SelectedIndex = 0;
-      foreach (var prob in problems.OrderBy(x => x.ToString()).ToList()) {
-        prob.DisplayProblemType = problemTypesDifferent;
-        prob.DisplayProblemName = problemNamesDifferent || allEqual;
-        prob.DisplayEvaluator = evaluatorDifferent;
-        prob.DisplayMaximization = maximizationDifferent;
-        problemComboBox.Items.Add(prob);
-        if (prob.Equals(selectedProblemItem)) problemComboBox.SelectedItem = prob;
+      var selectedProblemItem = (ProblemInstance)problemComboBox.SelectedItem;
+      problems.Clear();
+      problems.Add(ProblemInstance.MatchAll);
+      if (problems[0].Equals(selectedProblemItem)) problemComboBox.SelectedItem = problems[0];
+      foreach (var p in problemDict.ToList()) {
+        p.Key.BestKnownQuality = p.Value;
+        p.Key.DisplayProblemType = problemTypesDifferent;
+        p.Key.DisplayProblemName = problemNamesDifferent || allEqual;
+        p.Key.DisplayEvaluator = evaluatorDifferent;
+        p.Key.DisplayMaximization = maximizationDifferent;
+        problems.Add(p.Key);
+        if (p.Key.Equals(selectedProblemItem)) problemComboBox.SelectedItem = p.Key;
       }
       SetEnabledStateOfControls();
@@ -276,6 +282,6 @@
     }
 
-    private Dictionary<string, Dictionary<ProblemDescription, Tuple<double, List<IRun>>>> GroupRuns() {
-      var groupedRuns = new Dictionary<string, Dictionary<ProblemDescription, Tuple<double, List<IRun>>>>();
+    private Dictionary<string, Dictionary<ProblemInstance, List<IRun>>> GroupRuns() {
+      var groupedRuns = new Dictionary<string, Dictionary<ProblemInstance, List<IRun>>>();
 
       var table = (string)dataTableComboBox.SelectedItem;
@@ -285,9 +291,7 @@
       if (string.IsNullOrEmpty(selectedGroup)) return groupedRuns;
 
-      var selectedProblem = (ProblemDescription)problemComboBox.SelectedItem;
+      var selectedProblem = (ProblemInstance)problemComboBox.SelectedItem;
       if (selectedProblem == null) return groupedRuns;
-
-      var targetsPerProblem = CalculateBestTargetPerProblemInstance(table);
-
+      
       foreach (var x in (from r in Content
                          where (selectedGroup == AllRuns || r.Parameters.ContainsKey(selectedGroup))
@@ -295,14 +299,14 @@
                            && r.Results.ContainsKey(table)
                            && r.Visible
-                         group r by selectedGroup == AllRuns ? AllRuns : r.Parameters[selectedGroup].ToString() into g
+                         let key = selectedGroup == AllRuns ? AllRuns : r.Parameters[selectedGroup].ToString()
+                         group r by key into g
                          select Tuple.Create(g.Key, g.ToList()))) {
-        var pDict = new Dictionary<ProblemDescription, Tuple<double, List<IRun>>>();
-        foreach (var y in (from r in x.Item2
-                           let pd = new ProblemDescription(r)
-                           group r by pd into g
-                           select Tuple.Create(g.Key, g.ToList()))) {
-          pDict[y.Item1] = Tuple.Create(targetsPerProblem[y.Item1], y.Item2);
-        }
-        groupedRuns[x.Item1] = pDict;
+        var pDict = problems.ToDictionary(r => r, _ => new List<IRun>());
+        foreach (var y in x.Item2) {
+          var pd = new ProblemInstance(y);
+          List<IRun> l;
+          if (pDict.TryGetValue(pd, out l)) l.Add(y);
+        }
+        groupedRuns[x.Item1] = pDict.Where(a => a.Value.Count > 0).ToDictionary(a => a.Key, a => a.Value);
       }
 
@@ -326,38 +330,209 @@
 
       var xAxisTitles = new HashSet<string>();
+
+      // hits describes the number of target hits at a certain time for a certain group
+      var hits = new Dictionary<string, SortedList<double, int>>();
+      // misses describes the number of target misses after a certain time for a certain group
+      // for instance when a run ends, but has not achieved all targets, misses describes
+      // how many targets have been left open at the point when the run ended
+      var misses = new Dictionary<string, SortedList<double, int>>();
+
+      var aggregate = aggregateTargetsCheckBox.Checked;
+      double minEff = double.MaxValue, maxEff = double.MinValue;
+      var noRuns = 0;
+      foreach (var group in groupedRuns) {
+        SortedList<double, int> epdfHits = null, epdfMisses = null;
+        if (aggregate) {
+          hits[group.Key] = epdfHits = new SortedList<double, int>();
+          misses[group.Key] = epdfMisses = new SortedList<double, int>();
+        }
+        foreach (var problem in group.Value) {
+          var max = problem.Key.IsMaximization();
+          var absTargets = GetAbsoluteTargets(problem.Key).ToArray();
+          foreach (var run in problem.Value) {
+            noRuns++;
+            var resultsTable = (IndexedDataTable<double>)run.Results[table];
+            xAxisTitles.Add(resultsTable.VisualProperties.XAxisTitle);
+
+            var efforts = absTargets.Select(t => GetEffortToHitTarget(resultsTable.Rows.First().Values, t, max)).ToArray();
+            minEff = Math.Min(minEff, efforts.Min(x => x.Item2));
+            maxEff = Math.Max(maxEff, efforts.Max(x => x.Item2));
+            for (var idx = 0; idx < efforts.Length; idx++) {
+              var e = efforts[idx];
+              if (!aggregate) {
+                var key = group.Key + "@" + (targetsAreRelative
+                  ? (targets[idx] * 100).ToString(CultureInfo.CurrentCulture.NumberFormat) + "%"
+                  : targets[idx].ToString(CultureInfo.CurrentCulture.NumberFormat));
+                if (!hits.TryGetValue(key, out epdfHits))
+                  hits[key] = epdfHits = new SortedList<double, int>();
+                if (!misses.TryGetValue(key, out epdfMisses))
+                  misses[key] = epdfMisses = new SortedList<double, int>();
+              }
+              var list = e.Item1 ? epdfHits : epdfMisses;
+              int v;
+              if (list.TryGetValue(e.Item2, out v))
+                list[e.Item2] = v + 1;
+              else list[e.Item2] = 1;
+            }
+          }
+        }
+      }
+
+      UpdateTargetChartAxisXBounds(minEff, maxEff);
+
+      DrawTargetsEcdf(hits, misses, noRuns);
+
+      if (targets.Length == 1) {
+        if (targetsAreRelative)
+          targetChart.ChartAreas[0].AxisY.Title = "Probability to be " + (targets[0] * 100) + "% worse than best";
+        else targetChart.ChartAreas[0].AxisY.Title = "Probability to reach at least a fitness of " + targets[0];
+      } else targetChart.ChartAreas[0].AxisY.Title = "Proportion of reached targets";
+      targetChart.ChartAreas[0].AxisX.Title = string.Join(" / ", xAxisTitles);
+      targetChart.ChartAreas[0].AxisX.IsLogarithmic = CanDisplayLogarithmic();
+      targetChart.ChartAreas[0].CursorY.Interval = 0.05;
+
+      UpdateErtTables(groupedRuns);
+    }
+
+    private void DrawTargetsEcdf(Dictionary<string, SortedList<double, int>> hits, Dictionary<string, SortedList<double, int>> misses, int noRuns) {
       var colorCount = 0;
       var lineStyleCount = 0;
-
-      // if the group contains multiple different problem instances we want to use the
-      // minimal maximal observed effort otherwise we run into situations where we don't
-      // have data for a certain problem instance anymore this is a special case when
-      // aggregating over multiple problem instances      
-      var maxEfforts = new Dictionary<ProblemDescription, double>();
-      double minEff = double.MaxValue, maxEff = double.MinValue;
-      foreach (var group in groupedRuns) {
-        foreach (var problem in group.Value) {
-          double problemSpecificMaxEff;
-          if (!maxEfforts.TryGetValue(problem.Key, out problemSpecificMaxEff)) {
-            problemSpecificMaxEff = 0;
-          }
-          var bestKnownTarget = problem.Value.Item1;
-          var max = problem.Key.IsMaximization();
-          var worstTarget = (max ? (1 - targets.Max()) : (1 + targets.Max())) * bestKnownTarget;
-          var bestTarget = (max ? (1 - targets.Min()) : (1 + targets.Min())) * bestKnownTarget;
-          foreach (var run in problem.Value.Item2) {
-            var row = ((IndexedDataTable<double>)run.Results[table]).Rows.First().Values;
-            var a = row.FirstOrDefault(x => max ? x.Item2 >= worstTarget : x.Item2 <= worstTarget);
-            var b = row.FirstOrDefault(x => max ? x.Item2 >= bestTarget : x.Item2 <= bestTarget);
-            var firstEff = (a == default(Tuple<double, double>)) ? row.Last().Item1 : a.Item1;
-            var lastEff = (b == default(Tuple<double, double>)) ? row.Last().Item1 : b.Item1;
-            if (minEff > firstEff) minEff = firstEff;
-            if (maxEff < lastEff) maxEff = lastEff;
-            if (problemSpecificMaxEff < lastEff) problemSpecificMaxEff = lastEff;
-          }
-          maxEfforts[problem.Key] = problemSpecificMaxEff;
-        }
-      }
-      maxEff = Math.Min(maxEff, maxEfforts.Values.Min());
-
+      
+      var showMarkers = markerCheckBox.Checked;
+      foreach (var list in hits) {
+        var row = new Series(list.Key) {
+          ChartType = SeriesChartType.StepLine,
+          BorderWidth = 3,
+          Color = colors[colorCount],
+          BorderDashStyle = lineStyles[lineStyleCount],
+        };
+        var rowShade = new Series(list.Key + "-range") {
+          IsVisibleInLegend = false,
+          ChartType = SeriesChartType.Range,
+          Color = Color.FromArgb(32, colors[colorCount]),
+          YValuesPerPoint = 2
+        };
+
+        var ecdf = 0.0;
+        var missedecdf = 0.0;
+        var iter = misses[list.Key].GetEnumerator();
+        var moreMisses = iter.MoveNext();
+        var totalTargets = noRuns;
+        if (aggregateTargetsCheckBox.Checked) totalTargets *= targets.Length;
+        var movingTargets = totalTargets;
+        var labelPrinted = false;
+        foreach (var h in list.Value) {
+          var prevmissedecdf = missedecdf;
+          while (moreMisses && iter.Current.Key <= h.Key) {
+            if (!labelPrinted && row.Points.Count > 0) {
+              var point = row.Points.Last();
+              point.Label = row.Name;
+              point.MarkerStyle = MarkerStyle.Cross;
+              point.MarkerBorderWidth = 1;
+              point.MarkerSize = 10;
+              labelPrinted = true;
+              rowShade.Points.Add(new DataPoint(point.XValue, new[] {ecdf / totalTargets, (ecdf + missedecdf) / totalTargets}));
+            }
+            missedecdf += iter.Current.Value;
+            movingTargets -= iter.Current.Value;
+            if (row.Points.Count > 0 && row.Points.Last().XValue == iter.Current.Key) {
+              row.Points.Last().SetValueY(ecdf / movingTargets);
+              row.Points.Last().Color = Color.FromArgb(255 - (int)Math.Floor(255 * (prevmissedecdf / totalTargets)), colors[colorCount]);
+            } else {
+              var dp = new DataPoint(iter.Current.Key, ecdf / movingTargets) {
+                Color = Color.FromArgb(255 - (int)Math.Floor(255 * (prevmissedecdf / totalTargets)), colors[colorCount])
+              };
+              if (showMarkers) {
+                dp.MarkerStyle = MarkerStyle.Circle;
+                dp.MarkerBorderWidth = 1;
+                dp.MarkerSize = 5;
+              }
+              row.Points.Add(dp);
+              prevmissedecdf = missedecdf;
+            }
+            if (boundShadingCheckBox.Checked) {
+              if (rowShade.Points.Count > 0 && rowShade.Points.Last().XValue == iter.Current.Key)
+                rowShade.Points.Last().SetValueY(ecdf / totalTargets, (ecdf + missedecdf) / totalTargets);
+              else rowShade.Points.Add(new DataPoint(iter.Current.Key, new[] {ecdf / totalTargets, (ecdf + missedecdf) / totalTargets}));
+            }
+            moreMisses = iter.MoveNext();
+            if (!labelPrinted) {
+              var point = row.Points.Last();
+              point.Label = row.Name;
+              point.MarkerStyle = MarkerStyle.Cross;
+              point.MarkerBorderWidth = 1;
+              point.MarkerSize = 10;
+              labelPrinted = true;
+            }
+          }
+          ecdf += h.Value;
+          if (row.Points.Count > 0 && row.Points.Last().XValue == h.Key) {
+            row.Points.Last().SetValueY(ecdf / movingTargets);
+            row.Points.Last().Color = Color.FromArgb(255 - (int)Math.Floor(255 * (missedecdf / totalTargets)), colors[colorCount]);
+          } else {
+            var dp = new DataPoint(h.Key, ecdf / movingTargets) {
+              Color = Color.FromArgb(255 - (int)Math.Floor(255 * (missedecdf / totalTargets)), colors[colorCount])
+            };
+            if (showMarkers) {
+              dp.MarkerStyle = MarkerStyle.Circle;
+              dp.MarkerBorderWidth = 1;
+              dp.MarkerSize = 5;
+            }
+            row.Points.Add(dp);
+          }
+          if (missedecdf > 0 && boundShadingCheckBox.Checked) {
+            if (rowShade.Points.Count > 0 && rowShade.Points.Last().XValue == h.Key)
+              rowShade.Points.Last().SetValueY(ecdf / totalTargets, (ecdf + missedecdf) / totalTargets);
+            else rowShade.Points.Add(new DataPoint(h.Key, new[] {ecdf / totalTargets, (ecdf + missedecdf) / totalTargets}));
+          }
+        }
+
+        while (moreMisses) {
+          // if there are misses beyond the last hit we extend the shaded area
+          missedecdf += iter.Current.Value;
+          var dp = new DataPoint(iter.Current.Key, ecdf / movingTargets) {
+            Color = Color.FromArgb(255 - (int)Math.Floor(255 * (missedecdf / totalTargets)), colors[colorCount])
+          };
+          if (showMarkers) {
+            dp.MarkerStyle = MarkerStyle.Circle;
+            dp.MarkerBorderWidth = 1;
+            dp.MarkerSize = 5;
+          }
+          row.Points.Add(dp);
+          if (boundShadingCheckBox.Checked) {
+            rowShade.Points.Add(new DataPoint(iter.Current.Key, new[] {ecdf / totalTargets, (ecdf + missedecdf) / totalTargets}));
+          }
+          moreMisses = iter.MoveNext();
+
+          if (!labelPrinted && row.Points.Count > 0) {
+            var point = row.Points.Last();
+            point.Label = row.Name;
+            point.MarkerStyle = MarkerStyle.Cross;
+            point.MarkerBorderWidth = 1;
+            point.MarkerSize = 10;
+            labelPrinted = true;
+          }
+        }
+
+        if (!labelPrinted) {
+          var point = row.Points.Last();
+          point.Label = row.Name;
+          point.MarkerStyle = MarkerStyle.Cross;
+          point.MarkerBorderWidth = 1;
+          point.MarkerSize = 10;
+          rowShade.Points.Add(new DataPoint(point.XValue, new[] {ecdf / totalTargets, (ecdf + missedecdf) / totalTargets}));
+          labelPrinted = true;
+        }
+
+        ConfigureSeries(row);
+        targetChart.Series.Add(rowShade);
+        targetChart.Series.Add(row);
+
+        colorCount = (colorCount + 1) % colors.Length;
+        if (colorCount == 0) lineStyleCount = (lineStyleCount + 1) % lineStyles.Length;
+      }
+    }
+
+    private void UpdateTargetChartAxisXBounds(double minEff, double maxEff) {
       var minZeros = (int)Math.Floor(Math.Log10(minEff));
       var maxZeros = (int)Math.Floor(Math.Log10(maxEff));
@@ -372,118 +547,32 @@
       targetChart.ChartAreas[0].AxisX.Minimum = (double)axisMin;
       targetChart.ChartAreas[0].AxisX.Maximum = (double)axisMax;
-
-      foreach (var group in groupedRuns) {
-        // hits describes the number of target hits at a certain time for a certain group
-        var hits = new Dictionary<string, SortedList<double, int>>();
-        // misses describes the number of target misses after a certain time for a certain group
-        // for instance when a run ends, but has not achieved all targets, misses describes
-        // how many targets have been left open at the point when the run ended
-        var misses = new Dictionary<string, SortedList<double, int>>();
-        var maxLength = 0.0;
-
-        var noRuns = 0;
-        foreach (var problem in group.Value) {
-          foreach (var run in problem.Value.Item2) {
-            var resultsTable = (IndexedDataTable<double>)run.Results[table];
-            xAxisTitles.Add(resultsTable.VisualProperties.XAxisTitle);
-
-            if (aggregateTargetsCheckBox.Checked) {
-              var length = CalculateHitsForAllTargets(hits, misses, resultsTable.Rows.First(), problem.Key, group.Key, problem.Value.Item1, maxEff);
-              maxLength = Math.Max(length, maxLength);
-            } else {
-              CalculateHitsForEachTarget(hits, misses, resultsTable.Rows.First(), problem.Key, group.Key, problem.Value.Item1, maxEff);
-            }
-            noRuns++;
-          }
-        }
-        foreach (var list in hits) {
-          var row = new Series(list.Key) {
-            ChartType = SeriesChartType.StepLine,
-            BorderWidth = 2,
-            Color = colors[colorCount],
-            BorderDashStyle = lineStyles[lineStyleCount],
-          };
-          var rowShade = new Series(list.Key + "-range") {
-            IsVisibleInLegend = false,
-            ChartType = SeriesChartType.Range,
-            Color = Color.FromArgb(32, colors[colorCount])
-          };
-
-          var ecdf = 0.0;
-          var missedecdf = 0.0;
-          var iter = misses[list.Key].GetEnumerator();
-          var moreMisses = iter.MoveNext();
-          var totalTargets = noRuns;
-          if (aggregateTargetsCheckBox.Checked) totalTargets *= targets.Length;
-          var movingTargets = totalTargets;
-          foreach (var h in list.Value) {
-            while (moreMisses && iter.Current.Key <= h.Key) {
-              missedecdf += iter.Current.Value;
-              movingTargets -= iter.Current.Value;
-              if (row.Points.Count > 0 && row.Points.Last().XValue == iter.Current.Key)
-                row.Points.Last().SetValueY(ecdf / movingTargets);
-              else row.Points.AddXY(iter.Current.Key, ecdf / movingTargets);
-              if (boundShadingCheckBox.Checked) {
-                if (rowShade.Points.Count > 0 && rowShade.Points.Last().XValue == iter.Current.Key)
-                  rowShade.Points.Last().SetValueY(ecdf / totalTargets, (ecdf + missedecdf) / totalTargets);
-                else rowShade.Points.Add(new DataPoint(iter.Current.Key, new[] { ecdf / totalTargets, (ecdf + missedecdf) / totalTargets }));
-              }
-              moreMisses = iter.MoveNext();
-            }
-            ecdf += h.Value;
-            if (row.Points.Count > 0 && row.Points.Last().XValue == h.Key)
-              row.Points.Last().SetValueY(ecdf / movingTargets);
-            else row.Points.AddXY(h.Key, ecdf / movingTargets);
-            if (missedecdf > 0 && boundShadingCheckBox.Checked) {
-              if (rowShade.Points.Count > 0 && rowShade.Points.Last().XValue == h.Key)
-                rowShade.Points.Last().SetValueY(ecdf / totalTargets, (ecdf + missedecdf) / totalTargets);
-              else rowShade.Points.Add(new DataPoint(h.Key, new[] { ecdf / totalTargets, (ecdf + missedecdf) / totalTargets }));
-            }
-          }
-
-          while (moreMisses) {
-            // if there are misses beyond the last hit we extend the shaded area
-            missedecdf += iter.Current.Value;
-            //movingTargets -= iter.Current.Value;
-            if (row.Points.Count > 0 && row.Points.Last().XValue == iter.Current.Key)
-              row.Points.Last().SetValueY(ecdf / movingTargets);
-            else row.Points.AddXY(iter.Current.Key, ecdf / movingTargets);
-            if (boundShadingCheckBox.Checked) {
-              if (rowShade.Points.Count > 0 && rowShade.Points.Last().XValue == iter.Current.Key)
-                rowShade.Points.Last().SetValueY(ecdf / totalTargets, (ecdf + missedecdf) / totalTargets);
-              else rowShade.Points.Add(new DataPoint(iter.Current.Key, new[] { ecdf / totalTargets, (ecdf + missedecdf) / totalTargets }));
-            }
-            moreMisses = iter.MoveNext();
-          }
-
-          if (maxLength > 0 && (row.Points.Count == 0 || row.Points.Last().XValue < maxLength))
-            row.Points.AddXY(maxLength, ecdf / movingTargets);
-
-          if (row.Points.Count > 0) {
-            var point = row.Points.Last();
-            point.Label = row.Name;
-            point.MarkerStyle = MarkerStyle.Cross;
-            point.MarkerBorderWidth = 1;
-          }
-
-          ConfigureSeries(row);
-          targetChart.Series.Add(rowShade);
-          targetChart.Series.Add(row);
-        }
-        colorCount = (colorCount + 1) % colors.Length;
-        if (colorCount == 0) lineStyleCount = (lineStyleCount + 1) % lineStyles.Length;
-      }
-
-      if (targets.Length == 1)
-        targetChart.ChartAreas[0].AxisY.Title = "Probability to be " + (targets[0] * 100) + "% worse than best";
-      else targetChart.ChartAreas[0].AxisY.Title = "Proportion of reached targets";
-      targetChart.ChartAreas[0].AxisX.Title = string.Join(" / ", xAxisTitles);
-      targetChart.ChartAreas[0].AxisX.IsLogarithmic = CanDisplayLogarithmic();
-      targetChart.ChartAreas[0].CursorY.Interval = 0.05;
-      UpdateErtTables(groupedRuns);
+    }
+
+    private IEnumerable<double> GetAbsoluteTargets(ProblemInstance pInstance) {
+      if (!targetsAreRelative) return targets;
+      var maximization = pInstance.IsMaximization();
+      var bestKnown = pInstance.BestKnownQuality;
+      if (double.IsNaN(bestKnown)) throw new ArgumentException("Problem instance does not have a defined best - known quality.");
+      IEnumerable<double> tmp = null;
+      if (bestKnown > 0) {
+        tmp = targets.Select(x => (maximization ? (1 - x) : (1 + x)) * bestKnown);
+      } else if (bestKnown < 0) {
+        tmp = targets.Select(x => (!maximization ? (1 - x) : (1 + x)) * bestKnown);
+      } else {
+        // relative to 0 is impossible
+        tmp = targets;
+      }
+      return tmp;
+    }
+
+    private double[] GetAbsoluteTargetsWorstToBest(ProblemInstance pInstance) {
+      if (double.IsNaN(pInstance.BestKnownQuality)) throw new ArgumentException("Problem instance does not have a defined best-known quality.");
+      var absTargets = GetAbsoluteTargets(pInstance);
+      return (pInstance.IsMaximization()
+        ? absTargets.OrderBy(x => x) : absTargets.OrderByDescending(x => x)).ToArray();
     }
 
     private void GenerateDefaultTargets() {
-      targets = new[] { 0.1, 0.05, 0.02, 0.01, 0 };
+      targets = new[] { 0.1, 0.095, 0.09, 0.085, 0.08, 0.075, 0.07, 0.065, 0.06, 0.055, 0.05, 0.045, 0.04, 0.035, 0.03, 0.025, 0.02, 0.015, 0.01, 0.005, 0 };
       suppressTargetsEvents = true;
       targetsTextBox.Text = string.Join("% ; ", targets.Select(x => x * 100)) + "%";
@@ -491,71 +580,19 @@
     }
 
-    private void CalculateHitsForEachTarget(Dictionary<string, SortedList<double, int>> hits,
-                                            Dictionary<string, SortedList<double, int>> misses,
-                                            IndexedDataRow<double> row, ProblemDescription problem,
-                                            string group, double bestTarget, double maxEffort) {
-      foreach (var t in targets.Select(x => Tuple.Create((problem.IsMaximization() ? (1 - x) : (1 + x)) * bestTarget, x))) {
-        var l = t.Item1;
-        var key = group + "_" + (t.Item2 * 100) + "%_" + l;
-        if (!hits.ContainsKey(key)) {
-          hits.Add(key, new SortedList<double, int>());
-          misses.Add(key, new SortedList<double, int>());
-        }
-        var hit = false;
-        foreach (var v in row.Values) {
-          if (v.Item1 > maxEffort) break;
-          if (problem.IsMaximization() && v.Item2 >= l || !problem.IsMaximization() && v.Item2 <= l) {
-            if (hits[key].ContainsKey(v.Item1))
-              hits[key][v.Item1]++;
-            else hits[key][v.Item1] = 1;
-            hit = true;
-            break;
-          }
-        }
-        if (!hit) {
-          var max = Math.Min(row.Values.Last().Item1, maxEffort);
-          if (misses[key].ContainsKey(max))
-            misses[key][max]++;
-          else misses[key][max] = 1;
-        }
-      }
-    }
-
-    private double CalculateHitsForAllTargets(Dictionary<string, SortedList<double, int>> hits,
-                                              Dictionary<string, SortedList<double, int>> misses,
-                                              IndexedDataRow<double> row, ProblemDescription problem,
-                                              string group, double bestTarget, double maxEffort) {
-      var values = row.Values;
-      if (!hits.ContainsKey(group)) {
-        hits.Add(group, new SortedList<double, int>());
-        misses.Add(group, new SortedList<double, int>());
-      }
-
-      var i = 0;
-      var j = 0;
-      while (i < targets.Length && j < values.Count) {
-        var target = (problem.IsMaximization() ? (1 - targets[i]) : (1 + targets[i])) * bestTarget;
-        var current = values[j];
-        if (current.Item1 > maxEffort) break;
-        if (problem.IsMaximization() && current.Item2 >= target
-          || !problem.IsMaximization() && current.Item2 <= target) {
-          if (hits[group].ContainsKey(current.Item1)) hits[group][current.Item1]++;
-          else hits[group][current.Item1] = 1;
-          i++;
-        } else {
-          j++;
-        }
-      }
-      if (j == values.Count) j--;
-      var effort = Math.Min(values[j].Item1, maxEffort);
-      if (i < targets.Length) {
-        if (misses[group].ContainsKey(effort))
-          misses[group][effort] += targets.Length - i;
-        else misses[group][effort] = targets.Length - i;
-      }
-      return effort;
-    }
-
-    private void UpdateErtTables(Dictionary<string, Dictionary<ProblemDescription, Tuple<double, List<IRun>>>> groupedRuns) {
+    private Tuple<bool, double> GetEffortToHitTarget(
+        IEnumerable<Tuple<double, double>> convergenceGraph,
+        double absTarget, bool maximization) {
+      var hit = false;
+      var effort = double.NaN;
+      foreach (var dent in convergenceGraph) {
+        effort = dent.Item1;
+        hit = maximization && dent.Item2 >= absTarget || !maximization && dent.Item2 <= absTarget;
+        if (hit) break;
+      }
+      if (double.IsNaN(effort)) throw new ArgumentException("Convergence graph is empty.", "convergenceGraph");
+      return Tuple.Create(hit, effort);
+    }
+
+    private void UpdateErtTables(Dictionary<string, Dictionary<ProblemInstance, List<IRun>>> groupedRuns) {
       ertTableView.Content = null;
       var columns = 1 + targets.Length + 1;
@@ -565,18 +602,13 @@
       var tableName = (string)dataTableComboBox.SelectedItem;
       if (string.IsNullOrEmpty(tableName)) return;
-
-      var targetsPerProblem = CalculateBestTargetPerProblemInstance(tableName);
-
-      var colNames = new string[columns];
-      colNames[0] = colNames[colNames.Length - 1] = string.Empty;
-      for (var i = 0; i < targets.Length; i++) {
-        colNames[i + 1] = targets[i].ToString("0.0%");
-      }
+      
       var problems = groupedRuns.SelectMany(x => x.Value.Keys).Distinct().ToList();
 
       foreach (var problem in problems) {
+        var max = problem.IsMaximization();
         matrix[rowCount, 0] = problem.ToString();
-        for (var i = 0; i < targets.Length; i++) {
-          matrix[rowCount, i + 1] = (targetsPerProblem[problem] * (problem.IsMaximization() ? (1 - targets[i]) : (1 + targets[i]))).ToString(CultureInfo.CurrentCulture.NumberFormat);
+        var absTargets = GetAbsoluteTargetsWorstToBest(problem);
+        for (var i = 0; i < absTargets.Length; i++) {
+          matrix[rowCount, i + 1] = absTargets[i].ToString(CultureInfo.CurrentCulture.NumberFormat);
         }
         matrix[rowCount, columns - 1] = "#succ";
@@ -590,8 +622,8 @@
             continue;
           }
-          var runs = group.Value[problem].Item2;
-          ErtCalculationResult result = default(ErtCalculationResult);
-          for (var i = 0; i < targets.Length; i++) {
-            result = ExpectedRuntimeHelper.CalculateErt(runs, tableName, (problem.IsMaximization() ? (1 - targets[i]) : (1 + targets[i])) * group.Value[problem].Item1, problem.IsMaximization());
+          var runs = group.Value[problem];
+          var result = default(ErtCalculationResult);
+          for (var i = 0; i < absTargets.Length; i++) {
+            result = ExpectedRuntimeHelper.CalculateErt(runs, tableName, absTargets[i], max);
             matrix[rowCount, i + 1] = result.ToString();
           }
@@ -600,5 +632,5 @@
         }
       }
-      ertTableView.Content = new StringMatrix(matrix) { ColumnNames = colNames };
+      ertTableView.Content = new StringMatrix(matrix);
       ertTableView.DataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
     }
@@ -621,18 +653,16 @@
       var colorCount = 0;
       var lineStyleCount = 0;
-
-      var targetsPerProblem = CalculateBestTargetPerProblemInstance((string)dataTableComboBox.SelectedItem);
-
+      
       foreach (var group in groupedRuns) {
         var hits = new Dictionary<string, SortedList<double, double>>();
 
         foreach (var problem in group.Value) {
-          foreach (var run in problem.Value.Item2) {
+          foreach (var run in problem.Value) {
             var resultsTable = (IndexedDataTable<double>)run.Results[table];
 
-            if (eachOrAllBudgetsCheckBox.Checked) {
-              CalculateHitsForEachBudget(hits, resultsTable.Rows.First(), group.Value.Count, problem.Key, group.Key, problem.Value.Item2.Count, targetsPerProblem[problem.Key]);
+            if (aggregateBudgetsCheckBox.Checked) {
+              CalculateHitsForAllBudgets(hits, resultsTable.Rows.First(), group.Value.Count, problem.Key, group.Key, problem.Value.Count);
             } else {
-              CalculateHitsForAllBudgets(hits, resultsTable.Rows.First(), group.Value.Count, problem.Key, group.Key, problem.Value.Item2.Count, targetsPerProblem[problem.Key]);
+              CalculateHitsForEachBudget(hits, resultsTable.Rows.First(), group.Value.Count, problem.Key, group.Key, problem.Value.Count);
             }
           }
@@ -667,19 +697,9 @@
 
     private void GenerateDefaultBudgets(string table) {
-      var runs = GroupRuns().SelectMany(x => x.Value.Values).SelectMany(x => x.Item2).ToList();
+      var runs = GroupRuns().SelectMany(x => x.Value.Values).SelectMany(x => x).ToList();
       var min = runs.Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Select(y => y.Item1).Min()).Min();
       var max = runs.Select(x => ((IndexedDataTable<double>)x.Results[table]).Rows.First().Values.Select(y => y.Item1).Max()).Max();
-
-      var maxMagnitude = (int)Math.Ceiling(Math.Log10(max));
-      var minMagnitude = (int)Math.Floor(Math.Log10(min));
-      if (maxMagnitude - minMagnitude >= 3) {
-        budgets = new double[maxMagnitude - minMagnitude];
-        for (var i = minMagnitude; i < maxMagnitude; i++) {
-          budgets[i - minMagnitude] = Math.Pow(10, i);
-        }
-      } else {
-        var range = max - min;
-        budgets = Enumerable.Range(0, 6).Select(x => min + (x / 5.0) * range).ToArray();
-      }
+      var points = 20;
+      budgets = Enumerable.Range(1, points).Select(x => min + (x / (double)points) * (max - min)).ToArray();
       suppressBudgetsEvents = true;
       budgetsTextBox.Text = string.Join(" ; ", budgets);
@@ -687,5 +707,6 @@
     }
 
-    private void CalculateHitsForEachBudget(Dictionary<string, SortedList<double, double>> hits, IndexedDataRow<double> row, int groupCount, ProblemDescription problem, string groupName, int problemCount, double bestTarget) {
+    private void CalculateHitsForEachBudget(Dictionary<string, SortedList<double, double>> hits, IndexedDataRow<double> row, int groupCount, ProblemInstance problem, string groupName, int problemCount) {
+      var max = problem.IsMaximization();
       foreach (var b in budgets) {
         var key = groupName + "-" + b;
@@ -697,8 +718,8 @@
             if (prev == null && v.Item1 != b) break;
             var tgt = ((prev == null || v.Item1 == b) ? v.Item2 : prev.Item2);
-            tgt = problem.IsMaximization() ? bestTarget / tgt : tgt / bestTarget;
-            if (hits[key].ContainsKey(tgt))
-              hits[key][tgt] += 1.0 / (groupCount * problemCount);
-            else hits[key][tgt] = 1.0 / (groupCount * problemCount);
+            var relTgt = CalculateRelativeDifference(max, problem.BestKnownQuality, tgt) + 1;
+            if (hits[key].ContainsKey(relTgt))
+              hits[key][relTgt] += 1.0 / (groupCount * problemCount);
+            else hits[key][relTgt] = 1.0 / (groupCount * problemCount);
             break;
           }
@@ -709,5 +730,5 @@
     }
 
-    private void CalculateHitsForAllBudgets(Dictionary<string, SortedList<double, double>> hits, IndexedDataRow<double> row, int groupCount, ProblemDescription problem, string groupName, int problemCount, double bestTarget) {
+    private void CalculateHitsForAllBudgets(Dictionary<string, SortedList<double, double>> hits, IndexedDataRow<double> row, int groupCount, ProblemInstance problem, string groupName, int problemCount) {
       var values = row.Values;
       if (!hits.ContainsKey(groupName)) hits.Add(groupName, new SortedList<double, double>());
@@ -716,4 +737,5 @@
       var j = 0;
       Tuple<double, double> prev = null;
+      var max = problem.IsMaximization();
       while (i < budgets.Length && j < values.Count) {
         var current = values[j];
@@ -721,7 +743,7 @@
           if (prev != null || current.Item1 == budgets[i]) {
             var tgt = (prev == null || current.Item1 == budgets[i]) ? current.Item2 : prev.Item2;
-            tgt = problem.IsMaximization() ? bestTarget / tgt : tgt / bestTarget;
-            if (!hits[groupName].ContainsKey(tgt)) hits[groupName][tgt] = 0;
-            hits[groupName][tgt] += 1.0 / (groupCount * problemCount * budgets.Length);
+            var relTgt = CalculateRelativeDifference(max, problem.BestKnownQuality, tgt) + 1;
+            if (!hits[groupName].ContainsKey(relTgt)) hits[groupName][relTgt] = 0;
+            hits[groupName][relTgt] += 1.0 / (groupCount * problemCount * budgets.Length);
           }
           i++;
@@ -732,8 +754,8 @@
       }
       var lastTgt = values.Last().Item2;
-      lastTgt = problem.IsMaximization() ? bestTarget / lastTgt : lastTgt / bestTarget;
-      if (i < budgets.Length && !hits[groupName].ContainsKey(lastTgt)) hits[groupName][lastTgt] = 0;
+      var lastRelTgt = CalculateRelativeDifference(max, problem.BestKnownQuality, lastTgt) + 1;
+      if (i < budgets.Length && !hits[groupName].ContainsKey(lastRelTgt)) hits[groupName][lastRelTgt] = 0;
       while (i < budgets.Length) {
-        hits[groupName][lastTgt] += 1.0 / (groupCount * problemCount * budgets.Length);
+        hits[groupName][lastRelTgt] += 1.0 / (groupCount * problemCount * budgets.Length);
         i++;
       }
@@ -756,4 +778,5 @@
       if (dataTableComboBox.SelectedIndex >= 0)
         GenerateDefaultBudgets((string)dataTableComboBox.SelectedItem);
+      UpdateBestKnownQualities();
       UpdateRuns();
       SetEnabledStateOfControls();
@@ -782,5 +805,7 @@
           return;
         }
-        targetList.Add(t / 100);
+        if (targetsAreRelative)
+          targetList.Add(t / 100);
+        else targetList.Add(t);
       }
       if (targetList.Count == 0) {
@@ -791,7 +816,11 @@
       e.Cancel = false;
       errorProvider.SetError(targetsTextBox, null);
-      targets = targetList.Select(x => (double)x).OrderByDescending(x => x).ToArray();
+      targets = targetsAreRelative ? targetList.Select(x => (double)x).OrderByDescending(x => x).ToArray() : targetList.Select(x => (double)x).ToArray();
       suppressTargetsEvents = true;
-      try { targetsTextBox.Text = string.Join("% ; ", targets.Select(x => x * 100)) + "%"; } finally { suppressTargetsEvents = false; }
+      try {
+        if (targetsAreRelative)
+          targetsTextBox.Text = string.Join("% ; ", targets.Select(x => x * 100)) + "%";
+        else targetsTextBox.Text = string.Join(" ; ", targets);
+      } finally { suppressTargetsEvents = false; }
 
       UpdateResultsByTarget();
@@ -804,4 +833,31 @@
         UpdateResultsByTarget();
       } finally { ResumeRepaint(true); }
+    }
+
+    private void relativeOrAbsoluteComboBox_SelectedIndexChanged(object sender, EventArgs e) {
+      if (suppressUpdates) return;
+      var pd = (ProblemInstance)problemComboBox.SelectedItem;
+      if (!double.IsNaN(pd.BestKnownQuality)) {
+        var max = pd.IsMaximization();
+        if (targetsAreRelative) targets = GetAbsoluteTargets(pd).ToArray();
+        else {
+          // Rounding to 5 digits since it's certainly appropriate for this application
+          if (pd.BestKnownQuality > 0) {
+            targets = targets.Select(x => Math.Round(max ? 1.0 - (x / pd.BestKnownQuality) : (x / pd.BestKnownQuality) - 1.0, 5)).ToArray();
+          } else if (pd.BestKnownQuality < 0) {
+            targets = targets.Select(x => Math.Round(!max ? 1.0 - (x / pd.BestKnownQuality) : (x / pd.BestKnownQuality) - 1.0, 5)).ToArray();
+          }
+        }
+      }
+      targetsAreRelative = (string)relativeOrAbsoluteComboBox.SelectedItem == "relative";
+      SuspendRepaint();
+      suppressTargetsEvents = true;
+      try {
+        if (targetsAreRelative)
+          targetsTextBox.Text = string.Join("% ; ", targets.Select(x => x * 100)) + "%";
+        else targetsTextBox.Text = string.Join(" ; ", targets);
+
+        UpdateResultsByTarget();
+      } finally { suppressTargetsEvents = false; ResumeRepaint(true); }
     }
 
@@ -823,5 +879,7 @@
             targets = dialog.Values.Select(x => (double)x).ToArray();
             suppressTargetsEvents = true;
-            targetsTextBox.Text = string.Join("% ; ", targets);
+            if (targetsAreRelative)
+              targetsTextBox.Text = string.Join("% ; ", targets);
+            else targetsTextBox.Text = string.Join(" ; ", targets);
             suppressTargetsEvents = false;
 
@@ -835,7 +893,6 @@
     private void addTargetsAsResultButton_Click(object sender, EventArgs e) {
       var table = (string)dataTableComboBox.SelectedItem;
-
-      var targetsPerProblem = CalculateBestTargetPerProblemInstance(table);
-
+      if (string.IsNullOrEmpty(table)) return;
+      
       foreach (var run in Content) {
         if (!run.Results.ContainsKey(table)) continue;
@@ -844,10 +901,11 @@
         var i = 0;
         var j = 0;
-        var pd = new ProblemDescription(run);
-        while (i < targets.Length && j < values.Count) {
-          var target = (pd.IsMaximization() ? (1 - targets[i]) : (1 + targets[i])) * targetsPerProblem[pd];
+        var pd = new ProblemInstance(run);
+        var max = pd.IsMaximization();
+        var absTargets = GetAbsoluteTargetsWorstToBest(problems.Single(x => x.Equals(pd)));
+        while (i < absTargets.Length && j < values.Count) {
+          var target = absTargets[i];
           var current = values[j];
-          if (pd.IsMaximization() && current.Item2 >= target
-              || !pd.IsMaximization() && current.Item2 <= target) {
+          if (max && current.Item2 >= target || !max && current.Item2 <= target) {
             run.Results[table + ".Target" + target] = new DoubleValue(current.Item1);
             i++;
@@ -857,4 +915,11 @@
         }
       }
+    }
+
+    private void markerCheckBox_CheckedChanged(object sender, EventArgs e) {
+      SuspendRepaint();
+      try {
+        UpdateResultsByTarget();
+      } finally { ResumeRepaint(true); }
     }
     #endregion
@@ -887,7 +952,5 @@
     }
 
-    private void eachOrAllBudgetsCheckBox_CheckedChanged(object sender, EventArgs e) {
-      var each = eachOrAllBudgetsCheckBox.Checked;
-      eachOrAllBudgetsCheckBox.Text = each ? "each" : "all";
+    private void aggregateBudgetsCheckBox_CheckedChanged(object sender, EventArgs e) {
       SuspendRepaint();
       try {
@@ -967,14 +1030,31 @@
 
     #region Helpers
-    private Dictionary<ProblemDescription, double> CalculateBestTargetPerProblemInstance(string table) {
+    private double CalculateRelativeDifference(bool maximization, double bestKnown, double fit) {
+      if (bestKnown == 0) {
+        // no relative difference with respect to bestKnown possible
+        return maximization ? -fit : fit;
+      }
+      var absDiff = (fit - bestKnown);
+      var relDiff = absDiff / bestKnown;
+      if (maximization) {
+        return bestKnown > 0 ? -relDiff : relDiff;
+      } else {
+        return bestKnown > 0 ? relDiff : -relDiff;
+      }
+    }
+
+    private Dictionary<ProblemInstance, double> CalculateBestTargetPerProblemInstance(string table) {
+      if (table == null) table = string.Empty;
       return (from r in Content
               where r.Visible
-              let pd = new ProblemDescription(r)
+              let pd = new ProblemInstance(r)
               let target = r.Parameters.ContainsKey("BestKnownQuality")
                            && r.Parameters["BestKnownQuality"] is DoubleValue
                 ? ((DoubleValue)r.Parameters["BestKnownQuality"]).Value
-                : ((IndexedDataTable<double>)r.Results[table]).Rows.First().Values.Last().Item2
+                : (r.Results.ContainsKey(table) && r.Results[table] is IndexedDataTable<double>
+                  ? ((IndexedDataTable<double>)r.Results[table]).Rows.First().Values.Last().Item2
+                  : double.NaN)
               group target by pd into g
-              select new { Problem = g.Key, Target = g.Key.IsMaximization() ? g.Max() : g.Min() })
+              select new { Problem = g.Key, Target = g.Any(x => !double.IsNaN(x)) ? (g.Key.IsMaximization() ? g.Max() : g.Where(x => !double.IsNaN(x)).Min()) : double.NaN })
         .ToDictionary(x => x.Problem, x => x.Target);
     }
@@ -990,4 +1070,18 @@
         UpdateResultsByCost();
       } finally { ResumeRepaint(true); }
+    }
+
+    private void UpdateBestKnownQualities() {
+      var table = (string)dataTableComboBox.SelectedItem;
+      if (string.IsNullOrEmpty(table)) return;
+
+      var targetsPerProblem = CalculateBestTargetPerProblemInstance(table);
+      foreach (var pd in problems) {
+        double bkq;
+        if (targetsPerProblem.TryGetValue(pd, out bkq))
+          pd.BestKnownQuality = bkq;
+        else pd.BestKnownQuality = double.NaN;
+      }
+      //problemComboBox.ResetBindings();
     }
     #endregion
@@ -1078,11 +1172,11 @@
     }
 
-    private class ProblemDescription {
+    private class ProblemInstance : INotifyPropertyChanged {
       private readonly bool matchAll;
-      public static readonly ProblemDescription MatchAll = new ProblemDescription() {
+      public static readonly ProblemInstance MatchAll = new ProblemInstance() {
         ProblemName = "All with Best-Known"
       };
 
-      private ProblemDescription() {
+      private ProblemInstance() {
         ProblemType = string.Empty;
         ProblemName = string.Empty;
@@ -1094,7 +1188,8 @@
         DisplayMaximization = false;
         matchAll = true;
-      }
-
-      public ProblemDescription(IRun run) {
+        BestKnownQuality = double.NaN;
+      }
+
+      public ProblemInstance(IRun run) {
         ProblemType = GetStringValueOrEmpty(run, "Problem Type");
         ProblemName = GetStringValueOrEmpty(run, "Problem Name");
@@ -1106,14 +1201,88 @@
         DisplayMaximization = !string.IsNullOrEmpty(Maximization);
         matchAll = false;
-      }
-
-      public bool DisplayProblemType { get; set; }
-      public string ProblemType { get; set; }
-      public bool DisplayProblemName { get; set; }
-      public string ProblemName { get; set; }
-      public bool DisplayEvaluator { get; set; }
-      public string Evaluator { get; set; }
-      public bool DisplayMaximization { get; set; }
-      public string Maximization { get; set; }
+        BestKnownQuality = GetDoubleValueOrNaN(run, "BestKnownQuality");
+      }
+
+      private bool displayProblemType;
+      public bool DisplayProblemType {
+        get { return displayProblemType; }
+        set {
+          if (displayProblemType == value) return;
+          displayProblemType = value;
+          OnPropertyChanged("DisplayProblemType");
+        }
+      }
+      private string problemType;
+      public string ProblemType {
+        get { return problemType; }
+        set {
+          if (problemType == value) return;
+          problemType = value;
+          OnPropertyChanged("ProblemType");
+        }
+      }
+      private bool displayProblemName;
+      public bool DisplayProblemName {
+        get { return displayProblemName; }
+        set {
+          if (displayProblemName == value) return;
+          displayProblemName = value;
+          OnPropertyChanged("DisplayProblemName");
+        }
+      }
+      private string problemName;
+      public string ProblemName {
+        get { return problemName; }
+        set {
+          if (problemName == value) return;
+          problemName = value;
+          OnPropertyChanged("ProblemName");
+        }
+      }
+      private bool displayEvaluator;
+      public bool DisplayEvaluator {
+        get { return displayEvaluator; }
+        set {
+          if (displayEvaluator == value) return;
+          displayEvaluator = value;
+          OnPropertyChanged("DisplayEvaluator");
+        }
+      }
+      private string evaluator;
+      public string Evaluator {
+        get { return evaluator; }
+        set {
+          if (evaluator == value) return;
+          evaluator = value;
+          OnPropertyChanged("Evaluator");
+        }
+      }
+      private bool displayMaximization;
+      public bool DisplayMaximization {
+        get { return displayMaximization; }
+        set {
+          if (displayMaximization == value) return;
+          displayMaximization = value;
+          OnPropertyChanged("DisplayMaximization");
+        }
+      }
+      private string maximization;
+      public string Maximization {
+        get { return maximization; }
+        set {
+          if (maximization == value) return;
+          maximization = value;
+          OnPropertyChanged("Maximization");
+        }
+      }
+      private double bestKnownQuality;
+      public double BestKnownQuality {
+        get { return bestKnownQuality; }
+        set {
+          if (bestKnownQuality == value) return;
+          bestKnownQuality = value;
+          OnPropertyChanged("BestKnownQuality");
+        }
+      }
 
       public bool IsMaximization() {
@@ -1129,14 +1298,33 @@
       }
 
+      private double GetDoubleValueOrNaN(IRun run, string key) {
+        IItem param;
+        if (run.Parameters.TryGetValue(key, out param)) {
+          var dv = param as DoubleValue;
+          return dv != null ? dv.Value : double.NaN;
+        }
+        return double.NaN;
+      }
+
       private string GetStringValueOrEmpty(IRun run, string key) {
-        return run.Parameters.ContainsKey(key) ? ((StringValue)run.Parameters[key]).Value : string.Empty;
+        IItem param;
+        if (run.Parameters.TryGetValue(key, out param)) {
+          var sv = param as StringValue;
+          return sv != null ? sv.Value : string.Empty;
+        }
+        return string.Empty;
       }
 
       private string GetMaximizationValueOrEmpty(IRun run, string key) {
-        return run.Parameters.ContainsKey(key) ? (((BoolValue)run.Parameters[key]).Value ? "MAX" : "MIN") : string.Empty;
+        IItem param;
+        if (run.Parameters.TryGetValue(key, out param)) {
+          var bv = param as BoolValue;
+          return bv != null ? (bv.Value ? "MAX" : "MIN") : string.Empty;
+        }
+        return string.Empty;
       }
 
       public override bool Equals(object obj) {
-        var other = obj as ProblemDescription;
+        var other = obj as ProblemInstance;
         if (other == null) return false;
         return ProblemType == other.ProblemType
@@ -1155,5 +1343,12 @@
           (DisplayProblemName ? ProblemName : string.Empty),
           (DisplayEvaluator ? Evaluator : string.Empty),
-          (DisplayMaximization ? Maximization : string.Empty)}.Where(x => !string.IsNullOrEmpty(x)));
+          (DisplayMaximization ? Maximization : string.Empty),
+          !double.IsNaN(BestKnownQuality) ? BestKnownQuality.ToString(CultureInfo.CurrentCulture.NumberFormat) : string.Empty }.Where(x => !string.IsNullOrEmpty(x)));
+      }
+
+      public event PropertyChangedEventHandler PropertyChanged;
+      protected virtual void OnPropertyChanged(string propertyName = null) {
+        var handler = PropertyChanged;
+        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
       }
     }
Index: /branches/symbreg-factors-2650/HeuristicLab.PluginInfrastructure/3.3/Advanced/DeploymentService/GenerateServiceClients.cmd
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.PluginInfrastructure/3.3/Advanced/DeploymentService/GenerateServiceClients.cmd	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.PluginInfrastructure/3.3/Advanced/DeploymentService/GenerateServiceClients.cmd	(revision 14751)
@@ -1,22 +1,40 @@
-echo off
+@ECHO OFF
 
-echo.
-echo *******************************************************************************************
-echo Generating DeploymentService clients
-echo.
+SET HOST=
+SET GENERATECONFIG=
 
-svcutil.exe ^
-  http://services.heuristiclab.com/Deployment-3.3/UpdateService.svc/mex ^
-  http://services.heuristiclab.com/Deployment-3.3/AdminService.svc/mex ^
-  /out:ServiceClients ^
-  /namespace:*,HeuristicLab.PluginInfrastructure.Advanced.DeploymentService ^
-  /targetClientVersion:Version35 ^
-  /mergeConfig ^
-  /config:..\..\app.config
+ECHO.
+ECHO *******************************************************************************************
 
-echo.
-echo Generation of DeploymentService clients finished.
-echo *******************************************************************************************
-echo.
+SET /P HOST=Which host should be used? [services.heuristiclab.com]: 
+IF "%HOST%"=="" SET HOST=services.heuristiclab.com
 
-pause
+SET /P GENERATECONFIG=Would you like to generate the configuration file? [y]: 
+IF "%GENERATECONFIG%"=="" SET GENERATECONFIG=y
+
+SET ARGS=http://%HOST%/Deployment-3.3/UpdateService.svc?wsdl http://%HOST%/Deployment-3.3/AdminService.svc?wsdl ^
+/out:ServiceClients ^
+/namespace:*,HeuristicLab.PluginInfrastructure.Advanced.DeploymentService ^
+/targetClientVersion:Version35 ^
+/syncOnly
+
+IF "%GENERATECONFIG%"=="y" (
+  SET ARGS=%ARGS% /config:..\..\app.config /mergeConfig
+) ELSE (
+  SET ARGS=%ARGS% /noConfig
+)
+
+ECHO.
+ECHO Generating UpdateService and AdminService clients
+ECHO.
+
+SETLOCAL ENABLEDELAYEDEXPANSION
+svcutil.exe %ARGS%
+ENDLOCAL
+
+ECHO.
+ECHO Generation of UpdateService and AdminService clients finished.
+ECHO *******************************************************************************************
+ECHO.
+
+PAUSE
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.BinPacking.Views/3.3/Container3DView.xaml
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.BinPacking.Views/3.3/Container3DView.xaml	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.BinPacking.Views/3.3/Container3DView.xaml	(revision 14751)
@@ -30,4 +30,5 @@
              MouseWheel="Container3DView_OnMouseWheel" 
              MouseEnter="Container3DView_OnMouseEnter"
+             KeyDown="Container3DView_OnKeyDown"
              Focusable="true"
              >
@@ -48,67 +49,4 @@
                 <ModelVisual3D.Content>
                     <Model3DGroup>
-                        <GeometryModel3D>
-                            <GeometryModel3D.Geometry>
-                                <!-- two opaque cubes, one in front one in back-->
-                                <MeshGeometry3D x:Name="meshMain" 
-                                Positions="0 0 -2.5 1 0 -2.5  0 1 -2.5  1 1 -2.5  0 0 -1.5  1 0 -1.5  0 1 -1.5  1 1 -1.5
-                                           0 0 0 1 0 0  0 1 0  1 1 0  0 0 1  1 0 1  0 1 1  1 1 1" 
-                                TriangleIndices="10 11 9  10 9 8  15 9 11  15 13 9  14 13 15  14 12 13  14 10 12  10 8 12  10 15 11  10 14 15  0 9 13  8 13 12
-                                                 2 3 1  2 1 0  7 1 3  7 5 1  6 5 7  6 4 5  6 2 4  2 0 4  2 7 3  2 6 7  0 1 5  0 5 4 ">
-                                </MeshGeometry3D>
-                            </GeometryModel3D.Geometry>
-                            <GeometryModel3D.Material>
-                                <DiffuseMaterial>
-                                    <DiffuseMaterial.Brush>
-                                        <SolidColorBrush Color="LightGreen"/>
-                                    </DiffuseMaterial.Brush>
-                                </DiffuseMaterial>
-                            </GeometryModel3D.Material>
-                        </GeometryModel3D>
-                        <GeometryModel3D>
-                            <GeometryModel3D.Geometry>
-                                <!-- two transparent cubes, one in front one in back-->
-                                <!-- draw transparent cubes from back to front and always draw outer and inner triangles and draw from front to back-->
-                                <MeshGeometry3D x:Name="meshInsides" 
-                                Positions="-1 -1 -1  2 -1 -1  -1 2 -1  2 2 -1  -1 -1 2  2 -1 2  -1 2 2  2 2 2
-                                           -1 -1 -3  2 -1 -3  -1 2 -3  2 2 -3  -1 -1 -1  2 -1 -1  -1 2 -1  2 2 -1" 
-                                TriangleIndices="9 11 10  8 9 10  11 9 15  9 13 15  15 13 14  13 12 14  12 10 14  12 8 10  11 15 10  15 14 10  13 9 8  12 13 8
-                                                 1 3 2  0 1 2  3 1 7  1 5 7  7 5 6  5 4 6  4 2 6  4 0 2  3 7 2  7 6 2  5 1 0  4 5 0 
-                                                 ">
-                                </MeshGeometry3D>
-                            </GeometryModel3D.Geometry>
-                            <GeometryModel3D.Material>
-                                <MaterialGroup>
-                                    <DiffuseMaterial>
-                                        <DiffuseMaterial.Brush>
-                                            <SolidColorBrush Opacity="0.1"  Color="DarkGray"/>
-                                        </DiffuseMaterial.Brush>
-                                    </DiffuseMaterial>
-                                </MaterialGroup>
-                            </GeometryModel3D.Material>
-                        </GeometryModel3D>
-                        <GeometryModel3D>
-                            <GeometryModel3D.Geometry>
-                                <!-- two transparent cubes, one in front one in back-->
-                                <!-- draw transparent cubes from back to front and always draw outer and inner triangles and draw from front to back-->
-                                <MeshGeometry3D x:Name="meshTransparent" 
-                                Positions="-1 -1 -1  2 -1 -1  -1 2 -1  2 2 -1  -1 -1 2  2 -1 2  -1 2 2  2 2 2
-                                           -1 -1 -3  2 -1 -3  -1 2 -3  2 2 -3  -1 -1 -1  2 -1 -1  -1 2 -1  2 2 -1" 
-                                TriangleIndices="2 3 1  2 1 0  7 1 3  7 5 1  6 5 7  6 4 5  6 2 4  2 0 4  2 7 3  2 6 7  0 1 5  0 5 4                                                 
-                                                 10 11 9  10 9 8  15 9 11  15 13 9  14 13 15  14 12 13  14 10 12  10 8 12  10 15 11  10 14 15  0 9 13  8 13 12
-                                                 ">
-                                </MeshGeometry3D>
-                            </GeometryModel3D.Geometry>
-                            <GeometryModel3D.Material>
-                                <MaterialGroup>
-                                    <DiffuseMaterial>
-                                        <DiffuseMaterial.Brush>
-                                            <SolidColorBrush Opacity="0.1"  Color="DarkGray"/>
-                                        </DiffuseMaterial.Brush>
-                                    </DiffuseMaterial>
-                                </MaterialGroup>
-                            </GeometryModel3D.Material>
-                        </GeometryModel3D>
-
                     </Model3DGroup>
                 </ModelVisual3D.Content>
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.BinPacking.Views/3.3/Container3DView.xaml.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.BinPacking.Views/3.3/Container3DView.xaml.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.BinPacking.Views/3.3/Container3DView.xaml.cs	(revision 14751)
@@ -19,8 +19,10 @@
 
 using System;
+using System.Collections.Generic;
 using System.Linq;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Input;
+using System.Windows.Media;
 using System.Windows.Media.Media3D;
 using HeuristicLab.Problems.BinPacking3D;
@@ -28,6 +30,27 @@
 namespace HeuristicLab.Problems.BinPacking.Views {
   public partial class Container3DView : UserControl {
+    private static readonly Color[] colors = new[] {
+      Color.FromRgb(0x40, 0x6A, 0xB7),
+      Color.FromRgb(0xB1, 0x6D, 0x01),
+      Color.FromRgb(0x4E, 0x8A, 0x06),
+      Color.FromRgb(0x75, 0x50, 0x7B),
+      Color.FromRgb(0x72, 0x9F, 0xCF),
+      Color.FromRgb(0xA4, 0x00, 0x00),
+      Color.FromRgb(0xAD, 0x7F, 0xA8),
+      Color.FromRgb(0x29, 0x50, 0xCF),
+      Color.FromRgb(0x90, 0xB0, 0x60),
+      Color.FromRgb(0xF5, 0x89, 0x30),
+      Color.FromRgb(0x55, 0x57, 0x53),
+      Color.FromRgb(0xEF, 0x59, 0x59),
+      Color.FromRgb(0xED, 0xD4, 0x30),
+      Color.FromRgb(0x63, 0xC2, 0x16),
+    };
+    
+    private static readonly Color hiddenColor = Color.FromArgb(0x1A, 0xAA, 0xAA, 0xAA);
+    private static readonly Color containerColor = Color.FromArgb(0x7F, 0xAA, 0xAA, 0xAA);
+
     private Point startPos;
     private bool mouseDown = false;
+    private bool ctrlDown = false;
     private double startAngleX;
     private double startAngleY;
@@ -45,7 +68,10 @@
     }
 
+    private Dictionary<int, DiffuseMaterial> materials;
+
     public Container3DView() {
       InitializeComponent();
       camMain.Position = new Point3D(0.5, 3, 3); // for design time we use a different camera position 
+      materials = new Dictionary<int, DiffuseMaterial>();
       Clear();
     }
@@ -74,11 +100,8 @@
       if (packing == null) return; // nothing to display
 
-      // draw all items
-      // order by Z position to reduce artifacts (because of transparent objects) 
-      // TODO: improve code to reduce artifacts 
-      //  - from triangle definitions and lighting
-      //  - from rotation and Z-ordering
-
-      foreach (var item in packing.Items.OrderBy(i => packing.Positions[i.Key].Z)) {
+      var modelGroup = (Model3DGroup)MyModel.Content;
+      var hiddenMaterial = new DiffuseMaterial(new SolidColorBrush(hiddenColor));
+
+      foreach (var item in packing.Items) {
         var position = packing.Positions[item.Key];
 
@@ -87,37 +110,44 @@
         var d = position.Rotated ? item.Value.Width : item.Value.Depth;
 
-        // ignore the item.Material
-
-        // if nothing is selected then draw all cubes opaque 
-        // otherwise draw only the selected cube opaque and all others transparent
-        if (selectedItemKey < 0 || selectedItemKey == item.Key) {
-          AddCube(meshMain, position.X, position.Y, position.Z, w, h, d);
-        } else {
-          AddCube(meshTransparent, position.X, position.Y, position.Z, w, h, d, addInsideTriangles: true);
+        var model = new GeometryModel3D { Geometry = new MeshGeometry3D() };
+        DiffuseMaterial material;
+        if (selectedItemKey >= 0 && selectedItemKey != item.Key)
+          material = hiddenMaterial;
+        else {
+          if (!materials.TryGetValue(item.Value.Material, out material)) {
+            var colorIdx = item.Value.Material;
+            while (colorIdx < 0) colorIdx += colors.Length;
+            colorIdx = colorIdx % colors.Length;
+            var color = colors[colorIdx];
+            material = new DiffuseMaterial { Brush = new SolidColorBrush(color) };
+            materials[item.Value.Material] = material;
+          }
         }
+        model.Material = material;
+        modelGroup.Children.Add(model);
+
+        AddSolidCube((MeshGeometry3D)model.Geometry, position.X, position.Y, position.Z, w, h, d);
       }
 
       var container = packing.BinShape;
-      // draw a transparent container
-      AddCube(meshTransparent, container.Origin.X, container.Origin.Y, container.Origin.Z, container.Width, container.Height, container.Depth, addInsideTriangles: true);
-
-      // TODO: support cuboids with different side lengths
-      // apply scaling so that the container fits into the unit cube (necessary for the transformations)
-      scale.ScaleX = 1.0 / (container.Width);
-      scale.ScaleY = 1.0 / (container.Height);
-      scale.ScaleZ = 1.0 / (container.Depth);
+      var containerModel = new GeometryModel3D(new MeshGeometry3D(), new DiffuseMaterial(new SolidColorBrush(containerColor)));
+      modelGroup.Children.Add(containerModel);
+      AddWireframeCube((MeshGeometry3D)containerModel.Geometry, container.Origin.X - .5, container.Origin.Y - .5, container.Origin.Z - .5, container.Width + 1, container.Height + 1, container.Depth + 1);
+
+      var ratio = Math.Max(container.Width, Math.Max(container.Height, container.Depth));
+      scale.ScaleX = 1.0 / ratio;
+      scale.ScaleY = 1.0 / ratio;
+      scale.ScaleZ = 1.0 / ratio;
+
+      scale.CenterX = .5;
+      scale.CenterY = .5;
+      scale.CenterZ = 0;
     }
 
 
     private void Clear() {
-      meshInsides.Positions.Clear();
-      meshInsides.TriangleIndices.Clear();
-
-      meshMain.Positions.Clear();
-      meshMain.TriangleIndices.Clear();
-
-      meshTransparent.Positions.Clear();
-      meshTransparent.TriangleIndices.Clear();
-
+      ((Model3DGroup)MyModel.Content).Children.Clear();
+      materials.Clear();
+      
       mouseDown = false;
       startAngleX = 0;
@@ -128,4 +158,5 @@
       if (!mouseDown) return;
       var pos = e.GetPosition((IInputElement)this);
+      
       rotateX.Angle = startAngleX + (pos.X - startPos.X) / 4;
       rotateY.Angle = startAngleY + (pos.Y - startPos.Y) / 4;
@@ -155,147 +186,177 @@
     }
 
+    private void Container3DView_OnMouseEnter(object sender, MouseEventArgs e) {
+      Focus(); // for mouse wheel events
+    }
+
+    private void Container3DView_OnKeyDown(object sender, KeyEventArgs e) {
+      ctrlDown = e.Key.HasFlag(Key.LeftCtrl) || e.Key.HasFlag(Key.RightCtrl);
+    }
+
 
     #region helper for cubes
-
-    private void AddCube(MeshGeometry3D mesh, int x, int y, int z, int width, int height, int depth, bool addInsideTriangles = false) {
-      AddOutsideTriangles(mesh, AddPoints(mesh, x, y, z, width, height, depth));
-      if (addInsideTriangles) AddInsideTriangles(meshInsides, AddPoints(meshInsides, x, y, z, width, height, depth));
-    }
-
-    private void AddOutsideTriangles(MeshGeometry3D mesh, int[] pointIdx) {
-      // point indices counter-clockwise
-      // back side
-      mesh.TriangleIndices.Add(pointIdx[0]);
-      mesh.TriangleIndices.Add(pointIdx[2]);
-      mesh.TriangleIndices.Add(pointIdx[1]);
-
-      mesh.TriangleIndices.Add(pointIdx[0]);
-      mesh.TriangleIndices.Add(pointIdx[3]);
-      mesh.TriangleIndices.Add(pointIdx[2]);
-
-      // bottom side
-      mesh.TriangleIndices.Add(pointIdx[5]);
-      mesh.TriangleIndices.Add(pointIdx[4]);
-      mesh.TriangleIndices.Add(pointIdx[0]);
-
-      mesh.TriangleIndices.Add(pointIdx[5]);
-      mesh.TriangleIndices.Add(pointIdx[0]);
-      mesh.TriangleIndices.Add(pointIdx[1]);
-
-      // right side
-      mesh.TriangleIndices.Add(pointIdx[2]);
-      mesh.TriangleIndices.Add(pointIdx[6]);
-      mesh.TriangleIndices.Add(pointIdx[5]);
-
-      mesh.TriangleIndices.Add(pointIdx[1]);
-      mesh.TriangleIndices.Add(pointIdx[2]);
-      mesh.TriangleIndices.Add(pointIdx[5]);
-
-      // left side
-      mesh.TriangleIndices.Add(pointIdx[7]);
-      mesh.TriangleIndices.Add(pointIdx[3]);
-      mesh.TriangleIndices.Add(pointIdx[4]);
-
-      mesh.TriangleIndices.Add(pointIdx[4]);
-      mesh.TriangleIndices.Add(pointIdx[3]);
-      mesh.TriangleIndices.Add(pointIdx[0]);
-
-      // top side
-      mesh.TriangleIndices.Add(pointIdx[3]);
-      mesh.TriangleIndices.Add(pointIdx[7]);
-      mesh.TriangleIndices.Add(pointIdx[6]);
-
-      mesh.TriangleIndices.Add(pointIdx[3]);
-      mesh.TriangleIndices.Add(pointIdx[6]);
-      mesh.TriangleIndices.Add(pointIdx[2]);
-
-      // front side
-      mesh.TriangleIndices.Add(pointIdx[6]);
-      mesh.TriangleIndices.Add(pointIdx[7]);
-      mesh.TriangleIndices.Add(pointIdx[4]);
-
-      mesh.TriangleIndices.Add(pointIdx[6]);
-      mesh.TriangleIndices.Add(pointIdx[4]);
-      mesh.TriangleIndices.Add(pointIdx[5]);
-    }
-
-    private void AddInsideTriangles(MeshGeometry3D mesh, int[] pointIdx) {
-      // for each cube we also draw the triangles facing inside because they are visible when a cube is transparent
-      // point indices clockwise
-
-      // back side
-      mesh.TriangleIndices.Add(pointIdx[1]);
-      mesh.TriangleIndices.Add(pointIdx[2]);
-      mesh.TriangleIndices.Add(pointIdx[0]);
-
-      mesh.TriangleIndices.Add(pointIdx[2]);
-      mesh.TriangleIndices.Add(pointIdx[3]);
-      mesh.TriangleIndices.Add(pointIdx[0]);
-
-      // bottom side
-      mesh.TriangleIndices.Add(pointIdx[0]);
-      mesh.TriangleIndices.Add(pointIdx[4]);
-      mesh.TriangleIndices.Add(pointIdx[5]);
-
-      mesh.TriangleIndices.Add(pointIdx[1]);
-      mesh.TriangleIndices.Add(pointIdx[0]);
-      mesh.TriangleIndices.Add(pointIdx[5]);
-
-      // right side
-      mesh.TriangleIndices.Add(pointIdx[5]);
-      mesh.TriangleIndices.Add(pointIdx[6]);
-      mesh.TriangleIndices.Add(pointIdx[2]);
-
-      mesh.TriangleIndices.Add(pointIdx[5]);
-      mesh.TriangleIndices.Add(pointIdx[2]);
-      mesh.TriangleIndices.Add(pointIdx[1]);
-
-      // left side
-      mesh.TriangleIndices.Add(pointIdx[4]);
-      mesh.TriangleIndices.Add(pointIdx[3]);
-      mesh.TriangleIndices.Add(pointIdx[7]);
-
-      mesh.TriangleIndices.Add(pointIdx[0]);
-      mesh.TriangleIndices.Add(pointIdx[3]);
-      mesh.TriangleIndices.Add(pointIdx[4]);
-
-      // top side
-      mesh.TriangleIndices.Add(pointIdx[6]);
-      mesh.TriangleIndices.Add(pointIdx[7]);
-      mesh.TriangleIndices.Add(pointIdx[3]);
-
-      mesh.TriangleIndices.Add(pointIdx[2]);
-      mesh.TriangleIndices.Add(pointIdx[6]);
-      mesh.TriangleIndices.Add(pointIdx[3]);
-
-      // front side
-      mesh.TriangleIndices.Add(pointIdx[4]);
-      mesh.TriangleIndices.Add(pointIdx[7]);
-      mesh.TriangleIndices.Add(pointIdx[6]);
-
-      mesh.TriangleIndices.Add(pointIdx[5]);
-      mesh.TriangleIndices.Add(pointIdx[4]);
-      mesh.TriangleIndices.Add(pointIdx[6]);
-    }
-
-    private int[] AddPoints(MeshGeometry3D mesh, int x, int y, int z, int w, int h, int d) {
+    /// <summary>
+    /// Creates a solid cube by adding the respective points and triangles.
+    /// </summary>
+    /// <param name="mesh">The mesh to which points and triangles are added.</param>
+    /// <param name="x">The leftmost point</param>
+    /// <param name="y">The frontmost point</param>
+    /// <param name="z">The lowest point</param>
+    /// <param name="width">The extension to the right</param>
+    /// <param name="height">The extension to the back</param>
+    /// <param name="depth">The extension to the top</param>
+    private void AddSolidCube(MeshGeometry3D mesh, int x, int y, int z, int width, int height, int depth) {
       // ground
       mesh.Positions.Add(new Point3D(x, y, z));
-      mesh.Positions.Add(new Point3D(x + w, y, z));
-      mesh.Positions.Add(new Point3D(x + w, y + h, z));
-      mesh.Positions.Add(new Point3D(x, y + h, z));
+      mesh.Positions.Add(new Point3D(x + width, y, z));
+      mesh.Positions.Add(new Point3D(x + width, y + height, z));
+      mesh.Positions.Add(new Point3D(x, y + height, z));
       // top
-      mesh.Positions.Add(new Point3D(x, y, z + d));
-      mesh.Positions.Add(new Point3D(x + w, y, z + d));
-      mesh.Positions.Add(new Point3D(x + w, y + h, z + d));
-      mesh.Positions.Add(new Point3D(x, y + h, z + d));
-
-      return Enumerable.Range(mesh.Positions.Count - 8, 8).ToArray();
+      mesh.Positions.Add(new Point3D(x, y, z + depth));
+      mesh.Positions.Add(new Point3D(x + width, y, z + depth));
+      mesh.Positions.Add(new Point3D(x + width, y + height, z + depth));
+      mesh.Positions.Add(new Point3D(x, y + height, z + depth));
+
+      // front
+      AddPlane(mesh, 0, 1, 5, 4);
+      // right side
+      AddPlane(mesh, 1, 2, 6, 5);
+      // back
+      AddPlane(mesh, 3, 7, 6, 2);
+      // left side
+      AddPlane(mesh, 0, 4, 7, 3);
+      // top
+      AddPlane(mesh, 4, 5, 6, 7);
+      // bottom
+      AddPlane(mesh, 0, 3, 2, 1);
+    }
+
+    /// <summary>
+    /// Creates a wireframe cube by adding the respective points and triangles.
+    /// </summary>
+    /// <param name="mesh">The mesh to which points and triangles are added.</param>
+    /// <param name="x">The leftmost point</param>
+    /// <param name="y">The frontmost point</param>
+    /// <param name="z">The lowest point</param>
+    /// <param name="width">The extension to the right</param>
+    /// <param name="height">The extension to the back</param>
+    /// <param name="depth">The extension to the top</param>
+    /// <param name="thickness">The thickness of the frame</param>
+    private void AddWireframeCube(MeshGeometry3D mesh, double x, double y, double z, double width, double height, double depth, double thickness = double.NaN) {
+      // default thickness of the wireframe is 5% of smallest dimension
+      if (double.IsNaN(thickness))
+        thickness = Math.Min(width, Math.Min(height, depth)) * 0.05;
+
+      // The cube contains of 8 corner, each corner has 4 points:
+      // 1. The corner point
+      // 2. A point on the edge to the right of the corner
+      // 3. A point on the edge atop or below the corner
+      // 4. A point on the edge to the left of the corner
+
+      // Point 0, Front Left Bottom
+      mesh.Positions.Add(new Point3D(x, y, z));
+      mesh.Positions.Add(new Point3D(x + thickness, y, z));
+      mesh.Positions.Add(new Point3D(x, y, z + thickness));
+      mesh.Positions.Add(new Point3D(x, y + thickness, z));
+
+      // Point 1, Front Right Bottom
+      mesh.Positions.Add(new Point3D(x + width, y, z));
+      mesh.Positions.Add(new Point3D(x + width, y + thickness, z));
+      mesh.Positions.Add(new Point3D(x + width, y, z + thickness));
+      mesh.Positions.Add(new Point3D(x + width - thickness, y, z));
+
+      // Point 2, Back Right Bottom
+      mesh.Positions.Add(new Point3D(x + width, y + height, z));
+      mesh.Positions.Add(new Point3D(x + width - thickness, y + height, z));
+      mesh.Positions.Add(new Point3D(x + width, y + height, z + thickness));
+      mesh.Positions.Add(new Point3D(x + width, y + height - thickness, z));
+
+      // Point 3, Back Left Bottom
+      mesh.Positions.Add(new Point3D(x, y + height, z));
+      mesh.Positions.Add(new Point3D(x, y + height - thickness, z));
+      mesh.Positions.Add(new Point3D(x, y + height, z + thickness));
+      mesh.Positions.Add(new Point3D(x + thickness, y + height, z));
+
+      // Point 4, Front Left Top
+      mesh.Positions.Add(new Point3D(x, y, z + depth));
+      mesh.Positions.Add(new Point3D(x + thickness, y, z + depth));
+      mesh.Positions.Add(new Point3D(x, y, z + depth - thickness));
+      mesh.Positions.Add(new Point3D(x, y + thickness, z + depth));
+
+      // Point 5, Front Right Top
+      mesh.Positions.Add(new Point3D(x + width, y, z + depth));
+      mesh.Positions.Add(new Point3D(x + width, y + thickness, z + depth));
+      mesh.Positions.Add(new Point3D(x + width, y, z + depth - thickness));
+      mesh.Positions.Add(new Point3D(x + width - thickness, y, z + depth));
+
+      // Point 6, Back Right Top
+      mesh.Positions.Add(new Point3D(x + width, y + height, z + depth));
+      mesh.Positions.Add(new Point3D(x + width - thickness, y + height, z + depth));
+      mesh.Positions.Add(new Point3D(x + width, y + height, z + depth - thickness));
+      mesh.Positions.Add(new Point3D(x + width, y + height - thickness, z + depth));
+
+      // Point 7, Back Left Top
+      mesh.Positions.Add(new Point3D(x, y + height, z + depth));
+      mesh.Positions.Add(new Point3D(x, y + height - thickness, z + depth));
+      mesh.Positions.Add(new Point3D(x, y + height, z + depth - thickness));
+      mesh.Positions.Add(new Point3D(x + thickness, y + height, z + depth));
+
+      AddPlane(mesh, 0, 4, 6, 2);
+      AddPlane(mesh, 0, 3, 5, 4);
+
+      AddPlane(mesh, 4, 8, 10, 6);
+      AddPlane(mesh, 4, 7, 9, 8);
+
+      AddPlane(mesh, 8, 12, 14, 10);
+      AddPlane(mesh, 8, 11, 13, 12);
+
+      AddPlane(mesh, 0, 2, 14, 12);
+      AddPlane(mesh, 0, 12, 15, 1);
+
+      AddPlane(mesh, 0, 1, 17, 16);
+      AddPlane(mesh, 0, 16, 19, 3);
+
+      AddPlane(mesh, 4, 20, 23, 7);
+      AddPlane(mesh, 4, 5, 21, 20);
+
+      AddPlane(mesh, 8, 24, 27, 11);
+      AddPlane(mesh, 8, 9, 25, 24);
+
+      AddPlane(mesh, 12, 28, 31, 15);
+      AddPlane(mesh, 12, 13, 29, 28);
+
+      AddPlane(mesh, 16, 18, 22, 20);
+      AddPlane(mesh, 16, 20, 21, 19);
+
+      AddPlane(mesh, 20, 22, 26, 24);
+      AddPlane(mesh, 20, 24, 25, 23);
+
+      AddPlane(mesh, 24, 28, 29, 27);
+      AddPlane(mesh, 24, 26, 30, 28);
+
+      AddPlane(mesh, 28, 30, 18, 16);
+      AddPlane(mesh, 28, 16, 17, 31);
+    }
+
+    /// <summary>
+    /// Adds a plane by two triangles. The indices of the points have to be given
+    /// in counter-clockwise sequence.
+    /// </summary>
+    /// <param name="mesh">The mesh to add the triangles to</param>
+    /// <param name="a">The index of the first point</param>
+    /// <param name="b">The index of the second point</param>
+    /// <param name="c">The index of the third point</param>
+    /// <param name="d">The index of the fourth point</param>
+    private void AddPlane(MeshGeometry3D mesh, int a, int b, int c, int d) {
+      // two triangles form a plane
+      mesh.TriangleIndices.Add(a);
+      mesh.TriangleIndices.Add(b);
+      mesh.TriangleIndices.Add(d);
+      mesh.TriangleIndices.Add(c);
+      mesh.TriangleIndices.Add(d);
+      mesh.TriangleIndices.Add(b);
     }
     #endregion
 
-    private void Container3DView_OnMouseEnter(object sender, MouseEventArgs e) {
-      Focus(); // for mouse wheel events
-    }
   }
 }
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/MultiObjective/SymbolicRegressionMultiObjectiveTrainingBestSolutionAnalyzer.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/MultiObjective/SymbolicRegressionMultiObjectiveTrainingBestSolutionAnalyzer.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/MultiObjective/SymbolicRegressionMultiObjectiveTrainingBestSolutionAnalyzer.cs	(revision 14751)
@@ -44,4 +44,5 @@
     private const string MaximumSymbolicExpressionTreeLengthParameterName = "MaximumSymbolicExpressionTreeLength";
     private const string ValidationPartitionParameterName = "ValidationPartition";
+    private const string AnalyzeTestErrorParameterName = "Analyze Test Error";
 
     #region parameter properties
@@ -62,5 +63,14 @@
       get { return (IValueLookupParameter<IntRange>)Parameters[ValidationPartitionParameterName]; }
     }
+
+    public IFixedValueParameter<BoolValue> AnalyzeTestErrorParameter {
+      get { return (IFixedValueParameter<BoolValue>)Parameters[AnalyzeTestErrorParameterName]; }
+    }
     #endregion
+
+    public bool AnalyzeTestError {
+      get { return AnalyzeTestErrorParameter.Value.Value; }
+      set { AnalyzeTestErrorParameter.Value.Value = value; }
+    }
 
     [StorableConstructor]
@@ -74,4 +84,5 @@
       Parameters.Add(new LookupParameter<IntValue>(MaximumSymbolicExpressionTreeLengthParameterName, "Maximal length of the symbolic expression.") { Hidden = true });
       Parameters.Add(new ValueLookupParameter<IntRange>(ValidationPartitionParameterName, "The validation partition."));
+      Parameters.Add(new FixedValueParameter<BoolValue>(AnalyzeTestErrorParameterName, "Flag whether the test error should be displayed in the Pareto-Front", new BoolValue(false)));
     }
 
@@ -82,4 +93,6 @@
       if (!Parameters.ContainsKey(ValidationPartitionParameterName))
         Parameters.Add(new ValueLookupParameter<IntRange>(ValidationPartitionParameterName, "The validation partition."));
+      if (!Parameters.ContainsKey(AnalyzeTestErrorParameterName))
+        Parameters.Add(new FixedValueParameter<BoolValue>(AnalyzeTestErrorParameterName, "Flag whether the test error should be displayed in the Pareto-Front", new BoolValue(false)));
     }
 
@@ -135,4 +148,11 @@
       qualityToTreeSize.Rows.Add(trainingRow);
 
+      if (AnalyzeTestError) {
+        var testRow = new ScatterPlotDataRow("Test NMSE", "",
+          sizeParetoFront.Select(x => new Point2D<double>(x.Model.SymbolicExpressionTree.Length, x.TestNormalizedMeanSquaredError)));
+        testRow.VisualProperties.PointSize = 8;
+        qualityToTreeSize.Rows.Add(testRow);
+      }
+
       var validationPartition = ValidationPartitionParameter.ActualValue;
       if (validationPartition.Size != 0) {
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/TreeEditDialogs/SymbolicExpressionTreeVariableNodeEditDialog.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/TreeEditDialogs/SymbolicExpressionTreeVariableNodeEditDialog.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/TreeEditDialogs/SymbolicExpressionTreeVariableNodeEditDialog.cs	(revision 14751)
@@ -34,8 +34,14 @@
       get { return variableTreeNode; }
       set {
-        if (InvokeRequired)
-          Invoke(new Action<SymbolicExpressionTreeNode>(x => variableTreeNode = (VariableTreeNode)x), value);
-        else
+        if (InvokeRequired) {
+          Invoke(new Action<SymbolicExpressionTreeNode>(x =>
+          {
+            variableTreeNode = (VariableTreeNode) x;
+            variableNameTextBox.Text = variableTreeNode.VariableName;
+          }), value);
+        } else {
           variableTreeNode = value;
+          variableNameTextBox.Text = variableTreeNode.VariableName;
+        }
       }
     }
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisSingleObjectiveTrainingBestSolutionAnalyzer.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisSingleObjectiveTrainingBestSolutionAnalyzer.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisSingleObjectiveTrainingBestSolutionAnalyzer.cs	(revision 14751)
@@ -35,13 +35,13 @@
   [Item("SymbolicDataAnalysisSingleObjectiveTrainingBestSolutionAnalyzer", "An operator that analyzes the training best symbolic data analysis solution for single objective symbolic data analysis problems.")]
   [StorableClass]
-  public abstract class SymbolicDataAnalysisSingleObjectiveTrainingBestSolutionAnalyzer<T> : SymbolicDataAnalysisSingleObjectiveAnalyzer, IIterationBasedOperator
-
-    where T : class, ISymbolicDataAnalysisSolution {
+  public abstract class SymbolicDataAnalysisSingleObjectiveTrainingBestSolutionAnalyzer<T> : SymbolicDataAnalysisSingleObjectiveAnalyzer, IIterationBasedOperator where T : class, ISymbolicDataAnalysisSolution {
     private const string TrainingBestSolutionParameterName = "Best training solution";
     private const string TrainingBestSolutionQualityParameterName = "Best training solution quality";
     private const string TrainingBestSolutionGenerationParameterName = "Best training solution generation";
+    private const string TrainingBestSolutionsHistoryParameterName = "Best training solutions history";
     private const string UpdateAlwaysParameterName = "Always update best solution";
     private const string IterationsParameterName = "Iterations";
     private const string MaximumIterationsParameterName = "Maximum Iterations";
+    private const string StoreHistoryParameterName = "Store History";
 
     #region parameter properties
@@ -55,4 +55,7 @@
       get { return (ILookupParameter<IntValue>)Parameters[TrainingBestSolutionGenerationParameterName]; }
     }
+    public ILookupParameter<ItemList<T>> TrainingBestSolutionsHistoryParameter {
+      get { return (ILookupParameter<ItemList<T>>)Parameters[TrainingBestSolutionsHistoryParameterName]; }
+    }
     public IFixedValueParameter<BoolValue> UpdateAlwaysParameter {
       get { return (IFixedValueParameter<BoolValue>)Parameters[UpdateAlwaysParameterName]; }
@@ -63,4 +66,8 @@
     public IValueLookupParameter<IntValue> MaximumIterationsParameter {
       get { return (IValueLookupParameter<IntValue>)Parameters[MaximumIterationsParameterName]; }
+    }
+
+    public IFixedValueParameter<BoolValue> StoreHistoryParameter {
+      get { return (IFixedValueParameter<BoolValue>)Parameters[StoreHistoryParameterName]; }
     }
     #endregion
@@ -74,8 +81,15 @@
       set { TrainingBestSolutionQualityParameter.ActualValue = value; }
     }
-    public BoolValue UpdateAlways {
-      get { return UpdateAlwaysParameter.Value; }
+    public bool UpdateAlways {
+      get { return UpdateAlwaysParameter.Value.Value; }
+      set { UpdateAlwaysParameter.Value.Value = value; }
+    }
+
+    public bool StoreHistory {
+      get { return StoreHistoryParameter.Value.Value; }
+      set { StoreHistoryParameter.Value.Value = value; }
     }
     #endregion
+
 
     [StorableConstructor]
@@ -84,5 +98,5 @@
     public SymbolicDataAnalysisSingleObjectiveTrainingBestSolutionAnalyzer()
       : base() {
-      Parameters.Add(new LookupParameter<T>(TrainingBestSolutionParameterName, "The training best symbolic data analyis solution."));
+      Parameters.Add(new LookupParameter<T>(TrainingBestSolutionParameterName, "The best training symbolic data analyis solution."));
       Parameters.Add(new LookupParameter<DoubleValue>(TrainingBestSolutionQualityParameterName, "The quality of the training best symbolic data analysis solution."));
       Parameters.Add(new LookupParameter<IntValue>(TrainingBestSolutionGenerationParameterName, "The generation in which the best training solution was found."));
@@ -90,4 +104,6 @@
       Parameters.Add(new LookupParameter<IntValue>(IterationsParameterName, "The number of performed iterations."));
       Parameters.Add(new ValueLookupParameter<IntValue>(MaximumIterationsParameterName, "The maximum number of performed iterations.") { Hidden = true });
+      Parameters.Add(new FixedValueParameter<BoolValue>(StoreHistoryParameterName, "Flag that determines whether all encountered best solutions should be stored as results.", new BoolValue(false)));
+      Parameters.Add(new LookupParameter<ItemList<T>>(TrainingBestSolutionsHistoryParameterName, "The history of the best training symbolic data analysis solutions."));
       UpdateAlwaysParameter.Hidden = true;
     }
@@ -105,7 +121,27 @@
       if (!Parameters.ContainsKey(MaximumIterationsParameterName))
         Parameters.Add(new ValueLookupParameter<IntValue>(MaximumIterationsParameterName, "The maximum number of performed iterations.") { Hidden = true });
+      if (!Parameters.ContainsKey(StoreHistoryParameterName))
+        Parameters.Add(new FixedValueParameter<BoolValue>(StoreHistoryParameterName, "Flag that determines whether all encountered best solutions should be stored as results.", new BoolValue(false)));
+      if (!Parameters.ContainsKey(TrainingBestSolutionsHistoryParameterName))
+        Parameters.Add(new LookupParameter<ItemList<T>>(TrainingBestSolutionsHistoryParameterName, "The history of the best training symbolic data analysis solutions."));
     }
 
     public override IOperation Apply() {
+      var results = ResultCollection;
+      #region create results
+      if (!results.ContainsKey(TrainingBestSolutionParameter.Name))
+        results.Add(new Result(TrainingBestSolutionParameter.Name, TrainingBestSolutionParameter.Description, typeof(T)));
+      if (!results.ContainsKey(TrainingBestSolutionQualityParameter.Name))
+        results.Add(new Result(TrainingBestSolutionQualityParameter.Name, TrainingBestSolutionQualityParameter.Description, typeof(DoubleValue)));
+      if (!results.ContainsKey(TrainingBestSolutionGenerationParameter.Name) && IterationsParameter.ActualValue != null)
+        results.Add(new Result(TrainingBestSolutionGenerationParameter.Name, TrainingBestSolutionGenerationParameter.Description, typeof(IntValue)));
+      if (StoreHistory && !results.ContainsKey(TrainingBestSolutionsHistoryParameter.Name)) {
+
+        results.Add(new Result(TrainingBestSolutionsHistoryParameter.Name, TrainingBestSolutionsHistoryParameter.Description, typeof(ItemList<T>)));
+        TrainingBestSolutionsHistoryParameter.ActualValue = new ItemList<T>();
+        results[TrainingBestSolutionsHistoryParameter.Name].Value = TrainingBestSolutionsHistoryParameter.ActualValue;
+      }
+      #endregion
+
       #region find best tree
       double bestQuality = Maximization.Value ? double.NegativeInfinity : double.PositiveInfinity;
@@ -121,6 +157,5 @@
       #endregion
 
-      var results = ResultCollection;
-      if (bestTree != null && (UpdateAlways.Value || TrainingBestSolutionQuality == null ||
+      if (bestTree != null && (UpdateAlways || TrainingBestSolutionQuality == null ||
         IsBetter(bestQuality, TrainingBestSolutionQuality.Value, Maximization.Value))) {
         TrainingBestSolution = CreateSolution(bestTree, bestQuality);
@@ -129,15 +164,11 @@
           TrainingBestSolutionGenerationParameter.ActualValue = new IntValue(IterationsParameter.ActualValue.Value);
 
-        if (!results.ContainsKey(TrainingBestSolutionParameter.Name)) {
-          results.Add(new Result(TrainingBestSolutionParameter.Name, TrainingBestSolutionParameter.Description, TrainingBestSolution));
-          results.Add(new Result(TrainingBestSolutionQualityParameter.Name, TrainingBestSolutionQualityParameter.Description, TrainingBestSolutionQuality));
-          if (TrainingBestSolutionGenerationParameter.ActualValue != null)
-            results.Add(new Result(TrainingBestSolutionGenerationParameter.Name, TrainingBestSolutionGenerationParameter.Description, TrainingBestSolutionGenerationParameter.ActualValue));
-        } else {
-          results[TrainingBestSolutionParameter.Name].Value = TrainingBestSolution;
-          results[TrainingBestSolutionQualityParameter.Name].Value = TrainingBestSolutionQuality;
-          if (TrainingBestSolutionGenerationParameter.ActualValue != null)
-            results[TrainingBestSolutionGenerationParameter.Name].Value = TrainingBestSolutionGenerationParameter.ActualValue;
+        results[TrainingBestSolutionParameter.Name].Value = TrainingBestSolution;
+        results[TrainingBestSolutionQualityParameter.Name].Value = TrainingBestSolutionQuality;
+        if (TrainingBestSolutionGenerationParameter.ActualValue != null)
+          results[TrainingBestSolutionGenerationParameter.Name].Value = TrainingBestSolutionGenerationParameter.ActualValue;
 
+        if (StoreHistory) {
+          TrainingBestSolutionsHistoryParameter.ActualValue.Add(TrainingBestSolution);
         }
       }
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/HeuristicLab.Problems.Instances.DataAnalysis-3.3.csproj
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/HeuristicLab.Problems.Instances.DataAnalysis-3.3.csproj	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/HeuristicLab.Problems.Instances.DataAnalysis-3.3.csproj	(revision 14751)
@@ -157,4 +157,6 @@
     <Compile Include="Regression\FeatureSelection\FeatureSelection.cs" />
     <Compile Include="Regression\FeatureSelection\FeatureSelectionInstanceProvider.cs" />
+    <Compile Include="Regression\VariableNetworks\LinearVariableNetwork.cs" />
+    <Compile Include="Regression\VariableNetworks\GaussianProcessVariableNetwork.cs" />
     <Compile Include="Regression\VariableNetworks\VariableNetwork.cs" />
     <Compile Include="Regression\VariableNetworks\VariableNetworkInstanceProvider.cs" />
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/Friedman/FriedmanRandomFunction.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/Friedman/FriedmanRandomFunction.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/Friedman/FriedmanRandomFunction.cs	(revision 14751)
@@ -94,5 +94,5 @@
     }
 
-    // as described in Greedy Function Approxination paper
+    // as described in Greedy Function Approximation paper
     private IEnumerable<double> GenerateRandomFunction(IRandom rand, List<List<double>> xs, int nTerms = 20) {
       int nRows = xs.First().Count;
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/VariableNetworks/GaussianProcessVariableNetwork.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/VariableNetworks/GaussianProcessVariableNetwork.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/VariableNetworks/GaussianProcessVariableNetwork.cs	(revision 14751)
@@ -0,0 +1,133 @@
+﻿#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.Linq;
+using HeuristicLab.Core;
+using HeuristicLab.Problems.DataAnalysis;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Problems.Instances.DataAnalysis {
+  public sealed class GaussianProcessVariableNetwork : VariableNetwork {
+    private int numberOfFeatures;
+    private double noiseRatio;
+
+    public override string Name { get { return string.Format("GaussianProcessVariableNetwork-{0:0%} ({1} dim)", noiseRatio, numberOfFeatures); } }
+
+    public GaussianProcessVariableNetwork(int numberOfFeatures, double noiseRatio,
+      IRandom rand)
+      : base(250, 250, numberOfFeatures, noiseRatio, rand) {
+      this.noiseRatio = noiseRatio;
+      this.numberOfFeatures = numberOfFeatures;
+    }
+
+    // sample the input variables that are actually used and sample from a Gaussian process
+    protected override IEnumerable<double> GenerateRandomFunction(IRandom rand, List<List<double>> xs, out string[] selectedVarNames, out double[] relevance) {
+      int nl = SampleNumberOfVariables(rand, xs.Count);
+
+      var selectedIdx = Enumerable.Range(0, xs.Count).Shuffle(rand)
+        .Take(nl).ToArray();
+
+      var selectedVars = selectedIdx.Select(i => xs[i]).ToArray();
+      selectedVarNames = selectedIdx.Select(i => VariableNames[i]).ToArray();
+      return SampleGaussianProcess(rand, selectedVars, out relevance);
+    }
+
+    private IEnumerable<double> SampleGaussianProcess(IRandom rand, List<double>[] xs, out double[] relevance) {
+      int nl = xs.Length;
+      int nRows = xs.First().Count;
+
+      // sample u iid ~ N(0, 1)
+      var u = Enumerable.Range(0, nRows).Select(_ => NormalDistributedRandom.NextDouble(rand, 0, 1)).ToArray();
+
+      // sample actual length-scales
+      var l = Enumerable.Range(0, nl)
+        .Select(_ => rand.NextDouble() * 2 + 0.5)
+        .ToArray();
+
+      double[,] K = CalculateCovariance(xs, l);
+
+      // decompose
+      alglib.trfac.spdmatrixcholesky(ref K, nRows, false);
+
+
+      // calc y = Lu
+      var y = new double[u.Length];
+      alglib.ablas.rmatrixmv(nRows, nRows, K, 0, 0, 0, u, 0, ref y, 0);
+
+      // calculate relevance by removing dimensions
+      relevance = CalculateRelevance(y, u, xs, l);
+
+      return y;
+    }
+
+    // calculate variable relevance based on removal of variables
+    //  1) to remove a variable we set it's length scale to infinity (no relation of the variable value to the target)
+    //  2) calculate MSE of the original target values (y) to the updated targes y' (after variable removal)
+    //  3) relevance is larger if MSE(y,y') is large
+    //  4) scale impacts so that the most important variable has impact = 1
+    private double[] CalculateRelevance(double[] y, double[] u, List<double>[] xs, double[] l) {
+      int nRows = xs.First().Count;
+      var changedL = new double[l.Length];
+      var relevance = new double[l.Length];
+      for(int i = 0; i < l.Length; i++) {
+        Array.Copy(l, changedL, changedL.Length);
+        changedL[i] = double.MaxValue;
+        var changedK = CalculateCovariance(xs, changedL);
+
+        var yChanged = new double[u.Length];
+        alglib.ablas.rmatrixmv(nRows, nRows, changedK, 0, 0, 0, u, 0, ref yChanged, 0);
+
+        OnlineCalculatorError error;
+        var mse = OnlineMeanSquaredErrorCalculator.Calculate(y, yChanged, out error);
+        if(error != OnlineCalculatorError.None) mse = double.MaxValue;
+        relevance[i] = mse;
+      }
+      // scale so that max relevance is 1.0
+      var maxRel = relevance.Max();
+      for(int i = 0; i < relevance.Length; i++) relevance[i] /= maxRel;
+      return relevance;
+    }
+
+    private double[,] CalculateCovariance(List<double>[] xs, double[] l) {
+      int nRows = xs.First().Count;
+      double[,] K = new double[nRows, nRows];
+      for(int r = 0; r < nRows; r++) {
+        double[] xi = xs.Select(x => x[r]).ToArray();
+        for(int c = 0; c <= r; c++) {
+          double[] xj = xs.Select(x => x[c]).ToArray();
+          double dSqr = xi.Zip(xj, (xik, xjk) => (xik - xjk))
+            .Select(dk => dk * dk)
+            .Zip(l, (dk, lk) => dk / lk)
+            .Sum();
+          K[r, c] = Math.Exp(-dSqr);
+        }
+      }
+      // add a small diagonal matrix for numeric stability
+      for(int i = 0; i < nRows; i++) {
+        K[i, i] += 1.0E-7;
+      }
+
+      return K;
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/VariableNetworks/LinearVariableNetwork.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/VariableNetworks/LinearVariableNetwork.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/VariableNetworks/LinearVariableNetwork.cs	(revision 14751)
@@ -0,0 +1,109 @@
+﻿#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.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Problems.DataAnalysis;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Problems.Instances.DataAnalysis {
+  public sealed class LinearVariableNetwork : VariableNetwork {
+    private int numberOfFeatures;
+    private double noiseRatio;
+
+    public override string Name { get { return string.Format("LinearVariableNetwork-{0:0%} ({1} dim)", noiseRatio, numberOfFeatures); } }
+
+    public LinearVariableNetwork(int numberOfFeatures, double noiseRatio,
+      IRandom rand)
+      : base(250, 250, numberOfFeatures, noiseRatio, rand) {
+      this.noiseRatio = noiseRatio;
+      this.numberOfFeatures = numberOfFeatures;
+    }
+
+    protected override IEnumerable<double> GenerateRandomFunction(IRandom rand, List<List<double>> xs, out string[] selectedVarNames, out double[] relevance) {
+      int nl = SampleNumberOfVariables(rand, numberOfFeatures);
+
+      var selectedIdx = Enumerable.Range(0, xs.Count).Shuffle(rand)
+        .Take(nl).ToArray();
+
+      var selectedVars = selectedIdx.Select(i => xs[i]).ToArray();
+      selectedVarNames = selectedIdx.Select(i => VariableNames[i]).ToArray();
+      return SampleLinearFunction(rand, selectedVars, out relevance);
+    }
+
+    private IEnumerable<double> SampleLinearFunction(IRandom rand, List<double>[] xs, out double[] relevance) {
+      int nl = xs.Length;
+      int nRows = xs.First().Count;
+
+      // sample standardized coefficients iid ~ N(0, 1)
+      var c = Enumerable.Range(0, nRows).Select(_ => NormalDistributedRandom.NextDouble(rand, 0, 1)).ToArray();
+
+      // calculate scaled coefficients (variables with large variance should have smaller coefficients)
+      var scaledC = Enumerable.Range(0, nl)
+        .Select(i => c[i] / xs[i].StandardDeviationPop())
+        .ToArray();
+
+      var y = EvaluteLinearModel(xs, scaledC);
+
+      relevance = CalculateRelevance(y, xs, scaledC);
+
+      return y;
+    }
+
+    private double[] EvaluteLinearModel(List<double>[] xs, double[] c) {
+      int nRows = xs.First().Count;
+      var y = new double[nRows];
+      for(int row = 0; row < nRows; row++) {
+        y[row] = xs.Select(xi => xi[row]).Zip(c, (xij, cj) => xij * cj).Sum();
+        y[row] /= c.Length;
+      }
+      return y;
+    }
+
+    // calculate variable relevance based on removal of variables
+    //  1) to remove a variable we set it's coefficient to zero
+    //  2) calculate MSE of the original target values (y) to the updated targes y' (after variable removal)
+    //  3) relevance is larger if MSE(y,y') is large
+    //  4) scale impacts so that the most important variable has impact = 1
+    private double[] CalculateRelevance(double[] y, List<double>[] xs, double[] l) {
+      var changedL = new double[l.Length];
+      var relevance = new double[l.Length];
+      for(int i = 0; i < l.Length; i++) {
+        Array.Copy(l, changedL, changedL.Length);
+        changedL[i] = 0.0;
+
+        var yChanged = EvaluteLinearModel(xs, changedL);
+
+        OnlineCalculatorError error;
+        var mse = OnlineMeanSquaredErrorCalculator.Calculate(y, yChanged, out error);
+        if(error != OnlineCalculatorError.None) mse = double.MaxValue;
+        relevance[i] = mse;
+      }
+      // scale so that max relevance is 1.0
+      var maxRel = relevance.Max();
+      for(int i = 0; i < relevance.Length; i++) relevance[i] /= maxRel;
+      return relevance;
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/VariableNetworks/VariableNetwork.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/VariableNetworks/VariableNetwork.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/VariableNetworks/VariableNetwork.cs	(revision 14751)
@@ -30,5 +30,5 @@
 
 namespace HeuristicLab.Problems.Instances.DataAnalysis {
-  public class VariableNetwork : ArtificialRegressionDataDescriptor {
+  public abstract class VariableNetwork : ArtificialRegressionDataDescriptor {
     private int nTrainingSamples;
     private int nTestSamples;
@@ -38,5 +38,4 @@
     private IRandom random;
 
-    public override string Name { get { return string.Format("VariableNetwork-{0:0%} ({1} dim)", noiseRatio, numberOfFeatures); } }
     private string networkDefinition;
     public string NetworkDefinition { get { return networkDefinition; } }
@@ -47,9 +46,5 @@
     }
 
-    public VariableNetwork(int numberOfFeatures, double noiseRatio,
-      IRandom rand)
-      : this(250, 250, numberOfFeatures, noiseRatio, rand) { }
-
-    public VariableNetwork(int nTrainingSamples, int nTestSamples,
+    protected VariableNetwork(int nTrainingSamples, int nTestSamples,
       int numberOfFeatures, double noiseRatio, IRandom rand) {
       this.nTrainingSamples = nTrainingSamples;
@@ -105,9 +100,15 @@
 
       var nrand = new NormalDistributedRandom(random, 0, 1);
-      for (int c = 0; c < numLvl0; c++) {
+      for(int c = 0; c < numLvl0; c++) {
         inputVarNames.Add(new string[] { });
         relevances.Add(new double[] { });
-        description.Add(" ~ N(0, 1)");
-        lvl0.Add(Enumerable.Range(0, TestPartitionEnd).Select(_ => nrand.NextDouble()).ToList());
+        description.Add(" ~ N(0, 1 + noiseLvl)");
+        // use same generation procedure for all variables
+        var x = Enumerable.Range(0, TestPartitionEnd).Select(_ => nrand.NextDouble()).ToList();
+        var sigma = x.StandardDeviationPop();
+        var mean = x.Average();
+        for(int i = 0; i < x.Count; i++) x[i] = (x[i] - mean) / sigma;
+        var noisePrng = new NormalDistributedRandom(random, 0, Math.Sqrt(noiseRatio / (1.0 - noiseRatio)));
+        lvl0.Add(x.Select(t => t + noisePrng.NextDouble()).ToList());
       }
 
@@ -125,5 +126,5 @@
 
       this.variableRelevances.Clear();
-      for (int i = 0; i < variableNames.Length; i++) {
+      for(int i = 0; i < variableNames.Length; i++) {
         var targetVarName = variableNames[i];
         var targetRelevantInputs =
@@ -136,9 +137,9 @@
       // for graphviz
       networkDefinition += Environment.NewLine + "digraph G {";
-      for (int i = 0; i < variableNames.Length; i++) {
+      for(int i = 0; i < variableNames.Length; i++) {
         var name = variableNames[i];
         var selectedVarNames = inputVarNames[i];
         var selectedRelevances = relevances[i];
-        for (int j = 0; j < selectedVarNames.Length; j++) {
+        for(int j = 0; j < selectedVarNames.Length; j++) {
           var selectedVarName = selectedVarNames[j];
           var selectedRelevance = selectedRelevances[j];
@@ -157,12 +158,16 @@
 
     private List<List<double>> CreateVariables(List<List<double>> allowedInputs, int numVars, List<string[]> inputVarNames, List<string> description, List<double[]> relevances) {
-      var res = new List<List<double>>();
-      for (int c = 0; c < numVars; c++) {
+      var newVariables = new List<List<double>>();
+      for(int c = 0; c < numVars; c++) {
         string[] selectedVarNames;
         double[] relevance;
-        var x = GenerateRandomFunction(random, allowedInputs, out selectedVarNames, out relevance);
+        var x = GenerateRandomFunction(random, allowedInputs, out selectedVarNames, out relevance).ToArray();
+        // standardize x
         var sigma = x.StandardDeviation();
-        var noisePrng = new NormalDistributedRandom(random, 0, sigma * Math.Sqrt(noiseRatio / (1.0 - noiseRatio)));
-        res.Add(x.Select(t => t + noisePrng.NextDouble()).ToList());
+        var mean = x.Average();
+        for(int i = 0; i < x.Length; i++) x[i] = (x[i] - mean) / sigma;
+
+        var noisePrng = new NormalDistributedRandom(random, 0, Math.Sqrt(noiseRatio / (1.0 - noiseRatio)));
+        newVariables.Add(x.Select(t => t + noisePrng.NextDouble()).ToList());
         Array.Sort(selectedVarNames, relevance);
         inputVarNames.Add(selectedVarNames);
@@ -176,108 +181,15 @@
         description.Add(string.Format(" ~ N({0}, {1:N3}) [Relevances: {2}]", desc, noisePrng.Sigma, relevanceStr));
       }
-      return res;
+      return newVariables;
     }
 
-    // sample the input variables that are actually used and sample from a Gaussian process
-    private IEnumerable<double> GenerateRandomFunction(IRandom rand, List<List<double>> xs, out string[] selectedVarNames, out double[] relevance) {
+    public int SampleNumberOfVariables(IRandom rand, int maxNumberOfVariables) {
       double r = -Math.Log(1.0 - rand.NextDouble()) * 2.0; // r is exponentially distributed with lambda = 2
       int nl = (int)Math.Floor(1.5 + r); // number of selected vars is likely to be between three and four
-      if (nl > xs.Count) nl = xs.Count; // limit max
-
-      var selectedIdx = Enumerable.Range(0, xs.Count).Shuffle(random)
-        .Take(nl).ToArray();
-
-      var selectedVars = selectedIdx.Select(i => xs[i]).ToArray();
-      selectedVarNames = selectedIdx.Select(i => VariableNames[i]).ToArray();
-      return SampleGaussianProcess(random, selectedVars, out relevance);
+      return Math.Min(maxNumberOfVariables, nl);
     }
 
-    private IEnumerable<double> SampleGaussianProcess(IRandom random, List<double>[] xs, out double[] relevance) {
-      int nl = xs.Length;
-      int nRows = xs.First().Count;
-
-      // sample u iid ~ N(0, 1)
-      var u = Enumerable.Range(0, nRows).Select(_ => NormalDistributedRandom.NextDouble(random, 0, 1)).ToArray();
-
-      // sample actual length-scales
-      var l = Enumerable.Range(0, nl)
-        .Select(_ => random.NextDouble() * 2 + 0.5)
-        .ToArray();
-
-      double[,] K = CalculateCovariance(xs, l);
-
-      // decompose
-      alglib.trfac.spdmatrixcholesky(ref K, nRows, false);
-
-
-      // calc y = Lu
-      var y = new double[u.Length];
-      alglib.ablas.rmatrixmv(nRows, nRows, K, 0, 0, 0, u, 0, ref y, 0);
-
-      // calculate relevance by removing dimensions
-      relevance = CalculateRelevance(y, u, xs, l);
-
-
-      // calculate variable relevance
-      // as per Rasmussen and Williams "Gaussian Processes for Machine Learning" page 106:
-      // ,,For the squared exponential covariance function [...] the l1, ..., lD hyperparameters
-      // play the role of characteristic length scales [...]. Such a covariance function implements 
-      // automatic relevance determination (ARD) [Neal, 1996], since the inverse of the length-scale 
-      // determines how relevant an input is: if the length-scale has a very large value, the covariance 
-      // will become almost independent of that input, effectively removing it from inference.''
-      // relevance = l.Select(li => 1.0 / li).ToArray();
-
-      return y;
-    }
-
-    // calculate variable relevance based on removal of variables
-    //  1) to remove a variable we set it's length scale to infinity (no relation of the variable value to the target)
-    //  2) calculate MSE of the original target values (y) to the updated targes y' (after variable removal)
-    //  3) relevance is larger if MSE(y,y') is large
-    //  4) scale impacts so that the most important variable has impact = 1
-    private double[] CalculateRelevance(double[] y, double[] u, List<double>[] xs, double[] l) {
-      int nRows = xs.First().Count;
-      var changedL = new double[l.Length];
-      var relevance = new double[l.Length];
-      for (int i = 0; i < l.Length; i++) {
-        Array.Copy(l, changedL, changedL.Length);
-        changedL[i] = double.MaxValue;
-        var changedK = CalculateCovariance(xs, changedL);
-
-        var yChanged = new double[u.Length];
-        alglib.ablas.rmatrixmv(nRows, nRows, changedK, 0, 0, 0, u, 0, ref yChanged, 0);
-
-        OnlineCalculatorError error;
-        var mse = OnlineMeanSquaredErrorCalculator.Calculate(y, yChanged, out error);
-        if (error != OnlineCalculatorError.None) mse = double.MaxValue;
-        relevance[i] = mse;
-      }
-      // scale so that max relevance is 1.0
-      var maxRel = relevance.Max();
-      for (int i = 0; i < relevance.Length; i++) relevance[i] /= maxRel;
-      return relevance;
-    }
-
-    private double[,] CalculateCovariance(List<double>[] xs, double[] l) {
-      int nRows = xs.First().Count;
-      double[,] K = new double[nRows, nRows];
-      for (int r = 0; r < nRows; r++) {
-        double[] xi = xs.Select(x => x[r]).ToArray();
-        for (int c = 0; c <= r; c++) {
-          double[] xj = xs.Select(x => x[c]).ToArray();
-          double dSqr = xi.Zip(xj, (xik, xjk) => (xik - xjk))
-            .Select(dk => dk * dk)
-            .Zip(l, (dk, lk) => dk / lk)
-            .Sum();
-          K[r, c] = Math.Exp(-dSqr);
-        }
-      }
-      // add a small diagonal matrix for numeric stability
-      for (int i = 0; i < nRows; i++) {
-        K[i, i] += 1.0E-7;
-      }
-
-      return K;
-    }
+    // sample a random function and calculate the variable relevances
+    protected abstract IEnumerable<double> GenerateRandomFunction(IRandom rand, List<List<double>> xs, out string[] selectedVarNames, out double[] relevance);
   }
 }
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/VariableNetworks/VariableNetworkInstanceProvider.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/VariableNetworks/VariableNetworkInstanceProvider.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.DataAnalysis/3.3/Regression/VariableNetworks/VariableNetworkInstanceProvider.cs	(revision 14751)
@@ -49,11 +49,17 @@
     public override IEnumerable<IDataDescriptor> GetDataDescriptors() {
       var numVariables = new int[] { 10, 20, 50, 100 };
-      var noiseRatios = new double[] { 0, 0.01, 0.05, 0.1 };
+      var noiseRatios = new double[] { 0, 0.01, 0.05, 0.1, 0.2 };
       var rand = new MersenneTwister((uint)Seed); // use fixed seed for deterministic problem generation
-      return (from size in numVariables
-              from noiseRatio in noiseRatios
-              select new VariableNetwork(size, noiseRatio, new MersenneTwister((uint)rand.Next())))
-              .Cast<IDataDescriptor>()
-              .ToList();
+      var lr = (from size in numVariables
+                from noiseRatio in noiseRatios
+                select new LinearVariableNetwork(size, noiseRatio, new MersenneTwister((uint)rand.Next())))
+                .Cast<IDataDescriptor>()
+                .ToList();
+      var gp = (from size in numVariables
+                from noiseRatio in noiseRatios
+                select new GaussianProcessVariableNetwork(size, noiseRatio, new MersenneTwister((uint)rand.Next())))
+                .Cast<IDataDescriptor>()
+                .ToList();
+      return lr.Concat(gp);
     }
 
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.Scheduling/3.3/JSSPORLIBParser.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.Scheduling/3.3/JSSPORLIBParser.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.Scheduling/3.3/JSSPORLIBParser.cs	(revision 14751)
@@ -54,5 +54,5 @@
 
     public void Export(string file) {
-      using (Stream stream = new FileStream(file, FileMode.Create, FileAccess.ReadWrite)) {
+      using (Stream stream = new FileStream(file, FileMode.Create, FileAccess.Write)) {
         Export(stream);
       }
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/Data/atsp.qual
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/Data/atsp.qual	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/Data/atsp.qual	(revision 14751)
@@ -0,0 +1,19 @@
+br17;39
+ft53;6905
+ft70;38673
+ftv33;1286
+ftv35;1473
+ftv38;1530
+ftv44;1613
+ftv47;1776
+ftv55;1608
+ftv64;1839
+ftv70;1950
+ftv170;2755
+kro124p;36230
+p43;5620
+rbg323;1326
+rbg358;1163
+rbg403;2465
+rbg443;2720
+ry48p;14422
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/Data/tsp.qual
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/Data/tsp.qual	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/Data/tsp.qual	(revision 14751)
@@ -0,0 +1,110 @@
+a280;2579
+ali535;202339
+att48;10628
+att532;27686
+bayg29;1610
+bays29;2020
+berlin52;7542
+bier127;118282
+brazil58;25395
+brd14051;469385
+brg180;1950
+burma14;3323
+ch130;6110
+ch150;6528
+d1291;50801
+d15112;1573084
+d1655;62128
+d18512;645238
+d198;15780
+d2103;80450
+d493;35002
+d657;48912
+dantzig42;699
+dsj1000;18660188
+eil101;629
+eil51;426
+eil76;538
+fl1400;20127
+fl1577;22249
+fl3795;28772
+fl417;11861
+fnl4461;182566
+fri26;937
+gil262;2378
+gr120;6942
+gr137;69853
+gr17;2085
+gr202;40160
+gr21;2707
+gr229;134602
+gr24;1272
+gr431;171414
+gr48;5046
+gr666;294358
+gr96;55209
+hk48;11461
+kroA100;21282
+kroA150;26524
+kroA200;29368
+kroB100;22141
+kroB150;26130
+kroB200;29437
+kroC100;20749
+kroD100;21294
+kroE100;22068
+lin105;14379
+lin318;42029
+nrw1379;56638
+p654;34643
+pa561;2763
+pcb1173;56892
+pcb3038;137694
+pcb442;50778
+pla33810;66048945
+pla7397;23260728
+pla85900;142382641
+pr1002;259045
+pr107;44303
+pr124;59030
+pr136;96772
+pr144;58537
+pr152;73682
+pr226;80369
+pr2392;378032
+pr264;49135
+pr299;48191
+pr439;107217
+pr76;108159
+rat195;2323
+rat575;6773
+rat783;8806
+rat99;1211
+rd100;7910
+rd400;15281
+rl11849;923288
+rl1304;252948
+rl1323;270199
+rl1889;316536
+rl5915;565530
+rl5934;556045
+si1032;92650
+si175;21407
+si535;48450
+st70;675
+swiss42;1273
+ts225;126643
+tsp225;3916
+u1060;224094
+u1432;152970
+u159;42080
+u1817;57201
+u2152;64253
+u2319;234256
+u574;36905
+u724;41910
+ulysses16;6859
+ulysses22;7013
+usa13509;19982859
+vm1084;239297
+vm1748;336556
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/HeuristicLab.Problems.Instances.TSPLIB-3.3.csproj
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/HeuristicLab.Problems.Instances.TSPLIB-3.3.csproj	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/HeuristicLab.Problems.Instances.TSPLIB-3.3.csproj	(revision 14751)
@@ -130,4 +130,6 @@
     <EmbeddedResource Include="Data\tsp.opt.tour.zip" />
     <EmbeddedResource Include="Data\vrp.zip" />
+    <EmbeddedResource Include="Data\tsp.qual" />
+    <EmbeddedResource Include="Data\atsp.qual" />
     <None Include="Plugin.cs.frame" />
     <Compile Include="Plugin.cs" />
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBATSPInstanceProvider.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBATSPInstanceProvider.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBATSPInstanceProvider.cs	(revision 14751)
@@ -56,4 +56,8 @@
       instance.BestKnownTour = parser.Tour.FirstOrDefault();
     }
+
+    protected override void LoadQuality(double? bestQuality, ATSPData instance) {
+      instance.BestKnownQuality = bestQuality;
+    }
   }
 }
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBCVRPInstanceProvider.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBCVRPInstanceProvider.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBCVRPInstanceProvider.cs	(revision 14751)
@@ -78,4 +78,8 @@
       instance.BestKnownTour = parser.Tour;
     }
+
+    protected override void LoadQuality(double? bestQuality, CVRPData instance) {
+      instance.BestKnownQuality = bestQuality;
+    }
   }
 }
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBDataDescriptor.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBDataDescriptor.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBDataDescriptor.cs	(revision 14751)
@@ -27,10 +27,12 @@
     internal string InstanceIdentifier { get; set; }
     internal string SolutionIdentifier { get; set; }
+    internal double? BestQuality { get; set; }
 
-    internal TSPLIBDataDescriptor(string name, string description, string instanceIdentifier, string solutionIdentifier) {
+    internal TSPLIBDataDescriptor(string name, string description, string instanceIdentifier, string solutionIdentifier, double? bestQuality) {
       this.Name = name;
       this.Description = description;
       this.InstanceIdentifier = instanceIdentifier;
       this.SolutionIdentifier = solutionIdentifier;
+      this.BestQuality = bestQuality;
     }
   }
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBHeterogeneousPTSPDataDescriptor.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBHeterogeneousPTSPDataDescriptor.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBHeterogeneousPTSPDataDescriptor.cs	(revision 14751)
@@ -26,5 +26,5 @@
 
     internal TSPLIBHeterogeneousPTSPDataDescriptor(string name, string description, string instanceIdentifier, string solutionIdentifier, int seed)
-      : base(name, description, instanceIdentifier, solutionIdentifier) {
+      : base(name, description, instanceIdentifier, solutionIdentifier, null) {
       this.Name = name;
       this.Description = description;
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBHomogeneousPTSPDataDescriptor.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBHomogeneousPTSPDataDescriptor.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBHomogeneousPTSPDataDescriptor.cs	(revision 14751)
@@ -25,5 +25,5 @@
 
     internal TSPLIBHomogeneousPTSPDataDescriptor(string name, string description, string instanceIdentifier, string solutionIdentifier, double probability)
-      : base(name, description, instanceIdentifier, solutionIdentifier) {
+      : base(name, description, instanceIdentifier, solutionIdentifier, null) {
       this.Name = name;
       this.Description = description;
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBInstanceProvider.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBInstanceProvider.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBInstanceProvider.cs	(revision 14751)
@@ -47,4 +47,5 @@
     protected abstract T LoadInstance(TSPLIBParser parser, IDataDescriptor descriptor = null);
     protected abstract void LoadSolution(TSPLIBParser parser, T instance);
+    protected abstract void LoadQuality(double? bestQuality, T instance);
 
     public override IEnumerable<IDataDescriptor> GetDataDescriptors() {
@@ -57,4 +58,16 @@
         }
       }
+
+      Dictionary<string, double> qualities = new Dictionary<string, double>();
+      var qualitiesArchiveName = GetResourceName(FileExtension + @"\.qual");
+      if (!String.IsNullOrEmpty(qualitiesArchiveName)) {
+        using (var qualitiesReader = new StreamReader(GetType().Assembly.GetManifestResourceStream(qualitiesArchiveName))) {
+          while (!qualitiesReader.EndOfStream) {
+            var line = qualitiesReader.ReadLine().Split(';');
+            qualities.Add(line[0] + "." + FileExtension, double.Parse(line[1]));
+          }
+        }
+      }
+
       var instanceArchiveName = GetResourceName(FileExtension + @"\.zip");
       if (String.IsNullOrEmpty(instanceArchiveName)) yield break;
@@ -62,5 +75,9 @@
       using (var instanceStream = new ZipArchive(GetType().Assembly.GetManifestResourceStream(instanceArchiveName), ZipArchiveMode.Read)) {
         foreach (var entry in instanceStream.Entries.Select(x => x.Name).OrderBy(x => x)) {
-          yield return new TSPLIBDataDescriptor(Path.GetFileNameWithoutExtension(entry), GetInstanceDescription(), entry, solutions.ContainsKey(entry) ? solutions[entry] : String.Empty);
+          yield return new TSPLIBDataDescriptor(Path.GetFileNameWithoutExtension(entry),
+                                                GetInstanceDescription(),
+                                                entry,
+                                                solutions.ContainsKey(entry) ? solutions[entry] : String.Empty,
+                                                qualities.ContainsKey(entry) ? (double?)qualities[entry] : null);
         }
       }
@@ -75,4 +92,5 @@
         var parser = new TSPLIBParser(stream);
         var instance = LoadInstance(parser, id);
+        LoadQuality(descriptor.BestQuality, instance);
 
         if (!String.IsNullOrEmpty(descriptor.SolutionIdentifier)) {
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBPTSPInstanceProvider.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBPTSPInstanceProvider.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBPTSPInstanceProvider.cs	(revision 14751)
@@ -81,4 +81,8 @@
     }
 
+    protected override void LoadQuality(double? bestQuality, PTSPData instance) {
+      instance.BestKnownQuality = bestQuality;
+    }
+
     public PTSPData LoadData(string tspFile, string tourFile, double? bestQuality) {
       var data = LoadInstance(new TSPLIBParser(tspFile));
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBTSPInstanceProvider.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBTSPInstanceProvider.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Instances.TSPLIB/3.3/TSPLIBTSPInstanceProvider.cs	(revision 14751)
@@ -81,4 +81,8 @@
     }
 
+    protected override void LoadQuality(double? bestQuality, TSPData instance) {
+      instance.BestKnownQuality = bestQuality;
+    }
+
     public TSPData LoadData(string tspFile, string tourFile, double? bestQuality) {
       var data = LoadInstance(new TSPLIBParser(tspFile));
Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.Programmable/3.3/HeuristicLab.Problems.Programmable-3.3.csproj
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.Programmable/3.3/HeuristicLab.Problems.Programmable-3.3.csproj	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.Programmable/3.3/HeuristicLab.Problems.Programmable-3.3.csproj	(revision 14751)
@@ -149,5 +149,5 @@
       <Name>HeuristicLab.Encodings.IntegerVectorEncoding-3.3</Name>
     </ProjectReference>
-    <ProjectReference Include="..\..\HeuristicLab.Encodings.LinearLinkageEncoding\3.3\HeuristicLab.Encodings.LinearLinkageEncoding-3.4.csproj">
+    <ProjectReference Include="..\..\HeuristicLab.Encodings.LinearLinkageEncoding\3.4\HeuristicLab.Encodings.LinearLinkageEncoding-3.4.csproj">
       <Project>{be698769-975a-429e-828c-72bb2b6182c8}</Project>
       <Name>HeuristicLab.Encodings.LinearLinkageEncoding-3.4</Name>
Index: /branches/symbreg-factors-2650/HeuristicLab.Services.WebApp.Status/3.3/WebApi/DataController.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Services.WebApp.Status/3.3/WebApi/DataController.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Services.WebApp.Status/3.3/WebApi/DataController.cs	(revision 14751)
@@ -91,5 +91,5 @@
           AvgWaitingTime = count > 0 ? (long)calculatingTasks.OrderBy(x => x.StartTime).Take(count).Average(x => x.InitialWaitingTime) : 0,
           AvgCalculatingTime = lastCalculatingTimes != null ? lastCalculatingTimes.Avg : 0,
-          TotalCpuTime = factTasks.Sum(x => Convert.ToInt64(x.CalculatingTime)),
+          TotalCpuTime = factTasks.Sum(x => (long?)x.CalculatingTime) ?? 0,
           StandardDeviationCalculatingTime = (long)StandardDeviation(completedTasks.Select(x => (double)x.CalculatingTime)),
           BeginDate = factTasks.Where(x => x.StartTime.HasValue).OrderBy(x => x.StartTime).Select(x => x.StartTime).FirstOrDefault()
Index: /branches/symbreg-factors-2650/HeuristicLab.Services.WebApp/3.3/WebApp/helper.js
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Services.WebApp/3.3/WebApp/helper.js	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Services.WebApp/3.3/WebApp/helper.js	(revision 14751)
@@ -31,5 +31,5 @@
     var month = (1 + date.getMonth()).zeropad(2);
     var year = date.getFullYear();
-    var hour = (date.getHours() - 2).zeropad(2);
+    var hour = (date.getHours() + date.getTimezoneOffset() / 60).zeropad(2);
     var minute = date.getMinutes().zeropad(2);
     var second = date.getSeconds().zeropad(2);
Index: /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Encodings.LinearLinkageEncoding-3.4/Auxiliary.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Encodings.LinearLinkageEncoding-3.4/Auxiliary.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Encodings.LinearLinkageEncoding-3.4/Auxiliary.cs	(revision 14751)
@@ -1,5 +1,5 @@
 ﻿#region License Information
 /* HeuristicLab
- * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
  *
  * This file is part of HeuristicLab.
Index: /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Encodings.LinearLinkageEncoding-3.4/ConversionsTest.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Encodings.LinearLinkageEncoding-3.4/ConversionsTest.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Encodings.LinearLinkageEncoding-3.4/ConversionsTest.cs	(revision 14751)
@@ -1,3 +1,26 @@
-﻿using System;
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 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.Linq;
+using HeuristicLab.Random;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 
@@ -95,4 +118,22 @@
       }
     }
+
+    [TestMethod]
+    [TestCategory("Encodings.LinearLinkage")]
+    [TestProperty("Time", "short")]
+    public void ConvertFromLLEtoLLEe() {
+      var random = new MersenneTwister(42);
+      var lle = MaxGroupsLinearLinkageCreator.Apply(random, 32, 8);
+      Assert.IsTrue(lle.SequenceEqual(LinearLinkage.FromEndLinks(lle.ToEndLinks())));
+    }
+
+    [TestMethod]
+    [TestCategory("Encodings.LinearLinkage")]
+    [TestProperty("Time", "short")]
+    public void ConvertFromLLEtoLLEb() {
+      var random = new MersenneTwister(42);
+      var lle = MaxGroupsLinearLinkageCreator.Apply(random, 32, 8);
+      Assert.IsTrue(lle.SequenceEqual(LinearLinkage.FromBackLinks(lle.ToBackLinks())));
+    }
   }
 }
Index: /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Encodings.LinearLinkageEncoding-3.4/GroupCrossoverTest.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Encodings.LinearLinkageEncoding-3.4/GroupCrossoverTest.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Encodings.LinearLinkageEncoding-3.4/GroupCrossoverTest.cs	(revision 14751)
@@ -1,3 +1,23 @@
-﻿using System;
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 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 HeuristicLab.Random;
 using HeuristicLab.Tests;
Index: /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Encodings.LinearLinkageEncoding-3.4/MoveTests.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Encodings.LinearLinkageEncoding-3.4/MoveTests.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Encodings.LinearLinkageEncoding-3.4/MoveTests.cs	(revision 14751)
@@ -0,0 +1,84 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 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.Linq;
+using HeuristicLab.Random;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding.Tests {
+  [TestClass]
+  public class LinearLinkageMoveTests {
+    [TestMethod]
+    [TestCategory("Encodings.LinearLinkage")]
+    [TestProperty("Time", "short")]
+    public void TestLinearLinkageMoveApplyUndo() {
+      var random = new MersenneTwister(42);
+      for (var p = 0; p < 7; p++) {
+        var lle = ExactGroupsLinearLinkageCreator.Apply(random, 64, (int)Math.Pow(2, p));
+        var before = (LinearLinkage)lle.Clone();
+        var beforeb = before.ToBackLinks();
+        var moves = 0;
+        foreach (var move in MoveGenerator.Generate(lle)) {
+          var lleb = lle.ToBackLinks();
+          move.Apply(lle);
+          move.ApplyToLLEb(lleb);
+          Assert.IsFalse(before.SequenceEqual(lle));
+          Assert.IsFalse(lleb.SequenceEqual(beforeb));
+          Assert.IsTrue(lleb.SequenceEqual(lle.ToBackLinks()));
+          Assert.IsTrue(lle.SequenceEqual(LinearLinkage.FromBackLinks(lleb)));
+          move.Undo(lle);
+          Assert.IsTrue(before.SequenceEqual(lle));
+          moves++;
+        }
+        Assert.IsTrue(moves > 0);
+      }
+    }
+
+    [TestMethod]
+    [TestCategory("Encodings.LinearLinkage")]
+    [TestProperty("Time", "short")]
+    public void TestLinearLinkageMoveApply() {
+      var random = new MersenneTwister(42);
+      for (var p = 0; p < 7; p++) {
+        var lle = ExactGroupsLinearLinkageCreator.Apply(random, 64, (int)Math.Pow(2, p));
+        var lleb = lle.ToBackLinks();
+        var groupItems = new List<int>() { 0 };
+        var moves = 0;
+        for (var i = 1; i < lle.Length; i++) {
+          var move = MoveGenerator.GenerateForItem(i, groupItems, lle, lleb).SampleRandom(random);
+          move.Apply(lle);
+          move.ApplyToLLEb(lleb);
+          Assert.IsTrue(lleb.SequenceEqual(lle.ToBackLinks()));
+          Assert.IsTrue(lle.SequenceEqual(LinearLinkage.FromBackLinks(lleb)));
+
+          if (lleb[i] != i)
+            groupItems.Remove(lleb[i]);
+          groupItems.Add(i);
+
+          moves++;
+        }
+        Assert.IsTrue(moves == lle.Length - 1);
+      }
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Encodings.PermutationEncoding-3.3/PermutationManipulationTest.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Encodings.PermutationEncoding-3.3/PermutationManipulationTest.cs	(revision 14751)
+++ /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Encodings.PermutationEncoding-3.3/PermutationManipulationTest.cs	(revision 14751)
@@ -0,0 +1,181 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 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 Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace HeuristicLab.Encodings.PermutationEncoding.Tests {
+  [TestClass]
+  public class PermutationManipulationTest {
+    [TestMethod]
+    [TestCategory("Encodings.Permutation")]
+    [TestProperty("Time", "short")]
+    public void TestPermutationSwap() {
+      var permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Swap(0, 0);
+      Assert.IsTrue(permutation.Validate());
+      var expected = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Swap(0, 9);
+      Assert.IsTrue(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 9, 1, 2, 3, 4, 5, 6, 7, 8, 0 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Swap(1, 3);
+      permutation.Swap(3, 5);
+      Assert.IsTrue(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 0, 3, 2, 5, 4, 1, 6, 7, 8, 9 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+    }
+
+    [TestMethod]
+    [TestCategory("Encodings.Permutation")]
+    [TestProperty("Time", "short")]
+    public void TestPermutationReverse() {
+      var permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Reverse(0, 0);
+      Assert.IsTrue(permutation.Validate());
+      var expected = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Reverse(9, 1);
+      Assert.IsTrue(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Reverse(0, 10);
+      Assert.IsTrue(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Reverse(1, 4);
+      Assert.IsTrue(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 0, 4, 3, 2, 1, 5, 6, 7, 8, 9 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+    }
+
+    [TestMethod]
+    [TestCategory("Encodings.Permutation")]
+    [TestProperty("Time", "short")]
+    public void TestPermutationMove() {
+      var permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Move(0, 0, 0);
+      Assert.IsTrue(permutation.Validate());
+      var expected = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Move(0, 0, 1);
+      Assert.IsTrue(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 1, 0, 2, 3, 4, 5, 6, 7, 8, 9 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Move(0, 2, 3);
+      Assert.IsTrue(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 3, 4, 5, 0, 1, 2, 6, 7, 8, 9 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Move(3, 5, 0);
+      Assert.IsTrue(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 3, 4, 5, 0, 1, 2, 6, 7, 8, 9 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Move(3, 5, 2);
+      Assert.IsTrue(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 3, 4, 5, 2, 6, 7, 8, 9 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Move(5, 6, 8);
+      Assert.IsTrue(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 7, 8, 9, 5, 6 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Move(5, 7, 7);
+      Assert.IsTrue(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 8, 9, 5, 6, 7 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Move(2, 6, 5);
+      Assert.IsTrue(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 7, 8, 9, 2, 3, 4, 5, 6 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Move(3, 5, 4);
+      Assert.IsTrue(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 6, 3, 4, 5, 7, 8, 9 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      var exception = false;
+      try {
+        permutation.Move(0, 5, 7);
+      } catch { exception = true; }
+      Assert.IsTrue(exception);
+    }
+
+    [TestMethod]
+    [TestCategory("Encodings.Permutation")]
+    [TestProperty("Time", "short")]
+    public void TestPermutationReplace() {
+      var permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Replace(0, new int[0]);
+      Assert.IsTrue(permutation.Validate());
+      var expected = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Replace(3, new [] { 3 });
+      Assert.IsTrue(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Replace(5, new [] { 8, 6, 5, 7 });
+      Assert.IsTrue(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 8, 6, 5, 7, 9 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Replace(0, new [] { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 });
+      Assert.IsTrue(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+
+      permutation = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+      permutation.Replace(3, new[] { 5, 6, 7 });
+      Assert.IsFalse(permutation.Validate());
+      expected = new Permutation(PermutationTypes.Absolute, new[] { 0, 1, 2, 5, 6, 7, 6, 7, 8, 9 });
+      Assert.IsTrue(Auxiliary.PermutationIsEqualByPosition(expected, permutation));
+    }
+  }
+}
Index: /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Tests.csproj
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Tests.csproj	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Tests/HeuristicLab.Tests.csproj	(revision 14751)
@@ -512,4 +512,5 @@
     <Compile Include="HeuristicLab.Encodings.LinearLinkageEncoding-3.4\ConversionsTest.cs" />
     <Compile Include="HeuristicLab.Encodings.LinearLinkageEncoding-3.4\GroupCrossoverTest.cs" />
+    <Compile Include="HeuristicLab.Encodings.LinearLinkageEncoding-3.4\MoveTests.cs" />
     <Compile Include="HeuristicLab.Encodings.PermutationEncoding-3.3\Auxiliary.cs" />
     <Compile Include="HeuristicLab.Encodings.PermutationEncoding-3.3\CosaCrossoverTest.cs" />
@@ -532,4 +533,5 @@
     <Compile Include="HeuristicLab.Encodings.PermutationEncoding-3.3\TranslocationManipulatorTest.cs" />
     <Compile Include="HeuristicLab.Encodings.PermutationEncoding-3.3\UniformLikeCrossoverTest.cs" />
+    <Compile Include="HeuristicLab.Encodings.PermutationEncoding-3.3\PermutationManipulationTest.cs" />
     <Compile Include="HeuristicLab.Encodings.RealVectorEncoding-3.3\Auxiliary.cs" />
     <Compile Include="HeuristicLab.Encodings.RealVectorEncoding-3.3\BlendAlphaBetaCrossoverTest.cs" />
Index: /branches/symbreg-factors-2650/HeuristicLab.Visualization.ChartControlsExtensions/3.3/EnhancedChart.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Visualization.ChartControlsExtensions/3.3/EnhancedChart.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Visualization.ChartControlsExtensions/3.3/EnhancedChart.cs	(revision 14751)
@@ -79,5 +79,5 @@
         if (result.ChartArea != null && (result.ChartElementType == ChartElementType.PlottingArea || result.ChartElementType == ChartElementType.Gridlines)) {
           foreach (var axis in result.ChartArea.Axes)
-            axis.ScaleView.ZoomReset(int.MaxValue);
+            axis.ScaleView.ZoomReset(0);
         }
       }
Index: /branches/symbreg-factors-2650/HeuristicLab.Visualization.ChartControlsExtensions/3.3/GanttChart.Designer.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Visualization.ChartControlsExtensions/3.3/GanttChart.Designer.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Visualization.ChartControlsExtensions/3.3/GanttChart.Designer.cs	(revision 14751)
@@ -45,7 +45,9 @@
     /// </summary>
     private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
       System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
       System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend();
       System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series();
+      System.Windows.Forms.DataVisualization.Charting.Title title1 = new System.Windows.Forms.DataVisualization.Charting.Title();
       this.chart = new HeuristicLab.Visualization.ChartControlsExtensions.EnhancedChart();
       ((System.ComponentModel.ISupportInitialize)(this.chart)).BeginInit();
@@ -54,7 +56,7 @@
       // chart
       // 
-      this.chart.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-                  | System.Windows.Forms.AnchorStyles.Left)
-                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.chart.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
       chartArea1.AxisX.ScaleView.SmallScrollMinSizeType = System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType.Milliseconds;
       chartArea1.AxisY.ScaleView.SmallScrollMinSizeType = System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType.Milliseconds;
@@ -79,8 +81,12 @@
       this.chart.TabIndex = 0;
       this.chart.Text = "chart";
+      title1.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+      title1.Name = "Gantt Title";
+      title1.Text = "Gantt Title";
+      title1.Visible = false;
+      this.chart.Titles.Add(title1);
       // 
       // GanttChart
       // 
-      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
       this.Controls.Add(this.chart);
Index: /branches/symbreg-factors-2650/HeuristicLab.Visualization.ChartControlsExtensions/3.3/GanttChart.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Visualization.ChartControlsExtensions/3.3/GanttChart.cs	(revision 14750)
+++ /branches/symbreg-factors-2650/HeuristicLab.Visualization.ChartControlsExtensions/3.3/GanttChart.cs	(revision 14751)
@@ -32,4 +32,12 @@
     private IDictionary<string, Color> categories = new Dictionary<string, Color>();
     private IDictionary<string, int> rowNames = new Dictionary<string, int>();
+
+    public string Title {
+      get { return chart.Titles[0].Text; }
+      set {
+        chart.Titles[0].Text = value ?? string.Empty;
+        chart.Titles[0].Visible = value != null;
+      }
+    }
 
     public GanttChart() {
