Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Algorithms/DirectedWalk.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Algorithms/DirectedWalk.cs	(revision 16136)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Algorithms/DirectedWalk.cs	(revision 16137)
@@ -29,4 +29,7 @@
   /// A generic implementation of a directed walk that can still be used in an efficient manner.
   /// </summary>
+  /// <remarks>
+  /// The algorithm is only implemented for use in the API
+  /// </remarks>
   /// <typeparam name="T">The type of the solution encoding.</typeparam>
   public static class DirectedWalk<T> where T : IDeepCloneable {
Index: anches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Algorithms/FLACharacterizer.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Algorithms/FLACharacterizer.cs	(revision 16136)
+++ 	(revision )
@@ -1,87 +1,0 @@
-﻿#region License Information
-/* HeuristicLab
- * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
- *
- * This file is part of HeuristicLab.
- *
- * HeuristicLab is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * HeuristicLab is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
- */
-#endregion
-
-using System;
-using System.Threading;
-using HeuristicLab.Common;
-using HeuristicLab.Core;
-using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
-using HeuristicLab.Optimization;
-using HeuristicLab.Parameters;
-
-namespace HeuristicLab.Analysis.FitnessLandscape {
-  [Item("FLA Characterizer", "An algorithm that executes a characteristic calculator.")]
-  [StorableClass]
-  [Creatable(CreatableAttribute.Categories.TestingAndAnalysis + CreatableAttribute.Categories.SplitToken + "1" + CreatableAttribute.Categories.OrderToken + "FLA", Priority = 300)]
-  public sealed class FLACharacterizer : BasicAlgorithm {
-    public override bool SupportsPause { get { return false; } }
-    
-    public IValueParameter<ICharacteristicCalculator> CalculatorParameter {
-      get { return (IValueParameter<ICharacteristicCalculator>)Parameters["Calculator"]; }
-    }
-
-    public ICharacteristicCalculator Calculator {
-      get { return CalculatorParameter.Value; }
-    }
-
-    [StorableConstructor]
-    private FLACharacterizer(bool deserializing) : base(deserializing) { }
-    private FLACharacterizer(FLACharacterizer original, Cloner cloner)
-      : base(original, cloner) {
-      RegisterEventHandlers();
-    }
-    public FLACharacterizer() : base() {
-      Parameters.Add(new ValueParameter<ICharacteristicCalculator>("Calculator", "The FLA characteristic calculator.", new RandomWalkCalculator()));
-      RegisterEventHandlers();
-    }
-
-    public override IDeepCloneable Clone(Cloner cloner) {
-      return new FLACharacterizer(this, cloner);
-    }
-    
-    [StorableHook(HookType.AfterDeserialization)]
-    private void AfterDeserialization() {
-      RegisterEventHandlers();
-    }
-
-    private void RegisterEventHandlers() {
-      CalculatorParameter.ValueChanged += CalculatorParameterOnValueChanged;
-    }
-
-    private void CalculatorParameterOnValueChanged(object sender, EventArgs e) {
-      Calculator.Problem = Problem;
-    }
-
-    protected override void OnProblemChanged() {
-      base.OnProblemChanged();
-      Calculator.Problem = Problem;
-    }
-
-    protected override void Run(CancellationToken cancellationToken) {
-      var calculator = Calculator;
-      if (calculator.CanCalculate()) {
-        foreach (var result in calculator.Calculate()) {
-          Results.Add(result);
-        }
-      } else throw new InvalidOperationException("Calculator cannot be applied.");
-    }
-  }
-}
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/InformationAnalysis.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/InformationAnalysis.cs	(revision 16136)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/InformationAnalysis.cs	(revision 16137)
@@ -38,7 +38,10 @@
 
     public List<double> InformationContent { get; private set; }
+    public List<double> SymmetricInformationContent { get; private set; }
     public List<double> PartialInformationContent { get; private set; }
     public List<double> DensityBasinInformation { get; private set; }
+    public List<double> SymmetricDensityBasinInformation { get; private set; }
     public List<double> TotalEntropy { get; private set; }
+    public List<double> SymmetricTotalEntropy { get; private set; }
     public List<double> QualityDelta { get; private set; }
     public double InformationStability { get; private set; }
@@ -46,21 +49,24 @@
     public int Diversity { get; private set; }
     public Peak PeakInformationContent { get; private set; }
-    public Peak PeakPartialInformationContent { get; private set; }
+    public Peak PeakSymmetricInformationContent { get; private set; }
     public Peak PeakDensityBasinInformation { get; private set; }
+    public Peak PeakSymmetricDensityBasinInformation { get; private set; }
     public Peak PeakTotalEntropy { get; private set; }
-
-    public InformationAnalysis(IList<double> qualities, int nQuantiles, int shapeSize) {
-      InformationContent = new List<double>();
-      PartialInformationContent = new List<double>();
-      DensityBasinInformation = new List<double>();
-      TotalEntropy = new List<double>();
-      QualityDelta = new List<double>();
-      if (shapeSize < 1)
-        throw new ArgumentException("Shape size must be at least 1 (better 2)");
+    public Peak PeakSymmetricTotalEntropy { get; private set; }
+
+    public InformationAnalysis(IList<double> qualities, int nQuantiles = 20) {
+      InformationContent = new List<double>(nQuantiles);
+      SymmetricInformationContent = new List<double>(nQuantiles);
+      PartialInformationContent = new List<double>(nQuantiles);
+      DensityBasinInformation = new List<double>(nQuantiles);
+      SymmetricDensityBasinInformation = new List<double>(nQuantiles);
+      TotalEntropy = new List<double>(nQuantiles);
+      SymmetricTotalEntropy = new List<double>(nQuantiles);
+      QualityDelta = new List<double>(nQuantiles);
       if (qualities.Count > 1)
-        PerformAnalysis(qualities, nQuantiles, shapeSize);
-    }
-
-    private void PerformAnalysis(IList<double> qualities, int nQuantiles, int shapeSize) {
+        PerformAnalysis(qualities, nQuantiles);
+    }
+
+    private void PerformAnalysis(IList<double> qualities, int nQuantiles) {
       var differences = Differences(qualities).ToList();
       InformationStability = differences.Select(Math.Abs).Max();
@@ -80,18 +86,36 @@
           PartialInformationContent.DuplicateLast();
         } else {
-          var slopes = Slopes(eps, differences).ToList();
-          var shapes = Shapes(shapeSize, slopes).ToList();
-          var shapeCounts = CountShapes(shapes);
+          var slopes = ToSlopes(eps, differences).ToList();
+          var shapes = ToShapes(slopes).ToList();
+          var symmetricShapes = shapes.Select(x => Shape.GetSymmetric(x)).ToList();
+
+          var shapeFreqs = GetFrequencies(shapes);
+          var symShapeFreqs = GetFrequencies(symmetricShapes);
+
+          foreach (var symShape in Shape.Symmetric.Where(x => x.Sum > 0)) {
+            if (symShapeFreqs.TryGetValue(symShape, out var freq)) {
+              symShapeFreqs[symShape] = freq / 2;
+            }
+          }
           QualityDelta.Add(eps);
-          InformationContent.Add(CalculateEntropy(Shape.GetAll(shapeSize, Shape.Form.NonUni), shapeCounts, shapes.Count));
-          DensityBasinInformation.Add(CalculateEntropy(Shape.GetAll(shapeSize, Shape.Form.Uni), shapeCounts, shapes.Count));
-          TotalEntropy.Add(CalculateEntropy(Shape.GetAll(shapeSize, Shape.Form.Any), shapeCounts, shapes.Count));
+
+          InformationContent.Add(CalculateEntropy(shapeFreqs, Shape.NonUniform.ToList(), shapes.Count));
+          SymmetricInformationContent.Add(CalculateEntropy(symShapeFreqs, Shape.SymmetricNonUniform.ToList(), symmetricShapes.Count));
+
+          DensityBasinInformation.Add(CalculateEntropy(shapeFreqs, Shape.Uniform.ToList(), shapes.Count));
+          SymmetricDensityBasinInformation.Add(CalculateEntropy(symShapeFreqs, Shape.SymmetricUniform.ToList(), symmetricShapes.Count));
+
+          TotalEntropy.Add(CalculateEntropy(shapeFreqs, Shape.All.ToList(), shapes.Count));
+          SymmetricTotalEntropy.Add(CalculateEntropy(symShapeFreqs, Shape.Symmetric.ToList(), symmetricShapes.Count));
+
           PartialInformationContent.Add(CalculatePartialInformationContent(eps, differences));
         }
       }
       PeakInformationContent = GetPeak(QualityDelta, InformationContent);
+      PeakSymmetricInformationContent = GetPeak(QualityDelta, SymmetricInformationContent);
       PeakDensityBasinInformation = GetPeak(QualityDelta, DensityBasinInformation);
-      PeakPartialInformationContent = GetPeak(QualityDelta, PartialInformationContent);
+      PeakSymmetricDensityBasinInformation = GetPeak(QualityDelta, SymmetricDensityBasinInformation);
       PeakTotalEntropy = GetPeak(QualityDelta, TotalEntropy);
+      PeakSymmetricTotalEntropy = GetPeak(QualityDelta, SymmetricTotalEntropy);
     }
 
@@ -107,135 +131,87 @@
     }
 
