Index: branches/HeuristicLab.TimeSeries/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis-3.4/StatisticCalculatorsTest.cs
===================================================================
--- branches/HeuristicLab.TimeSeries/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis-3.4/StatisticCalculatorsTest.cs	(revision 7098)
+++ branches/HeuristicLab.TimeSeries/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis-3.4/StatisticCalculatorsTest.cs	(revision 7099)
@@ -20,4 +20,5 @@
 #endregion
 
+using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -139,4 +140,245 @@
       }
     }
+
+    [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 CalculateMultiStepDirectionalSymmetryTest() {
+      // 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 };
+      {
+        var estimated = new double[][]
+                          {
+                            new double[] {0.01, 1.01, 1, -1, -1.01},
+                            new double[] {1.01, 1, -1, -1.01, -2.01},
+                            new double[] {1, -1, -1.01, -2.01, -2},
+                            new double[] {-1, -1.01, -2.01, -2, 0}
+                          };
+
+        // 5-step forecast
+        var startValues = original.Take(4);
+        var actualContinuations = from i in Enumerable.Range(1, original.Count() - 5)
+                                  select original.Skip(i).Take(5);
+        var predictedContinuations = estimated;
+        double expected = 1; // predictions are 100% correct
+        OnlineCalculatorError errorState;
+        double actual = OnlineDirectionalSymmetryCalculator.Calculate(startValues, actualContinuations,
+                                                                      predictedContinuations, out errorState);
+        Assert.AreEqual(expected, actual, 1E-9);
+      }
+      {
+        // only the direction is relevant
+        var estimated = new double[][]
+                          {
+                            new double[] {0.01, 0.01, 0.01, -0.01, -0.01},  // start=0, original deltas: 0.01, 1.01, 1.00, -1.00, -1.01
+                            new double[] {0.02, 0.02, 0.00, 0.00, 0.00}, // start=0.01, original deltas: 1.00, 0.90, -1.01, -1.02, -2.02,
+                            new double[] { 1.00, 1.00, 1.00, 1.00, 1.00}, // start=1.01, original deltas: -0.01, -2.01, -2.02, -3.02, -3.01
+                            new double[] { 0.90, 0.90, 0.90, 0.90, 0.90}  // start=1, original deltas: -2.00, -0.01, -3.01, -3.00, -1.00
+                          };
+
+        // 5-step forecast
+        var startValues = original.Take(4);
+        var actualContinuations = from i in Enumerable.Range(1, original.Count() - 5)
+                                  select original.Skip(i).Take(5);
+        var predictedContinuations = estimated;
+        double expected = 1; // half of the predicted deltas are correct
+        OnlineCalculatorError errorState;
+        double actual = OnlineDirectionalSymmetryCalculator.Calculate(startValues, actualContinuations,
+                                                                      predictedContinuations, out errorState);
+        Assert.AreEqual(expected, actual, 1E-9);
+      }
+      {
+        // also check incorrectly predicted directions
+        var estimated = new double[][]
+                          {
+                            new double[] {0.01, 0.01, 0.01, +0.01, +0.01},  // start=0, original deltas: 0.01, 1.01, 1.00, -1.00, -1.01
+                            new double[] {0.02, 0.00, 0.02, 0.00, 0.00}, // start=0.01, original deltas: 1.00, 0.90, -1.01, -1.02, -2.02,
+                            new double[] { 1.02, 1.00, 1.02, 1.00, 1.02}, // start=1.01, original deltas: -0.01, -2.01, -2.02, -3.02, -3.01
+                            new double[] { 0.90, 0.90, 0.90, 0.90, 0.90}  // start=1, original deltas: -2.00, -0.01, -3.01, -3.00, -1.00
+                          };
+
+        // 5-step forecast
+        var startValues = original.Take(4);
+        var actualContinuations = from i in Enumerable.Range(1, original.Count() - 5)
+                                  select original.Skip(i).Take(5);
+        var predictedContinuations = estimated;
+        double expected = (20 - 7) / 20.0; // 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 CalculateMultiStepTheilsUTest() {
+      var original = new double[] { 0, 0.01, 1.01, 1, -1, -1.01, -2.01, -2, 0 };
+      {
+        // prefect prediction
+        var estimated = new double[][]
+                          {
+                            new double[] {0.01, 1.01, 1, -1, -1.01},
+                            new double[] {1.01, 1, -1, -1.01, -2.01},
+                            new double[] {1, -1, -1.01, -2.01, -2},
+                            new double[] {-1, -1.01, -2.01, -2, 0}
+                          };
+        // 5-step forecast
+        var startValues = original.Take(4);
+        var actualContinuations = from i in Enumerable.Range(1, original.Count() - 5)
+                                  select original.Skip(i).Take(5);
+        var predictedContinuations = estimated;
+
+        double expected = 0;
+        OnlineCalculatorError errorState;
+        double actual = OnlineTheilsUStatisticCalculator.Calculate(startValues, actualContinuations,
+                                                                   predictedContinuations, out errorState);
+        Assert.AreEqual(expected, actual, 1E-9);
+      }
+      {
+        // naive prediction
+        var estimated = new double[][]
+                          {
+                            new double[] {0, 0, 0, 0, 0},
+                            new double[] {0.01, 0.01, 0.01, 0.01, 0.01},
+                            new double[] {1.01, 1.01, 1.01, 1.01, 1.01},
+                            new double[] {1, 1, 1, 1, 1}
+                          };
+        // 5-step forecast
+        var startValues = original.Take(4);
+        var actualContinuations = from i in Enumerable.Range(1, original.Count() - 5)
+                                  select original.Skip(i).Take(5);
+        var predictedContinuations = estimated;
+
+        double expected = 1;
+        OnlineCalculatorError errorState;
+        double actual = OnlineTheilsUStatisticCalculator.Calculate(startValues, actualContinuations,
+                                                                   predictedContinuations, out errorState);
+        Assert.AreEqual(expected, actual, 1E-9);
+      }
+      {
+        // realistic prediction
+        var estimated = new double[][]
+                          {
+                            new double[] {0.005, 0.5, 0.5, -0.5, -0.5},    // start = 0
+                            new double[] {0.60, 0.5, -0.5, -0.5, -1},   // start = 0.01
+                            new double[] {-0.005, 0, 0, -0.5, -1},     // start = 1.01
+                            new double[] {-0, 0, -0.5, -1, 0.5}      // start = 1
+                          };
+        // 5-step forecast
+        var startValues = original.Take(4);
+        var actualContinuations = from i in Enumerable.Range(1, original.Count() - 5)
+                                  select original.Skip(i).Take(5);
+        var predictedContinuations = estimated;
+
+        double expected = 0.47558387;
+        OnlineCalculatorError errorState;
+        double actual = OnlineTheilsUStatisticCalculator.Calculate(startValues, actualContinuations,
+                                                                   predictedContinuations, out errorState);
+        Assert.AreEqual(expected, actual, 1E-6);
+      }
+    }
+
+    [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);
+    }
   }
 }
