Index: branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SlidingWindow/GenerationalRegressionSlidingWindowAnalyzer.cs
===================================================================
--- branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SlidingWindow/GenerationalRegressionSlidingWindowAnalyzer.cs	(revision 10688)
+++ branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SlidingWindow/GenerationalRegressionSlidingWindowAnalyzer.cs	(revision 10720)
@@ -41,6 +41,6 @@
     public GenerationalRegressionSlidingWindowAnalyzer() { }
     public override IOperation Apply() {
-      if (!ResultCollection.ContainsKey("Best Solutions")) {
-        ResultCollection.Add(new Result("Best Solutions", new SlidingWindowBestRegressionSolutionsCollection()));
+      if (!ResultCollection.ContainsKey(BestSlidingWindowSolutionsResultName)) {
+        ResultCollection.Add(new Result(BestSlidingWindowSolutionsResultName, new SlidingWindowBestRegressionSolutionsCollection()));
       }
       return base.Apply();
Index: branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SlidingWindow/OffspringSelectionRegressionSlidingWindowAnalyzer.cs
===================================================================
--- branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SlidingWindow/OffspringSelectionRegressionSlidingWindowAnalyzer.cs	(revision 10688)
+++ branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SlidingWindow/OffspringSelectionRegressionSlidingWindowAnalyzer.cs	(revision 10720)
@@ -41,6 +41,6 @@
     public OffspringSelectionRegressionSlidingWindowAnalyzer() { }
     public override IOperation Apply() {
-      if (!ResultCollection.ContainsKey("Best Solutions")) {
-        ResultCollection.Add(new Result("Best Solutions", new SlidingWindowBestRegressionSolutionsCollection()));
+      if (!ResultCollection.ContainsKey(BestSlidingWindowSolutionsResultName)) {
+        ResultCollection.Add(new Result(BestSlidingWindowSolutionsResultName, new SlidingWindowBestRegressionSolutionsCollection()));
       }
       return base.Apply();
Index: branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/SlidingWindowBestSolutionsCollectionHeatMapView.cs
===================================================================
--- branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/SlidingWindowBestSolutionsCollectionHeatMapView.cs	(revision 10688)
+++ branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/SlidingWindowBestSolutionsCollectionHeatMapView.cs	(revision 10720)
@@ -21,10 +21,9 @@
 
 using System;
-using System.Collections.Generic;
+using System.ComponentModel;
 using System.Linq;
 using System.Windows.Forms;
 using HeuristicLab.Analysis;
 using HeuristicLab.Core.Views;
-using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
 using HeuristicLab.MainForm;
 using HeuristicLab.Optimization;
@@ -39,16 +38,30 @@
       set { base.Content = value; }
     }
+
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      if (Content != null) {
+        if (Content.SlidingWindowQualities != null) {
+          SetHeatmapContent(Content.SlidingWindowQualities);
+        } else if (!Content.QualitiesCalculationInProgress) {
+          progress.Start("Calculating best solution qualities...");
+          Content.CalculateQualities();
+        }
+      }
+    }
+
+    private readonly IProgress progress; // use this to show some progress when the sliding window qualities are calculated for each model
+
     public SlidingWindowBestSolutionsCollectionHeatMapView() {
       InitializeComponent();
-
-      qualityMeasure = (int)QualityMeasures.PEARSON;
+      progress = new Progress();
+      MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>().AddOperationProgressToView(this, progress);
     }
-
-    enum QualityMeasures { PEARSON, MSE };
-
-    private int qualityMeasure;
 
     protected override void DeregisterContentEvents() {
       // TODO: Deregister your event handlers here
+      Content.QualitiesCalculationCompleted -= bestSolutionsCollectionQualitiesCalculated;
+      Content.QualitiesCalculationProgress -= bestSolutionCollectionQualitiesCalculationProgress;
+      Content.QualitiesUpdated -= bestSolutionCollectionQualitiesUpdated;
       base.DeregisterContentEvents();
     }
@@ -57,10 +70,53 @@
       base.RegisterContentEvents();
       // TODO: Register your event handlers here
+      Content.QualitiesCalculationCompleted += bestSolutionsCollectionQualitiesCalculated;
+      Content.QualitiesCalculationProgress += bestSolutionCollectionQualitiesCalculationProgress;
+      Content.QualitiesUpdated += bestSolutionCollectionQualitiesUpdated;
     }
 
+    private void bestSolutionsCollectionQualitiesCalculated(object sender, RunWorkerCompletedEventArgs e) {
+      if (e.Cancelled || e.Error != null) {
+        progress.Cancel();
+        return;
+      }
+      SetHeatmapContent(Content.SlidingWindowQualities);
+      progress.Finish();
+    }
+
+    private void bestSolutionCollectionQualitiesUpdated(object sender, EventArgs e) {
+      SetHeatmapContent(Content.SlidingWindowQualities);
+    }
+
+    private void SetHeatmapContent(double[,] values) {
+      double min = 0, max = 0;
+      foreach (var q in values) {
+        if (min > q) min = q;
+        if (max < q) max = q;
+      }
+      int rows = values.GetLength(0);
+      int cols = values.GetLength(1);
+      var flipped = new double[rows, cols];
+      for (int i = 0; i < rows; ++i) {
+        for (int j = 0; j < cols; ++j) {
+          flipped[i, j] = values[rows - i - 1, j];
+        }
+      }
+      heatMapView.Content = new HeatMap(flipped, "Best Sliding Window Solutions", min, max);
+    }
+
+    private void bestSolutionCollectionQualitiesCalculationProgress(object sender, ProgressChangedEventArgs e) {
+      progress.ProgressValue = e.ProgressPercentage / 100.0;
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      // TODO: Enable or disable controls based on whether the content is null or the view is set readonly
+    }
+
+    #region Event Handlers (child controls)
     public void heatMapView_cellDoubleClick(object sender, DataGridViewCellEventArgs e) {
       var dataGridView = (DataGridView)sender;
       var cell = dataGridView.SelectedCells[0];
-      var bestSolutions = Content.BestSolutions.Values.ToList();
+      var bestSolutions = Content.SlidingWindowBestSolutions.Values.ToList();
       var tree = bestSolutions[cell.RowIndex];
       var model = Content.CreateModel(tree, Content.Interpreter);
@@ -74,100 +130,18 @@
     }
 
-    #region Event Handlers (Content)
-    // TODO: Put event handlers of the content here
-    #endregion
-
-    protected override void OnContentChanged() {
-      base.OnContentChanged();
-      if (Content == null) {
-        // TODO: Add code when content has been changed and is null
-      } else {
-        // TODO: Add code when content has been changed and is not null
-        UpdateQualitiesMap();
-      }
-    }
-
-    protected override void SetEnabledStateOfControls() {
-      base.SetEnabledStateOfControls();
-      // TODO: Enable or disable controls based on whether the content is null or the view is set readonly
-    }
-
-    #region Event Handlers (child controls)
-    // TODO: Put event handlers of child controls here.
-    #endregion
-
     private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e) {
       var combobox = (ComboBox)sender;
       switch ((string)combobox.SelectedItem) {
         case "Pearson R2": {
-            qualityMeasure = (int)QualityMeasures.PEARSON;
+            Content.QualityMeasure = SlidingWindowBestSolutionsCollection.QualityMeasures.PEARSON;
             break;
           }
         case "Mean Squared Error": {
-            qualityMeasure = (int)QualityMeasures.MSE;
+            Content.QualityMeasure = SlidingWindowBestSolutionsCollection.QualityMeasures.MSE;
             break;
           }
       }
-      UpdateQualitiesMap();
     }
