Index: /stable/HeuristicLab.Common/3.3/TimeSpanHelper.cs
===================================================================
--- /stable/HeuristicLab.Common/3.3/TimeSpanHelper.cs	(revision 12738)
+++ /stable/HeuristicLab.Common/3.3/TimeSpanHelper.cs	(revision 12739)
@@ -51,8 +51,10 @@
           case "hour":
           case "hours": result = result.Add(TimeSpan.FromHours(value)); break;
+          case "m":
           case "min":
           case "minute":
           case "minutes": result = result.Add(TimeSpan.FromMinutes(value)); break;
           case "s":
+          case "sec":
           case "second":
           case "seconds": result = result.Add(TimeSpan.FromSeconds(value)); break;
Index: /stable/HeuristicLab.Optimization.Views/3.3/HeuristicLab.Optimization.Views-3.3.csproj
===================================================================
--- /stable/HeuristicLab.Optimization.Views/3.3/HeuristicLab.Optimization.Views-3.3.csproj	(revision 12738)
+++ /stable/HeuristicLab.Optimization.Views/3.3/HeuristicLab.Optimization.Views-3.3.csproj	(revision 12739)
@@ -281,4 +281,10 @@
       <DependentUpon>RunCollectionChartAggregationView.cs</DependentUpon>
     </Compile>
+    <Compile Include="TimeLimitRunView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="TimeLimitRunView.Designer.cs">
+      <DependentUpon>TimeLimitRunView.cs</DependentUpon>
+    </Compile>
     <Compile Include="UserDefinedAlgorithmView.cs">
       <SubType>UserControl</SubType>