-    public class Shape : IEnumerable<Slope>, IComparable<Shape> {
-
-      #region types, fields and properties
-      public enum Form { Uni, NonUni, Any }
-
-      private readonly Slope[] slopes;
-
-      private static readonly Dictionary<Tuple<Form, int>, IList<Shape>> SHAPES =
-        new Dictionary<Tuple<Form, int>, IList<Shape>>();
-      #endregion
-
-      public Shape(IEnumerable<Slope> slopes) {
-        this.slopes = slopes.ToArray();
-      }
-
-      #region static methods
-
-      public static Shape Get(params Slope[] slopes) {
-        return new Shape(slopes);
-      }
-
-      public static IList<Shape> GetAll(int size, Form type) {
-        var key = Tuple.Create(type, size);
-        IList<Shape> allShapes;
-        if (!SHAPES.TryGetValue(key, out allShapes)) {
-          allShapes = CreateAll(size, type).ToList();
-          SHAPES[key] = allShapes;
+    public class Shape {
+      private static readonly Shape[] s = new Shape[] {
+        new Shape() { First = Slope.Down, Second = Slope.Down }, // 0
+        new Shape() { First = Slope.Down, Second = Slope.Flat }, // 1
+        new Shape() { First = Slope.Down, Second = Slope.Up   }, // 2
+        new Shape() { First = Slope.Flat, Second = Slope.Down }, // 3
+        new Shape() { First = Slope.Flat, Second = Slope.Flat }, // 4
+        new Shape() { First = Slope.Flat, Second = Slope.Up   }, // 5
+        new Shape() { First = Slope.Up,   Second = Slope.Down }, // 6
+        new Shape() { First = Slope.Up,   Second = Slope.Flat }, // 7
+        new Shape() { First = Slope.Up,   Second = Slope.Up   }, // 8
+      };
+      public static IEnumerable<Shape> All { get { return s; } }
+      public static IEnumerable<Shape> Uniform { get { return All.Where(x => x.First == x.Second); } }
+      public static IEnumerable<Shape> NonUniform { get { return All.Where(x => x.First != x.Second); } }
+      public static IEnumerable<Shape> Symmetric { get { return All.Where(x => x.Sum >= 0); } }
+      public static IEnumerable<Shape> SymmetricUniform { get { return Uniform.Where(x => x.First != Slope.Down); } }
+      public static IEnumerable<Shape> SymmetricNonUniform { get { return NonUniform.Where(x => x.Sum >= 0); } }
+
+      public Slope First { get; private set; }
+      public Slope Second { get; private set; }
+
+      internal int Sum { get { return (int)First + (int)Second; } }
+
+      public IEnumerable<Slope> Slopes {
+        get {
+          yield return First;
+          yield return Second;
         }
-        return allShapes;
-      }
-
-      private static IEnumerable<Shape> CreateAll(int size, Form type) {
-        if (size == 0) {
-          yield return Get();
-        } else {
-          foreach (var s in CreateAll(size - 1, type)) {
-            foreach (var s2 in s.ExtendAll(type))
-              yield return s2;
-          }
-        }
-      }
-      #endregion
-
-      private Shape Extend(Slope s) {
-        return new Shape(slopes.Concat(new[] { s }));
-      }
-
-      private IEnumerable<Shape> ExtendAll(Form t) {
-        if (Length == 0 || t == Form.Any) {
-          yield return Extend(Slope.Up);
-          yield return Extend(Slope.Flat);
-          yield return Extend(Slope.Down);
-        } else if (t == Form.Uni) {
-          yield return Extend(slopes[0]);
-        } else if (t == Form.NonUni) {
-          if (slopes.Last() != Slope.Up) yield return Extend(Slope.Up);
-          if (slopes.Last() != Slope.Flat) yield return Extend(Slope.Flat);
-          if (slopes.Last() != Slope.Down) yield return Extend(Slope.Down);
-        }
-      }
-
-      public int Length {
-        get { return slopes.Length; }
-      }
-
-      public Slope this[int i] {
-        get { return slopes[i]; }
-      }
-
-      #region IEnumerable Members
-      public IEnumerator<Slope> GetEnumerator() {
-        return (IEnumerator<Slope>)slopes.GetEnumerator();
-      }
-      System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
-        return GetEnumerator();
-      }
-      #endregion
-
-      #region IComparable Members
-
-      public int CompareTo(Shape other) {
-        if (other.Length < Length)
-          return -1;
-        if (other.Length > Length)
-          return 1;
-        for (var i = 0; i < Length; i++) {
-          var d = slopes[i].CompareTo(other.slopes[i]);
-          if (d != 0)
-            return d;
-        }
-        return 0;
-      }
-      public override bool Equals(object obj) {
-        var s = obj as Shape;
-        if (s == null) return false;
-        return CompareTo(s) == 0;
-      }
-      public override int GetHashCode() {
-        return slopes.Aggregate(0, (a, s) => a * 3 + ((int)s + 1)).GetHashCode();
-      }
-
-      #endregion
-
-      private string asString;
+      }
+
+      private Shape() {
+
+      }
+
+      public static Shape Get(Slope first, Slope second) {
+        var a = 1 + (int)first; var b = 1 + (int)second;
+        return s[a * 3 + b];
+      }
+
+      public static Shape GetSymmetric(Shape shape) {
+        if (shape.Sum >= 0) return shape;
+        if (shape.Sum == -2) return s[8]; // both are Slope.Down
+        if (shape.First == Slope.Down) return s[5];
+        return s[7]; // shape.Second == Slope.Down
+      }
+
       public override string ToString() {
-        return asString ?? (asString = string.Join("", slopes.Select(s => (s == Slope.Down ? "\\" : (s == Slope.Up ? "/" : "-")))));
-      }
-    }
-
-    private static IEnumerable<Slope> Slopes(double eps, IEnumerable<double> differences) {
+        return string.Join("", Slopes.Select(s => (s == Slope.Down ? "\\" : (s == Slope.Up ? "/" : "-"))));
+      }
+    }
+
+    
+
+    private static IEnumerable<Slope> ToSlopes(double eps, IEnumerable<double> differences) {
       return differences.Select(d => (d > eps ? Slope.Up : (d < -eps ? Slope.Down : Slope.Flat)));
     }
 
