Index: trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/HeuristicLab.Problems.DataAnalysis-3.4.csproj
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/HeuristicLab.Problems.DataAnalysis-3.4.csproj	(revision 6964)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/HeuristicLab.Problems.DataAnalysis-3.4.csproj	(revision 6974)
@@ -143,4 +143,5 @@
     <Compile Include="Interfaces\Regression\IRegressionEnsembleSolution.cs" />
     <Compile Include="Implementation\Regression\RegressionSolutionBase.cs" />
+    <Compile Include="Interfaces\TimeSeriesPrognosis\IOnlineTimeSeriesCalculator.cs" />
     <Compile Include="Interfaces\TimeSeriesPrognosis\ITimeSeriesPrognosisModel.cs" />
     <Compile Include="Interfaces\TimeSeriesPrognosis\ITimeSeriesPrognosisProblem.cs" />
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/TimeSeriesPrognosis/TimeSeriesPrognosisSolutionBase.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/TimeSeriesPrognosis/TimeSeriesPrognosisSolutionBase.cs	(revision 6964)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/TimeSeriesPrognosis/TimeSeriesPrognosisSolutionBase.cs	(revision 6974)
@@ -40,10 +40,10 @@
     private const string TrainingNormalizedMeanSquaredErrorResultName = "Normalized mean squared error (training)";
     private const string TestNormalizedMeanSquaredErrorResultName = "Normalized mean squared error (test)";
-    private const string TrainingDirectionalSymmetryResultName = "Directional symmetry (training)";
-    private const string TestDirectionalSymmetryResultName = "Directional symmetry (test)";
-    private const string TrainingWeightedDirectionalSymmetryResultName = "Weighted directional symmetry (training)";
-    private const string TestWeightedDirectionalSymmetryResultName = "Weighted directional symmetry (test)";
-    private const string TrainingTheilsUStatisticResultName = "Theil's U (training)";
-    private const string TestTheilsUStatisticResultName = "Theil's U (test)";
+    private const string TrainingDirectionalSymmetryResultName = "Average directional symmetry (training)";
+    private const string TestDirectionalSymmetryResultName = "Average directional symmetry (test)";
+    private const string TrainingWeightedDirectionalSymmetryResultName = "Average weighted directional symmetry (training)";
+    private const string TestWeightedDirectionalSymmetryResultName = "Average weighted directional symmetry (test)";
+    private const string TrainingTheilsUStatisticResultName = "Average Theil's U (training)";
+    private const string TestTheilsUStatisticResultName = "Average Theil's U (test)";
 
     public new ITimeSeriesPrognosisModel Model {
@@ -146,10 +146,10 @@
       Add(new Result(TrainingNormalizedMeanSquaredErrorResultName, "Normalized mean of squared errors of the model on the training partition", new DoubleValue()));
       Add(new Result(TestNormalizedMeanSquaredErrorResultName, "Normalized mean of squared errors of the model on the test partition", new DoubleValue()));
-      Add(new Result(TrainingDirectionalSymmetryResultName, "The directional symmetry of the output of the model on the training partition", new DoubleValue()));
-      Add(new Result(TestDirectionalSymmetryResultName, "The directional symmetry of the output of the model on the test partition", new DoubleValue()));
-      Add(new Result(TrainingWeightedDirectionalSymmetryResultName, "The weighted directional symmetry of the output of the model on the training partition", new DoubleValue()));
-      Add(new Result(TestWeightedDirectionalSymmetryResultName, "The weighted directional symmetry of the output of the model on the test partition", new DoubleValue()));
-      Add(new Result(TrainingTheilsUStatisticResultName, "The Theil's U statistic of the output of the model on the training partition", new DoubleValue()));
-      Add(new Result(TestTheilsUStatisticResultName, "The Theil's U statistic of the output of the model on the test partition", new DoubleValue()));
+      Add(new Result(TrainingDirectionalSymmetryResultName, "The average directional symmetry of the forecasts of the model on the training partition", new PercentValue()));
+      Add(new Result(TestDirectionalSymmetryResultName, "The average directional symmetry of the forecasts of the model on the test partition", new PercentValue()));
+      Add(new Result(TrainingWeightedDirectionalSymmetryResultName, "The average weighted directional symmetry of the forecasts of the model on the training partition", new DoubleValue()));
+      Add(new Result(TestWeightedDirectionalSymmetryResultName, "The average weighted directional symmetry of the forecasts of the model on the test partition", new DoubleValue()));
+      Add(new Result(TrainingTheilsUStatisticResultName, "The average Theil's U statistic of the forecasts of the model on the training partition", new DoubleValue()));
+      Add(new Result(TestTheilsUStatisticResultName, "The average Theil's U statistic of the forecasts of the model on the test partition", new DoubleValue()));
     }
 