-
-    private void UpdateQualitiesMap() {
-      var bestSolutions = Content.BestSolutions.Values.ToList();
-      var swPositions = Content.BestSolutions.Keys.ToList();
-
-      var nRows = bestSolutions.Count;
-      var nCols = swPositions.Count + 1;
-      var qualitiesMap = new double[nRows, nCols];
-      double min = 0, max = 0;
-
-      for (int i = 0; i < nCols - 1; ++i) {
-        var pos = swPositions[i];
-        var rows = Enumerable.Range(pos.Item1, pos.Item2 - pos.Item1).ToList();
-
-        for (int j = nRows - 1; j >= 0; --j) {
-          var tree = bestSolutions[j];
-          var q = CalculateQuality(tree, rows);
-          if (min > q) min = q;
-          if (max < q) max = q;
-          qualitiesMap[j, i] = q;
-        }
-      }
-      // deal separately with the last column which represents the whole training partition interval
-      for (int j = nRows - 1; j >= 0; --j) {
-        var tree = bestSolutions[j];
-        var q = CalculateQuality(tree, Content.ProblemData.TrainingIndices);
-        if (min > q) min = q;
-        if (max < q) max = q;
-        qualitiesMap[j, nCols - 1] = q;
-      }
-
-      heatMapView.Content = new HeatMap(qualitiesMap);
-      heatMapView.Content.Minimum = min;
-      heatMapView.Content.Maximum = max;
-    }
-
-    private string GetTargetVariable(IDataAnalysisProblemData problemData) {
-      var regressionProblemData = problemData as IRegressionProblemData;
-      var classificationProblemData = problemData as IClassificationProblemData;
-      if (regressionProblemData != null) return regressionProblemData.TargetVariable;
-      if (classificationProblemData != null) return classificationProblemData.TargetVariable;
-      throw new NotSupportedException();
-    }
-
-    private double CalculateQuality(ISymbolicExpressionTree tree, IEnumerable<int> rows) {
-      var estimatedValues = Content.Interpreter.GetSymbolicExpressionTreeValues(tree, Content.ProblemData.Dataset, rows);
-      var originalValues = Content.ProblemData.Dataset.GetDoubleValues(GetTargetVariable(Content.ProblemData), rows);
-      double quality = 0;
-      var errorState = new OnlineCalculatorError();
-      switch ((QualityMeasures)qualityMeasure) {
-        case QualityMeasures.PEARSON:
-          quality = OnlinePearsonsRSquaredCalculator.Calculate(estimatedValues, originalValues, out errorState);
-          break;
-        case QualityMeasures.MSE:
-          quality = OnlineMeanSquaredErrorCalculator.Calculate(estimatedValues, originalValues, out errorState);
-          break;
-      }
-      return errorState == OnlineCalculatorError.None ? quality : double.NaN;
-    }
+    #endregion
   }
 }