-    private static IEnumerable<Shape> Shapes(int size, IEnumerable<Slope> slopes) {
-      var q = new Queue<Slope>();
-      foreach (var s in slopes) {
-        q.Enqueue(s);
-        if (q.Count < size) continue;
-        yield return new Shape(q);
-        q.Dequeue();
-      }
-    }
-
-    private static double CalculateEntropy(IList<Shape> shapes, Dictionary<Shape, int> shapeCounts, int totalNShapes) {
-      return shapes.Aggregate(0.0, (current, s) => current - Entropy(shapeCounts.GetValueOrDefault(s, 0), totalNShapes, shapes.Count));
+    private static IEnumerable<Shape> ToShapes(IEnumerable<Slope> slopes) {
+      var iter = slopes.GetEnumerator();
+      if (!iter.MoveNext()) yield break;
+      var prev = iter.Current;
+      while (iter.MoveNext()) {
+        var cur = iter.Current;
+        yield return Shape.Get(prev, cur);
+        prev = cur;
+      }
+    }
+
+    private static double CalculateEntropy(Dictionary<Shape, int> shapeCounts, ICollection<Shape> shapeTypes, int totalShapes) {
+      return shapeTypes.Where(x => shapeCounts.ContainsKey(x))
+        .Aggregate(0.0, (current, s) => current - Entropy(shapeCounts[s], totalShapes, shapeTypes.Count));
     }
 
     private static double CalculatePartialInformationContent(double eps, ICollection<double> differences) {
-      int slope = 0;
-      int nPeaks = 0;
-      foreach (var d in differences) {
+      var iter = differences.GetEnumerator();
+      if (!iter.MoveNext()) return 0;
+
+      var slope = iter.Current < -eps ? -1 : (iter.Current > eps ? 1 : 0);
+      var nPeaks = Math.Abs(slope);
+      var count = 1;
+      while (iter.MoveNext()) {
+        var d = iter.Current;
         if (d > eps) {
           if (slope < 0) nPeaks++;
@@ -245,19 +221,16 @@
           slope = -1;
         }
-      }
-      return 1.0 * nPeaks / differences.Count;
-    }
-
-    private static Dictionary<Shape, int> CountShapes(IEnumerable<Shape> shapes) {
-      var shapeCounts = new Dictionary<Shape, int>();
-      foreach (var group in shapes.GroupBy(s => s))
-        shapeCounts[group.Key] = group.Count();
-      return shapeCounts;
+        count++;
+      }
+      return nPeaks / (double)count;
+    }
+
+    private static Dictionary<Shape, int> GetFrequencies(IEnumerable<Shape> shapes) {
+      return shapes.GroupBy(x => x).ToDictionary(x => x.Key, x => x.Count());
     }
 
     private static double Entropy(int count, int total, int nCases) {
-      if (count == 0)
-        return 0;
-      double freq = 1.0 * count / total;
+      if (count == 0) return 0;
+      var freq = 1.0 * count / total;
       return freq * Math.Log(freq, nCases);
     }
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/InformationAnalyzer.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/InformationAnalyzer.cs	(revision 16136)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/InformationAnalyzer.cs	(revision 16137)
@@ -91,5 +91,4 @@
 
       Parameters.Add(new ValueLookupParameter<IntValue>("NQuantiles", "The number of delta quantiles to display", new IntValue(20)));
-      Parameters.Add(new ValueLookupParameter<IntValue>("ShapeSize", "The number of slopes to consider as shapes.", new IntValue(2)));
 
       Parameters.Add(new LookupParameter<DoubleValue>("InformationContent", "The information content H(0) at eps = 0"));
@@ -118,8 +117,7 @@
 
       var nQuantiles = NQuantilesParameter.ActualValue.Value;
-      var shapeSize = ShapeSizeParameter.ActualValue.Value;
       var results = ResultsParameter.ActualValue;
 
-      var analysis = new InformationAnalysis(qualities, nQuantiles, shapeSize);
+      var analysis = new InformationAnalysis(qualities, nQuantiles);
       var ic = new DoubleValue(analysis.InformationContent.FirstOrDefault());
       InformationContentParameter.ActualValue = ic;
Index: anches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/UniqueThresholdCalculator.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/UniqueThresholdCalculator.cs	(revision 16136)
+++ 	(revision )
@@ -1,179 +1,0 @@
-﻿#region License Information
-/* HeuristicLab
- * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
- *
- * This file is part of HeuristicLab.
- *
- * HeuristicLab is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * HeuristicLab is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
- */
-#endregion
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-
-namespace HeuristicLab.Analysis.FitnessLandscape {
-
-  public static class UniqueThresholdCalculator {
-
-    private sealed class IndexRange {
-
-      public int Start, End, N;
-      public readonly bool Merge;
-      private readonly List<IndexRange> receivers, distributors;
-
-      public IndexRange(int start, int end, int n, bool merge) {
-        Start = start;
-        End = end;
-        N = n;
-        Merge = merge;
-        receivers = new List<IndexRange>();
-        distributors = new List<IndexRange>();
-      }
-
-      public void AddReceiver(IndexRange receiver) {
-        if (receiver != null) {
-          receivers.Add(receiver);
-          receiver.distributors.Add(this);
-        }
-      }
-
-      public void Distribute() {
-        int n = N - 1;
-        Debug.Assert(n >= 0);
-        switch (receivers.Count) {
-          case 0: break; // No receivers indexes are lost :-(
-          case 1: receivers[0].N += n; N = 1; break;
-          case 2: receivers[0].N += n / 2; receivers[1].N += n - n / 2; N = 1; break;
-          default: throw new InvalidOperationException("More than two receivers registered");
-        }
-        var followers = distributors.Where(d => d.Start > End).ToList();
-        if (followers.Count > 0)
-          End = followers.Min(g => g.Start);
-        var predecessors = distributors.Where(d => d.End < Start).ToList();
-        if (predecessors.Count > 0)
-          Start = predecessors.Max(g => g.End);
-      }
-
-      public override string ToString() {
-        return string.Format("[{0}-{1}, n={2}, {3}]", Start, End, N, Merge ? "Merge" : "Spread");
-      }
-    }
-
-    public static IEnumerable<double> DetermineThresholds(IEnumerable<double> differences, int n) {
-      var thresholds = differences.Select(d => Math.Abs(d)).ToList();
-      thresholds.Sort();
-      if (thresholds.Count <= n) {
-        foreach (var t in thresholds)
-          yield return t;
-      } else {
-        double min = thresholds[0];
-        double max = thresholds[thresholds.Count - 1];
-        if (min == max) {
-          yield return min;
-        } else {
-          yield return min;
-          foreach (var t in DetermineInnerThresholds(thresholds, n - 2))
-            yield return t;
-          yield return max;
-        }
-      }
-    }
-
-    private static IEnumerable<double> DetermineInnerThresholds(List<double> thresholds, int n) {
-      n = Math.Min(thresholds.Count - 2, n);
-      var indexes = Enumerable.Range(0, n + 2).Select(i => (int)Math.Round(1.0 * i * (thresholds.Count - 1) / (n + 1))).ToList();
-      var ranges = CreateRanges(thresholds, indexes);
-      RedistributeIndexes(ranges);
-      TrimBoundaryRanges(ranges);
-      if (ranges.Count == 1 && ranges[0].Start == 0 && ranges[0].End == thresholds.Count - 1 && ranges[0].N == n) {
-        if (ranges[0].Merge)
-          yield return thresholds[ranges[0].Start];
-        else
-          foreach (var i in indexes.Where(i => ranges[0].Start < i && i < ranges[0].End))
-            yield return thresholds[i];
-      } else {
-        foreach (var range in ranges) {
-          if (range.Merge)
-            yield return thresholds[range.Start];
-          else
-            foreach (var t in DetermineInnerThresholds(thresholds.GetRange(range.Start, range.End - range.Start + 1), range.N))
-              yield return t;
-        }
-      }
-    }
-
-    private static void TrimBoundaryRanges(List<IndexRange> ranges) {
-      if (ranges.Count > 0)
-        if (ranges[0].Merge)
-          ranges.RemoveAt(0);
-        else
-          ranges[0].N--;
-      if (ranges.Count > 0)
-        if (ranges[ranges.Count - 1].Merge)
-          ranges.RemoveAt(ranges.Count - 1);
-        else
-          ranges[ranges.Count - 1].N--;
-    }
-
-    private static void RedistributeIndexes(List<IndexRange> ranges) {
-      IndexRange lastReceiver = null;
-      List<IndexRange> lastDistributors = new List<IndexRange>();
-      foreach (var range in ranges) {
-        if (range.Merge) {
-          range.AddReceiver(lastReceiver);
-          lastDistributors.Add(range);
-        } else {
-          foreach (var d in lastDistributors)
-            d.AddReceiver(range);
-          lastDistributors = new List<IndexRange>();
-          lastReceiver = range;
-        }
-      }
-      foreach (var range in ranges) {
-        range.Distribute();
-      }
-    }
-
-    private static List<IndexRange> CreateRanges(List<double> thresholds, List<int> indexes) {
-      var values = indexes.Select(i => new { i, v = thresholds[i] }).ToList();
-      var mergedRanges = values.GroupBy(iv => iv.v)
-        .Select(g => g.Count() == 1 ?
-          new IndexRange(g.First().i, g.First().i, 1, false) :
-          new IndexRange(g.Min(iv => iv.i), g.Max(iv => iv.i), g.Count(), true)).ToList();
-      IndexRange lastSpreadRange = null;
-      var ranges = new List<IndexRange>();
-      foreach (var group in mergedRanges) {
-        if (group.Merge) {
-          if (lastSpreadRange != null) {
-            ranges.Add(lastSpreadRange);
-            lastSpreadRange = null;
-          }
-          ranges.Add(group);
-        } else {
-          if (lastSpreadRange != null) {
-            lastSpreadRange.End = group.End;
-            lastSpreadRange.N++;
-          } else {
-            lastSpreadRange = group;
-          }
-        }
-      }
-      if (lastSpreadRange != null)
-        ranges.Add(lastSpreadRange);
-      return ranges;
-    }
-  }
-}
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/AdaptiveWalkCalculator.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/AdaptiveWalkCalculator.cs	(revision 16136)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/AdaptiveWalkCalculator.cs	(revision 16137)
@@ -20,4 +20,6 @@
 #endregion
 
+using System.Collections.Generic;
+using System.Linq;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -25,42 +27,16 @@
 using HeuristicLab.Optimization;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
 
 namespace HeuristicLab.Analysis.FitnessLandscape {
   [Item("Adaptive Walk Calculator", "Calculates characteristics from an adaptive walk.")]
   [StorableClass]
-  public class AdaptiveWalkCalculator : NamedItem, ICharacteristicCalculator {
-
-    [Storable]
-    private IProblem problem;
-    public IProblem Problem {
-      get { return problem; }
-      set {
-        if (problem == value) return;
-        problem = value;
-        var soProblem = problem as ISingleObjectiveHeuristicOptimizationProblem;
-        walker.Problem = soProblem;
-      }
-    }
-
-    [Storable]
-    private AdaptiveWalk walker;
+  public sealed class AdaptiveWalkCalculator : AlgorithmCharacteristicCalculator<AdaptiveWalk> {
 
     [StorableConstructor]
     private AdaptiveWalkCalculator(bool deserializing) : base(deserializing) { }
     private AdaptiveWalkCalculator(AdaptiveWalkCalculator original, Cloner cloner)
-      : base(original, cloner) {
-      problem = cloner.Clone(original.problem);
-      walker = cloner.Clone(original.walker);
-      characteristics = cloner.Clone(original.characteristics);
-    }
+      : base(original, cloner) { }
     public AdaptiveWalkCalculator() : this(new AdaptiveWalk()) { }
-    public AdaptiveWalkCalculator(AdaptiveWalk walker) {
-      Name = ItemName;
-      Description = ItemDescription;
-      this.walker = walker;
+    public AdaptiveWalkCalculator(AdaptiveWalk walker) : base(walker) {
       characteristics = new CheckedItemList<StringValue>(
         new[] { "AutoCorrelation1", "CorrelationLength", "InformationContent",
@@ -74,38 +50,11 @@
     }
 
-    private CheckedItemList<StringValue> characteristics;
-    public ReadOnlyCheckedItemList<StringValue> Characteristics {
-      get { return characteristics.AsReadOnly(); }
+    public override bool CanCalculate() {
+      return base.CanCalculate() && Problem.Operators.Any(x => x is IManipulator);
     }
 
-    public bool CanCalculate() {
-      return Problem is ISingleObjectiveHeuristicOptimizationProblem
-        && Problem.Operators.Any(x => x is IManipulator);
-    }
-
-    public IEnumerable<IResult> Calculate() {
-      walker.Prepare(true);
-      using (var waitHandle = new AutoResetEvent(false)) {
-        EventHandler evHandle = (sender, e) => {
-          if (walker.ExecutionState == ExecutionState.Paused
-          || walker.ExecutionState == ExecutionState.Stopped) waitHandle.Set();
-        };
-        walker.ExecutionStateChanged += evHandle;
-        walker.Start();
-        waitHandle.WaitOne();
-        walker.ExecutionStateChanged -= evHandle;
-      }
-      foreach (var p in characteristics.CheckedItems) {
-        yield return new Result("AdaptiveWalk." + walker.MutatorParameter.Value.Name + "." + p.Value.Value, walker.Results[p.Value.Value].Value);
-      }
-      walker.Prepare(true);
-    }
-
-    public void CollectParameterValues(IDictionary<string, IItem> values) {
-      walker.CollectParameterValues(values);
-    }
-
-    public IKeyedItemCollection<string, IParameter> Parameters {
-      get { return ((IParameterizedItem)walker).Parameters; }
+    public override IEnumerable<IResult> Calculate() {
+      foreach (var result in base.Calculate())
+        yield return new Result("AdaptiveWalk." + Algorithm.MutatorParameter.Value.Name + "." + result.Name, result.Value);
     }
   }
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/AlgorithmCharacteristicCalculator.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/AlgorithmCharacteristicCalculator.cs	(revision 16137)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/AlgorithmCharacteristicCalculator.cs	(revision 16137)
@@ -0,0 +1,79 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Collections.Generic;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Analysis.FitnessLandscape {
+  [StorableClass]
+  public abstract class AlgorithmCharacteristicCalculator<TAlgorithm> : CharacteristicCalculator
+    where TAlgorithm : class, IAlgorithm {
+
+    public override IProblem Problem {
+      get { return base.Problem; }
+      set {
+        base.Problem = value;
+        Algorithm.Problem = value;
+      }
+    }
+
+    [Storable]
+    private ValueParameter<TAlgorithm> algorithmParameter;
+    public IValueParameter<TAlgorithm> AlgorithmParameter { get { return algorithmParameter; } }
+    public TAlgorithm Algorithm {
+      get { return algorithmParameter.Value; }
+      set { algorithmParameter.Value = value; }
+    }
+    
+    [StorableConstructor]
+    protected AlgorithmCharacteristicCalculator(bool deserializing) : base(deserializing) { }
+    protected AlgorithmCharacteristicCalculator(AlgorithmCharacteristicCalculator<TAlgorithm> original, Cloner cloner)
+    : base(original, cloner) {
+      algorithmParameter = cloner.Clone(original.algorithmParameter);
+    }
+    protected AlgorithmCharacteristicCalculator() {
+      Parameters.Add(algorithmParameter = new ValueParameter<TAlgorithm>("Algorithm", "The algorithm that calculates characteristics."));
+    }
+    protected AlgorithmCharacteristicCalculator(TAlgorithm algorithm) {
+      Parameters.Add(algorithmParameter = new ValueParameter<TAlgorithm>("Algorithm", "The algorithm that calculates characteristics.", algorithm));
+    }
+
+    public override bool CanCalculate() {
+      return Algorithm != null
+        && Problem != null
+        && Algorithm.ProblemType.IsAssignableFrom(Problem.GetType());
+    }
+
+    public override IEnumerable<IResult> Calculate() {
+      Algorithm.Prepare(true);
+      Algorithm.Start();
+      
+      foreach (var p in characteristics.CheckedItems) {
+        yield return new Result(p.Value.Value, Algorithm.Results[p.Value.Value].Value);
+      }
+      Algorithm.Prepare(true);
+    }
+  }
+}
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/CharacteristicCalculator.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/CharacteristicCalculator.cs	(revision 16137)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/CharacteristicCalculator.cs	(revision 16137)
@@ -0,0 +1,63 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Collections.Generic;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Optimization;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Analysis.FitnessLandscape {
+  [Item("Characteristic Calculator", "")]
+  [StorableClass]
+  public abstract class CharacteristicCalculator : ParameterizedNamedItem, ICharacteristicCalculator {
+
+    [Storable]
+    private IProblem problem;
+    public virtual IProblem Problem {
+      get { return problem; }
+      set { problem = value; }
+    }
+
+    [Storable]
+    protected CheckedItemList<StringValue> characteristics;
+    public ReadOnlyCheckedItemList<StringValue> Characteristics { get { return characteristics.AsReadOnly(); } }
+
+    [StorableConstructor]
+    protected CharacteristicCalculator(bool deserializing) : base(deserializing) { }
+    protected CharacteristicCalculator(CharacteristicCalculator original, Cloner cloner)
+      : base(original, cloner) {
+      characteristics = cloner.Clone(original.characteristics);
+      problem = cloner.Clone(original.problem);
+    }
+    protected CharacteristicCalculator()
+      : base() {
+      name = ItemName;
+      description = ItemDescription;
+      characteristics = new CheckedItemList<StringValue>();
+    }
+
+    public abstract bool CanCalculate();
+
+    public abstract IEnumerable<IResult> Calculate();
+  }
+}
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/FLACharacterizer.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/FLACharacterizer.cs	(revision 16137)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/FLACharacterizer.cs	(revision 16137)
@@ -0,0 +1,87 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Threading;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+
+namespace HeuristicLab.Analysis.FitnessLandscape {
+  [Item("FLA Characterizer", "An algorithm that executes a characteristic calculator.")]
+  [StorableClass]
+  [Creatable(CreatableAttribute.Categories.TestingAndAnalysis + CreatableAttribute.Categories.SplitToken + "1" + CreatableAttribute.Categories.OrderToken + "FLA", Priority = 300)]
+  public sealed class FLACharacterizer : BasicAlgorithm {
+    public override bool SupportsPause { get { return false; } }
+    
+    public IValueParameter<ICharacteristicCalculator> CalculatorParameter {
+      get { return (IValueParameter<ICharacteristicCalculator>)Parameters["Calculator"]; }
+    }
+
+    public ICharacteristicCalculator Calculator {
+      get { return CalculatorParameter.Value; }
+    }
+
+    [StorableConstructor]
+    private FLACharacterizer(bool deserializing) : base(deserializing) { }
+    private FLACharacterizer(FLACharacterizer original, Cloner cloner)
+      : base(original, cloner) {
+      RegisterEventHandlers();
+    }
+    public FLACharacterizer() : base() {
+      Parameters.Add(new ValueParameter<ICharacteristicCalculator>("Calculator", "The FLA characteristic calculator.", new RandomWalkCalculator()));
+      RegisterEventHandlers();
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new FLACharacterizer(this, cloner);
+    }
+    
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      RegisterEventHandlers();
+    }
+
+    private void RegisterEventHandlers() {
+      CalculatorParameter.ValueChanged += CalculatorParameterOnValueChanged;
+    }
+
+    private void CalculatorParameterOnValueChanged(object sender, EventArgs e) {
+      Calculator.Problem = Problem;
+    }
+
+    protected override void OnProblemChanged() {
+      base.OnProblemChanged();
+      Calculator.Problem = Problem;
+    }
+
+    protected override void Run(CancellationToken cancellationToken) {
+      var calculator = Calculator;
+      if (calculator.CanCalculate()) {
+        foreach (var result in calculator.Calculate()) {
+          Results.Add(result);
+        }
+      } else throw new InvalidOperationException("Calculator cannot be applied.");
+    }
+  }
+}
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/QAP/QAPCharacteristicCalculator.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/QAP/QAPCharacteristicCalculator.cs	(revision 16137)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/QAP/QAPCharacteristicCalculator.cs	(revision 16137)
@@ -0,0 +1,86 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Optimization;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Problems.QuadraticAssignment;
+
+namespace HeuristicLab.Analysis.FitnessLandscape {
+  [Item("QAP Characteristic Calculator", "")]
+  [StorableClass]
+  public sealed class QAPCharacteristicCalculator : CharacteristicCalculator {
+
+    [StorableConstructor]
+    private QAPCharacteristicCalculator(bool deserializing) : base(deserializing) { }
+    private QAPCharacteristicCalculator(QAPCharacteristicCalculator original, Cloner cloner) : base(original, cloner) { }
+    public QAPCharacteristicCalculator() {
+      characteristics.AddRange(new[] { "Dimension",
+        "FlowDominance", "DistanceDominance",
+        "FlowAsymmetry", "DistanceAsymmetry",
+        "FlowSparsity", "DistanceSparsity",
+        "FlowSkewness", "DistanceSkewness",
+        "FlowDisparity", "DistanceDisparity" }.Select(x => new StringValue(x)).ToList());
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new QAPCharacteristicCalculator(this, cloner);
+    }
+
+    public override bool CanCalculate() {
+      return Problem is QuadraticAssignmentProblem;
+    }
+
+    public override IEnumerable<IResult> Calculate() {
+      var qap = Problem as QuadraticAssignmentProblem;
+      if (qap == null) throw new ArgumentException("Instance is not of type QuadraticAssignmentProblem");
+      foreach (var chara in characteristics.CheckedItems.Select(x => x.Value.Value)) {
+        if (chara == "Dimension")
+          yield return new Result(chara, new IntValue(qap.Weights.Rows));
+        else if (chara == "FlowDominance")
+          yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristics.CoeffVariation(qap.Weights)));
+        else if (chara == "DistanceDominance")
+          yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristics.CoeffVariation(qap.Distances)));
+        else if (chara == "FlowAsymmetry")
+          yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristics.Asymmetry(qap.Weights)));
+        else if (chara == "DistanceAsymmetry")
+          yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristics.Asymmetry(qap.Distances)));
+        else if (chara == "FlowSparsity")
+          yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristics.Sparsity(qap.Weights)));
+        else if (chara == "DistanceSparsity")
+          yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristics.Sparsity(qap.Distances)));
+        else if (chara == "FlowSkewness")
+          yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristics.Skewness(qap.Weights)));
+        else if (chara == "DistanceSkewness")
+          yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristics.Skewness(qap.Distances)));
+        else if (chara == "FlowDisparity")
+          yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristics.Disparity(qap.Weights)));
+        else if (chara == "DistanceDisparity")
+          yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristics.Disparity(qap.Distances)));
+      }
+    }
+  }
+}
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/QAP/QAPDirectedWalk.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/QAP/QAPDirectedWalk.cs	(revision 16137)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/QAP/QAPDirectedWalk.cs	(revision 16137)
@@ -0,0 +1,305 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Encodings.PermutationEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Problems.QuadraticAssignment;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Analysis.FitnessLandscape {
+  [Item("Directed Walk (QAP-specific)", "")]
+  [StorableClass]
+  public class QAPDirectedWalk : CharacteristicCalculator {
+
+    public enum WalkType { RandomRandom, RandomGlobal, RandomLocal, LocalLocal, LocalGlobal, LocalInverse }
+
+    private const string NBHOOD_STR = "Swap2";
+    
+    public IFixedValueParameter<IntValue> PathsParameter {
+      get { return (IFixedValueParameter<IntValue>)Parameters["Paths"]; }
+    }
+
+    public IFixedValueParameter<BoolValue> BestImprovementParameter {
+      get { return (IFixedValueParameter<BoolValue>)Parameters["BestImprovement"]; }
+    }
+
+    public IValueParameter<IntValue> SeedParameter {
+      get { return (IValueParameter<IntValue>)Parameters["Seed"]; }
+    }
+
+    public IFixedValueParameter<EnumValue<WalkType>> TypeParameter {
+      get { return (IFixedValueParameter<EnumValue<WalkType>>)Parameters["Type"]; }
+    }
+
+    public int Paths {
+      get { return PathsParameter.Value.Value; }
+      set { PathsParameter.Value.Value = value; }
+    }
+
+    public bool BestImprovement {
+      get { return BestImprovementParameter.Value.Value; }
+      set { BestImprovementParameter.Value.Value = value; }
+    }
+
+    public int? Seed {
+      get { return SeedParameter.Value != null ? SeedParameter.Value.Value : (int?)null; }
+      set { SeedParameter.Value = value.HasValue ? new IntValue(value.Value) : null; }
+    }
+
+    public WalkType Type {
+      get { return TypeParameter.Value.Value; }
+      set { TypeParameter.Value.Value = value;}
+    }
+
+    [StorableConstructor]
+    private QAPDirectedWalk(bool deserializing) : base(deserializing) { }
+    private QAPDirectedWalk(QAPDirectedWalk original, Cloner cloner) : base(original, cloner) { }
+    public QAPDirectedWalk() {
+      characteristics.AddRange(CurveAnalysisResult.AllFeatures.Select(x => new StringValue(x.ToString())));
+      characteristics.AddRange(new[] { "AutoCorrelation1", "CorrelationLength", "InformationContent",
+        "DensityBasinInformation", "PartialInformationContent", "InformationStability",
+        "Diversity", "Regularity", "TotalEntropy", "PeakInformationContent",
+        "PeakDensityBasinInformation" }.Select(x => new StringValue(x)));
+
+      Parameters.Add(new FixedValueParameter<IntValue>("Paths", "The number of paths to explore (a path is a set of solutions that connect two randomly chosen solutions).", new IntValue(50)));
+      Parameters.Add(new FixedValueParameter<BoolValue>("BestImprovement", "Whether the best of all alternatives should be chosen for each step in the path or just the first improving (least degrading) move should be made.", new BoolValue(true)));
+      Parameters.Add(new OptionalValueParameter<IntValue>("Seed", "The seed for the random number generator."));
+      Parameters.Add(new FixedValueParameter<EnumValue<WalkType>>("Type", "Type of directed walk to perfom.", new EnumValue<WalkType>(WalkType.RandomRandom)));
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new QAPDirectedWalk(this, cloner);
+    }
+
+    public override bool CanCalculate() {
+      return Problem is QuadraticAssignmentProblem;
+    }
+
+    private double _evaluatedSolutions;
+
+    public override IEnumerable<IResult> Calculate() {
+      _evaluatedSolutions = 0;
+      var permutations = CalculateRelinkingPoints();
+
+      var trajectories = Run(permutations).ToList();
+      var result = CurveAnalysis<Permutation>.GetCharacteristics(trajectories, (left, right) => (1.0 - HammingSimilarityCalculator.CalculateSimilarity(left, right)) * left.Length);
+
+      #region Calculate Features
+      double avgAc1 = 0, avgCorLen = 0, avgIc = 0, avgDbi = 0, avgPic = 0, avgIS = 0, avgDiv = 0,
+        avgReg = 0, avgEnt = 0, avgPkIc = 0, avgPkDbi = 0;
+      int count = 0;
+      foreach (var t in trajectories) {
+        var trail = t.Select(x => x.Item2).ToArray();
+        if (trail.Length < 4) continue;
+        count++;
+        double[] acf;
+        var len = RuggednessCalculator.CalculateCorrelationLength(trail, out acf);
+        avgAc1 += acf[1];
+        avgCorLen += len;
+        var analysis = new InformationAnalysis(trail);
+        avgIc += analysis.InformationContent[0];
+        avgDbi += analysis.DensityBasinInformation[0];
+        avgPic += analysis.PartialInformationContent[0];
+        avgIS += analysis.InformationStability;
+        avgDiv += analysis.Diversity;
+        avgReg += analysis.Regularity;
+        avgEnt += analysis.TotalEntropy[0];
+        avgPkIc += analysis.PeakInformationContent.Value;
+        avgPkDbi += analysis.PeakDensityBasinInformation.Value;
+      }
+      avgAc1 /= count;
+      avgCorLen /= count;
+      avgIc /= count;
+      avgDbi /= count;
+      avgPic /= count;
+      avgIS /= count;
+      avgDiv /= count;
+      avgReg /= count;
+      avgEnt /= count;
+      avgPkIc /= count;
+      avgPkDbi /= count;
+      var isResults = new Dictionary<string, double>() {
+        { "AutoCorrelation1", avgAc1 },
+        { "CorrelationLength", avgCorLen },
+        { "InformationContent", avgIc },
+        { "DensityBasinInformation", avgDbi },
+        { "PartialInformationContent", avgPic },
+        { "InformationStability", avgIS },
+        { "Diversity", avgDiv },
+        { "Regularity", avgReg },
+        { "TotalEntropy", avgEnt },
+        { "PeakInformationContent", avgPkIc },
+        { "PeakDensityBasinInformation", avgPkDbi }
+      };
+      #endregion
+
+      foreach (var chara in characteristics.CheckedItems.Select(x => x.Value.Value)) {
+        if (Enum.TryParse<CurveAnalysisFeature>(chara, out var caf))
+          yield return new Result(Type.ToString() + "-DW." + NBHOOD_STR + "." + chara, new DoubleValue(result.GetValue(caf)));
+        else yield return new Result(Type.ToString() + "-DW." + NBHOOD_STR + "." + chara, new DoubleValue(isResults[chara]));
+      }
+      yield return new Result("EvaluatedSolutions", new IntValue((int)Math.Round(_evaluatedSolutions)));
+    }
+
+    private IEnumerable<Permutation> CalculateRelinkingPoints() {
+      IRandom random = Seed.HasValue ? new MersenneTwister((uint)Seed.Value) : new MersenneTwister();
+      var qap = (QuadraticAssignmentProblem)Problem;
+      var pathCount = Paths;
+
+      var perm = new Permutation(PermutationTypes.Absolute, qap.Weights.Rows, random);
+      var startLocal = Type == WalkType.LocalGlobal || Type == WalkType.LocalLocal;
+      var targetLocal = Type == WalkType.LocalLocal || Type == WalkType.RandomLocal;
+      var targetGlobal = Type == WalkType.LocalGlobal || Type == WalkType.RandomGlobal;
+      var inverseWalk = Type == WalkType.LocalInverse;
+
+      if (Type == WalkType.LocalInverse) pathCount--; // inverse walks equal one walk per solution
+      for (var i = 0; i <= pathCount; i++) {
+        var start = i % 2 == 0;
+        var target = i % 2 == 1;
+        if (inverseWalk || start && startLocal) {
+          perm = new Permutation(PermutationTypes.Absolute, qap.Weights.Rows, random);
+          var fit = new DoubleValue(QAPEvaluator.Apply(perm, qap.Weights, qap.Distances));
+          _evaluatedSolutions++;
+          var evalSol = new IntValue(0);
+          QAPExhaustiveSwap2LocalImprovement.ImproveFast(perm, qap.Weights, qap.Distances, fit, new IntValue(0), evalSol, qap.Maximization.Value, int.MaxValue, CancellationToken.None);
+          _evaluatedSolutions += evalSol.Value;
+        } else if (target && (targetLocal || targetGlobal)) {
+          if (targetGlobal) {
+            perm = qap.BestKnownSolutions.SampleRandom(random);
+          } else {
+            perm = new Permutation(PermutationTypes.Absolute, qap.Weights.Rows, random);
+            var fit = new DoubleValue(QAPEvaluator.Apply(perm, qap.Weights, qap.Distances));
+            _evaluatedSolutions++;
+            var evalSol = new IntValue(0);
+            QAPExhaustiveSwap2LocalImprovement.ImproveFast(perm, qap.Weights, qap.Distances, fit, new IntValue(0), evalSol, qap.Maximization.Value, int.MaxValue, CancellationToken.None);
+            _evaluatedSolutions += evalSol.Value;
+          }
+        } else { // random
+          BiasedShuffle(perm, random);
+        }
+        yield return (Permutation)perm.Clone();
+      }
+    }
+
+    private IEnumerable<List<Tuple<Permutation, double>>> Run(IEnumerable<Permutation> permutations) {
+      if (Type == WalkType.LocalInverse) return RunInverse(permutations);
+      return RunRegular(permutations);
+    }
+
+    private IEnumerable<List<Tuple<Permutation, double>>> RunInverse(IEnumerable<Permutation> permutations) {
+      var qap = (QuadraticAssignmentProblem)Problem;
+      var min = qap.LowerBound.Value;
+      var max = qap.AverageQuality.Value;
+      var bestImprovement = BestImprovement;
+
+      foreach (var start in permutations) {
+        var startFitness = QAPEvaluator.Apply(start, qap.Weights, qap.Distances);
+        _evaluatedSolutions++;
+
+        // inverse walks are applied to all solutions
+        Func<Tuple<Permutation, double>, IEnumerable<Tuple<Permutation, double>>> inverseNeighborFunc = (p) => RestrictedInverseNeighborhood(qap, p.Item1, p.Item2, start);
+        var walk = DirectedWalk<Permutation>.WalkRestricted(qap.Maximization.Value, inverseNeighborFunc, start, startFitness, !bestImprovement);
+        yield return walk.Select(x => Tuple.Create(x.Item1, (x.Item2 - min) / (max - min))).ToList();
+      } // end paths
+    }
+
+    private IEnumerable<List<Tuple<Permutation, double>>> RunRegular(IEnumerable<Permutation> permutations) {
+      var iter = permutations.GetEnumerator();
+      if (!iter.MoveNext()) yield break;
+      var bestImprovement = BestImprovement;
+
+      var qap = (QuadraticAssignmentProblem)Problem;
+      var min = qap.LowerBound.Value;
+      var max = qap.AverageQuality.Value;
+
+      var start = iter.Current;
+      
+      while (iter.MoveNext()) {
+        var startFitness = QAPEvaluator.Apply(start, qap.Weights, qap.Distances);
+        _evaluatedSolutions++;
+        var end = iter.Current;
+
+        var invSol = new int[start.Length];
+        Func<Tuple<Permutation, double>, IEnumerable<Tuple<Permutation, double>>> regularNeighborFunc = (p) => RestrictedNeighborhood(qap, p.Item1, p.Item2, end, invSol);
+        var walk = DirectedWalk<Permutation>.WalkRestricted(qap.Maximization.Value, regularNeighborFunc, start, startFitness, !bestImprovement);
+
+        var trail = new List<Tuple<Permutation, double>>();
+        foreach (var step in walk) {
+          for (var i = 0; i < invSol.Length; i++)
+            invSol[step.Item1[i]] = i;
+          trail.Add(Tuple.Create(step.Item1, (step.Item2 - min) / (max - min)));
+        }
+        yield return trail;
+
+        start = end;
+      } // end paths
+    }
+
+    private IEnumerable<Tuple<Permutation, double>> RestrictedInverseNeighborhood(QuadraticAssignmentProblem qap, Permutation current, double currentFit, Permutation start) {
+      var evalSolPerMove = 4.0 / current.Length;
+      var N = current.Length;
+      for (var index = 0; index < N; index++) {
+        if (current[index] != start[index]) continue;
+        for (var k = 0; k < N; k++) {
+          if (k == index) continue;
+          var fit = QAPSwap2MoveEvaluator.Apply(current, new Swap2Move(index, k), qap.Weights, qap.Distances);
+          _evaluatedSolutions += evalSolPerMove;
+          current.Swap(index, k);
+          yield return Tuple.Create(current, currentFit + fit);
+          current.Swap(index, k); // reverse
+        }
+      }
+    }
+
+    private IEnumerable<Tuple<Permutation, double>> RestrictedNeighborhood(QuadraticAssignmentProblem qap, Permutation current, double currentFit, Permutation target, int[] inverse) {
+      var evalSolPerMove = 4.0 / current.Length;
+      for (var index = 0; index < current.Length; index++) {
+        if (current[index] == target[index]) continue;
+        var k = inverse[target[index]];
+        var fit = QAPSwap2MoveEvaluator.Apply(current, new Swap2Move(index, k), qap.Weights, qap.Distances);
+        _evaluatedSolutions += evalSolPerMove;
+        current.Swap(index, k);
+        yield return Tuple.Create(current, currentFit + fit);
+        current.Swap(index, k); // reverse
+      }
+    }
+
+    // permutation must be strictly different in every position
+    private static void BiasedShuffle(Permutation p, IRandom random) {
+      for (var i = p.Length - 1; i > 0; i--) {
+        // Swap element "i" with a random earlier element (excluding itself)
+        var swapIndex = random.Next(i);
+        var h = p[swapIndex];
+        p[swapIndex] = p[i];
+        p[i] = h;
+      }
+    }
+  }
+}
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/RandomWalkCalculator.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/RandomWalkCalculator.cs	(revision 16136)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/RandomWalkCalculator.cs	(revision 16137)
@@ -20,4 +20,6 @@
 #endregion
 