@@ -191,20 +191,34 @@
       TestNormalizedMeanSquaredError = errorState == OnlineCalculatorError.None ? testNmse : double.NaN;
 
-      double trainingDirectionalSymmetry = OnlineDirectionalSymmetryCalculator.Calculate(originalTrainingValues, estimatedTrainingValues, out errorState);
+      var startTrainingValues = originalTrainingValues;
+      // each continuation is only one element long
+      var actualContinuationsTraining = from x in originalTrainingValues.Skip(1)
+                                        select Enumerable.Repeat(x, 1);
+      // each forecast is only one elemnt long
+      // disregards the first estimated value (we could include this again by extending the list of original values by one step to the left
+      // this is the easier way
+      var predictedContinuationsTraining = from x in estimatedTrainingValues.Skip(1)
+                                           select Enumerable.Repeat(x, 1);
+
+      var startTestValues = originalTestValues;
+      var actualContinuationsTest = from x in originalTestValues.Skip(1)
+                                    select Enumerable.Repeat(x, 1);
+      var predictedContinuationsTest = from x in estimatedTestValues.Skip(1)
+                                       select Enumerable.Repeat(x, 1);
+
+      double trainingDirectionalSymmetry = OnlineDirectionalSymmetryCalculator.Calculate(startTrainingValues, actualContinuationsTraining, predictedContinuationsTraining, out errorState);
       TrainingDirectionalSymmetry = errorState == OnlineCalculatorError.None ? trainingDirectionalSymmetry : double.NaN;
-      double testDirectionalSymmetry = OnlineDirectionalSymmetryCalculator.Calculate(originalTestValues, estimatedTestValues, out errorState);
+      double testDirectionalSymmetry = OnlineDirectionalSymmetryCalculator.Calculate(startTestValues, actualContinuationsTest, predictedContinuationsTest, out errorState);
       TestDirectionalSymmetry = errorState == OnlineCalculatorError.None ? testDirectionalSymmetry : double.NaN;
 
-      double trainingWeightedDirectionalSymmetry = OnlineWeightedDirectionalSymmetryCalculator.Calculate(originalTrainingValues, estimatedTrainingValues, out errorState);
+      double trainingWeightedDirectionalSymmetry = OnlineWeightedDirectionalSymmetryCalculator.Calculate(startTrainingValues, actualContinuationsTraining, predictedContinuationsTraining, out errorState);
       TrainingWeightedDirectionalSymmetry = errorState == OnlineCalculatorError.None ? trainingWeightedDirectionalSymmetry : double.NaN;
-      double testWeightedDirectionalSymmetry = OnlineWeightedDirectionalSymmetryCalculator.Calculate(originalTestValues, estimatedTestValues, out errorState);
+      double testWeightedDirectionalSymmetry = OnlineWeightedDirectionalSymmetryCalculator.Calculate(startTestValues, actualContinuationsTest, predictedContinuationsTest, out errorState);
       TestWeightedDirectionalSymmetry = errorState == OnlineCalculatorError.None ? testWeightedDirectionalSymmetry : double.NaN;
 
-      double trainingTheilsU = OnlineTheilsUStatisticCalculator.Calculate(originalTrainingValues, estimatedTrainingValues, out errorState);
+      double trainingTheilsU = OnlineTheilsUStatisticCalculator.Calculate(startTrainingValues, actualContinuationsTraining, predictedContinuationsTraining, out errorState);
       TrainingTheilsUStatistic = errorState == OnlineCalculatorError.None ? trainingTheilsU : double.NaN;
-      double testTheilsU = OnlineTheilsUStatisticCalculator.Calculate(originalTestValues, estimatedTestValues, out errorState);
+      double testTheilsU = OnlineTheilsUStatisticCalculator.Calculate(startTestValues, actualContinuationsTest, predictedContinuationsTest, out errorState);
       TestTheilsUStatistic = errorState == OnlineCalculatorError.None ? testTheilsU : double.NaN;