Index: /stable/HeuristicLab.Optimization.Views/3.3/TimeLimitRunView.Designer.cs
===================================================================
--- /stable/HeuristicLab.Optimization.Views/3.3/TimeLimitRunView.Designer.cs	(revision 12739)
+++ /stable/HeuristicLab.Optimization.Views/3.3/TimeLimitRunView.Designer.cs	(revision 12739)
@@ -0,0 +1,361 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2012 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 TimeLimitRunView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    #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() {
+      System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TimeLimitRunView));
+      this.timeLimitLabel = new System.Windows.Forms.Label();
+      this.timeLimitTextBox = new System.Windows.Forms.TextBox();
+      this.label1 = new System.Windows.Forms.Label();
+      this.snapshotsTextBox = new System.Windows.Forms.TextBox();
+      this.storeAlgorithmInEachSnapshotCheckBox = new System.Windows.Forms.CheckBox();
+      this.tabControl = new HeuristicLab.MainForm.WindowsForms.DragOverTabControl();
+      this.algorithmTabPage = new System.Windows.Forms.TabPage();
+      this.algorithmViewHost = new HeuristicLab.MainForm.WindowsForms.ViewHost();
+      this.openAlgorithmButton = new System.Windows.Forms.Button();
+      this.newAlgorithmButton = new System.Windows.Forms.Button();
+      this.snapshotsTabPage = new System.Windows.Forms.TabPage();
+      this.snapshotsView = new HeuristicLab.Optimization.Views.RunCollectionView();
+      this.runsTabPage = new System.Windows.Forms.TabPage();
+      this.runsView = new HeuristicLab.Optimization.Views.RunCollectionView();
+      this.openFileDialog = new System.Windows.Forms.OpenFileDialog();
+      this.snapshotButton = new System.Windows.Forms.Button();
+      this.sequenceButton = new System.Windows.Forms.Button();
+      ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).BeginInit();
+      this.tabControl.SuspendLayout();
+      this.algorithmTabPage.SuspendLayout();
+      this.snapshotsTabPage.SuspendLayout();
+      this.runsTabPage.SuspendLayout();
+      this.SuspendLayout();
+      // 
+      // startButton
+      // 
+      this.startButton.Location = new System.Drawing.Point(0, 440);
+      this.startButton.TabIndex = 9;
+      this.toolTip.SetToolTip(this.startButton, "Start/Resume Optimizer");
+      // 
+      // executionTimeTextBox
+      // 
+      this.executionTimeTextBox.Location = new System.Drawing.Point(412, 444);
+      // 
+      // executionTimeLabel
+      // 
+      this.executionTimeLabel.Location = new System.Drawing.Point(323, 447);
+      // 
+      // pauseButton
+      // 
+      this.pauseButton.Location = new System.Drawing.Point(30, 440);
+      this.pauseButton.TabIndex = 10;
+      this.toolTip.SetToolTip(this.pauseButton, "Pause Optimizer");
+      // 
+      // stopButton
+      // 
+      this.stopButton.Location = new System.Drawing.Point(60, 440);
+      this.stopButton.TabIndex = 11;
+      this.toolTip.SetToolTip(this.stopButton, "Stop Optimizer");
+      // 
+      // resetButton
+      // 
+      this.resetButton.Location = new System.Drawing.Point(90, 440);
+      this.resetButton.TabIndex = 12;
+      this.toolTip.SetToolTip(this.resetButton, "Reset Optimizer");
+      // 
+      // nameTextBox
+      // 
+      this.errorProvider.SetIconAlignment(this.nameTextBox, System.Windows.Forms.ErrorIconAlignment.MiddleLeft);
+      this.errorProvider.SetIconPadding(this.nameTextBox, 2);
+      this.nameTextBox.Location = new System.Drawing.Point(69, 0);
+      this.nameTextBox.Size = new System.Drawing.Size(455, 20);
+      // 
+      // infoLabel
+      // 
+      this.infoLabel.Location = new System.Drawing.Point(530, 3);
+      // 
+      // timeLimitLabel
+      // 
+      this.timeLimitLabel.AutoSize = true;
+      this.timeLimitLabel.Location = new System.Drawing.Point(3, 29);
+      this.timeLimitLabel.Name = "timeLimitLabel";
+      this.timeLimitLabel.Size = new System.Drawing.Size(53, 13);
+      this.timeLimitLabel.TabIndex = 3;
+      this.timeLimitLabel.Text = "Time limit:";
+      // 
+      // timeLimitTextBox
+      // 
+      this.timeLimitTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.timeLimitTextBox.Location = new System.Drawing.Point(69, 26);
+      this.timeLimitTextBox.Name = "timeLimitTextBox";
+      this.timeLimitTextBox.Size = new System.Drawing.Size(455, 20);
+      this.timeLimitTextBox.TabIndex = 4;
+      this.timeLimitTextBox.Validating += new System.ComponentModel.CancelEventHandler(this.timeLimitTextBox_Validating);
+      // 
+      // label1
+      // 
+      this.label1.AutoSize = true;
+      this.label1.Location = new System.Drawing.Point(3, 55);
+      this.label1.Name = "label1";
+      this.label1.Size = new System.Drawing.Size(60, 13);
+      this.label1.TabIndex = 5;
+      this.label1.Text = "Snapshots:";
+      // 
+      // snapshotsTextBox
+      // 
+      this.snapshotsTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.snapshotsTextBox.Location = new System.Drawing.Point(69, 52);
+      this.snapshotsTextBox.Name = "snapshotsTextBox";
+      this.snapshotsTextBox.Size = new System.Drawing.Size(175, 20);
+      this.snapshotsTextBox.TabIndex = 6;
+      this.snapshotsTextBox.Validating += new System.ComponentModel.CancelEventHandler(this.snapshotsTextBox_Validating);
+      // 
+      // storeAlgorithmInEachSnapshotCheckBox
+      // 
+      this.storeAlgorithmInEachSnapshotCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+      this.storeAlgorithmInEachSnapshotCheckBox.AutoSize = true;
+      this.storeAlgorithmInEachSnapshotCheckBox.Location = new System.Drawing.Point(340, 54);
+      this.storeAlgorithmInEachSnapshotCheckBox.Name = "storeAlgorithmInEachSnapshotCheckBox";
+      this.storeAlgorithmInEachSnapshotCheckBox.Size = new System.Drawing.Size(184, 17);
+      this.storeAlgorithmInEachSnapshotCheckBox.TabIndex = 7;
+      this.storeAlgorithmInEachSnapshotCheckBox.Text = "Store Algorithm in Each Snapshot";
+      this.storeAlgorithmInEachSnapshotCheckBox.UseVisualStyleBackColor = true;
+      this.storeAlgorithmInEachSnapshotCheckBox.CheckedChanged += new System.EventHandler(this.storeAlgorithmInEachSnapshotCheckBox_CheckedChanged);
+      // 
+      // tabControl
+      // 
+      this.tabControl.AllowDrop = true;
+      this.tabControl.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.tabControl.Controls.Add(this.algorithmTabPage);
+      this.tabControl.Controls.Add(this.snapshotsTabPage);
+      this.tabControl.Controls.Add(this.runsTabPage);
+      this.tabControl.Location = new System.Drawing.Point(0, 78);
+      this.tabControl.Name = "tabControl";
+      this.tabControl.SelectedIndex = 0;
+      this.tabControl.Size = new System.Drawing.Size(546, 356);
+      this.tabControl.TabIndex = 8;
+      // 
+      // algorithmTabPage
+      // 
+      this.algorithmTabPage.AllowDrop = true;
+      this.algorithmTabPage.Controls.Add(this.algorithmViewHost);
+      this.algorithmTabPage.Controls.Add(this.openAlgorithmButton);
+      this.algorithmTabPage.Controls.Add(this.newAlgorithmButton);
+      this.algorithmTabPage.Location = new System.Drawing.Point(4, 22);
+      this.algorithmTabPage.Name = "algorithmTabPage";
+      this.algorithmTabPage.Padding = new System.Windows.Forms.Padding(3);
+      this.algorithmTabPage.Size = new System.Drawing.Size(538, 330);
+      this.algorithmTabPage.TabIndex = 1;
+      this.algorithmTabPage.Text = "Algorithm";
+      this.algorithmTabPage.UseVisualStyleBackColor = true;
+      this.algorithmTabPage.DragDrop += new System.Windows.Forms.DragEventHandler(this.algorithmTabPage_DragDrop);
+      this.algorithmTabPage.DragEnter += new System.Windows.Forms.DragEventHandler(this.algorithmTabPage_DragEnterOver);
+      this.algorithmTabPage.DragOver += new System.Windows.Forms.DragEventHandler(this.algorithmTabPage_DragEnterOver);
+      // 
+      // algorithmViewHost
+      // 
+      this.algorithmViewHost.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.algorithmViewHost.Caption = "View";
+      this.algorithmViewHost.Content = null;
+      this.algorithmViewHost.Enabled = false;
+      this.algorithmViewHost.Location = new System.Drawing.Point(6, 36);
+      this.algorithmViewHost.Name = "algorithmViewHost";
+      this.algorithmViewHost.ReadOnly = false;
+      this.algorithmViewHost.Size = new System.Drawing.Size(526, 288);
+      this.algorithmViewHost.TabIndex = 2;
+      this.algorithmViewHost.ViewsLabelVisible = true;
+      this.algorithmViewHost.ViewType = null;
+      // 
+      // openAlgorithmButton
+      // 
+      this.openAlgorithmButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Open;
+      this.openAlgorithmButton.Location = new System.Drawing.Point(36, 6);
+      this.openAlgorithmButton.Name = "openAlgorithmButton";
+      this.openAlgorithmButton.Size = new System.Drawing.Size(24, 24);
+      this.openAlgorithmButton.TabIndex = 1;
+      this.toolTip.SetToolTip(this.openAlgorithmButton, "Open Optimizer");
+      this.openAlgorithmButton.UseVisualStyleBackColor = true;
+      this.openAlgorithmButton.Click += new System.EventHandler(this.openAlgorithmButton_Click);
+      // 
+      // newAlgorithmButton
+      // 
+      this.newAlgorithmButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.NewDocument;
+      this.newAlgorithmButton.Location = new System.Drawing.Point(6, 6);
+      this.newAlgorithmButton.Name = "newAlgorithmButton";
+      this.newAlgorithmButton.Size = new System.Drawing.Size(24, 24);
+      this.newAlgorithmButton.TabIndex = 0;
+      this.toolTip.SetToolTip(this.newAlgorithmButton, "New Optimizer");
+      this.newAlgorithmButton.UseVisualStyleBackColor = true;
+      this.newAlgorithmButton.Click += new System.EventHandler(this.newAlgorithmButton_Click);
+      // 
+      // snapshotsTabPage
+      // 
+      this.snapshotsTabPage.Controls.Add(this.snapshotsView);
+      this.snapshotsTabPage.Location = new System.Drawing.Point(4, 22);
+      this.snapshotsTabPage.Name = "snapshotsTabPage";
+      this.snapshotsTabPage.Padding = new System.Windows.Forms.Padding(3);
+      this.snapshotsTabPage.Size = new System.Drawing.Size(538, 330);
+      this.snapshotsTabPage.TabIndex = 2;
+      this.snapshotsTabPage.Text = "Snapshots";
+      this.snapshotsTabPage.UseVisualStyleBackColor = true;
+      // 
+      // snapshotsView
+      // 
+      this.snapshotsView.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.snapshotsView.Caption = "RunCollection View";
+      this.snapshotsView.Content = null;
+      this.snapshotsView.Location = new System.Drawing.Point(6, 6);
+      this.snapshotsView.Name = "snapshotsView";
+      this.snapshotsView.ReadOnly = false;
+      this.snapshotsView.Size = new System.Drawing.Size(526, 317);
+      this.snapshotsView.TabIndex = 0;
+      // 
+      // runsTabPage
+      // 
+      this.runsTabPage.Controls.Add(this.runsView);
+      this.runsTabPage.Location = new System.Drawing.Point(4, 22);
+      this.runsTabPage.Name = "runsTabPage";
+      this.runsTabPage.Size = new System.Drawing.Size(538, 330);
+      this.runsTabPage.TabIndex = 3;
+      this.runsTabPage.Text = "Runs";
+      this.runsTabPage.UseVisualStyleBackColor = true;
+      // 
+      // runsView
+      // 
+      this.runsView.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.runsView.Caption = "RunCollection View";
+      this.runsView.Content = null;
+      this.runsView.Location = new System.Drawing.Point(6, 6);
+      this.runsView.Name = "runsView";
+      this.runsView.ReadOnly = false;
+      this.runsView.Size = new System.Drawing.Size(526, 317);
+      this.runsView.TabIndex = 1;
+      // 
+      // openFileDialog
+      // 
+      this.openFileDialog.DefaultExt = "hl";
+      this.openFileDialog.FileName = "Optimizer";
+      this.openFileDialog.Filter = "HeuristicLab Files|*.hl|All Files|*.*";
+      this.openFileDialog.Title = "Open Optimizer";
+      // 
+      // snapshotButton
+      // 
+      this.snapshotButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+      this.snapshotButton.Location = new System.Drawing.Point(120, 440);
+      this.snapshotButton.Name = "snapshotButton";
+      this.snapshotButton.Size = new System.Drawing.Size(24, 24);
+      this.snapshotButton.TabIndex = 13;
+      this.snapshotButton.Text = "Snapshot";
+      this.toolTip.SetToolTip(this.snapshotButton, "Create Snapshot");
+      this.snapshotButton.UseVisualStyleBackColor = true;
+      this.snapshotButton.Click += new System.EventHandler(this.snapshotButton_Click);
+      // 
+      // sequenceButton
+      // 
+      this.sequenceButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+      this.sequenceButton.Location = new System.Drawing.Point(250, 50);
+      this.sequenceButton.Name = "sequenceButton";
+      this.sequenceButton.Size = new System.Drawing.Size(75, 23);
+      this.sequenceButton.TabIndex = 16;
+      this.sequenceButton.Text = "Sequence...";
+      this.sequenceButton.UseVisualStyleBackColor = true;
+      this.sequenceButton.Click += new System.EventHandler(this.sequenceButton_Click);
+      // 
+      // TimeLimitRunView
+      // 
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
+      this.Controls.Add(this.sequenceButton);
+      this.Controls.Add(this.snapshotButton);
+      this.Controls.Add(this.tabControl);
+      this.Controls.Add(this.timeLimitTextBox);
+      this.Controls.Add(this.timeLimitLabel);
+      this.Controls.Add(this.label1);
+      this.Controls.Add(this.snapshotsTextBox);
+      this.Controls.Add(this.storeAlgorithmInEachSnapshotCheckBox);
+      this.Name = "TimeLimitRunView";
+      this.Size = new System.Drawing.Size(549, 464);
+      this.Controls.SetChildIndex(this.storeAlgorithmInEachSnapshotCheckBox, 0);
+      this.Controls.SetChildIndex(this.snapshotsTextBox, 0);
+      this.Controls.SetChildIndex(this.label1, 0);
+      this.Controls.SetChildIndex(this.timeLimitLabel, 0);
+      this.Controls.SetChildIndex(this.timeLimitTextBox, 0);
+      this.Controls.SetChildIndex(this.tabControl, 0);
+      this.Controls.SetChildIndex(this.nameLabel, 0);
+      this.Controls.SetChildIndex(this.nameTextBox, 0);
+      this.Controls.SetChildIndex(this.infoLabel, 0);
+      this.Controls.SetChildIndex(this.resetButton, 0);
+      this.Controls.SetChildIndex(this.stopButton, 0);
+      this.Controls.SetChildIndex(this.pauseButton, 0);
+      this.Controls.SetChildIndex(this.executionTimeLabel, 0);
+      this.Controls.SetChildIndex(this.executionTimeTextBox, 0);
+      this.Controls.SetChildIndex(this.startButton, 0);
+      this.Controls.SetChildIndex(this.snapshotButton, 0);
+      this.Controls.SetChildIndex(this.sequenceButton, 0);
+      ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).EndInit();
+      this.tabControl.ResumeLayout(false);
+      this.algorithmTabPage.ResumeLayout(false);
+      this.snapshotsTabPage.ResumeLayout(false);
+      this.runsTabPage.ResumeLayout(false);
+      this.ResumeLayout(false);
+      this.PerformLayout();
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.Label timeLimitLabel;
+    private System.Windows.Forms.TextBox timeLimitTextBox;
+    private System.Windows.Forms.Label label1;
+    private System.Windows.Forms.TextBox snapshotsTextBox;
+    private System.Windows.Forms.CheckBox storeAlgorithmInEachSnapshotCheckBox;
+    private MainForm.WindowsForms.DragOverTabControl tabControl;
+    private System.Windows.Forms.TabPage algorithmTabPage;
+    private MainForm.WindowsForms.ViewHost algorithmViewHost;
+    private System.Windows.Forms.Button openAlgorithmButton;
+    private System.Windows.Forms.Button newAlgorithmButton;
+    private System.Windows.Forms.TabPage snapshotsTabPage;
+    private RunCollectionView snapshotsView;
+    private System.Windows.Forms.TabPage runsTabPage;
+    private RunCollectionView runsView;
+    private System.Windows.Forms.OpenFileDialog openFileDialog;
+    private System.Windows.Forms.Button snapshotButton;
+    private System.Windows.Forms.Button sequenceButton;
+  }
+}
Index: /stable/HeuristicLab.Optimization.Views/3.3/TimeLimitRunView.cs
===================================================================
--- /stable/HeuristicLab.Optimization.Views/3.3/TimeLimitRunView.cs	(revision 12739)
+++ /stable/HeuristicLab.Optimization.Views/3.3/TimeLimitRunView.cs	(revision 12739)
@@ -0,0 +1,310 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2012 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.ComponentModel;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Windows.Forms;
+using HeuristicLab.Collections;
+using HeuristicLab.Common;
+using HeuristicLab.Common.Resources;
+using HeuristicLab.Core;
+using HeuristicLab.Core.Views;
+using HeuristicLab.MainForm;
+using HeuristicLab.MainForm.WindowsForms;
+using HeuristicLab.PluginInfrastructure;
+
+namespace HeuristicLab.Optimization.Views {
+  [View("TimeLimit Run View")]
+  [Content(typeof(TimeLimitRun), IsDefaultView = true)]
+  public partial class TimeLimitRunView : IOptimizerView {
+    protected TypeSelectorDialog algorithmTypeSelectorDialog;
+    protected virtual bool SuppressEvents { get; set; }
+
+    public new TimeLimitRun Content {
+      get { return (TimeLimitRun)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public TimeLimitRunView() {
+      InitializeComponent();
+      snapshotButton.Text = String.Empty;
+      snapshotButton.Image = VSImageLibrary.Camera;
+    }
+
+    protected override void Dispose(bool disposing) {
+      if (disposing) {
+        if (algorithmTypeSelectorDialog != null) algorithmTypeSelectorDialog.Dispose();
+        if (components != null) components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    protected override void DeregisterContentEvents() {
+      Content.PropertyChanged -= Content_PropertyChanged;
+      Content.SnapshotTimes.ItemsAdded -= Content_SnapshotTimes_Changed;
+      Content.SnapshotTimes.ItemsMoved -= Content_SnapshotTimes_Changed;
+      Content.SnapshotTimes.ItemsRemoved -= Content_SnapshotTimes_Changed;
+      Content.SnapshotTimes.ItemsReplaced -= Content_SnapshotTimes_Changed;
+      Content.SnapshotTimes.CollectionReset -= Content_SnapshotTimes_Changed;
+      base.DeregisterContentEvents();
+    }
+    protected override void RegisterContentEvents() {
+      base.RegisterContentEvents();
+      Content.PropertyChanged += Content_PropertyChanged;
+      Content.SnapshotTimes.ItemsAdded += Content_SnapshotTimes_Changed;
+      Content.SnapshotTimes.ItemsMoved += Content_SnapshotTimes_Changed;
+      Content.SnapshotTimes.ItemsRemoved += Content_SnapshotTimes_Changed;
+      Content.SnapshotTimes.ItemsReplaced += Content_SnapshotTimes_Changed;
+      Content.SnapshotTimes.CollectionReset += Content_SnapshotTimes_Changed;
+    }
+
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      SuppressEvents = true;
+      try {
+        if (Content == null) {
+          timeLimitTextBox.Text = TimeSpanHelper.FormatNatural(TimeSpan.FromSeconds(60));
+          snapshotsTextBox.Text = String.Empty;
+          storeAlgorithmInEachSnapshotCheckBox.Checked = false;
+          algorithmViewHost.Content = null;
+          snapshotsView.Content = null;
+          runsView.Content = null;
+        } else {
+          timeLimitTextBox.Text = TimeSpanHelper.FormatNatural(Content.MaximumExecutionTime);
+          snapshotsTextBox.Text = String.Join(" ; ", Content.SnapshotTimes.Select(x => TimeSpanHelper.FormatNatural(x, true)));
+          storeAlgorithmInEachSnapshotCheckBox.Checked = Content.StoreAlgorithmInEachSnapshot;
+          algorithmViewHost.Content = Content.Algorithm;
+          snapshotsView.Content = Content.Snapshots;
+          runsView.Content = Content.Runs;
+        }
+      } finally { SuppressEvents = false; }
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      timeLimitTextBox.Enabled = Content != null && !ReadOnly;
+      snapshotsTextBox.Enabled = Content != null && !ReadOnly;
+      storeAlgorithmInEachSnapshotCheckBox.Enabled = Content != null && !ReadOnly;
+      newAlgorithmButton.Enabled = Content != null && !ReadOnly;
+      openAlgorithmButton.Enabled = Content != null && !ReadOnly;
+      algorithmViewHost.Enabled = Content != null;
+      snapshotsView.Enabled = Content != null;
+      runsView.Enabled = Content != null;
+    }
+
+    protected override void SetEnabledStateOfExecutableButtons() {
+      base.SetEnabledStateOfExecutableButtons();
+      snapshotButton.Enabled = Content != null && Content.Algorithm != null && Content.ExecutionState == ExecutionState.Paused;
+    }
+
+    protected override void OnClosed(FormClosedEventArgs e) {
+      if ((Content != null) && (Content.ExecutionState == ExecutionState.Started)) {
+        //The content must be stopped if no other view showing the content is available
+        var optimizers = MainFormManager.MainForm.Views.OfType<IContentView>().Where(v => v != this).Select(v => v.Content).OfType<IAlgorithm>();
+        if (!optimizers.Contains(Content.Algorithm)) {
+          var nestedOptimizers = optimizers.SelectMany(opt => opt.NestedOptimizers);
+          if (!nestedOptimizers.Contains(Content)) Content.Stop();
+        }
+      }
+      base.OnClosed(e);
+    }
+
+    #region Event Handlers
+    #region Content events
+    private void Content_PropertyChanged(object sender, PropertyChangedEventArgs e) {
+      switch (e.PropertyName) {
+        case "MaximumExecutionTime":
+          SuppressEvents = true;
+          try {
+            timeLimitTextBox.Text = TimeSpanHelper.FormatNatural(Content.MaximumExecutionTime);
+          } finally { SuppressEvents = false; }
+          break;
+        case "SnapshotTimes":
+          SuppressEvents = true;
+          try {
+            if (Content.SnapshotTimes.Any())
+              snapshotsTextBox.Text = String.Join(" ; ", Content.SnapshotTimes.Select(x => TimeSpanHelper.FormatNatural(x, true)));
+            else snapshotsTextBox.Text = String.Empty;
+            Content.SnapshotTimes.ItemsAdded += Content_SnapshotTimes_Changed;
+            Content.SnapshotTimes.ItemsMoved += Content_SnapshotTimes_Changed;
+            Content.SnapshotTimes.ItemsRemoved += Content_SnapshotTimes_Changed;
+            Content.SnapshotTimes.ItemsReplaced += Content_SnapshotTimes_Changed;
+            Content.SnapshotTimes.CollectionReset += Content_SnapshotTimes_Changed;
+          } finally { SuppressEvents = false; }
+          break;
+        case "StoreAlgorithmInEachSnapshot":
+          SuppressEvents = true;
+          try {
+            storeAlgorithmInEachSnapshotCheckBox.Checked = Content.StoreAlgorithmInEachSnapshot;
+          } finally { SuppressEvents = false; }
+          break;
+        case "Algorithm":
+          SuppressEvents = true;
+          try {
+            algorithmViewHost.Content = Content.Algorithm;
+          } finally { SuppressEvents = false; }
+          break;
+        case "Snapshots":
+          SuppressEvents = true;
+          try {
+            snapshotsView.Content = Content.Snapshots;
+          } finally { SuppressEvents = false; }
+          break;
+        case "Runs":
+          SuppressEvents = true;
+          try {
+            runsView.Content = Content.Runs;
+          } finally { SuppressEvents = false; }
+          break;
+      }
+    }
+
+    private void Content_SnapshotTimes_Changed(object sender, EventArgs e) {
+      SuppressEvents = true;
+      try {
+        if (Content.SnapshotTimes.Any())
+          snapshotsTextBox.Text = string.Join(" ; ", Content.SnapshotTimes.Select(x => TimeSpanHelper.FormatNatural(x, true)));
+        else snapshotsTextBox.Text = String.Empty;
+      } finally { SuppressEvents = false; }
+    }
+    #endregion
+
+    #region Control events
+    private void timeLimitTextBox_Validating(object sender, CancelEventArgs e) {
+      if (SuppressEvents) return;
+      TimeSpan ts;
+      if (!TimeSpanHelper.TryGetFromNaturalFormat(timeLimitTextBox.Text, out ts)) {
+        e.Cancel = !timeLimitTextBox.ReadOnly && timeLimitTextBox.Enabled;
+        errorProvider.SetError(timeLimitTextBox, "Please enter a valid time span, e.g. 20 seconds ; 45s ; 4min ; 1h ; 3 hours ; 2 days ; 4d");
+      } else {
+        Content.MaximumExecutionTime = ts;
+        e.Cancel = false;
+        errorProvider.SetError(timeLimitTextBox, null);
+      }
+    }
+
+    private void snapshotsTextBox_Validating(object sender, CancelEventArgs e) {
+      if (SuppressEvents) return;
+      e.Cancel = false;
+      errorProvider.SetError(snapshotsTextBox, null);
+
+      var snapshotTimes = new ObservableList<TimeSpan>();
+      var matches = Regex.Matches(snapshotsTextBox.Text, @"(\d+[ ;,\t]*\w+)");
+      foreach (Match m in matches) {
+        TimeSpan value;
+        if (!TimeSpanHelper.TryGetFromNaturalFormat(m.Value, out value)) {
+          e.Cancel = !snapshotsTextBox.ReadOnly && snapshotsTextBox.Enabled; // don't cancel an operation that cannot be edited
+          errorProvider.SetError(snapshotsTextBox, "Error parsing " + m.Value + ", please provide a valid time span, e.g. 20 seconds ; 45s ; 4min ; 1h ; 3 hours ; 2 days ; 4d");
+          return;
+        } else {
+          snapshotTimes.Add(value);
+        }
+      }
+      Content.SnapshotTimes = snapshotTimes;
+    }
+
+    private void storeAlgorithmInEachSnapshotCheckBox_CheckedChanged(object sender, EventArgs e) {
+      if (SuppressEvents) return;
+      SuppressEvents = true;
+      try {
+        Content.StoreAlgorithmInEachSnapshot = storeAlgorithmInEachSnapshotCheckBox.Checked;
+      } finally { SuppressEvents = false; }
+    }
+
+    private void newAlgorithmButton_Click(object sender, EventArgs e) {
+      if (algorithmTypeSelectorDialog == null) {
+        algorithmTypeSelectorDialog = new TypeSelectorDialog { Caption = "Select Algorithm" };
+        algorithmTypeSelectorDialog.TypeSelector.Caption = "Available Algorithms";
+        algorithmTypeSelectorDialog.TypeSelector.Configure(typeof(IAlgorithm), false, true);
+      }
+      if (algorithmTypeSelectorDialog.ShowDialog(this) == DialogResult.OK) {
+        try {
+          Content.Algorithm = (IAlgorithm)algorithmTypeSelectorDialog.TypeSelector.CreateInstanceOfSelectedType();
+        } catch (Exception ex) {
+          ErrorHandling.ShowErrorDialog(this, ex);
+        }
+      }
+    }
+
+    private void openAlgorithmButton_Click(object sender, EventArgs e) {
+      openFileDialog.Title = "Open Algorithm";
+      if (openFileDialog.ShowDialog(this) == DialogResult.OK) {
+        newAlgorithmButton.Enabled = openAlgorithmButton.Enabled = false;
+        algorithmViewHost.Enabled = false;
+
+        ContentManager.LoadAsync(openFileDialog.FileName, delegate(IStorableContent content, Exception error) {
+          try {
+            if (error != null) throw error;
+            var algorithm = content as IAlgorithm;
+            if (algorithm == null)
+              MessageBox.Show(this, "The selected file does not contain an algorithm.", "Invalid File", MessageBoxButtons.OK, MessageBoxIcon.Error);
+            else
+              Content.Algorithm = algorithm;
+          } catch (Exception ex) {
+            ErrorHandling.ShowErrorDialog(this, ex);
+          } finally {
+            Invoke(new Action(delegate() {
+              algorithmViewHost.Enabled = true;
+              newAlgorithmButton.Enabled = openAlgorithmButton.Enabled = true;
+            }));
+          }
+        });
+      }
+    }
+
+    private void algorithmTabPage_DragEnterOver(object sender, DragEventArgs e) {
+      e.Effect = DragDropEffects.None;
+      if (!ReadOnly && (e.Data.GetData(Constants.DragDropDataFormat) is IAlgorithm)) {
+        if ((e.KeyState & 32) == 32) e.Effect = DragDropEffects.Link;  // ALT key
+        else if ((e.KeyState & 4) == 4) e.Effect = DragDropEffects.Move;  // SHIFT key
+        else if (e.AllowedEffect.HasFlag(DragDropEffects.Copy)) e.Effect = DragDropEffects.Copy;
+        else if (e.AllowedEffect.HasFlag(DragDropEffects.Move)) e.Effect = DragDropEffects.Move;
+        else if (e.AllowedEffect.HasFlag(DragDropEffects.Link)) e.Effect = DragDropEffects.Link;
+      }
+    }
+
+    private void algorithmTabPage_DragDrop(object sender, DragEventArgs e) {
+      if (e.Effect != DragDropEffects.None) {
+        var algorithm = e.Data.GetData(Constants.DragDropDataFormat) as IAlgorithm;
+        if (e.Effect.HasFlag(DragDropEffects.Copy)) algorithm = (IAlgorithm)algorithm.Clone();
+        Content.Algorithm = algorithm;
+      }
+    }
+
+    private void snapshotButton_Click(object sender, EventArgs e) {
+      Content.Snapshot();
+    }
+
+    private void sequenceButton_Click(object sender, EventArgs e) {
+      using (var dialog = new DefineArithmeticTimeSpanProgressionDialog(TimeSpan.FromSeconds(1), Content.MaximumExecutionTime, TimeSpan.FromSeconds(1))) {
+        if (dialog.ShowDialog() == DialogResult.OK) {
+          if (dialog.Values.Any())
+            Content.SnapshotTimes = new ObservableList<TimeSpan>(dialog.Values);
+          else Content.SnapshotTimes = new ObservableList<TimeSpan>();
+        }
+      }
+    }
+    #endregion
+    #endregion
+  }
+}
Index: /stable/HeuristicLab.Optimization/3.3/HeuristicLab.Optimization-3.3.csproj
===================================================================
--- /stable/HeuristicLab.Optimization/3.3/HeuristicLab.Optimization-3.3.csproj	(revision 12738)
+++ /stable/HeuristicLab.Optimization/3.3/HeuristicLab.Optimization-3.3.csproj	(revision 12739)
@@ -165,4 +165,5 @@
     <Compile Include="MetaOptimizers\BatchRun.cs" />
     <Compile Include="MetaOptimizers\Experiment.cs" />
+    <Compile Include="MetaOptimizers\TimeLimitRun.cs" />
     <Compile Include="RunCollectionModification\RunCollectionRunRemover.cs" />
     <Compile Include="Plugin.cs" />
Index: /stable/HeuristicLab.Optimization/3.3/MetaOptimizers/TimeLimitRun.cs
===================================================================
--- /stable/HeuristicLab.Optimization/3.3/MetaOptimizers/TimeLimitRun.cs	(revision 12739)
+++ /stable/HeuristicLab.Optimization/3.3/MetaOptimizers/TimeLimitRun.cs	(revision 12739)
@@ -0,0 +1,393 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2012 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 System.Linq;
+using System.Threading.Tasks;
+using HeuristicLab.Collections;
+using HeuristicLab.Common;
+using HeuristicLab.Common.Resources;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Optimization {
+  /// <summary>
+  /// A run in which an algorithm is executed for a certain maximum time only.
+  /// </summary>
+  [Item("Timelimit Run", "A run in which an optimizer is executed a certain maximum time.")]
+  [Creatable(CreatableAttribute.Categories.TestingAndAnalysis, Priority = 115)]
+  [StorableClass]
+  public sealed class TimeLimitRun : NamedItem, IOptimizer, IStorableContent, INotifyPropertyChanged {
+    public string Filename { get; set; }
+
+    #region ItemImage
+    public static new Image StaticItemImage {
+      get { return VSImageLibrary.Event; }
+    }
+    public override Image ItemImage {
+      get { return (Algorithm != null) ? Algorithm.ItemImage : VSImageLibrary.ExecutableStopped; }
+    }
+    #endregion
+
+    private bool pausedForSnapshot = false;
+    private bool pausedForTermination = false;
+
+    [Storable]
+    private TimeSpan maximumExecutionTime;
+    public TimeSpan MaximumExecutionTime {
+      get { return maximumExecutionTime; }
+      set {
+        if (maximumExecutionTime == value) return;
+        maximumExecutionTime = value;
+        OnPropertyChanged("MaximumExecutionTime");
+      }
+    }
+
+    [Storable]
+    private int snapshotTimesIndex;
+    [Storable]
+    private ObservableList<TimeSpan> snapshotTimes;
+    public ObservableList<TimeSpan> SnapshotTimes {
+      get { return snapshotTimes; }
+      set {
+        if (snapshotTimes == value) return;
+        snapshotTimes = value;
+        snapshotTimes.Sort();
+        FindNextSnapshotTimeIndex(ExecutionTime);
+        OnPropertyChanged("SnapshotTimes");
+      }
+    }
+
+    [Storable]
+    private bool storeAlgorithmInEachSnapshot;
+    [Storable]
+    public bool StoreAlgorithmInEachSnapshot {
+      get { return storeAlgorithmInEachSnapshot; }
+      set {
+        if (storeAlgorithmInEachSnapshot == value) return;
+        storeAlgorithmInEachSnapshot = value;
+        OnPropertyChanged("StoreAlgorithmInEachSnapshot");
+      }
+    }
+
+    [Storable]
+    private RunCollection snapshots;
+    public RunCollection Snapshots {
+      get { return snapshots; }
+      set {
+        if (snapshots == value) return;
+        snapshots = value;
+        OnPropertyChanged("Snapshots");
+      }
+    }
+
+    #region Inherited Properties
+    public ExecutionState ExecutionState {
+      get { return (Algorithm != null) ? Algorithm.ExecutionState : ExecutionState.Stopped; }
+    }
+
+    public TimeSpan ExecutionTime {
+      get { return (Algorithm != null) ? Algorithm.ExecutionTime : TimeSpan.FromSeconds(0); }
+    }
+
+    [Storable]
+    private IAlgorithm algorithm;
+    public IAlgorithm Algorithm {
+      get { return algorithm; }
+      set {
+        if (algorithm == value) return;
+        if (algorithm != null) DeregisterAlgorithmEvents();
+        algorithm = value;
+        if (algorithm != null) {
+          RegisterAlgorithmEvents();
+        }
+        OnPropertyChanged("Algorithm");
+        Prepare();
+      }
+    }
+
+    [Storable]
+    private RunCollection runs;
+    public RunCollection Runs {
+      get { return runs; }
+      private set {
+        if (value == null) throw new ArgumentNullException();
+        if (runs == value) return;
+        runs = value;
+        OnPropertyChanged("Runs");
+      }
+    }
+
+    public IEnumerable<IOptimizer> NestedOptimizers {
+      get {
+        if (Algorithm == null) yield break;
+        yield return Algorithm;
+        foreach (var opt in Algorithm.NestedOptimizers)
+          yield return opt;
+      }
+    }
+    #endregion
+
+    [StorableConstructor]
+    private TimeLimitRun(bool deserializing) : base(deserializing) { }
+    private TimeLimitRun(TimeLimitRun original, Cloner cloner)
+      : base(original, cloner) {
+      maximumExecutionTime = original.maximumExecutionTime;
+      snapshotTimes = new ObservableList<TimeSpan>(original.snapshotTimes);
+      snapshotTimesIndex = original.snapshotTimesIndex;
+      snapshots = cloner.Clone(original.snapshots);
+      storeAlgorithmInEachSnapshot = original.storeAlgorithmInEachSnapshot;
+      algorithm = cloner.Clone(original.algorithm);
+      runs = cloner.Clone(original.runs);
+
+      Initialize();
+    }
+    public TimeLimitRun()
+      : base() {
+      name = ItemName;
+      description = ItemDescription;
+      maximumExecutionTime = TimeSpan.FromMinutes(.5);
+      snapshotTimes = new ObservableList<TimeSpan>(new[] {
+          TimeSpan.FromSeconds(5),
+          TimeSpan.FromSeconds(10),
+          TimeSpan.FromSeconds(15) });
+      snapshotTimesIndex = 0;
+      snapshots = new RunCollection();
+      Runs = new RunCollection { OptimizerName = Name };
+      Initialize();
+    }
+    public TimeLimitRun(string name)
+      : base(name) {
+      description = ItemDescription;
+      maximumExecutionTime = TimeSpan.FromMinutes(.5);
+      snapshotTimes = new ObservableList<TimeSpan>(new[] {
+          TimeSpan.FromSeconds(5),
+          TimeSpan.FromSeconds(10),
+          TimeSpan.FromSeconds(15) });
+      snapshotTimesIndex = 0;
+      Runs = new RunCollection { OptimizerName = Name };
+      Initialize();
+    }
+    public TimeLimitRun(string name, string description)
+      : base(name, description) {
+      maximumExecutionTime = TimeSpan.FromMinutes(.5);
+      snapshotTimes = new ObservableList<TimeSpan>(new[] {
+          TimeSpan.FromSeconds(5),
+          TimeSpan.FromSeconds(10),
+          TimeSpan.FromSeconds(15) });
+      snapshotTimesIndex = 0;
+      Runs = new RunCollection { OptimizerName = Name };
+      Initialize();
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      if (ExecutionState == ExecutionState.Started) throw new InvalidOperationException(string.Format("Clone not allowed in execution state \"{0}\".", ExecutionState));
+      return new TimeLimitRun(this, cloner);
+    }
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      Initialize();
+    }
+
+    private void Initialize() {
+      if (algorithm != null) RegisterAlgorithmEvents();
+      snapshotTimes.ItemsAdded += snapshotTimes_Changed;
+      snapshotTimes.ItemsMoved += snapshotTimes_Changed;
+      snapshotTimes.ItemsRemoved += snapshotTimes_Changed;
+      snapshotTimes.ItemsReplaced += snapshotTimes_Changed;
+      snapshotTimes.CollectionReset += snapshotTimes_Changed;
+    }
+
+    private void snapshotTimes_Changed(object sender, CollectionItemsChangedEventArgs<IndexedItem<TimeSpan>> e) {
+      if (e.Items.Any()) snapshotTimes.Sort();
+      FindNextSnapshotTimeIndex(ExecutionTime);
+    }
+
+    public void Snapshot() {
+      if (Algorithm == null || Algorithm.ExecutionState != ExecutionState.Paused) throw new InvalidOperationException("Snapshot not allowed in execution states other than Paused");
+      Task.Factory.StartNew(MakeSnapshot);
+    }
+
+    public void Prepare() {
+      Prepare(false);
+    }
+    public void Prepare(bool clearRuns) {
+      Algorithm.Prepare(clearRuns);
+    }
+    public void Start() {
+      Algorithm.Start();
+    }
+    public void Pause() {
+      Algorithm.Pause();
+    }
+    public void Stop() {
+      Algorithm.Stop();
+    }
+
+    #region Events
+    protected override void OnNameChanged() {
+      base.OnNameChanged();
+      runs.OptimizerName = Name;
+    }
+
+    public event PropertyChangedEventHandler PropertyChanged;
+    private void OnPropertyChanged(string property) {
+      var handler = PropertyChanged;
+      if (handler != null) handler(this, new PropertyChangedEventArgs(property));
+    }
+
+    #region IExecutable Events
+    public event EventHandler ExecutionStateChanged;
+    private void OnExecutionStateChanged() {
+      var handler = ExecutionStateChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler ExecutionTimeChanged;
+    private void OnExecutionTimeChanged() {
+      var handler = ExecutionTimeChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler Prepared;
+    private void OnPrepared() {
+      var handler = Prepared;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler Started;
+    private void OnStarted() {
+      var handler = Started;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler Paused;
+    private void OnPaused() {
+      var handler = Paused;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler Stopped;
+    private void OnStopped() {
+      var handler = Stopped;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler<EventArgs<Exception>> ExceptionOccurred;
+    private void OnExceptionOccurred(Exception exception) {
+      var handler = ExceptionOccurred;
+      if (handler != null) handler(this, new EventArgs<Exception>(exception));
+    }
+    #endregion
+
+    #region Algorithm Events
+    private void RegisterAlgorithmEvents() {
+      algorithm.ExceptionOccurred += Algorithm_ExceptionOccurred;
+      algorithm.ExecutionTimeChanged += Algorithm_ExecutionTimeChanged;
+      algorithm.ExecutionStateChanged += Algorithm_ExecutionStateChanged;
+      algorithm.Paused += Algorithm_Paused;
+      algorithm.Prepared += Algorithm_Prepared;
+      algorithm.Started += Algorithm_Started;
+      algorithm.Stopped += Algorithm_Stopped;
+    }
+    private void DeregisterAlgorithmEvents() {
+      algorithm.ExceptionOccurred -= Algorithm_ExceptionOccurred;
+      algorithm.ExecutionTimeChanged -= Algorithm_ExecutionTimeChanged;
+      algorithm.ExecutionStateChanged -= Algorithm_ExecutionStateChanged;
+      algorithm.Paused -= Algorithm_Paused;
+      algorithm.Prepared -= Algorithm_Prepared;
+      algorithm.Started -= Algorithm_Started;
+      algorithm.Stopped -= Algorithm_Stopped;
+    }
+    private void Algorithm_ExceptionOccurred(object sender, EventArgs<Exception> e) {
+      OnExceptionOccurred(e.Value);
+    }
+    private void Algorithm_ExecutionTimeChanged(object sender, EventArgs e) {
+      if (snapshotTimesIndex < SnapshotTimes.Count && ExecutionTime >= SnapshotTimes[snapshotTimesIndex]
+        && !pausedForSnapshot) {
+        pausedForSnapshot = true;
+        Algorithm.Pause();
+      }
+      if (ExecutionTime >= MaximumExecutionTime && !pausedForTermination) {
+        pausedForTermination = true;
+        if (!pausedForSnapshot) Algorithm.Pause();
+      }
+      OnExecutionTimeChanged();
+    }
+    private void Algorithm_ExecutionStateChanged(object sender, EventArgs e) {
+      OnExecutionStateChanged();
+    }
+    private void Algorithm_Paused(object sender, EventArgs e) {
+      var action = pausedForTermination ? ExecutionState.Stopped : (pausedForSnapshot ? ExecutionState.Started : ExecutionState.Paused);
+      if (pausedForSnapshot || pausedForTermination) {
+        pausedForSnapshot = pausedForTermination = false;
+        MakeSnapshot();
+        FindNextSnapshotTimeIndex(ExecutionTime);
+      }
+      OnPaused();
+      if (action == ExecutionState.Started) Algorithm.Start();
+      else if (action == ExecutionState.Stopped) Algorithm.Stop();
+    }
+    private void Algorithm_Prepared(object sender, EventArgs e) {
+      snapshotTimesIndex = 0;
+      snapshots.Clear();
+      OnPrepared();
+    }
+    private void Algorithm_Started(object sender, EventArgs e) {
+      OnStarted();
+    }
+    private void Algorithm_Stopped(object sender, EventArgs e) {
+      var cloner = new Cloner();
+      var algRun = cloner.Clone(Algorithm.Runs.Last());
+      var clonedSnapshots = cloner.Clone(snapshots);
+      algRun.Results.Add("TimeLimitRunSnapshots", clonedSnapshots);
+      Runs.Add(algRun);
+      Algorithm.Runs.Clear();
+      OnStopped();
+    }
+    #endregion
+    #endregion
+
+    private void FindNextSnapshotTimeIndex(TimeSpan reference) {
+      var index = 0;
+      while (index < snapshotTimes.Count && snapshotTimes[index] <= reference) {
+        index++;
+      };
+      snapshotTimesIndex = index;
+    }
+
+    private void MakeSnapshot() {
+      string time = Math.Round(ExecutionTime.TotalSeconds, 1).ToString("0.0");
+      string runName = "Snapshot " + time + "s " + algorithm.Name;
+      var changed = false;
+      if (StoreAlgorithmInEachSnapshot && !Algorithm.StoreAlgorithmInEachRun) {
+        Algorithm.StoreAlgorithmInEachRun = true;
+        changed = true;
+      } else if (!StoreAlgorithmInEachSnapshot && Algorithm.StoreAlgorithmInEachRun) {
+        Algorithm.StoreAlgorithmInEachRun = false;
+        changed = true;
+      }
+      var run = new Run(runName, Algorithm);
+      if (changed)
+        Algorithm.StoreAlgorithmInEachRun = !Algorithm.StoreAlgorithmInEachRun;
+
+      snapshots.Add(run);
+    }
+  }
+}