+using System.Collections.Generic;
+using System.Linq;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -25,42 +27,17 @@
 using HeuristicLab.Optimization;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
 
 namespace HeuristicLab.Analysis.FitnessLandscape {
   [Item("Random Walk Calculator", "Calculates characteristics from a random walk.")]
   [StorableClass]
-  public class RandomWalkCalculator : NamedItem, ICharacteristicCalculator {
-
-    [Storable]
-    private IProblem problem;
-    public IProblem Problem {
-      get { return problem; }
-      set {
-        if (problem == value) return;
-        problem = value;
-        var soProblem = problem as ISingleObjectiveHeuristicOptimizationProblem;
-        walker.Problem = soProblem;
-      }
-    }
-
-    [Storable]
-    private RandomWalk walker;
+  public sealed class RandomWalkCalculator : AlgorithmCharacteristicCalculator<RandomWalk> {
 
     [StorableConstructor]
     private RandomWalkCalculator(bool deserializing) : base(deserializing) { }
-    private RandomWalkCalculator(RandomWalkCalculator original, Cloner cloner)
-      : base(original, cloner) {
-      problem = cloner.Clone(original.problem);
-      walker = cloner.Clone(original.walker);
-      characteristics = cloner.Clone(original.characteristics);
-    }
+    private RandomWalkCalculator(RandomWalkCalculator original, Cloner cloner) : base(original, cloner) { }
     public RandomWalkCalculator() : this(new RandomWalk()) { }
-    public RandomWalkCalculator(RandomWalk walker) {
+    public RandomWalkCalculator(RandomWalk walker) : base(walker) {
       Name = ItemName;
       Description = ItemDescription;
-      this.walker = walker;
       characteristics = new CheckedItemList<StringValue>(
         new[] { "AutoCorrelation1", "CorrelationLength", "InformationContent",
@@ -74,43 +51,11 @@
     }
 
-    private CheckedItemList<StringValue> characteristics;
-    public ReadOnlyCheckedItemList<StringValue> Characteristics {
-      get { return characteristics.AsReadOnly(); }
+    public override bool CanCalculate() {
+      return base.CanCalculate() && Problem.Operators.Any(x => x is IManipulator);
     }
 
-    public bool CanCalculate() {
-      return Problem is ISingleObjectiveHeuristicOptimizationProblem
-        && Problem.Operators.Any(x => x is IManipulator);
-    }
-
-    public IEnumerable<IResult> Calculate() {
-      walker.Prepare(true);
-      using (var waitHandle = new AutoResetEvent(false)) {
-        EventHandler evHandle = (sender, e) => {
-          if (walker.ExecutionState == ExecutionState.Paused
-          || walker.ExecutionState == ExecutionState.Stopped) waitHandle.Set();
-        };
-        walker.ExecutionStateChanged += evHandle;
-        try {
-          walker.Start();
-          waitHandle.WaitOne();
-        } finally { walker.ExecutionStateChanged -= evHandle; }
-      }
-      foreach (var p in characteristics.CheckedItems) {
-        var resultName = "RandomWalk." + walker.MutatorParameter.Value.Name + "." + p.Value.Value;
-        IResult result;
-        if (walker.Results.TryGetValue(p.Value.Value, out result)) {
-          yield return new Result(resultName, result.Value);
-        } else yield return new Result(resultName, new DoubleValue(0));
-      }
-      walker.Prepare(true);
-    }
-
-    public void CollectParameterValues(IDictionary<string, IItem> values) {
-      walker.CollectParameterValues(values);
-    }
-
-    public IKeyedItemCollection<string, IParameter> Parameters {
-      get { return ((IParameterizedItem)walker).Parameters; }
+    public override IEnumerable<IResult> Calculate() {
+      foreach (var result in base.Calculate())
+        yield return new Result("RandomWalk." + Algorithm.MutatorParameter.Value.Name + "." + result.Name, result.Value);
     }
   }
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/UpDownWalkCalculator.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/UpDownWalkCalculator.cs	(revision 16136)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/CharacteristicCalculator/UpDownWalkCalculator.cs	(revision 16137)
@@ -20,4 +20,6 @@
 #endregion
 
+using System.Collections.Generic;
+using System.Linq;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -25,42 +27,17 @@
 using HeuristicLab.Optimization;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
 
 namespace HeuristicLab.Analysis.FitnessLandscape {
   [Item("Up/Down Walk Calculator", "Calculates characteristics from an up/down walk.")]
   [StorableClass]
-  public class UpDownWalkCalculator : NamedItem, ICharacteristicCalculator {
-
-    [Storable]
-    private IProblem problem;
-    public IProblem Problem {
-      get { return problem; }
-      set {
-        if (problem == value) return;
-        problem = value;
-        var soProblem = problem as ISingleObjectiveHeuristicOptimizationProblem;
-        walker.Problem = soProblem;
-      }
-    }
-
-    [Storable]
-    private UpDownWalk walker;
+  public sealed class UpDownWalkCalculator : AlgorithmCharacteristicCalculator<UpDownWalk> {
 
     [StorableConstructor]
     private UpDownWalkCalculator(bool deserializing) : base(deserializing) { }
-    private UpDownWalkCalculator(UpDownWalkCalculator original, Cloner cloner)
-      : base(original, cloner) {
-      problem = cloner.Clone(original.problem);
-      walker = cloner.Clone(original.walker);
-      characteristics = cloner.Clone(original.characteristics);
-    }
+    private UpDownWalkCalculator(UpDownWalkCalculator original, Cloner cloner) : base(original, cloner) { }
     public UpDownWalkCalculator() : this(new UpDownWalk()) { }
-    public UpDownWalkCalculator(UpDownWalk walker) {
+    public UpDownWalkCalculator(UpDownWalk walker) : base(walker) {
       Name = ItemName;
       Description = ItemDescription;
-      this.walker = walker;
       characteristics = new CheckedItemList<StringValue>(
         new[] { "AutoCorrelation1", "CorrelationLength", "InformationContent",
@@ -75,38 +52,11 @@
     }
 
-    private CheckedItemList<StringValue> characteristics;
-    public ReadOnlyCheckedItemList<StringValue> Characteristics {
-      get { return characteristics.AsReadOnly(); }
+    public override bool CanCalculate() {
+      return base.CanCalculate() && Problem.Operators.Any(x => x is IManipulator);
     }
 
-    public bool CanCalculate() {
-      return Problem is ISingleObjectiveHeuristicOptimizationProblem
-        && Problem.Operators.Any(x => x is IManipulator);
-    }
-
-    public IEnumerable<IResult> Calculate() {
-      walker.Prepare(true);
-      using (var waitHandle = new AutoResetEvent(false)) {
-        EventHandler evHandle = (sender, e) => {
-          if (walker.ExecutionState == ExecutionState.Paused
-          || walker.ExecutionState == ExecutionState.Stopped) waitHandle.Set();
-        };
-        walker.ExecutionStateChanged += evHandle;
-        walker.Start();
-        waitHandle.WaitOne();
-        walker.ExecutionStateChanged -= evHandle;
-      }
-      foreach (var p in characteristics.CheckedItems) {
-        yield return new Result("UpDownWalk." + walker.MutatorParameter.Value.Name + "." + p.Value.Value, walker.Results[p.Value.Value].Value);
-      }
-      walker.Prepare(true);
-    }
-
-    public void CollectParameterValues(IDictionary<string, IItem> values) {
-      walker.CollectParameterValues(values);
-    }
-
-    public IKeyedItemCollection<string, IParameter> Parameters {
-      get { return ((IParameterizedItem)walker).Parameters; }
+    public override IEnumerable<IResult> Calculate() {
+      foreach (var result in base.Calculate())
+        yield return new Result("UpDownWalk." + Algorithm.MutatorParameter.Value.Name + "." + result.Name, result.Value);
     }
   }
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/HeuristicLab.Analysis.FitnessLandscape-3.3.csproj
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/HeuristicLab.Analysis.FitnessLandscape-3.3.csproj	(revision 16136)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/HeuristicLab.Analysis.FitnessLandscape-3.3.csproj	(revision 16137)
@@ -187,5 +187,5 @@
   <ItemGroup>
     <Compile Include="Algorithms\AdaptiveWalk.cs" />
-    <Compile Include="Algorithms\FLACharacterizer.cs" />
+    <Compile Include="CharacteristicCalculator\FLACharacterizer.cs" />
     <Compile Include="Algorithms\UpDownWalk.cs" />
     <Compile Include="Algorithms\RandomWalk.cs" />
@@ -201,15 +201,15 @@
     <Compile Include="Algorithms\LocalAnalysisMainLoop.cs" />
     <Compile Include="Analysis\InformationAnalyzer.cs" />
-    <Compile Include="Analysis\UniqueThresholdCalculator.cs" />
+    <Compile Include="CharacteristicCalculator\AlgorithmCharacteristicCalculator.cs" />
+    <Compile Include="Utilities\UniqueThresholdCalculator.cs" />
     <Compile Include="CharacteristicCalculator\AdaptiveWalkCalculator.cs" />
     <Compile Include="CharacteristicCalculator\UpDownWalkCalculator.cs" />
     <Compile Include="CharacteristicCalculator\RandomWalkCalculator.cs" />
-    <Compile Include="ProblemCharacteristicAnalysis\CharacteristicCalculator.cs" />
-    <Compile Include="ProblemCharacteristicAnalysis\CurveAnalysis.cs" />
+    <Compile Include="CharacteristicCalculator\CharacteristicCalculator.cs" />
+    <Compile Include="Utilities\CurveAnalysis.cs" />
     <Compile Include="Algorithms\DirectedWalk.cs" />
-    <Compile Include="ProblemCharacteristicAnalysis\DoubleMatrixCharacteristicCalculator.cs" />
-    <Compile Include="ProblemCharacteristicAnalysis\PermutationPathAnalysis.cs" />
-    <Compile Include="ProblemCharacteristicAnalysis\QAP\QAPCharacteristicCalculator.cs" />
-    <Compile Include="ProblemCharacteristicAnalysis\QAP\QAPDirectedWalk.cs" />
+    <Compile Include="Utilities\DoubleMatrixCharacteristics.cs" />
+    <Compile Include="CharacteristicCalculator\QAP\QAPCharacteristicCalculator.cs" />
+    <Compile Include="CharacteristicCalculator\QAP\QAPDirectedWalk.cs" />
     <Compile Include="ProblemInstanceAnalysis\DirectedPath.cs" />
     <Compile Include="ProblemInstanceAnalysis\ProblemInstanceAnalyzer.cs" />
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/ProblemInstanceAnalysis/QAPPRProblemInstanceAnalyzer.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/ProblemInstanceAnalysis/QAPPRProblemInstanceAnalyzer.cs	(revision 16136)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/ProblemInstanceAnalysis/QAPPRProblemInstanceAnalyzer.cs	(revision 16137)
@@ -49,5 +49,6 @@
       if (trajectories.Count == 0) return null;
 
-      return new DoubleArray(PermutationPathAnalysis.GetCharacteristics(trajectories).GetValues().ToArray());
+      var result = CurveAnalysis<Permutation>.GetCharacteristics(trajectories, (left, right) => (1.0 - HammingSimilarityCalculator.CalculateSimilarity(left, right)) * left.Length);
+      return new DoubleArray(result.GetValues().ToArray());
     }
   }
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Utilities/CurveAnalysis.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Utilities/CurveAnalysis.cs	(revision 16137)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Utilities/CurveAnalysis.cs	(revision 16137)
@@ -0,0 +1,192 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace HeuristicLab.Analysis.FitnessLandscape {
+  public static class CurveAnalysis<T> {
+    public static CurveAnalysisResult GetCharacteristics(List<List<Tuple<T, double>>> trajectories, Func<T, T, double> distFunc) {
+      trajectories = trajectories.Where(x => x.Count > 5).ToList();
+      if (trajectories.Count == 0) return new CurveAnalysisResult(0, 0, 0, new[] { 0.0, 0.0, 0.0 }, new[] { 0.0, 0.0, 0.0 }, new[] { 0.0, 0.0, 0.0 });
+
+      var symbols = GetSymbols(trajectories);
+      var f1 = trajectories.Select(path => ApproximateDerivative(path, distFunc).ToList()).ToList();
+      var f2 = f1.Select(d1 => ApproximateDerivative(d1, distFunc).ToList()).ToList();
+
+      var sharpness = f1.Average(x => x.Average(y => Math.Abs(y.Item2)));
+      var bumpiness = 0.0;
+      var flatness = 0.0;
+      var count = 0;
+      for (var p = 0; p < f2.Count; p++) {
+        if (f2[p].Count <= 2) continue;
+        count++;
+        var bump = 0;
+        var flat = 0;
+        for (var i = 0; i < f2[p].Count - 1; i++) {
+          if ((f2[p][i].Item2 > 0 && f2[p][i + 1].Item2 < 0) || (f2[p][i].Item2 < 0 && f2[p][i + 1].Item2 > 0)) {
+            bump++;
+          } else if (f2[p][i].Item2 == 0) {
+            flat++;
+          }
+        }
+        bumpiness += bump / (f2[p].Count - 1.0);
+        flatness += flat / (f2[p].Count - 1.0);
+      }
+      bumpiness /= count;
+      flatness /= count;
+      var per = new[] { 25, 50, 75 };
+      return new CurveAnalysisResult(sharpness, bumpiness, flatness,
+        per.Select(p => symbols.Downward.GetPercentileOrDefault(p, 0)).ToArray(),
+        per.Select(p => symbols.Neutral.GetPercentileOrDefault(p, 0)).ToArray(),
+        per.Select(p => symbols.Upward.GetPercentileOrDefault(p, 0)).ToArray());
+    }
+
+    private static Symbols GetSymbols(List<List<Tuple<T, double>>> trajectories) {
+      var sym = new Symbols();
+      foreach (var t in trajectories) {
+        var prev = t[0];
+        for (var i = 1; i < t.Count; i++) {
+          sym.Add(i / (double)t.Count, t[i].Item2, prev.Item2);
+          prev = t[i];
+        }
+      }
+      return sym;
+    }
+
+    private static IEnumerable<Tuple<T, double>> ApproximateDerivative(IEnumerable<Tuple<T, double>> data, Func<T, T, double> distFunc) {
+      Tuple<T, double> prev = null, prev2 = null;
+      foreach (var d in data) {
+        if (prev == null) {
+          prev = d;
+          continue;
+        }
+        if (prev2 == null) {
+          prev2 = prev;
+          prev = d;
+          continue;
+        }
+        var dist = distFunc(prev2.Item1, d.Item1);
+        yield return Tuple.Create(prev.Item1, (d.Item2 - prev2.Item2) / dist);
+        prev2 = prev;
+        prev = d;
+      }
+    }
+  }
+
+  public enum CurveAnalysisFeature { Sharpness, Bumpiness, Flatness,
+                                     DownQ1, DownQ2, DownQ3,
+                                     NeutQ1, NeutQ2, NeutQ3,
+                                     UpQ1, UpQ2, UpQ3 }
+
+  public class CurveAnalysisResult {
+    private Dictionary<CurveAnalysisFeature, double> results = new Dictionary<CurveAnalysisFeature, double>();
+
+    public double GetValue(CurveAnalysisFeature name) {
+      return results[name];
+    }
+
+    public static IEnumerable<CurveAnalysisFeature> AllFeatures {
+      get { return Enum.GetValues(typeof(CurveAnalysisFeature)).Cast<CurveAnalysisFeature>(); }
+    }
+
+    public double[] GetValues() {
+      return AllFeatures.Select(x => results[x]).ToArray();
+    }
+
+    public CurveAnalysisResult(double sharpness, double bumpiness, double flatness, double[] down, double[] neut, double[] up) {
+      foreach (var v in AllFeatures.Zip(new[] { sharpness, bumpiness, flatness }.Concat(down).Concat(neut).Concat(up), (n, v) => Tuple.Create(n, v))) {
+        results[v.Item1] = v.Item2;
+      }
+    }
+  }
+
+  public class Symbols {
+    public Statistics Downward { get; } = new Statistics();
+    public Statistics Neutral { get; } = new Statistics();
+    public Statistics Upward { get; } = new Statistics();
+
+    public void Add(double step, double fit, double prev) {
+      if (fit < prev) Downward.Add(step);
+      else if (fit > prev) Upward.Add(step);
+      else Neutral.Add(step);
+    }
+  }
+
+  public sealed class Statistics {
+    private List<double> values = new List<double>();
+
+    public int Count { get { return values.Count; } }
+
+    public double Min { get; private set; }
+    public double Max { get; private set; }
+    public double Total { get; private set; }
+    public double Mean { get; private set; }
+    public double StdDev { get { return Math.Sqrt(Variance); } }
+    public double Variance { get { return Count > 0 ? variance / Count : 0.0; } }
+    
+    private double variance;
+    private bool sorted = false;
+    
+    public double GetPercentileOrDefault(int p, double @default = default(double)) {
+      if (p < 0 || p > 100) throw new ArgumentOutOfRangeException(nameof(p), p, "Must be in range [0;100]");
+      SortIfNecessary();
+      if (Count == 0) return @default;
+      else if (Count == 1) return values[0];
+      if (p == 100) return values[Count - 1];
+
+      var x = p / 100.0 * (Count - 1);
+      var inte = (int)x;
+      var frac = x - inte;
+
+      return values[inte] + frac * (values[inte + 1] - values[inte]);
+    }
+
+    public void Add(double value) {
+      sorted = false;
+      values.Add(value);
+
+      if (Count == 1) {
+        Min = Max = Mean = Total = value;
+      } else {
+        if (value < Min) Min = value;
+        if (value > Max) Max = value;
+
+        Total += value;
+        var oldMean = Mean;
+        Mean = oldMean + (value - oldMean) / Count;
+        variance = variance + (value - oldMean) * (value - Mean);
+      }
+    }
+
+    public void AddRange(IEnumerable<double> values) {
+      foreach (var v in values) Add(v);
+    }
+
+    private void SortIfNecessary() {
+      if (!sorted) {
+        values.Sort();
+        sorted = true;
+      }
+    }
+  }
+}
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Utilities/DoubleMatrixCharacteristics.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Utilities/DoubleMatrixCharacteristics.cs	(revision 16137)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Utilities/DoubleMatrixCharacteristics.cs	(revision 16137)
@@ -0,0 +1,75 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using HeuristicLab.Common;
+using HeuristicLab.Data;
+using System;
+using System.Linq;
+
+namespace HeuristicLab.Analysis.FitnessLandscape {
+  public static class DoubleMatrixCharacteristics {
+    public static double CoeffVariation(DoubleMatrix m) {
+      var avg = m.Average();
+      var stdDev = m.StandardDeviation();
+      return stdDev / avg;
+    }
+
+    public static double Sparsity(DoubleMatrix m) {
+      return m.Count(x => x == 0) / (double)(m.Rows * m.Columns);
+    }
+
+    public static double Disparity(DoubleMatrix m) {
+      if (m.Rows != m.Columns) throw new ArgumentException("not a quadratic matrix", "m");
+      var n = m.Rows;
+      var avg = m.Sum() / n;
+      var disparity = 0.0;
+      for (var i = 0; i < n; i++) {
+        var tmp = 0.0;
+        for (var j = 0; j < n; j++) {
+          tmp += m[i, j];
+        }
+        disparity += (tmp - avg) * (tmp - avg);
+      }
+      return Math.Sqrt(disparity / n) / avg;
+    }
+
+    public static double Skewness(DoubleMatrix m) {
+      double mean = 0, variance = 0, skewness = 0, kurtosis = 0;
+      alglib.basestat.samplemoments(m.ToArray(), m.Rows * m.Columns, ref mean, ref variance, ref skewness, ref kurtosis);
+      return skewness;
+    }
+
+    public static double Asymmetry(DoubleMatrix m) {
+      if (m.Rows != m.Columns) throw new ArgumentException("not a quadratic matrix", "m");
+      var n = m.Rows;
+      var count = 0;
+      for (var i = 0; i < n; i++) {
+        for (var j = 0; j < i; j++) {
+          var v = Math.Abs(m[i, j] - m[j, i]);
+          if (v > 0) {
+            count++;
+          }
+        }
+      }
+      return count / (n * (n - 1) / 2.0);
+    }
+  }
+}
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Utilities/UniqueThresholdCalculator.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Utilities/UniqueThresholdCalculator.cs	(revision 16137)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Utilities/UniqueThresholdCalculator.cs	(revision 16137)
@@ -0,0 +1,179 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+
+namespace HeuristicLab.Analysis.FitnessLandscape {
+
+  public static class UniqueThresholdCalculator {
+
+    private sealed class IndexRange {
+
+      public int Start, End, N;
+      public readonly bool Merge;
+      private readonly List<IndexRange> receivers, distributors;
+
+      public IndexRange(int start, int end, int n, bool merge) {
+        Start = start;
+        End = end;
+        N = n;
+        Merge = merge;
+        receivers = new List<IndexRange>();
+        distributors = new List<IndexRange>();
+      }
+
+      public void AddReceiver(IndexRange receiver) {
+        if (receiver != null) {
+          receivers.Add(receiver);
+          receiver.distributors.Add(this);
+        }
+      }
+
+      public void Distribute() {
+        int n = N - 1;
+        Debug.Assert(n >= 0);
+        switch (receivers.Count) {
+          case 0: break; // No receivers indexes are lost :-(
+          case 1: receivers[0].N += n; N = 1; break;
+          case 2: receivers[0].N += n / 2; receivers[1].N += n - n / 2; N = 1; break;
+          default: throw new InvalidOperationException("More than two receivers registered");
+        }
+        var followers = distributors.Where(d => d.Start > End).ToList();
+        if (followers.Count > 0)
+          End = followers.Min(g => g.Start);
+        var predecessors = distributors.Where(d => d.End < Start).ToList();
+        if (predecessors.Count > 0)
+          Start = predecessors.Max(g => g.End);
+      }
+
+      public override string ToString() {
+        return string.Format("[{0}-{1}, n={2}, {3}]", Start, End, N, Merge ? "Merge" : "Spread");
+      }
+    }
+
+    public static IEnumerable<double> DetermineThresholds(IEnumerable<double> differences, int n) {
+      var thresholds = differences.Select(d => Math.Abs(d)).ToList();
+      thresholds.Sort();
+      if (thresholds.Count <= n) {
+        foreach (var t in thresholds)
+          yield return t;
+      } else {
+        double min = thresholds[0];
+        double max = thresholds[thresholds.Count - 1];
+        if (min == max) {
+          yield return min;
+        } else {
+          yield return min;
+          foreach (var t in DetermineInnerThresholds(thresholds, n - 2))
+            yield return t;
+          yield return max;
+        }
+      }
+    }
+
+    private static IEnumerable<double> DetermineInnerThresholds(List<double> thresholds, int n) {
+      n = Math.Min(thresholds.Count - 2, n);
+      var indexes = Enumerable.Range(0, n + 2).Select(i => (int)Math.Round(1.0 * i * (thresholds.Count - 1) / (n + 1))).ToList();
+      var ranges = CreateRanges(thresholds, indexes);
+      RedistributeIndexes(ranges);
+      TrimBoundaryRanges(ranges);
+      if (ranges.Count == 1 && ranges[0].Start == 0 && ranges[0].End == thresholds.Count - 1 && ranges[0].N == n) {
+        if (ranges[0].Merge)
+          yield return thresholds[ranges[0].Start];
+        else
+          foreach (var i in indexes.Where(i => ranges[0].Start < i && i < ranges[0].End))
+            yield return thresholds[i];
+      } else {
+        foreach (var range in ranges) {
+          if (range.Merge)
+            yield return thresholds[range.Start];
+          else
+            foreach (var t in DetermineInnerThresholds(thresholds.GetRange(range.Start, range.End - range.Start + 1), range.N))
+              yield return t;
+        }
+      }
+    }
+
+    private static void TrimBoundaryRanges(List<IndexRange> ranges) {
+      if (ranges.Count > 0)
+        if (ranges[0].Merge)
+          ranges.RemoveAt(0);
+        else
+          ranges[0].N--;
+      if (ranges.Count > 0)
+        if (ranges[ranges.Count - 1].Merge)
+          ranges.RemoveAt(ranges.Count - 1);
+        else
+          ranges[ranges.Count - 1].N--;
+    }
+
+    private static void RedistributeIndexes(List<IndexRange> ranges) {
+      IndexRange lastReceiver = null;
+      List<IndexRange> lastDistributors = new List<IndexRange>();
+      foreach (var range in ranges) {
+        if (range.Merge) {
+          range.AddReceiver(lastReceiver);
+          lastDistributors.Add(range);
+        } else {
+          foreach (var d in lastDistributors)
+            d.AddReceiver(range);
+          lastDistributors = new List<IndexRange>();
+          lastReceiver = range;
+        }
+      }
+      foreach (var range in ranges) {
+        range.Distribute();
+      }
+    }
+
+    private static List<IndexRange> CreateRanges(List<double> thresholds, List<int> indexes) {
+      var values = indexes.Select(i => new { i, v = thresholds[i] }).ToList();
+      var mergedRanges = values.GroupBy(iv => iv.v)
+        .Select(g => g.Count() == 1 ?
+          new IndexRange(g.First().i, g.First().i, 1, false) :
+          new IndexRange(g.Min(iv => iv.i), g.Max(iv => iv.i), g.Count(), true)).ToList();
+      IndexRange lastSpreadRange = null;
+      var ranges = new List<IndexRange>();
+      foreach (var group in mergedRanges) {
+        if (group.Merge) {
+          if (lastSpreadRange != null) {
+            ranges.Add(lastSpreadRange);
+            lastSpreadRange = null;
+          }
+          ranges.Add(group);
+        } else {
+          if (lastSpreadRange != null) {
+            lastSpreadRange.End = group.End;
+            lastSpreadRange.N++;
+          } else {
+            lastSpreadRange = group;
+          }
+        }
+      }
+      if (lastSpreadRange != null)
+        ranges.Add(lastSpreadRange);
+      return ranges;
+    }
+  }
+}
Index: /branches/2457_ExpertSystem/PerformanceComparison.sln
===================================================================
--- /branches/2457_ExpertSystem/PerformanceComparison.sln	(revision 16136)
+++ /branches/2457_ExpertSystem/PerformanceComparison.sln	(revision 16137)
@@ -29,4 +29,6 @@
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeuristicLab.Encodings.LinearLinkageEncoding-3.4", "HeuristicLab.Encodings.LinearLinkageEncoding\3.4\HeuristicLab.Encodings.LinearLinkageEncoding-3.4.csproj", "{BE698769-975A-429E-828C-72BB2B6182C8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{76241AF2-B428-4127-805D-667F79983F0F}"
 EndProject
 Global
@@ -196,7 +198,22 @@
 		{BE698769-975A-429E-828C-72BB2B6182C8}.Release|x86.ActiveCfg = Release|x86
 		{BE698769-975A-429E-828C-72BB2B6182C8}.Release|x86.Build.0 = Release|x86
+		{76241AF2-B428-4127-805D-667F79983F0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{76241AF2-B428-4127-805D-667F79983F0F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{76241AF2-B428-4127-805D-667F79983F0F}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{76241AF2-B428-4127-805D-667F79983F0F}.Debug|x64.Build.0 = Debug|Any CPU
+		{76241AF2-B428-4127-805D-667F79983F0F}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{76241AF2-B428-4127-805D-667F79983F0F}.Debug|x86.Build.0 = Debug|Any CPU
+		{76241AF2-B428-4127-805D-667F79983F0F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{76241AF2-B428-4127-805D-667F79983F0F}.Release|Any CPU.Build.0 = Release|Any CPU
+		{76241AF2-B428-4127-805D-667F79983F0F}.Release|x64.ActiveCfg = Release|Any CPU
+		{76241AF2-B428-4127-805D-667F79983F0F}.Release|x64.Build.0 = Release|Any CPU
+		{76241AF2-B428-4127-805D-667F79983F0F}.Release|x86.ActiveCfg = Release|Any CPU
+		{76241AF2-B428-4127-805D-667F79983F0F}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {C8E53BE4-B79D-431E-AFE7-03BB2062436C}
+	EndGlobalSection
 EndGlobal
Index: /branches/2457_ExpertSystem/UnitTests/InformationAnalysisTest.cs
===================================================================
--- /branches/2457_ExpertSystem/UnitTests/InformationAnalysisTest.cs	(revision 16137)
+++ /branches/2457_ExpertSystem/UnitTests/InformationAnalysisTest.cs	(revision 16137)
@@ -0,0 +1,27 @@
+﻿using HeuristicLab.Analysis.FitnessLandscape;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace UnitTests {
+  [TestClass]
+  public class InformationAnalysisTest {
+    [TestMethod]
+    public void TestFeatures() {
+      var qualities = new double[] { 1, 2, 1, 3, 2, 4, 2, 1, 1, 2, 4, 4, 3, 1 };
+      var ia = new InformationAnalysis(qualities);
+
+      Assert.AreEqual(0.8224, ia.InformationContent[0], 1e-04);
+      Assert.AreEqual(0.4603, ia.DensityBasinInformation[0], 1e-04);
+      Assert.AreEqual(0.6154, ia.PartialInformationContent[0], 1e-04);
+      Assert.AreEqual(0.9008, ia.TotalEntropy[0], 1e-04);
+      Assert.AreEqual(2, ia.InformationStability);
+      Assert.AreEqual(4, ia.Diversity);
+      Assert.AreEqual(5, ia.Regularity);
+      Assert.AreEqual(0.4603, ia.PeakDensityBasinInformation.Value, 1e-04);
+      Assert.AreEqual(0.8224, ia.PeakInformationContent.Value, 1e-04);
+      Assert.AreEqual(0.9008, ia.PeakTotalEntropy.Value, 1e-04);
+      Assert.AreEqual(0.7642, ia.SymmetricInformationContent[0], 1e-04);
+      Assert.AreEqual(0.2987, ia.SymmetricDensityBasinInformation[0], 1e-04);
+      Assert.AreEqual(0.7068, ia.SymmetricTotalEntropy[0], 1e-04);
+    }
+  }
+}
Index: /branches/2457_ExpertSystem/UnitTests/Properties/AssemblyInfo.cs
===================================================================
--- /branches/2457_ExpertSystem/UnitTests/Properties/AssemblyInfo.cs	(revision 16137)
+++ /branches/2457_ExpertSystem/UnitTests/Properties/AssemblyInfo.cs	(revision 16137)
@@ -0,0 +1,20 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("UnitTests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("UnitTests")]
+[assembly: AssemblyCopyright("Copyright Â©  2018")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("76241af2-b428-4127-805d-667f79983f0f")]
+
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
Index: /branches/2457_ExpertSystem/UnitTests/UnitTests.csproj
===================================================================
--- /branches/2457_ExpertSystem/UnitTests/UnitTests.csproj	(revision 16137)
+++ /branches/2457_ExpertSystem/UnitTests/UnitTests.csproj	(revision 16137)
@@ -0,0 +1,81 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{76241AF2-B428-4127-805D-667F79983F0F}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>UnitTests</RootNamespace>
+    <AssemblyName>UnitTests</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
+    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+    <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
+    <IsCodedUITest>False</IsCodedUITest>
+    <TestProjectType>UnitTest</TestProjectType>
+    <NuGetPackageImportStamp>
+    </NuGetPackageImportStamp>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\MSTest.TestFramework.1.2.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\MSTest.TestFramework.1.2.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="InformationAnalysisTest.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\HeuristicLab.Analysis.FitnessLandscape\3.3\HeuristicLab.Analysis.FitnessLandscape-3.3.csproj">
+      <Project>{5fbdcd4a-3c2a-4ec6-83ce-34b29f43621a}</Project>
+      <Name>HeuristicLab.Analysis.FitnessLandscape-3.3</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\HeuristicLab.Analysis\3.3\HeuristicLab.Analysis-3.3.csproj">
+      <Project>{887425b4-4348-49ed-a457-b7d2c26ddbf9}</Project>
+      <Name>HeuristicLab.Analysis-3.3</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\HeuristicLab.Optimization\3.3\HeuristicLab.Optimization-3.3.csproj">
+      <Project>{14ab8d24-25bc-400c-a846-4627aa945192}</Project>
+      <Name>HeuristicLab.Optimization-3.3</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.props'))" />
+    <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.targets'))" />
+  </Target>
+  <Import Project="..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.targets')" />
+</Project>
Index: /branches/2457_ExpertSystem/UnitTests/packages.config
===================================================================
--- /branches/2457_ExpertSystem/UnitTests/packages.config	(revision 16137)
+++ /branches/2457_ExpertSystem/UnitTests/packages.config	(revision 16137)
@@ -0,0 +1,5 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="MSTest.TestAdapter" version="1.2.0" targetFramework="net45" />
+  <package id="MSTest.TestFramework" version="1.2.0" targetFramework="net45" />
+</packages>