-
-
     }
   }
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/Interfaces/TimeSeriesPrognosis/IOnlineTimeSeriesCalculator.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/Interfaces/TimeSeriesPrognosis/IOnlineTimeSeriesCalculator.cs	(revision 6974)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/Interfaces/TimeSeriesPrognosis/IOnlineTimeSeriesCalculator.cs	(revision 6974)
@@ -0,0 +1,33 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2011 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;
+
+namespace HeuristicLab.Problems.DataAnalysis {
+  public interface IOnlineTimeSeriesCalculator {
+    OnlineCalculatorError ErrorState { get; }
+    double Value { get; }
+    void Reset();
+    void Add(double startValue, IEnumerable<double> actualContinuation, IEnumerable<double> predictedContinuation);
+  }
+}
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/OnlineCalculators/OnlineDirectionalSymmetryCalculator.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/OnlineCalculators/OnlineDirectionalSymmetryCalculator.cs	(revision 6964)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/OnlineCalculators/OnlineDirectionalSymmetryCalculator.cs	(revision 6974)
@@ -22,10 +22,9 @@
 using System;
 using System.Collections.Generic;
+using HeuristicLab.Common;
 
 
 namespace HeuristicLab.Problems.DataAnalysis {
-  public class OnlineDirectionalSymmetryCalculator : IOnlineCalculator {
-    private double prevEstimated;
-    private double prevOriginal;
+  public class OnlineDirectionalSymmetryCalculator : IOnlineTimeSeriesCalculator {
     private int n;
     private int nCorrect;
@@ -33,6 +32,6 @@
     public double DirectionalSymmetry {
       get {
-        if (n <= 1) return 0.0;
-        return (double)nCorrect / (n - 1) * 100.0;
+        if (n < 1) return 0.0;
+        return (double)nCorrect / n;
       }
     }
@@ -51,19 +50,33 @@
     }
 
-    public void Add(double original, double estimated) {
-      if (double.IsNaN(estimated) || double.IsInfinity(estimated) || double.IsNaN(original) || double.IsInfinity(original) || (errorState & OnlineCalculatorError.InvalidValueAdded) > 0) {
+    public void Add(double startValue, IEnumerable<double> actualContinuation, IEnumerable<double> predictedContinuation) {
+      if (double.IsNaN(startValue) || (errorState & OnlineCalculatorError.InvalidValueAdded) > 0) {
         errorState = errorState | OnlineCalculatorError.InvalidValueAdded;
-      } else if (n == 0) {
-        prevOriginal = original;
-        prevEstimated = estimated;
-        n++;
       } else {
-        if ((original - prevOriginal) * (estimated - prevEstimated) >= 0.0) {
-          nCorrect++;
+        var actualEnumerator = actualContinuation.GetEnumerator();
+        var predictedEnumerator = predictedContinuation.GetEnumerator();
+        double prevActual = startValue;
+        double prevPredicted = startValue;
+        while (actualEnumerator.MoveNext() & predictedEnumerator.MoveNext() & errorState != OnlineCalculatorError.InvalidValueAdded) {
+          double actual = actualEnumerator.Current;
+          double predicted = predictedEnumerator.Current;
+          if (double.IsNaN(actual) || double.IsNaN(predicted)) {
+            errorState = errorState | OnlineCalculatorError.InvalidValueAdded;
+          } else {
+            // count a prediction correct if the trend (positive/negative/no change) is predicted correctly
+            if ((actual - prevActual) * (predicted - prevPredicted) > 0.0 ||
+                (actual - prevActual).IsAlmost(predicted - prevPredicted)
+              ) {
+              nCorrect++;
+            }
+            n++;
+          }
         }
-        n++;
-        errorState = errorState & (~OnlineCalculatorError.InsufficientElementsAdded);        // n >= 1
-        prevOriginal = original;
-        prevEstimated = estimated;
+        // check if both enumerators are at the end to make sure both enumerations have the same length
+        if (actualEnumerator.MoveNext() || predictedEnumerator.MoveNext()) {
+          errorState = errorState | OnlineCalculatorError.InvalidValueAdded;
+        } else {
+          errorState = errorState & (~OnlineCalculatorError.InsufficientElementsAdded); // n >= 1
+        }
       }
     }
@@ -72,32 +85,24 @@
       n = 0;
       nCorrect = 0;
-      prevOriginal = double.NaN;
-      prevEstimated = double.NaN;
       errorState = OnlineCalculatorError.InsufficientElementsAdded;
     }
 
 
-    public static double Calculate(IEnumerable<double> originalValues, IEnumerable<double> estimatedValues, out OnlineCalculatorError errorState) {
-      IEnumerator<double> originalEnumerator = originalValues.GetEnumerator();
-      IEnumerator<double> estimatedEnumerator = estimatedValues.GetEnumerator();
+    public static double Calculate(IEnumerable<double> startValues, IEnumerable<IEnumerable<double>> actualContinuations, IEnumerable<IEnumerable<double>> predictedContinuations, out OnlineCalculatorError errorState) {
+      IEnumerator<double> startValueEnumerator = startValues.GetEnumerator();
+      IEnumerator<IEnumerable<double>> actualContinuationsEnumerator = actualContinuations.GetEnumerator();
+      IEnumerator<IEnumerable<double>> predictedContinuationsEnumerator = predictedContinuations.GetEnumerator();
       OnlineDirectionalSymmetryCalculator dsCalculator = new OnlineDirectionalSymmetryCalculator();
 
-      // add first element of time series as a reference point
-      originalEnumerator.MoveNext();
-      estimatedEnumerator.MoveNext();
-      dsCalculator.Add(originalEnumerator.Current, estimatedEnumerator.Current);
-      
-      // always move forward both enumerators (do not use short-circuit evaluation!)
-      while (originalEnumerator.MoveNext() & estimatedEnumerator.MoveNext()) {
-        double original = originalEnumerator.Current;
-        double estimated = estimatedEnumerator.Current;
-        dsCalculator.Add(original, estimated);
+      // always move forward all enumerators (do not use short-circuit evaluation!)
+      while (startValueEnumerator.MoveNext() & actualContinuationsEnumerator.MoveNext() & predictedContinuationsEnumerator.MoveNext()) {
+        dsCalculator.Add(startValueEnumerator.Current, actualContinuationsEnumerator.Current, predictedContinuationsEnumerator.Current);
         if (dsCalculator.ErrorState != OnlineCalculatorError.None) break;
       }
 
-      // check if both enumerators are at the end to make sure both enumerations have the same length
+      // check if all enumerators are at the end to make sure both enumerations have the same length
       if (dsCalculator.ErrorState == OnlineCalculatorError.None &&
-          (originalEnumerator.MoveNext() || estimatedEnumerator.MoveNext())) {
-        throw new ArgumentException("Number of elements in originalValues and estimatedValues enumerations doesn't match.");
+          (startValueEnumerator.MoveNext() || actualContinuationsEnumerator.MoveNext() || predictedContinuationsEnumerator.MoveNext())) {
+        throw new ArgumentException("Number of elements in startValues, actualContinuations and estimatedValues predictedContinuations doesn't match.");
       } else {
         errorState = dsCalculator.ErrorState;
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/OnlineCalculators/OnlineTheilsUStatisticCalculator.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/OnlineCalculators/OnlineTheilsUStatisticCalculator.cs	(revision 6964)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/OnlineCalculators/OnlineTheilsUStatisticCalculator.cs	(revision 6974)
@@ -22,11 +22,10 @@
 using System;
 using System.Collections.Generic;
+using HeuristicLab.Common;
 
 namespace HeuristicLab.Problems.DataAnalysis {
-  public class OnlineTheilsUStatisticCalculator : IOnlineCalculator {
+  public class OnlineTheilsUStatisticCalculator : IOnlineTimeSeriesCalculator {
     private OnlineMeanAndVarianceCalculator squaredErrorMeanCalculator;
     private OnlineMeanAndVarianceCalculator unbiasedEstimatorMeanCalculator;
-    private double prevOriginal;
-    private int n;
 
     public double TheilsUStatistic {
@@ -52,27 +51,34 @@
     }
 
-    public void Add(double original, double estimated) {
-      if (double.IsNaN(estimated) || double.IsInfinity(estimated) || double.IsNaN(original) || double.IsInfinity(original) || (errorState & OnlineCalculatorError.InvalidValueAdded) > 0) {
+    public void Add(double startValue, IEnumerable<double> actualContinuation, IEnumerable<double> predictedContinuation) {
+      if (double.IsNaN(startValue) || (errorState & OnlineCalculatorError.InvalidValueAdded) > 0) {
         errorState = errorState | OnlineCalculatorError.InvalidValueAdded;
-      } else if (n == 0) {
-        prevOriginal = original;
-        n++;
       } else {
-        // error of predicted change
-        double errorEstimatedChange = (estimated - original);
-        squaredErrorMeanCalculator.Add(errorEstimatedChange * errorEstimatedChange);
+        var actualEnumerator = actualContinuation.GetEnumerator();
+        var predictedEnumerator = predictedContinuation.GetEnumerator();
+        while (actualEnumerator.MoveNext() & predictedEnumerator.MoveNext() & ErrorState != OnlineCalculatorError.InvalidValueAdded) {
+          double actual = actualEnumerator.Current;
+          double predicted = predictedEnumerator.Current;
+          if (double.IsNaN(actual) || double.IsNaN(predicted)) {
+            errorState = errorState | OnlineCalculatorError.InvalidValueAdded;
+          } else {
+            // error of predicted change
+            double errorPredictedChange = (predicted - startValue) - (actual - startValue);
+            squaredErrorMeanCalculator.Add(errorPredictedChange * errorPredictedChange);
 
-        double errorNoChange = (original - prevOriginal);
-        unbiasedEstimatorMeanCalculator.Add(errorNoChange * errorNoChange);
-        errorState = errorState & (~OnlineCalculatorError.InsufficientElementsAdded);        // n >= 1
-        prevOriginal = original;
-        n++;
+            double errorNoChange = (actual - startValue);
+            unbiasedEstimatorMeanCalculator.Add(errorNoChange * errorNoChange);
+          }
+        }
+        // check if both enumerators are at the end to make sure both enumerations have the same length
+        if (actualEnumerator.MoveNext() || predictedEnumerator.MoveNext()) {
+          errorState = errorState | OnlineCalculatorError.InvalidValueAdded;
+        } else {
+          errorState = errorState & (~OnlineCalculatorError.InsufficientElementsAdded); // n >= 1
+        }
       }
     }
 
-
     public void Reset() {
-      prevOriginal = double.NaN;
-      n = 0;
       squaredErrorMeanCalculator.Reset();
       unbiasedEstimatorMeanCalculator.Reset();
@@ -82,26 +88,20 @@
     #endregion
 
-    public static double Calculate(IEnumerable<double> originalValues, IEnumerable<double> estimatedValues, out OnlineCalculatorError errorState) {
-      IEnumerator<double> originalValuesEnumerator = originalValues.GetEnumerator();
-      IEnumerator<double> estimatedValuesEnumerator = estimatedValues.GetEnumerator();
+    public static double Calculate(IEnumerable<double> startValues, IEnumerable<IEnumerable<double>> actualContinuations, IEnumerable<IEnumerable<double>> predictedContinuations, out OnlineCalculatorError errorState) {
+      IEnumerator<double> startValueEnumerator = startValues.GetEnumerator();
+      IEnumerator<IEnumerable<double>> actualContinuationsEnumerator = actualContinuations.GetEnumerator();
+      IEnumerator<IEnumerable<double>> predictedContinuationsEnumerator = predictedContinuations.GetEnumerator();
       OnlineTheilsUStatisticCalculator calculator = new OnlineTheilsUStatisticCalculator();
 
-      // add first element of time series as a reference point
-      originalValuesEnumerator.MoveNext();
-      estimatedValuesEnumerator.MoveNext();
-      calculator.Add(originalValuesEnumerator.Current, estimatedValuesEnumerator.Current);
-
-      // always move forward both enumerators (do not use short-circuit evaluation!)
-      while (originalValuesEnumerator.MoveNext() & estimatedValuesEnumerator.MoveNext()) {
-        double estimated = estimatedValuesEnumerator.Current;
-        double original = originalValuesEnumerator.Current;
-        calculator.Add(original, estimated);
+      // always move forward all enumerators (do not use short-circuit evaluation!)
+      while (startValueEnumerator.MoveNext() & actualContinuationsEnumerator.MoveNext() & predictedContinuationsEnumerator.MoveNext()) {
+        calculator.Add(startValueEnumerator.Current, actualContinuationsEnumerator.Current, predictedContinuationsEnumerator.Current);
         if (calculator.ErrorState != OnlineCalculatorError.None) break;
       }
 
-      // check if both enumerators are at the end to make sure both enumerations have the same length
+      // check if all enumerators are at the end to make sure both enumerations have the same length
       if (calculator.ErrorState == OnlineCalculatorError.None &&
-          (estimatedValuesEnumerator.MoveNext() || originalValuesEnumerator.MoveNext())) {
-        throw new ArgumentException("Number of elements in originalValues and estimatedValues enumerations doesn't match.");
+          (startValueEnumerator.MoveNext() || actualContinuationsEnumerator.MoveNext() || predictedContinuationsEnumerator.MoveNext())) {
+        throw new ArgumentException("Number of elements in startValues, actualContinuations and estimatedValues predictedContinuations doesn't match.");
       } else {
         errorState = calculator.ErrorState;
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/OnlineCalculators/OnlineWeightedDirectionalSymmetryCalculator.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/OnlineCalculators/OnlineWeightedDirectionalSymmetryCalculator.cs	(revision 6964)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/OnlineCalculators/OnlineWeightedDirectionalSymmetryCalculator.cs	(revision 6974)
@@ -22,8 +22,9 @@
 using System;
 using System.Collections.Generic;
+using HeuristicLab.Common;
 
 
 namespace HeuristicLab.Problems.DataAnalysis {
-  public class OnlineWeightedDirectionalSymmetryCalculator : IOnlineCalculator {
+  public class OnlineWeightedDirectionalSymmetryCalculator : IOnlineTimeSeriesCalculator {
     private double prevEstimated;
     private double prevOriginal;
@@ -52,22 +53,33 @@
     }
 
-    public void Add(double original, double estimated) {
-      if (double.IsNaN(estimated) || double.IsInfinity(estimated) || double.IsNaN(original) || double.IsInfinity(original) || (errorState & OnlineCalculatorError.InvalidValueAdded) > 0) {
+    public void Add(double startValue, IEnumerable<double> actualContinuation, IEnumerable<double> predictedContinuation) {
+      if (double.IsNaN(startValue) || (errorState & OnlineCalculatorError.InvalidValueAdded) > 0) {
         errorState = errorState | OnlineCalculatorError.InvalidValueAdded;
-      } else if (n == 0) {
-        prevOriginal = original;
-        prevEstimated = estimated;
-        n++;
       } else {
-        double err = Math.Abs(original - estimated);
-        if ((original - prevOriginal) * (estimated - prevEstimated) >= 0.0) {
-          correctSum += err;
+        var actualEnumerator = actualContinuation.GetEnumerator();
+        var predictedEnumerator = predictedContinuation.GetEnumerator();
+        while (actualEnumerator.MoveNext() & predictedEnumerator.MoveNext() & errorState != OnlineCalculatorError.InvalidValueAdded) {
+          double actual = actualEnumerator.Current;
+          double predicted = predictedEnumerator.Current;
+          if (double.IsNaN(actual) || double.IsNaN(predicted)) {
+            errorState = errorState | OnlineCalculatorError.InvalidValueAdded;
+          } else {
+            double err = Math.Abs(actual - predicted);
+            // count as correct only if the trend (positive/negative/no change) is predicted correctly
+            if ((actual - startValue) * (predicted - startValue) > 0.0 ||
+              (actual - startValue).IsAlmost(predicted - startValue)) {
+              correctSum += err;
+            } else {
+              incorrectSum += err;
+            }
+            n++;
+          }
+        }
+        // check if both enumerators are at the end to make sure both enumerations have the same length
+        if (actualEnumerator.MoveNext() || predictedEnumerator.MoveNext()) {
+          errorState = errorState | OnlineCalculatorError.InvalidValueAdded;
         } else {
-          incorrectSum += err;
+          errorState = errorState & (~OnlineCalculatorError.InsufficientElementsAdded); // n >= 1
         }
-        n++;
-        errorState = errorState & (~OnlineCalculatorError.InsufficientElementsAdded);        // n >= 1
-        prevOriginal = original;
-        prevEstimated = estimated;
       }
     }
@@ -83,29 +95,23 @@
 
 
-    public static double Calculate(IEnumerable<double> originalValues, IEnumerable<double> estimatedValues, out OnlineCalculatorError errorState) {
-      IEnumerator<double> originalEnumerator = originalValues.GetEnumerator();
-      IEnumerator<double> estimatedEnumerator = estimatedValues.GetEnumerator();
-      OnlineWeightedDirectionalSymmetryCalculator dsCalculator = new OnlineWeightedDirectionalSymmetryCalculator();
+    public static double Calculate(IEnumerable<double> startValues, IEnumerable<IEnumerable<double>> actualContinuations, IEnumerable<IEnumerable<double>> predictedContinuations, out OnlineCalculatorError errorState) {
+      IEnumerator<double> startValueEnumerator = startValues.GetEnumerator();
+      IEnumerator<IEnumerable<double>> actualContinuationsEnumerator = actualContinuations.GetEnumerator();
+      IEnumerator<IEnumerable<double>> predictedContinuationsEnumerator = predictedContinuations.GetEnumerator();
+      OnlineWeightedDirectionalSymmetryCalculator calculator = new OnlineWeightedDirectionalSymmetryCalculator();
 
-      // add first element of time series as a reference point
-      originalEnumerator.MoveNext();
-      estimatedEnumerator.MoveNext();
-      dsCalculator.Add(originalEnumerator.Current, estimatedEnumerator.Current);
-
-      // always move forward both enumerators (do not use short-circuit evaluation!)
-      while (originalEnumerator.MoveNext() & estimatedEnumerator.MoveNext()) {
-        double original = originalEnumerator.Current;
-        double estimated = estimatedEnumerator.Current;
-        dsCalculator.Add(original, estimated);
-        if (dsCalculator.ErrorState != OnlineCalculatorError.None) break;
+      // always move forward all enumerators (do not use short-circuit evaluation!)
+      while (startValueEnumerator.MoveNext() & actualContinuationsEnumerator.MoveNext() & predictedContinuationsEnumerator.MoveNext()) {
+        calculator.Add(startValueEnumerator.Current, actualContinuationsEnumerator.Current, predictedContinuationsEnumerator.Current);
+        if (calculator.ErrorState != OnlineCalculatorError.None) break;
       }
 
-      // check if both enumerators are at the end to make sure both enumerations have the same length
-      if (dsCalculator.ErrorState == OnlineCalculatorError.None &&
-          (originalEnumerator.MoveNext() || estimatedEnumerator.MoveNext())) {
-        throw new ArgumentException("Number of elements in originalValues and estimatedValues enumerations doesn't match.");
+      // check if all enumerators are at the end to make sure both enumerations have the same length
+      if (calculator.ErrorState == OnlineCalculatorError.None &&
+          (startValueEnumerator.MoveNext() || actualContinuationsEnumerator.MoveNext() || predictedContinuationsEnumerator.MoveNext())) {
+        throw new ArgumentException("Number of elements in startValues, actualContinuations and estimatedValues predictedContinuations doesn't match.");
       } else {
-        errorState = dsCalculator.ErrorState;
-        return dsCalculator.WeightedDirectionalSymmetry;
+        errorState = calculator.ErrorState;
+        return calculator.WeightedDirectionalSymmetry;
       }
     }
Index: trunk/sources/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis-3.4/StatisticCalculatorsTest.cs
===================================================================
--- trunk/sources/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis-3.4/StatisticCalculatorsTest.cs	(revision 6964)
+++ trunk/sources/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis-3.4/StatisticCalculatorsTest.cs	(revision 6974)
@@ -20,4 +20,5 @@
 #endregion
 
+using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -140,4 +141,105 @@
       }
     }
+
+    [TestMethod]
+    public void CalculateDirectionalSymmetryTest() {
+      // delta: +0.01, +1, -0.01, -2, -0.01, -1, +0.01, +2
+      var original = new double[]
+                       {
+                         0, 
+                         0.01, 
+                         1.01, 
+                         1, 
+                         -1, 
+                         -1.01, 
+                         -2.01, 
+                         -2, 
+                         0
+                       };
+      // delta to original(t-1): +1, +0, -1, -0, -1, +0.01, +0.01, +2
+      var estimated = new double[]
+                        {
+                          -1, 
+                          1, 
+                          0.01,
+                          0.01,
+                          1,
+                          -1,
+                          -1.02,
+                          -2.02,
+                          0
+                        };
+
+      // one-step forecast
+      var startValues = original;
+      var actualContinuations = from x in original.Skip(1)
+                                select Enumerable.Repeat(x, 1);
+      var predictedContinuations = from x in estimated.Skip(1)
+                                   select Enumerable.Repeat(x, 1);
+      double expected = 0.5;  // half of the predicted deltas are correct
+      OnlineCalculatorError errorState;
+      double actual = OnlineDirectionalSymmetryCalculator.Calculate(startValues, actualContinuations, predictedContinuations, out errorState);
+      Assert.AreEqual(expected, actual, 1E-9);
+    }
+    [TestMethod]
+    public void CalculateWeightedDirectionalSymmetryTest() {
+      var original = new double[] { 0, 0.01, 1.01, 1, -1, -1.01, -2.01, -2, 0 }; // +0.01, +1, -0.01, -2, -0.01, -1, +0.01, +2
+      var estimated = new double[] { 1, 2, 2, 1, 1, 0, 0.01, 0.02, 2.02 }; // delta to original: +2, +1.99, -0.01, 0, +1, -1.02, +2.01, +4.02
+      // one-step forecast
+      var startValues = original;
+      var actualContinuations = from x in original.Skip(1)
+                                select Enumerable.Repeat(x, 1);
+      var predictedContinuations = from x in estimated.Skip(1)
+                                   select Enumerable.Repeat(x, 1);
+      // absolute errors = 1.99, 0.99, 0, 2, 1.01, 2.02, 2.02, 2.02     
+      // sum of absolute errors for correctly predicted deltas = 2.97
+      // sum of absolute errors for incorrectly predicted deltas = 3.03 
+      double expected = 5.03 / 7.02;
+      OnlineCalculatorError errorState;
+      double actual = OnlineWeightedDirectionalSymmetryCalculator.Calculate(startValues, actualContinuations, predictedContinuations, out errorState);
+      Assert.AreEqual(expected, actual, 1E-9);
+    }
+    [TestMethod]
+    public void CalculateTheilsUTest() {
+      var original = new double[] { 0, 0.01, 1.01, 1, -1, -1.01, -2.01, -2, 0 };
+      var estimated = new double[] { 1, 1.01, 0.01, 2, 0, -0.01, -1.01, -3, 1 };
+      // one-step forecast
+      var startValues = original;
+      var actualContinuations = from x in original.Skip(1)
+                                select Enumerable.Repeat(x, 1);
+      var predictedContinuations = from x in estimated.Skip(1)
+                                   select Enumerable.Repeat(x, 1);
+      // Sum of squared errors of model y(t+1) = y(t) = 10.0004
+      // Sum of squared errors of predicted values = 8  
+      double expected = Math.Sqrt(8 / 10.0004);
+      OnlineCalculatorError errorState;
+      double actual = OnlineTheilsUStatisticCalculator.Calculate(startValues, actualContinuations, predictedContinuations, out errorState);
+      Assert.AreEqual(expected, actual, 1E-9);
+    }
+    [TestMethod]
+    public void CalculateAccuracyTest() {
+      var original = new double[] { 1, 1, 0, 0 };
+      var estimated = new double[] { 1, 0, 1, 0 };
+      double expected = 0.5;
+      OnlineCalculatorError errorState;
+      double actual = OnlineAccuracyCalculator.Calculate(original, estimated, out errorState);
+      Assert.AreEqual(expected, actual, 1E-9);
+    }
+
+    [TestMethod]
+    public void CalculateMeanAbsolutePercentageErrorTest() {
+      var original = new double[] { 1, 2, 3, 1, 5 };
+      var estimated = new double[] { 2, 1, 3, 1, 0 };
+      double expected = 0.5;
+      OnlineCalculatorError errorState;
+      double actual = OnlineMeanAbsolutePercentageErrorCalculator.Calculate(original, estimated, out errorState);
+      Assert.AreEqual(expected, actual, 1E-9);
+      Assert.AreEqual(OnlineCalculatorError.None, errorState);
+
+      // if the original contains zero values the result is not defined
+      var original2 = new double[] { 1, 2, 0, 0, 0 };
+      OnlineMeanAbsolutePercentageErrorCalculator.Calculate(original2, estimated, out errorState);
+      Assert.AreEqual(OnlineCalculatorError.InvalidValueAdded, errorState);
+    }
   }
 }