Index: branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/SlidingWindowBestSolutionsCollectionView.cs
===================================================================
--- branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/SlidingWindowBestSolutionsCollectionView.cs	(revision 10688)
+++ branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/SlidingWindowBestSolutionsCollectionView.cs	(revision 10720)
@@ -22,9 +22,9 @@
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Linq;
 using System.Windows.Forms;
 using HeuristicLab.Core.Views;
 using HeuristicLab.Data;
-using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
 using HeuristicLab.MainForm;
 using HeuristicLab.Optimization;
@@ -39,17 +39,25 @@
       set { base.Content = value; }
     }
-    public SlidingWindowBestSolutionsCollectionView() {
-      InitializeComponent();
 
-      qualityMeasure = (int)QualityMeasures.PEARSON;
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      if (Content != null) {
+        if (Content.SlidingWindowQualities != null) {
+          SetMatrixContent(Content.SlidingWindowQualities);
+        } else if (!Content.QualitiesCalculationInProgress) {
+          progress.Start("Calculating best solution qualities...");
+          Content.CalculateQualities();
+        }
+      }
     }
 
-    enum QualityMeasures { PEARSON, MSE };
-
-    private int qualityMeasure;
+    private readonly Progress progress;
 
     protected override void DeregisterContentEvents() {
       // TODO: Deregister your event handlers here
       enhancedStringConvertibleMatrixView.DataGridView.CellDoubleClick -= enhancedStringConvertibleMatrixView_cellDoubleClick;
+      Content.QualitiesCalculationCompleted -= bestSolutionsCollectionQualitiesCalculated;
+      Content.QualitiesCalculationProgress -= bestSolutionCollectionQualitiesCalculationProgress;
+      Content.QualitiesUpdated -= bestSolutionCollectionQualitiesUpdated;
       base.DeregisterContentEvents();
     }
@@ -57,6 +65,71 @@
     protected override void RegisterContentEvents() {
       base.RegisterContentEvents();
+      // TODO: Register your event handlers here
       enhancedStringConvertibleMatrixView.DataGridView.CellDoubleClick += enhancedStringConvertibleMatrixView_cellDoubleClick;
-      // TODO: Register your event handlers here
+      Content.QualitiesCalculationCompleted += bestSolutionsCollectionQualitiesCalculated;
+      Content.QualitiesCalculationProgress += bestSolutionCollectionQualitiesCalculationProgress;
+      Content.QualitiesUpdated += bestSolutionCollectionQualitiesUpdated;
+    }
+
+    public SlidingWindowBestSolutionsCollectionView() {
+      InitializeComponent();
+      progress = new Progress();
+      MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>().AddOperationProgressToView(this, progress);
+    }
+
+    #region Event Handlers (Content)
+    private void bestSolutionsCollectionQualitiesCalculated(object sender, RunWorkerCompletedEventArgs e) {
+      if (e.Cancelled || e.Error != null) {
+        progress.Cancel();
+        return;
+      }
+
+      SetMatrixContent(Content.SlidingWindowQualities);
+      progress.Finish();
+    }
+
+    private void SetMatrixContent(double[,] values) {
+      var qualitiesMap = new DoubleMatrix(values); // add an extra columns for the training data
+      qualitiesMap.ColumnNames = Enumerable.Range(0, qualitiesMap.Columns - 1).Select(x => Content.SlidingWindowRanges[x].Start + " - " + Content.SlidingWindowRanges[x].End).Concat(new List<string> { "Training" });
+      qualitiesMap.RowNames = Enumerable.Range(1, qualitiesMap.Rows).Select(x => "M" + x);
+
+      double min = 0, max = 0;
+      foreach (var q in qualitiesMap) {
+        if (min > q) min = q;
+        if (max < q) max = q;
+      }
+
+      enhancedStringConvertibleMatrixView.Minimum = min;
+      enhancedStringConvertibleMatrixView.Maximum = max;
+      enhancedStringConvertibleMatrixView.Content = qualitiesMap;
+    }
+
+    private void bestSolutionCollectionQualitiesCalculationProgress(object sender, ProgressChangedEventArgs e) {
+      progress.ProgressValue = e.ProgressPercentage / 100.0;
+    }
+
+    private void bestSolutionCollectionQualitiesUpdated(object sender, EventArgs e) {
+      SetMatrixContent(Content.SlidingWindowQualities);
+    }
+    #endregion
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      // TODO: Enable or disable controls based on whether the content is null or the view is set readonly
+    }
+
+    #region Event Handlers (child controls)
+    private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e) {
+      var combobox = (ComboBox)sender;
+      switch ((string)combobox.SelectedItem) {
+        case "Pearson R2": {
+            Content.QualityMeasure = SlidingWindowBestSolutionsCollection.QualityMeasures.PEARSON;
+            break;
+          }
+        case "Mean Squared Error": {
+            Content.QualityMeasure = SlidingWindowBestSolutionsCollection.QualityMeasures.MSE;
+            break;
+          }
+      }
     }
 
@@ -64,5 +137,5 @@
       var dataGridView = (DataGridView)sender;
       var cell = dataGridView.SelectedCells[0];
-      var bestSolutions = Content.BestSolutions.Values.ToList();
+      var bestSolutions = Content.SlidingWindowBestSolutions.Values.ToList();
       var tree = bestSolutions[cell.RowIndex];
       var model = Content.CreateModel(tree, Content.Interpreter);
@@ -75,100 +148,5 @@
       }
     }
-
-    #region Event Handlers (Content)
-    // TODO: Put event handlers of the content here
     #endregion
-
-    protected override void OnContentChanged() {
-      base.OnContentChanged();
-      if (Content == null) {
-        // TODO: Add code when content has been changed and is null
-      } else {
-        // TODO: Add code when content has been changed and is not null
-        UpdateQualitiesMap();
-      }
-    }
-
-    protected override void SetEnabledStateOfControls() {
-      base.SetEnabledStateOfControls();
-      // TODO: Enable or disable controls based on whether the content is null or the view is set readonly
-    }
-
-    #region Event Handlers (child controls)
-    // TODO: Put event handlers of child controls here.
-    #endregion
-
-    private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e) {
-      var combobox = (ComboBox)sender;
-      switch ((string)combobox.SelectedItem) {
-        case "Pearson R2": {
-            qualityMeasure = (int)QualityMeasures.PEARSON;
-            break;
-          }
-        case "Mean Squared Error": {
-            qualityMeasure = (int)QualityMeasures.MSE;
-            break;
-          }
-      }
-      UpdateQualitiesMap();
-    }
-
-    private void UpdateQualitiesMap() {
-      var bestSolutions = Content.BestSolutions.Values.ToList();
-      var swPositions = Content.BestSolutions.Keys.ToList();
-      var qualitiesMap = new DoubleMatrix(bestSolutions.Count, swPositions.Count + 1);
-      qualitiesMap.ColumnNames = swPositions.Select(x => x.Item1 + "-" + x.Item2).Concat(new List<string> { "Training" });
-      qualitiesMap.RowNames = Enumerable.Range(1, bestSolutions.Count).Select(x => "M" + x);
-      double min = 0, max = 0;
-
-      for (int i = 0; i < qualitiesMap.Columns - 1; ++i) {
-        var pos = swPositions[i];
-        var rows = Enumerable.Range(pos.Item1, pos.Item2 - pos.Item1).ToList();
-
-        for (int j = 0; j < qualitiesMap.Rows; ++j) {
-          var tree = bestSolutions[j];
-          var q = CalculateQuality(tree, rows);
-          if (min > q) min = q;
-          if (max < q) max = q;
-          qualitiesMap[j, i] = q;
-        }
-      }
-      // deal separately with the last column which represents the whole training partition interval
-      for (int j = 0; j < qualitiesMap.Rows; ++j) {
-        var tree = bestSolutions[j];
-        var q = CalculateQuality(tree, Content.ProblemData.TrainingIndices);
-        if (min > q) min = q;
-        if (max < q) max = q;
-        qualitiesMap[j, qualitiesMap.Columns - 1] = q;
-      }
-
-      enhancedStringConvertibleMatrixView.Minimum = min;
-      enhancedStringConvertibleMatrixView.Maximum = max;
-      enhancedStringConvertibleMatrixView.Content = qualitiesMap;
-    }
-
-    private string GetTargetVariable(IDataAnalysisProblemData problemData) {
-      var regressionProblemData = problemData as IRegressionProblemData;
-      var classificationProblemData = problemData as IClassificationProblemData;
-      if (regressionProblemData != null) return regressionProblemData.TargetVariable;
-      if (classificationProblemData != null) return classificationProblemData.TargetVariable;
-      throw new NotSupportedException();
-    }
-
-    private double CalculateQuality(ISymbolicExpressionTree tree, IEnumerable<int> rows) {
-      var estimatedValues = Content.Interpreter.GetSymbolicExpressionTreeValues(tree, Content.ProblemData.Dataset, rows);
-      var originalValues = Content.ProblemData.Dataset.GetDoubleValues(GetTargetVariable(Content.ProblemData), rows);
-      double quality = 0;
-      var errorState = new OnlineCalculatorError();
-      switch ((QualityMeasures)qualityMeasure) {
-        case QualityMeasures.PEARSON:
-          quality = OnlinePearsonsRSquaredCalculator.Calculate(estimatedValues, originalValues, out errorState);
-          break;
-        case QualityMeasures.MSE:
-          quality = OnlineMeanSquaredErrorCalculator.Calculate(estimatedValues, originalValues, out errorState);
-          break;
-      }
-      return errorState == OnlineCalculatorError.None ? quality : double.NaN;
-    }
   }
 }
Index: branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SlidingWindow/SlidingWindowAnalyzer.cs
===================================================================
--- branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SlidingWindow/SlidingWindowAnalyzer.cs	(revision 10688)
+++ branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SlidingWindow/SlidingWindowAnalyzer.cs	(revision 10720)
@@ -31,4 +31,5 @@
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
 
+
 namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
   [StorableClass]
@@ -45,4 +46,5 @@
     private const string TerminateSlidingWindowParameterName = "TerminateSlidingWindow";
     private const string InterpreterParameterName = "SymbolicExpressionTreeInterpreter";
+    protected const string BestSlidingWindowSolutionsResultName = "Best Sliding Window Solutions";
 
     #region parameter properties
@@ -179,15 +181,13 @@
 
     private void SaveBestSolution() {
-      var bestSolutionsCollection = (SlidingWindowBestSolutionsCollection)ResultCollection["Best Solutions"].Value;
+      var bestSolutionsCollection = (SlidingWindowBestSolutionsCollection)ResultCollection[BestSlidingWindowSolutionsResultName].Value;
 
-      if (bestSolutionsCollection.ProblemData == null)
-        bestSolutionsCollection.ProblemData = ProblemData;
-
-      if (bestSolutionsCollection.Interpreter == null)
-        bestSolutionsCollection.Interpreter = Interpreter;
+      bestSolutionsCollection.ProblemData = ProblemData;
+      bestSolutionsCollection.Interpreter = Interpreter;
+      bestSolutionsCollection.ApplyLinearScaling = ApplyLinearScalingParameter.ActualValue.Value;
 
       var fitnessPartition = FitnessCalculationPartitionParameter.ActualValue;
       var best = FindBestIndividual();
-      var range = new Tuple<int, int>(fitnessPartition.Start, fitnessPartition.End);
+      var range = new SlidingWindowRange(fitnessPartition.Start, fitnessPartition.End);
 
       bestSolutionsCollection[range] = best;
Index: branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SlidingWindow/SlidingWindowBestSolutionsCollection.cs
===================================================================
--- branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SlidingWindow/SlidingWindowBestSolutionsCollection.cs	(revision 10688)
+++ branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SlidingWindow/SlidingWindowBestSolutionsCollection.cs	(revision 10720)
@@ -20,5 +20,8 @@
 #endregion
 
+using System;
 using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -26,6 +29,4 @@
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
 
-using SlidingWindowRange = System.Tuple<int, int>;
-
 namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
   [StorableClass]
@@ -33,8 +34,28 @@
   public abstract class SlidingWindowBestSolutionsCollection : Item {
     [Storable]
-    private Dictionary<SlidingWindowRange, ISymbolicExpressionTree> bestSolutions;
-    public Dictionary<SlidingWindowRange, ISymbolicExpressionTree> BestSolutions {
-      get { return bestSolutions; }
-      set { bestSolutions = value; }
+    private List<SlidingWindowRange> slidingWindowRanges;
+    public List<SlidingWindowRange> SlidingWindowRanges {
+      get { return slidingWindowRanges; }
+      private set { slidingWindowRanges = value; }
+    }
+
+    [Storable(AllowOneWay = true, Name = "bestSolutions")]
+    private Dictionary<Tuple<int, int>, ISymbolicExpressionTree> StorableBestSolutions {
+      set {
+        var bestSolutions = value;
+        var ranges = bestSolutions.Keys.OrderBy(x => x.Item1).ToList();
+        slidingWindowRanges = ranges.Select(x => new SlidingWindowRange(x.Item1, x.Item2)).ToList();
+        slidingWindowBestSolutions = new Dictionary<SlidingWindowRange, ISymbolicExpressionTree>();
+        for (int i = 0; i < slidingWindowRanges.Count; ++i) {
+          slidingWindowBestSolutions.Add(slidingWindowRanges[i], bestSolutions[ranges[i]]);
+        }
+      }
+    }
+
+    [Storable]
+    private Dictionary<SlidingWindowRange, ISymbolicExpressionTree> slidingWindowBestSolutions;
+    public Dictionary<SlidingWindowRange, ISymbolicExpressionTree> SlidingWindowBestSolutions {
+      get { return slidingWindowBestSolutions; }
+      set { slidingWindowBestSolutions = value; }
     }
 
@@ -52,6 +73,57 @@
       set { interpreter = value; }
     }
+
+    [Storable]
+    private bool applyLinearScaling;
+    public bool ApplyLinearScaling {
+      get { return applyLinearScaling; }
+      set { applyLinearScaling = value; }
+    }
+
     [StorableHook(HookType.AfterDeserialization)]
     private void AfterDeserialization() {
+      if (bw == null) {
+        bw = new BackgroundWorker();
+        bw.WorkerSupportsCancellation = true;
+        bw.WorkerReportsProgress = true;
+        bw.DoWork += CalculateQualities;
+      }
+    }
+
+    public double[,] SlidingWindowQualities { get; set; }
+
+    private BackgroundWorker bw;
+
+    public enum QualityMeasures { PEARSON, MSE };
+
+    private QualityMeasures qualityMeasure;
+    public QualityMeasures QualityMeasure {
+      get { return qualityMeasure; }
+      set {
+        if (qualityMeasure != value) {
+          qualityMeasure = value;
+          CalculateQualities();
+        }
+      }
+    }
+
+    public bool QualitiesCalculationInProgress {
+      get { return bw.IsBusy; }
+    }
+
+    public event ProgressChangedEventHandler QualitiesCalculationProgress {
+      add { bw.ProgressChanged += value; }
+      remove { bw.ProgressChanged -= value; }
+    }
+
+    public event RunWorkerCompletedEventHandler QualitiesCalculationCompleted {
+      add { bw.RunWorkerCompleted += value; }
+      remove { bw.RunWorkerCompleted -= value; }
+    }
+
+    public event EventHandler QualitiesUpdated;
+    private void OnQualitiesUpdated(object sender, EventArgs e) {
+      var updated = QualitiesUpdated;
+      if (updated != null) { updated(sender, e); }
     }
 
@@ -60,34 +132,47 @@
     protected SlidingWindowBestSolutionsCollection(SlidingWindowBestSolutionsCollection original, Cloner cloner)
       : base(original, cloner) {
-      this.bestSolutions = original.bestSolutions;
+      this.slidingWindowBestSolutions = original.slidingWindowBestSolutions;
       this.problemData = original.problemData;
       this.interpreter = original.interpreter;
+      this.applyLinearScaling = original.ApplyLinearScaling;
     }
     protected SlidingWindowBestSolutionsCollection() {
-      bestSolutions = new Dictionary<SlidingWindowRange, ISymbolicExpressionTree>();
+      slidingWindowBestSolutions = new Dictionary<SlidingWindowRange, ISymbolicExpressionTree>();
+      slidingWindowRanges = new List<SlidingWindowRange>();
+      qualityMeasure = QualityMeasures.PEARSON;
+
+      bw = new BackgroundWorker();
+      bw.WorkerSupportsCancellation = true;
+      bw.WorkerReportsProgress = true;
+
+      bw.DoWork += CalculateQualities;
     }
 
     public bool ContainsKey(SlidingWindowRange key) {
-      return bestSolutions.ContainsKey(key);
+      return slidingWindowBestSolutions.ContainsKey(key);
     }
 
     public ISymbolicExpressionTree this[SlidingWindowRange key] {
       get {
-        return bestSolutions[key];
+        return slidingWindowBestSolutions[key];
       }
       set {
-        if (bestSolutions.ContainsKey(key))
-          bestSolutions[key] = value;
-        else
-          bestSolutions.Add(key, value);
+        AddSolution(key, value); // this should be fast so there's no need for a background worker
+        OnQualitiesUpdated(this, EventArgs.Empty);
       }
     }
 
     public void Add(SlidingWindowRange range, ISymbolicExpressionTree solution) {
-      bestSolutions.Add(range, solution);
+      if (!slidingWindowBestSolutions.ContainsKey(range)) {
+        slidingWindowBestSolutions.Add(range, solution);
+        slidingWindowRanges.Add(range);
+      } else {
+        slidingWindowBestSolutions[range] = solution;
+      }
     }
 
     public void Clear() {
-      if (bestSolutions != null) bestSolutions.Clear();
+      if (slidingWindowBestSolutions != null) slidingWindowBestSolutions.Clear();
+      if (slidingWindowRanges != null) slidingWindowRanges.Clear();
     }
 
@@ -96,4 +181,124 @@
 
     public abstract ISymbolicDataAnalysisSolution CreateSolution(ISymbolicDataAnalysisModel model, IDataAnalysisProblemData problemData);
+
+    private void AddSolution(SlidingWindowRange range, ISymbolicExpressionTree solution) {
+      // add a solution and update the qualities matrix
+      Add(range, solution);
+
+      var solutions = slidingWindowRanges.Select(x => slidingWindowBestSolutions[x]).ToList();
+
+      var nRows = solutions.Count;
+      var nCols = nRows + 1; // an extra column corresponding to the whole trainig partition
+
+      var trainingIndices = problemData.TrainingIndices.ToList();
+      var matrix = new double[nRows, nCols];
+      List<int> rows;
+      // copy old qualities into the new matrix
+      for (int i = 0; i < nRows - 1; ++i) {
+        for (int j = 0; j < nCols - 1; ++j) {
+          matrix[i, j] = SlidingWindowQualities[i, j];
+        }
+      }
+      // copy qualities of new solution into the new matrix
+      for (int i = 0; i < nCols; ++i) {
+        rows = i == nCols - 1 ? trainingIndices : Enumerable.Range(slidingWindowRanges[i].Start, slidingWindowRanges[i].Size).ToList();
+        matrix[nRows - 1, i] = CalculateQuality(solution, rows);
+      }
+      // shift old training qualities one column to the right
+      rows = Enumerable.Range(range.Start, range.Size).ToList();
+      for (int i = 0; i < nRows; ++i) {
+        matrix[i, nCols - 1] = matrix[i, nCols - 2];
+        matrix[i, nCols - 2] = CalculateQuality(solutions[i], rows);
+      }
+      // replace old matrix with new matrix
+      SlidingWindowQualities = matrix;
+    }
+
+    private void CalculateQualities(object sender, DoWorkEventArgs e) {
+      var worker = sender as BackgroundWorker;
+      if (worker == null) return;
+      if (worker.CancellationPending) {
+        e.Cancel = true;
+        return;
+      }
+
+      var ranges = SlidingWindowRanges;
+      var solutions = ranges.Select(x => SlidingWindowBestSolutions[x]).ToList();
+
+      int rows = solutions.Count;
+      int columns = ranges.Count + 1;
+
+      SlidingWindowQualities = new double[rows, columns];
+
+      for (int i = 0; i < rows; ++i) {
+        if (worker.CancellationPending) {
+          e.Cancel = true;
+          return;
+        }
+
+        var solution = solutions[i];
+        for (int j = 0; j < columns; ++j) {
+          var range = (j == columns - 1) ? ProblemData.TrainingIndices : Enumerable.Range(ranges[j].Start, ranges[j].Size);
+          var q = CalculateQuality(solution, range);
+
+          SlidingWindowQualities[i, j] = q;
+        }
+
+        worker.ReportProgress((int)Math.Round(i * 100.0 / rows));
+      }
+    }
+
+    public void CalculateQualities() {
+      bw.RunWorkerAsync();
+    }
+
+    private string GetTargetVariable(IDataAnalysisProblemData problemData) {
+      var regressionProblemData = problemData as IRegressionProblemData;
+      var classificationProblemData = problemData as IClassificationProblemData;
+      if (regressionProblemData != null) return regressionProblemData.TargetVariable;
+      if (classificationProblemData != null) return classificationProblemData.TargetVariable;
+      throw new NotSupportedException();
+    }
+
+    private double CalculateQuality(ISymbolicExpressionTree tree, IEnumerable<int> rows) {
+      var estimatedValues = Interpreter.GetSymbolicExpressionTreeValues(tree, ProblemData.Dataset, rows);
+      var originalValues = ProblemData.Dataset.GetDoubleValues(GetTargetVariable(ProblemData), rows);
+      double quality = 0;
+      var errorState = new OnlineCalculatorError();
+      switch (QualityMeasure) {
+        case QualityMeasures.PEARSON:
+          quality = OnlinePearsonsRSquaredCalculator.Calculate(estimatedValues, originalValues, out errorState);
+          break;
+        case QualityMeasures.MSE:
+          quality = OnlineMeanSquaredErrorCalculator.Calculate(estimatedValues, originalValues, out errorState);
+          break;
+      }
+      return errorState == OnlineCalculatorError.None ? quality : double.NaN;
+    }
+  }
+
+  public class SlidingWindowRange : IEquatable<SlidingWindowRange> {
+    private readonly Tuple<int, int> tuple;
+
+    public int Start { get { return tuple.Item1; } }
+
+    public int End { get { return tuple.Item2; } }
+
+    public SlidingWindowRange(int start, int end) {
+      if (start > end) throw new ArgumentException("SlidingWindowRange: Start cannot be greater than End.");
+      tuple = new Tuple<int, int>(start, end);
+    }
+
+    public bool Equals(SlidingWindowRange other) {
+      return tuple.Equals(other.tuple);
+    }
+
+    public override int GetHashCode() {
+      return tuple.GetHashCode();
+    }
+
+    public int Size {
+      get { return End - Start; }
+    }
   }
 }
