Index: /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Binary/BinaryMemPR.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Binary/BinaryMemPR.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Binary/BinaryMemPR.cs	(revision 14544)
@@ -38,6 +38,4 @@
   [Creatable(CreatableAttribute.Categories.PopulationBasedAlgorithms, Priority = 999)]
   public class BinaryMemPR : MemPRAlgorithm<SingleObjectiveBasicProblem<BinaryVectorEncoding>, BinaryVector, BinaryMemPRPopulationContext, BinaryMemPRSolutionContext> {
-    private const double UncommonBitSubsetMutationProbabilityMagicConst = 0.05;
-    
     [StorableConstructor]
     protected BinaryMemPR(bool deserializing) : base(deserializing) { }
@@ -91,5 +89,5 @@
     }
 
-    protected override int TabuWalk(ISingleObjectiveSolutionScope<BinaryVector> scope, int maxEvals, CancellationToken token, ISolutionSubspace<BinaryVector> subspace = null) {
+    protected override void AdaptiveWalk(ISingleObjectiveSolutionScope<BinaryVector> scope, int maxEvals, CancellationToken token, ISolutionSubspace<BinaryVector> subspace = null) {
       var evaluations = 0;
       var subset = subspace != null ? ((BinarySolutionSubspace)subspace).Subspace : null;
@@ -102,17 +100,24 @@
       var current = currentScope.Solution;
       var N = current.Length;
-      var tabu = new Tuple<double, double>[N];
-      for (var i = 0; i < N; i++) tabu[i] = Tuple.Create(current[i] ? double.NaN : currentScope.Fitness, !current[i] ? double.NaN : currentScope.Fitness);
+
       var subN = subset != null ? subset.Count(x => x) : N;
-      if (subN == 0) return 0;
+      if (subN == 0) return;
       var order = Enumerable.Range(0, N).Where(x => subset == null || subset[x]).Shuffle(Context.Random).ToArray();
 
-      var steps = 0;
-      var stepsUntilBestOfWalk = 0;
+      var max = Context.Population.Max(x => x.Fitness);
+      var min = Context.Population.Min(x => x.Fitness);
+      var range = (max - min);
+      if (range.IsAlmost(0)) range = Math.Abs(max * 0.05);
+      //else range += range * 0.05;
+      if (range.IsAlmost(0)) { // because min = max = 0
+        Context.IncrementEvaluatedSolutions(evaluations);
+        return;
+      }
+
+      //var upperBound = Problem.Maximization ? min - range : max + range;
+      //var lowerBound = Problem.Maximization ? max : min;
+      var temp = 1.0;
       for (var iter = 0; iter < int.MaxValue; iter++) {
-        var allTabu = true;
-        var bestOfTheRestF = double.NaN;
-        int bestOfTheRest = -1;
-        var improved = false;
+        var moved = false;
 
         for (var i = 0; i < subN; i++) {
@@ -124,34 +129,21 @@
           var after = currentScope.Fitness;
 
-          if (IsBetter(after, before) && (bestOfTheWalk == null || IsBetter(after, bestOfTheWalk.Fitness))) {
+          if (Context.IsBetter(after, before) && (bestOfTheWalk == null || Context.IsBetter(after, bestOfTheWalk.Fitness))) {
             bestOfTheWalk = (SingleObjectiveSolutionScope<BinaryVector>)currentScope.Clone();
-            stepsUntilBestOfWalk = steps;
-          }
-
-          var qualityToBeat = current[idx] ? tabu[idx].Item2 : tabu[idx].Item1;
-          var isTabu = !IsBetter(after, qualityToBeat);
-          if (!isTabu) allTabu = false;
-
-          if (IsBetter(after, before) && !isTabu) {
-            improved = true;
-            steps++;
-            tabu[idx] = current[idx] ? Tuple.Create(after, tabu[idx].Item2) : Tuple.Create(tabu[idx].Item1, after);
-          } else { // undo the move
-            if (!isTabu && IsBetter(after, bestOfTheRestF)) {
-              bestOfTheRest = idx;
-              bestOfTheRestF = after;
+          }
+          var diff = Problem.Maximization ? after - before : before - after;
+          if (diff > 0) moved = true;
+          else {
+            var prob = Math.Exp(temp * diff / range);
+            if (Context.Random.NextDouble() >= prob) {
+              // the move is not good enough -> undo the move
+              current[idx] = !current[idx];
+              currentScope.Fitness = before;
             }
-            current[idx] = !current[idx];
-            currentScope.Fitness = before;
-          }
+          }
+          temp += 100.0 / maxEvals;
           if (evaluations >= maxEvals) break;
         }
-        if (!allTabu && !improved) {
-          var better = currentScope.Fitness;
-          current[bestOfTheRest] = !current[bestOfTheRest];
-          tabu[bestOfTheRest] = current[bestOfTheRest] ? Tuple.Create(better, tabu[bestOfTheRest].Item2) : Tuple.Create(tabu[bestOfTheRest].Item1, better);
-          currentScope.Fitness = bestOfTheRestF;
-          steps++;
-        } else if (allTabu) break;
+        if (!moved) break;
         if (evaluations >= maxEvals) break;
       }
@@ -159,71 +151,84 @@
       Context.IncrementEvaluatedSolutions(evaluations);
       scope.Adopt(bestOfTheWalk ?? currentScope);
-      return stepsUntilBestOfWalk;
-    }
-
-    protected override ISingleObjectiveSolutionScope<BinaryVector> Cross(ISingleObjectiveSolutionScope<BinaryVector> p1, ISingleObjectiveSolutionScope<BinaryVector> p2, CancellationToken token) {
-      var offspring = (ISingleObjectiveSolutionScope<BinaryVector>)p1.Clone();
-      offspring.Fitness = double.NaN;
-      var code = offspring.Solution;
-      var p2Code = p2.Solution;
-      var bp = 0;
-      var lastbp = 0;
-      for (var i = 0; i < code.Length; i++) {
-        if (bp % 2 == 1) {
-          code[i] = p2Code[i];
-        }
-        if (Context.Random.Next(code.Length) < i - lastbp + 1) {
-          bp = (bp + 1) % 2;
-          lastbp = i;
-        }
-      }
+    }
+
+    protected override ISingleObjectiveSolutionScope<BinaryVector> Breed(ISingleObjectiveSolutionScope<BinaryVector> p1, ISingleObjectiveSolutionScope<BinaryVector> p2, CancellationToken token) {
+      var evaluations = 0;
+      var N = p1.Solution.Length;
+      if (double.IsNaN(p1.Fitness)) {
+        Evaluate(p1, token);
+        evaluations++;
+      }
+      if (double.IsNaN(p2.Fitness)) {
+        Evaluate(p2, token);
+        evaluations++;
+      }
+      var better = Problem.Maximization ? Math.Max(p1.Fitness, p2.Fitness)
+                                        : Math.Min(p1.Fitness, p2.Fitness);
+
+      var offspring = ToScope(null);
+      var probe = ToScope(new BinaryVector(N));
+      var order = Enumerable.Range(0, N - 1).Shuffle(Context.Random).ToList();
+      for (var x = 0; x < N - 1; x++) {
+        var b = order[x];
+        if (p1.Solution[b] == p2.Solution[b]) continue;
+        for (var i = 0; i <= b; i++)
+          probe.Solution[i] = p1.Solution[i];
+        for (var i = b + 1; i < probe.Solution.Length; i++)
+          probe.Solution[i] = p2.Solution[i];
+
+        Evaluate(probe, token);
+        evaluations++;
+        if (Context.IsBetter(probe, offspring)) {
+          if (Context.IsBetter(probe.Fitness, better)) {
+            Context.IncrementEvaluatedSolutions(evaluations);
+            return probe;
+          }
+          offspring = (ISingleObjectiveSolutionScope<BinaryVector>)probe.Clone();
+        }
+      }
+
+      while (evaluations < Context.LocalSearchEvaluations) {
+        probe.Solution = UniformCrossover.Apply(Context.Random, p1.Solution, p2.Solution);
+        Evaluate(probe, token);
+        evaluations++;
+        if (Context.IsBetter(probe, offspring)) {
+          if (Context.IsBetter(probe.Fitness, better)) {
+            Context.IncrementEvaluatedSolutions(evaluations);
+            return probe;
+          }
+          offspring = (ISingleObjectiveSolutionScope<BinaryVector>)probe.Clone();
+        }
+      }
+      Context.IncrementEvaluatedSolutions(evaluations);
       return offspring;
     }
 
-    protected override void Mutate(ISingleObjectiveSolutionScope<BinaryVector> offspring, CancellationToken token, ISolutionSubspace<BinaryVector> subspace = null) {
-      var subset = subspace != null ? ((BinarySolutionSubspace)subspace).Subspace : null;
-      offspring.Fitness = double.NaN;
-      var code = offspring.Solution;
-      for (var i = 0; i < code.Length; i++) {
-        if (subset != null && subset[i]) continue;
-        if (Context.Random.NextDouble() < UncommonBitSubsetMutationProbabilityMagicConst) {
-          code[i] = !code[i];
-          if (subset != null) subset[i] = true;
-        }
-      }
-    }
-
-    protected override ISingleObjectiveSolutionScope<BinaryVector> Relink(ISingleObjectiveSolutionScope<BinaryVector> a, ISingleObjectiveSolutionScope<BinaryVector> b, CancellationToken token) {
+    protected override ISingleObjectiveSolutionScope<BinaryVector> Link(ISingleObjectiveSolutionScope<BinaryVector> a, ISingleObjectiveSolutionScope<BinaryVector> b, CancellationToken token, bool delink = false) {
+      var evaluations = 0;
       if (double.IsNaN(a.Fitness)) {
         Evaluate(a, token);
-        Context.IncrementEvaluatedSolutions(1);
+        evaluations++;
       }
       if (double.IsNaN(b.Fitness)) {
         Evaluate(b, token);
-        Context.IncrementEvaluatedSolutions(1);
-      }
-      if (Context.Random.NextDouble() < 0.5)
-        return IsBetter(a, b) ? Relink(a, b, token, false) : Relink(b, a, token, true);
-      else return IsBetter(a, b) ? Relink(b, a, token, true) : Relink(a, b, token, false);
-    }
-
-    protected virtual ISingleObjectiveSolutionScope<BinaryVector> Relink(ISingleObjectiveSolutionScope<BinaryVector> betterScope, ISingleObjectiveSolutionScope<BinaryVector> worseScope, CancellationToken token, bool fromWorseToBetter) {
-      var evaluations = 0;
-      var childScope = (ISingleObjectiveSolutionScope<BinaryVector>)(fromWorseToBetter ? worseScope : betterScope).Clone();
+        evaluations++;
+      }
+
+      var childScope = (ISingleObjectiveSolutionScope<BinaryVector>)a.Clone();
       var child = childScope.Solution;
-      var better = betterScope.Solution;
-      var worse = worseScope.Solution;
       ISingleObjectiveSolutionScope<BinaryVector> best = null;
-      var cF = fromWorseToBetter ? worseScope.Fitness : betterScope.Fitness;
+      var cF = a.Fitness;
       var bF = double.NaN;
-      var order = Enumerable.Range(0, better.Length).Shuffle(Context.Random).ToArray();
+      var order = Enumerable.Range(0, child.Length)
+        .Where(x => !delink && child[x] != b.Solution[x] || delink && child[x] == b.Solution[x])
+        .Shuffle(Context.Random).ToList();
+      if (order.Count == 0) return childScope;
+
       while (true) {
         var bestS = double.NaN;
-        var bestIdx = -1;
-        for (var i = 0; i < child.Length; i++) {
+        var bestI = -1;
+        for (var i = 0; i < order.Count; i++) {
           var idx = order[i];
-          // either move from worse to better or move from better away from worse
-          if (fromWorseToBetter && child[idx] == better[idx] ||
-            !fromWorseToBetter && child[idx] != worse[idx]) continue;
           child[idx] = !child[idx]; // move
           Evaluate(childScope, token);
@@ -232,23 +237,24 @@
           childScope.Fitness = cF;
           child[idx] = !child[idx]; // undo move
-          if (IsBetter(s, cF)) {
+          if (Context.IsBetter(s, cF)) {
             bestS = s;
-            bestIdx = idx;
+            bestI = i;
             break; // first-improvement
           }
-          if (double.IsNaN(bestS) || IsBetter(s, bestS)) {
+          if (Context.IsBetter(s, bestS)) {
             // least-degrading
             bestS = s;
-            bestIdx = idx;
-          }
-        }
-        if (bestIdx < 0) break;
-        child[bestIdx] = !child[bestIdx];
+            bestI = i;
+          }
+        }
+        child[order[bestI]] = !child[order[bestI]];
+        order.RemoveAt(bestI);
         cF = bestS;
         childScope.Fitness = cF;
-        if (IsBetter(cF, bF)) {
+        if (Context.IsBetter(cF, bF)) {
           bF = cF;
           best = (ISingleObjectiveSolutionScope<BinaryVector>)childScope.Clone();
         }
+        if (order.Count == 0) break;
       }
       Context.IncrementEvaluatedSolutions(evaluations);
Index: /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/HeuristicLab.Algorithms.MemPR-3.3.csproj
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/HeuristicLab.Algorithms.MemPR-3.3.csproj	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/HeuristicLab.Algorithms.MemPR-3.3.csproj	(revision 14544)
@@ -74,4 +74,9 @@
   </PropertyGroup>
   <ItemGroup>
+    <Reference Include="ALGLIB-3.7.0, Version=3.7.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\bin\ALGLIB-3.7.0.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
@@ -128,4 +133,9 @@
   </ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\..\HeuristicLab.Algorithms.DataAnalysis\3.4\HeuristicLab.Algorithms.DataAnalysis-3.4.csproj">
+      <Project>{2e782078-fa81-4b70-b56f-74ce38dac6c8}</Project>
+      <Name>HeuristicLab.Algorithms.DataAnalysis-3.4</Name>
+      <Private>False</Private>
+    </ProjectReference>
     <ProjectReference Include="..\..\HeuristicLab.Analysis\3.3\HeuristicLab.Analysis-3.3.csproj">
       <Project>{887425b4-4348-49ed-a457-b7d2c26ddbf9}</Project>
@@ -202,4 +212,8 @@
       <Name>HeuristicLab.PluginInfrastructure-3.3</Name>
       <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.Problems.DataAnalysis\3.4\HeuristicLab.Problems.DataAnalysis-3.4.csproj">
+      <Project>{df87c13e-a889-46ff-8153-66dcaa8c5674}</Project>
+      <Name>HeuristicLab.Problems.DataAnalysis-3.4</Name>
     </ProjectReference>
     <ProjectReference Include="..\..\HeuristicLab.Random\3.3\HeuristicLab.Random-3.3.csproj">
Index: /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Interfaces/Interfaces.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Interfaces/Interfaces.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Interfaces/Interfaces.cs	(revision 14544)
@@ -22,5 +22,5 @@
 using System.Collections.Generic;
 using HeuristicLab.Algorithms.MemPR.Binary;
-using HeuristicLab.Algorithms.MemPR.LinearLinkage;
+using HeuristicLab.Algorithms.MemPR.Grouping;
 using HeuristicLab.Algorithms.MemPR.Permutation;
 using HeuristicLab.Core;
Index: /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageMemPR.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageMemPR.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageMemPR.cs	(revision 14544)
@@ -34,11 +34,9 @@
 using HeuristicLab.Random;
 
-namespace HeuristicLab.Algorithms.MemPR.LinearLinkage {
+namespace HeuristicLab.Algorithms.MemPR.Grouping {
   [Item("MemPR (linear linkage)", "MemPR implementation for linear linkage vectors.")]
   [StorableClass]
   [Creatable(CreatableAttribute.Categories.PopulationBasedAlgorithms, Priority = 999)]
-  public class LinearLinkageMemPR : MemPRAlgorithm<SingleObjectiveBasicProblem<LinearLinkageEncoding>, Encodings.LinearLinkageEncoding.LinearLinkage, LinearLinkageMemPRPopulationContext, LinearLinkageMemPRSolutionContext> {
-    private const double UncommonBitSubsetMutationProbabilityMagicConst = 0.05;
-    
+  public class LinearLinkageMemPR : MemPRAlgorithm<SingleObjectiveBasicProblem<LinearLinkageEncoding>, LinearLinkage, LinearLinkageMemPRPopulationContext, LinearLinkageMemPRSolutionContext> {
     [StorableConstructor]
     protected LinearLinkageMemPR(bool deserializing) : base(deserializing) { }
@@ -57,5 +55,5 @@
     }
 
-    protected override bool Eq(ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage> a, ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage> b) {
+    protected override bool Eq(ISingleObjectiveSolutionScope<LinearLinkage> a, ISingleObjectiveSolutionScope<LinearLinkage> b) {
       var s1 = a.Solution;
       var s2 = b.Solution;
@@ -66,17 +64,21 @@
     }
 
-    protected override double Dist(ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage> a, ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage> b) {
-      return 1.0 - HammingSimilarityCalculator.CalculateSimilarity(a.Solution, b.Solution);
-    }
-
-    protected override ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage> ToScope(Encodings.LinearLinkageEncoding.LinearLinkage code, double fitness = double.NaN) {
+    protected override double Dist(ISingleObjectiveSolutionScope<LinearLinkage> a, ISingleObjectiveSolutionScope<LinearLinkage> b) {
+      return Dist(a.Solution, b.Solution);
+    }
+
+    private double Dist(LinearLinkage a, LinearLinkage b) {
+      return 1.0 - HammingSimilarityCalculator.CalculateSimilarity(a, b);
+    }
+
+    protected override ISingleObjectiveSolutionScope<LinearLinkage> ToScope(LinearLinkage code, double fitness = double.NaN) {
       var creator = Problem.SolutionCreator as ILinearLinkageCreator;
       if (creator == null) throw new InvalidOperationException("Can only solve linear linkage encoded problems with MemPR (linear linkage)");
-      return new SingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage>(code, creator.LLEParameter.ActualName, fitness, Problem.Evaluator.QualityParameter.ActualName) {
+      return new SingleObjectiveSolutionScope<LinearLinkage>(code, creator.LLEParameter.ActualName, fitness, Problem.Evaluator.QualityParameter.ActualName) {
         Parent = Context.Scope
       };
     }
 
-    protected override ISolutionSubspace<Encodings.LinearLinkageEncoding.LinearLinkage> CalculateSubspace(IEnumerable<Encodings.LinearLinkageEncoding.LinearLinkage> solutions, bool inverse = false) {
+    protected override ISolutionSubspace<LinearLinkage> CalculateSubspace(IEnumerable<LinearLinkage> solutions, bool inverse = false) {
       var pop = solutions.ToList();
       var N = pop[0].Length;
@@ -92,8 +94,8 @@
     }
 
-    protected override int TabuWalk(
-        ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage> scope,
+    protected override void AdaptiveWalk(
+        ISingleObjectiveSolutionScope<LinearLinkage> scope,
         int maxEvals, CancellationToken token,
-        ISolutionSubspace<Encodings.LinearLinkageEncoding.LinearLinkage> sub_space = null) {
+        ISolutionSubspace<LinearLinkage> sub_space = null) {
       var maximization = Context.Problem.Maximization;
       var subspace = sub_space is LinearLinkageSolutionSubspace ? ((LinearLinkageSolutionSubspace)sub_space).Subspace : null;
@@ -104,10 +106,10 @@
         quality = scope.Fitness;
         evaluations++;
-        if (evaluations >= maxEvals) return evaluations;
+        if (evaluations >= maxEvals) return;
       }
       var bestQuality = quality;
-      var currentScope = (ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage>)scope.Clone();
+      var currentScope = (ISingleObjectiveSolutionScope<LinearLinkage>)scope.Clone();
       var current = currentScope.Solution;
-      Encodings.LinearLinkageEncoding.LinearLinkage bestOfTheWalk = null;
+      LinearLinkage bestOfTheWalk = null;
       var bestOfTheWalkF = double.NaN;
 
@@ -153,5 +155,5 @@
               }
               if (FitnessComparer.IsBetter(maximization, bestOfTheRestF, bestOfTheWalkF)) {
-                bestOfTheWalk = (Encodings.LinearLinkageEncoding.LinearLinkage)current.Clone();
+                bestOfTheWalk = (LinearLinkage)current.Clone();
                 bestOfTheWalkF = bestOfTheRestF;
               }
@@ -190,5 +192,5 @@
 
                 if (FitnessComparer.IsBetter(maximization, moveF, bestOfTheWalkF)) {
-                  bestOfTheWalk = (Encodings.LinearLinkageEncoding.LinearLinkage)current.Clone();
+                  bestOfTheWalk = (LinearLinkage)current.Clone();
                   bestOfTheWalkF = moveF;
                 }
@@ -232,5 +234,5 @@
             }
             if (FitnessComparer.IsBetter(maximization, bestOfTheRestF, bestOfTheWalkF)) {
-              bestOfTheWalk = (Encodings.LinearLinkageEncoding.LinearLinkage)current.Clone();
+              bestOfTheWalk = (LinearLinkage)current.Clone();
               bestOfTheWalkF = bestOfTheRestF;
             }
@@ -247,72 +249,76 @@
         scope.Fitness = bestOfTheWalkF;
       }
-      return evaluations;
-    }
-
-    protected override ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage> Cross(ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage> p1Scope, ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage> p2Scope, CancellationToken token) {
-      var p1 = p1Scope.Solution;
-      var p2 = p2Scope.Solution;
-      return ToScope(GroupCrossover.Apply(Context.Random, p1, p2));
-    }
-
-    protected override void Mutate(ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage> offspring, CancellationToken token, ISolutionSubspace<Encodings.LinearLinkageEncoding.LinearLinkage> subspace = null) {
-      var lle = offspring.Solution;
-      var subset = subspace is LinearLinkageSolutionSubspace ? ((LinearLinkageSolutionSubspace)subspace).Subspace : null;
-      for (var i = 0; i < lle.Length - 1; i++) {
-        if (subset == null || subset[i]) continue; // mutation works against crossover so aims to mutate noTouch points
-        if (Context.Random.NextDouble() < UncommonBitSubsetMutationProbabilityMagicConst) {
-          subset[i] = true;
-          var index = Context.Random.Next(i, lle.Length);
-          for (var j = index - 1; j >= i; j--) {
-            if (lle[j] == index) index = j;
-          }
-          lle[i] = index;
-          index = i;
-          var idx2 = i;
-          for (var j = i - 1; j >= 0; j--) {
-            if (lle[j] == lle[index]) {
-              lle[j] = idx2;
-              index = idx2 = j;
-            } else if (lle[j] == idx2) idx2 = j;
-          }
-        }
-      }
-    }
-
-    protected override ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage> Relink(ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage> a, ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage> b, CancellationToken token) {
-      var maximization = Context.Problem.Maximization;
+    }
+
+    protected override ISingleObjectiveSolutionScope<LinearLinkage> Breed(ISingleObjectiveSolutionScope<LinearLinkage> p1Scope, ISingleObjectiveSolutionScope<LinearLinkage> p2Scope, CancellationToken token) {
+      var cache = new HashSet<LinearLinkage>(new LinearLinkageEqualityComparer());
+      var cachehits = 0;
+      var evaluations = 1;
+      ISingleObjectiveSolutionScope<LinearLinkage> offspring = null;
+      for (; evaluations < Context.LocalSearchEvaluations; evaluations++) {
+        var code = GroupCrossover.Apply(Context.Random, p1Scope.Solution, p2Scope.Solution);
+        if (cache.Contains(code)) {
+          cachehits++;
+          if (cachehits > 10) break;
+          continue;
+        }
+        var probe = ToScope(code);
+        Evaluate(probe, token);
+        cache.Add(code);
+        if (offspring == null || Context.IsBetter(probe, offspring)) {
+          offspring = probe;
+          if (Context.IsBetter(offspring, p1Scope) && Context.IsBetter(offspring, p2Scope))
+            break;
+        }
+      }
+      Context.IncrementEvaluatedSolutions(evaluations-1);
+      return offspring;
+    }
+
+    protected override ISingleObjectiveSolutionScope<LinearLinkage> Link(ISingleObjectiveSolutionScope<LinearLinkage> a, ISingleObjectiveSolutionScope<LinearLinkage> b, CancellationToken token, bool delink = false) {
+      var evaluations = 0;
       if (double.IsNaN(a.Fitness)) {
         Evaluate(a, token);
-        Context.IncrementEvaluatedSolutions(1);
+        evaluations++;
       }
       if (double.IsNaN(b.Fitness)) {
         Evaluate(b, token);
-        Context.IncrementEvaluatedSolutions(1);
-      }
-      var child = (ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage>)a.Clone();
-      var cgroups = child.Solution.GetGroups().Select(x => new HashSet<int>(x)).ToList();
-      var g2 = b.Solution.GetGroups().ToList();
-      var order = Enumerable.Range(0, g2.Count).Shuffle(Context.Random).ToList();
-      ISingleObjectiveSolutionScope <Encodings.LinearLinkageEncoding.LinearLinkage> bestChild = null;
-      for (var j = 0; j < g2.Count; j++) {
-        var g = g2[order[j]];
-        var changed = false;
-        for (var k = j; k < cgroups.Count; k++) {
-          foreach (var f in g) if (cgroups[k].Remove(f)) changed = true;
-          if (cgroups[k].Count == 0) {
-            cgroups.RemoveAt(k);
-            k--;
-          }
-        }
-        cgroups.Insert(0, new HashSet<int>(g));
-        child.Solution.SetGroups(cgroups);
-        if (changed) {
-          Evaluate(child, token);
-          if (bestChild == null || FitnessComparer.IsBetter(maximization, child.Fitness, bestChild.Fitness)) {
-            bestChild = (ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage>)child.Clone();
-          }
-        }
-      };
-      return bestChild;
+        evaluations++;
+      }
+
+      var probe = (ISingleObjectiveSolutionScope<LinearLinkage>)a.Clone();
+      ISingleObjectiveSolutionScope<LinearLinkage> best = null;
+      while (true) {
+        Move bestMove = null;
+        var bestMoveQ = double.NaN;
+        // this approach may not fully relink the two solutions
+        foreach (var m in MoveGenerator.Generate(probe.Solution)) {
+          var distBefore = Dist(probe, b);
+          m.Apply(probe.Solution);
+          var distAfter = Dist(probe, b);
+          if (delink && distAfter > distBefore || !delink && distAfter < distBefore) {
+            var beforeQ = probe.Fitness;
+            Evaluate(probe, token);
+            evaluations++;
+            var q = probe.Fitness;
+            m.Undo(probe.Solution);
+            probe.Fitness = beforeQ;
+
+            if (Context.IsBetter(q, bestMoveQ)) {
+              bestMove = m;
+              bestMoveQ = q;
+            }
+            if (Context.IsBetter(q, beforeQ)) break;
+          } else m.Undo(probe.Solution);
+        }
+        if (bestMove == null) break;
+        bestMove.Apply(probe.Solution);
+        probe.Fitness = bestMoveQ;
+        if (best == null || Context.IsBetter(probe, best))
+          best = (ISingleObjectiveSolutionScope<LinearLinkage>)probe.Clone();
+      }
+      Context.IncrementEvaluatedSolutions(evaluations);
+
+      return best ?? probe;
     }
   }
Index: /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageMemPRContext.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageMemPRContext.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageMemPRContext.cs	(revision 14544)
@@ -28,8 +28,8 @@
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
 
-namespace HeuristicLab.Algorithms.MemPR.LinearLinkage {
+namespace HeuristicLab.Algorithms.MemPR.Grouping {
   [Item("MemPR Population Context (linear linkage)", "MemPR population context for linear linkage encoded problems.")]
   [StorableClass]
-  public sealed class LinearLinkageMemPRPopulationContext : MemPRPopulationContext<SingleObjectiveBasicProblem<LinearLinkageEncoding>, Encodings.LinearLinkageEncoding.LinearLinkage, LinearLinkageMemPRPopulationContext, LinearLinkageMemPRSolutionContext> {
+  public sealed class LinearLinkageMemPRPopulationContext : MemPRPopulationContext<SingleObjectiveBasicProblem<LinearLinkageEncoding>, LinearLinkage, LinearLinkageMemPRPopulationContext, LinearLinkageMemPRSolutionContext> {
 
     [StorableConstructor]
@@ -44,5 +44,5 @@
     }
 
-    public override LinearLinkageMemPRSolutionContext CreateSingleSolutionContext(ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage> solution) {
+    public override LinearLinkageMemPRSolutionContext CreateSingleSolutionContext(ISingleObjectiveSolutionScope<LinearLinkage> solution) {
       return new LinearLinkageMemPRSolutionContext(this, solution);
     }
@@ -51,5 +51,5 @@
   [Item("MemPR Solution Context (linear linkage)", "MemPR solution context for linear linkage encoded problems.")]
   [StorableClass]
-  public sealed class LinearLinkageMemPRSolutionContext : MemPRSolutionContext<SingleObjectiveBasicProblem<LinearLinkageEncoding>, Encodings.LinearLinkageEncoding.LinearLinkage, LinearLinkageMemPRPopulationContext, LinearLinkageMemPRSolutionContext>, ILinearLinkageSubspaceContext {
+  public sealed class LinearLinkageMemPRSolutionContext : MemPRSolutionContext<SingleObjectiveBasicProblem<LinearLinkageEncoding>, LinearLinkage, LinearLinkageMemPRPopulationContext, LinearLinkageMemPRSolutionContext>, ILinearLinkageSubspaceContext {
 
     [Storable]
@@ -58,5 +58,5 @@
       get { return subspace.Value; }
     }
-    ISolutionSubspace<Encodings.LinearLinkageEncoding.LinearLinkage> ISolutionSubspaceContext<Encodings.LinearLinkageEncoding.LinearLinkage>.Subspace {
+    ISolutionSubspace<LinearLinkage> ISolutionSubspaceContext<LinearLinkage>.Subspace {
       get { return Subspace; }
     }
@@ -68,5 +68,5 @@
 
     }
-    public LinearLinkageMemPRSolutionContext(LinearLinkageMemPRPopulationContext baseContext, ISingleObjectiveSolutionScope<Encodings.LinearLinkageEncoding.LinearLinkage> solution)
+    public LinearLinkageMemPRSolutionContext(LinearLinkageMemPRPopulationContext baseContext, ISingleObjectiveSolutionScope<LinearLinkage> solution)
       : base(baseContext, solution) {
 
Index: /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageSolutionSubspace.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageSolutionSubspace.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageSolutionSubspace.cs	(revision 14544)
@@ -23,10 +23,11 @@
 using HeuristicLab.Common;
 using HeuristicLab.Core;
+using HeuristicLab.Encodings.LinearLinkageEncoding;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
 
-namespace HeuristicLab.Algorithms.MemPR.LinearLinkage {
+namespace HeuristicLab.Algorithms.MemPR.Grouping {
   [Item("Solution subspace (linear linkage)", "")]
   [StorableClass]
-  public sealed class LinearLinkageSolutionSubspace : Item, ISolutionSubspace<Encodings.LinearLinkageEncoding.LinearLinkage> {
+  public sealed class LinearLinkageSolutionSubspace : Item, ISolutionSubspace<LinearLinkage> {
 
     [Storable]
Index: /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LocalSearch/ExhaustiveSubspace.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LocalSearch/ExhaustiveSubspace.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LocalSearch/ExhaustiveSubspace.cs	(revision 14544)
@@ -29,9 +29,9 @@
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
 
-namespace HeuristicLab.Algorithms.MemPR.LinearLinkage.LocalSearch {
+namespace HeuristicLab.Algorithms.MemPR.Grouping.LocalSearch {
   [Item("Exhaustive Local (Subspace) Search (linear linkage)", "", ExcludeGenericTypeInfo = true)]
   [StorableClass]
   public class ExhaustiveSubspace<TContext> : NamedItem, ILocalSearch<TContext>
-      where TContext : ISingleSolutionHeuristicAlgorithmContext<SingleObjectiveBasicProblem<LinearLinkageEncoding>, Encodings.LinearLinkageEncoding.LinearLinkage>, ILinearLinkageSubspaceContext {
+      where TContext : ISingleSolutionHeuristicAlgorithmContext<SingleObjectiveBasicProblem<LinearLinkageEncoding>, LinearLinkage>, ILinearLinkageSubspaceContext {
 
     [StorableConstructor]
@@ -48,5 +48,5 @@
 
     public void Optimize(TContext context) {
-      var evalWrapper = new EvaluationWrapper<Encodings.LinearLinkageEncoding.LinearLinkage>(context.Problem, context.Solution);
+      var evalWrapper = new EvaluationWrapper<LinearLinkage>(context.Problem, context.Solution);
       var quality = context.Solution.Fitness;
       try {
Index: /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LocalSearch/StaticAPI/ExhaustiveLocalSearch.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LocalSearch/StaticAPI/ExhaustiveLocalSearch.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LocalSearch/StaticAPI/ExhaustiveLocalSearch.cs	(revision 14544)
@@ -26,8 +26,9 @@
 using HeuristicLab.Collections;
 using HeuristicLab.Core;
-
-namespace HeuristicLab.Algorithms.MemPR.LinearLinkage.LocalSearch {
+using HeuristicLab.Encodings.LinearLinkageEncoding;
+
+namespace HeuristicLab.Algorithms.MemPR.Grouping.LocalSearch {
   public static class ExhaustiveLocalSearch {
-    public static Tuple<int, int> Optimize(IRandom random, Encodings.LinearLinkageEncoding.LinearLinkage solution, ref double quality, bool maximization, Func<Encodings.LinearLinkageEncoding.LinearLinkage, IRandom, double> eval, CancellationToken token, bool[] subspace = null) {
+    public static Tuple<int, int> Optimize(IRandom random, LinearLinkage solution, ref double quality, bool maximization, Func<LinearLinkage, IRandom, double> eval, CancellationToken token, bool[] subspace = null) {
       var evaluations = 0;
       var current = solution;
@@ -153,5 +154,5 @@
     }
 
-    public static Tuple<int, int> OptimizeSwapOnly(IRandom random, Encodings.LinearLinkageEncoding.LinearLinkage solution, ref double quality, bool maximization, Func<Encodings.LinearLinkageEncoding.LinearLinkage, IRandom, double> eval, CancellationToken token) {
+    public static Tuple<int, int> OptimizeSwapOnly(IRandom random, LinearLinkage solution, ref double quality, bool maximization, Func<LinearLinkage, IRandom, double> eval, CancellationToken token) {
       var evaluations = 0;
       var current = solution;
Index: /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/StaticAPI/Trainer.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/StaticAPI/Trainer.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/StaticAPI/Trainer.cs	(revision 14544)
@@ -23,10 +23,11 @@
 using HeuristicLab.Algorithms.MemPR.Interfaces;
 using HeuristicLab.Core;
+using HeuristicLab.Encodings.LinearLinkageEncoding;
 
-namespace HeuristicLab.Algorithms.MemPR.LinearLinkage.SolutionModel.Univariate {
+namespace HeuristicLab.Algorithms.MemPR.Grouping.SolutionModel.Univariate {
   public static class Trainer {
 
-    public static ISolutionModel<Encodings.LinearLinkageEncoding.LinearLinkage> Train(IRandom random,
-        IEnumerable<Encodings.LinearLinkageEncoding.LinearLinkage> pop) {
+    public static ISolutionModel<LinearLinkage> Train(IRandom random,
+        IEnumerable<LinearLinkage> pop) {
       return UnivariateModel.Create(random, pop);
     }
Index: /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/UnbiasedModelTrainer.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/UnbiasedModelTrainer.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/UnbiasedModelTrainer.cs	(revision 14544)
@@ -24,12 +24,13 @@
 using HeuristicLab.Common;
 using HeuristicLab.Core;
+using HeuristicLab.Encodings.LinearLinkageEncoding;
 using HeuristicLab.Optimization;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
 
-namespace HeuristicLab.Algorithms.MemPR.LinearLinkage.SolutionModel.Univariate {
+namespace HeuristicLab.Algorithms.MemPR.Grouping.SolutionModel.Univariate {
   [Item("Unbiased Univariate Model Trainer (linear linkage)", "", ExcludeGenericTypeInfo = true)]
   [StorableClass]
   public class UniasedModelTrainer<TContext> : NamedItem, ISolutionModelTrainer<TContext>
-    where TContext : IPopulationBasedHeuristicAlgorithmContext<SingleObjectiveBasicProblem<Encodings.LinearLinkageEncoding.LinearLinkageEncoding>, Encodings.LinearLinkageEncoding.LinearLinkage>, ISolutionModelContext<Encodings.LinearLinkageEncoding.LinearLinkage> {
+    where TContext : IPopulationBasedHeuristicAlgorithmContext<SingleObjectiveBasicProblem<LinearLinkageEncoding>, LinearLinkage>, ISolutionModelContext<LinearLinkage> {
     
     [StorableConstructor]
Index: /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/UnivariateSolutionModel.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/UnivariateSolutionModel.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/UnivariateSolutionModel.cs	(revision 14544)
@@ -26,10 +26,11 @@
 using HeuristicLab.Core;
 using HeuristicLab.Data;
+using HeuristicLab.Encodings.LinearLinkageEncoding;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
 
-namespace HeuristicLab.Algorithms.MemPR.LinearLinkage.SolutionModel.Univariate {
+namespace HeuristicLab.Algorithms.MemPR.Grouping.SolutionModel.Univariate {
   [Item("Univariate solution model (linear linkage)", "")]
   [StorableClass]
-  public sealed class UnivariateModel : Item, ISolutionModel<Encodings.LinearLinkageEncoding.LinearLinkage> {
+  public sealed class UnivariateModel : Item, ISolutionModel<LinearLinkage> {
     [Storable]
     public IntMatrix Frequencies { get; set; }
@@ -61,7 +62,7 @@
     }
 
-    public Encodings.LinearLinkageEncoding.LinearLinkage Sample() {
+    public LinearLinkage Sample() {
       var N = Frequencies.Rows;
-      var centroid = Encodings.LinearLinkageEncoding.LinearLinkage.SingleElementGroups(N);
+      var centroid = LinearLinkage.SingleElementGroups(N);
       var dict = new Dictionary<int, int>();
       for (var i = N - 1; i >= 0; i--) {
@@ -87,5 +88,5 @@
     }
 
-    public static ISolutionModel<Encodings.LinearLinkageEncoding.LinearLinkage> Create(IRandom random, IEnumerable<Encodings.LinearLinkageEncoding.LinearLinkage> population) {
+    public static ISolutionModel<LinearLinkage> Create(IRandom random, IEnumerable<LinearLinkage> population) {
       var iter = population.GetEnumerator();
       if (!iter.MoveNext()) throw new ArgumentException("Cannot create solution model from empty population.");
Index: /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/MemPRAlgorithm.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/MemPRAlgorithm.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/MemPRAlgorithm.cs	(revision 14544)
@@ -24,8 +24,6 @@
 using System.ComponentModel;
 using System.Linq;
-using System.Runtime.CompilerServices;
 using System.Threading;
 using HeuristicLab.Algorithms.MemPR.Interfaces;
-using HeuristicLab.Algorithms.MemPR.Util;
 using HeuristicLab.Analysis;
 using HeuristicLab.Common;
@@ -35,4 +33,5 @@
 using HeuristicLab.Parameters;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Random;
 
 namespace HeuristicLab.Algorithms.MemPR {
@@ -231,6 +230,7 @@
           var child = Create(token);
           Context.LocalSearchEvaluations += HillClimb(child, token);
-          if (Replace(child, token) >= 0)
-            Analyze(token);
+          Context.AddToPopulation(child);
+          Context.BestQuality = child.Fitness;
+          Analyze(token);
           token.ThrowIfCancellationRequested();
           if (Terminate()) return;
@@ -249,21 +249,12 @@
     private void Iterate(CancellationToken token) {
       var replaced = false;
-
-      var i1 = Context.Random.Next(Context.PopulationCount);
-      var i2 = Context.Random.Next(Context.PopulationCount);
-      while (i1 == i2) i2 = Context.Random.Next(Context.PopulationCount);
-
-      var p1 = Context.AtPopulation(i1);
-      var p2 = Context.AtPopulation(i2);
-
-      var parentDist = Dist(p1, p2);
-
       ISingleObjectiveSolutionScope<TSolution> offspring = null;
-      int replPos = -1;
-
-      if (Context.Random.NextDouble() > parentDist * parentDist) {
-        offspring = BreedAndImprove(p1, p2, token);
-        replPos = Replace(offspring, token);
-        if (replPos >= 0) {
+      
+      offspring = Breed(token);
+      if (offspring != null) {
+        if (Context.PopulationCount < MaximumPopulationSize)
+          HillClimb(offspring, token);
+        var replNew = Replace(offspring, token);
+        if (replNew) {
           replaced = true;
           Context.ByBreeding++;
@@ -271,8 +262,9 @@
       }
 
-      if (Context.Random.NextDouble() < Math.Sqrt(parentDist)) {
-        offspring = RelinkAndImprove(p1, p2, token);
-        replPos = Replace(offspring, token);
-        if (replPos >= 0) {
+      offspring = Relink(token);
+      if (offspring != null) {
+        if (Context.PopulationCount < MaximumPopulationSize)
+          HillClimb(offspring, token);
+        if (Replace(offspring, token)) {
           replaced = true;
           Context.ByRelinking++;
@@ -280,31 +272,46 @@
       }
 
-      offspring = PerformSampling(token);
-      replPos = Replace(offspring, token);
-      if (replPos >= 0) {
-        replaced = true;
-        Context.BySampling++;
-      }
-
-      if (!replaced) {
-        offspring = Create(token);
-        if (HillclimbingSuited(offspring)) {
+      offspring = Delink(token);
+      if (offspring != null) {
+        if (Context.PopulationCount < MaximumPopulationSize)
           HillClimb(offspring, token);
-          replPos = Replace(offspring, token);
-          if (replPos >= 0) {
+        if (Replace(offspring, token)) {
+          replaced = true;
+          Context.ByDelinking++;
+        }
+      }
+
+      offspring = Sample(token);
+      if (offspring != null) {
+        if (Context.PopulationCount < MaximumPopulationSize)
+          HillClimb(offspring, token);
+        if (Replace(offspring, token)) {
+          replaced = true;
+          Context.BySampling++;
+        }
+      }
+
+      if (!replaced && offspring != null) {
+        if (Context.HillclimbingSuited(offspring)) {
+          HillClimb(offspring, token);
+          if (Replace(offspring, token)) {
             Context.ByHillclimbing++;
             replaced = true;
           }
-        } else {
-          offspring = (ISingleObjectiveSolutionScope<TSolution>)Context.AtPopulation(Context.Random.Next(Context.PopulationCount)).Clone();
-          Mutate(offspring, token);
-          PerformTabuWalk(offspring, Context.LocalSearchEvaluations, token);
-          replPos = Replace(offspring, token);
-          if (replPos >= 0) {
-            Context.ByTabuwalking++;
-            replaced = true;
-          }
-        }
-      }
+        }
+      }
+
+      if (!replaced) {
+        offspring = (ISingleObjectiveSolutionScope<TSolution>)Context.Population.SampleRandom(Context.Random).Clone();
+        var before = offspring.Fitness;
+        AdaptiveWalk(offspring, Context.LocalSearchEvaluations * 2, token);
+        Context.AdaptivewalkingStat.Add(Tuple.Create(before, offspring.Fitness));
+        if (Context.AdaptivewalkingStat.Count % 10 == 0) Context.RelearnAdaptiveWalkPerformanceModel();
+        if (Replace(offspring, token)) {
+          Context.ByAdaptivewalking++;
+          replaced = true;
+        }
+      }
+
       Context.Iterations++;
     }
@@ -327,4 +334,7 @@
         Results.Add(new Result("ByRelinking", new IntValue(Context.ByRelinking)));
       else ((IntValue)res.Value).Value = Context.ByRelinking;
+      if (!Results.TryGetValue("ByDelinking", out res))
+        Results.Add(new Result("ByDelinking", new IntValue(Context.ByDelinking)));
+      else ((IntValue)res.Value).Value = Context.ByDelinking;
       if (!Results.TryGetValue("BySampling", out res))
         Results.Add(new Result("BySampling", new IntValue(Context.BySampling)));
@@ -333,41 +343,93 @@
         Results.Add(new Result("ByHillclimbing", new IntValue(Context.ByHillclimbing)));
       else ((IntValue)res.Value).Value = Context.ByHillclimbing;
-      if (!Results.TryGetValue("ByTabuwalking", out res))
-        Results.Add(new Result("ByTabuwalking", new IntValue(Context.ByTabuwalking)));
-      else ((IntValue)res.Value).Value = Context.ByTabuwalking;
-
-      var sp = new ScatterPlot("Parent1 vs Offspring", "");
-      sp.Rows.Add(new ScatterPlotDataRow("corr", "", Context.BreedingStat.Select(x => new Point2D<double>(x.Item1, x.Item3))) { VisualProperties = { PointSize = 6 }});
-      if (!Results.TryGetValue("BreedingStat1", out res)) {
-        Results.Add(new Result("BreedingStat1", sp));
+      if (!Results.TryGetValue("ByAdaptivewalking", out res))
+        Results.Add(new Result("ByAdaptivewalking", new IntValue(Context.ByAdaptivewalking)));
+      else ((IntValue)res.Value).Value = Context.ByAdaptivewalking;
+
+      var sp = new ScatterPlot("Breeding Correlation", "");
+      sp.Rows.Add(new ScatterPlotDataRow("Parent1 vs Offspring", "", Context.BreedingStat.Select(x => new Point2D<double>(x.Item1, x.Item3))) { VisualProperties = { PointSize = 6 }});
+      sp.Rows.Add(new ScatterPlotDataRow("Parent2 vs Offspring", "", Context.BreedingStat.Select(x => new Point2D<double>(x.Item2, x.Item3))) { VisualProperties = { PointSize = 6 } });
+      if (!Results.TryGetValue("BreedingStat", out res)) {
+        Results.Add(new Result("BreedingStat", sp));
       } else res.Value = sp;
 
-      sp = new ScatterPlot("Parent2 vs Offspring", "");
-      sp.Rows.Add(new ScatterPlotDataRow("corr", "", Context.BreedingStat.Select(x => new Point2D<double>(x.Item2, x.Item3))) { VisualProperties = { PointSize = 6 } });
-      if (!Results.TryGetValue("BreedingStat2", out res)) {
-        Results.Add(new Result("BreedingStat2", sp));
+      sp = new ScatterPlot("Relinking Correlation", "");
+      sp.Rows.Add(new ScatterPlotDataRow("A vs Relink", "", Context.RelinkingStat.Select(x => new Point2D<double>(x.Item1, x.Item3))) { VisualProperties = { PointSize = 6 } });
+      sp.Rows.Add(new ScatterPlotDataRow("B vs Relink", "", Context.RelinkingStat.Select(x => new Point2D<double>(x.Item2, x.Item3))) { VisualProperties = { PointSize = 6 } });
+      if (!Results.TryGetValue("RelinkingStat", out res)) {
+        Results.Add(new Result("RelinkingStat", sp));
       } else res.Value = sp;
 
-      sp = new ScatterPlot("Solution vs Local Optimum", "");
-      sp.Rows.Add(new ScatterPlotDataRow("corr", "", Context.HillclimbingStat.Select(x => new Point2D<double>(x.Item1, x.Item2))) { VisualProperties = { PointSize = 6 } });
+      sp = new ScatterPlot("Delinking Correlation", "");
+      sp.Rows.Add(new ScatterPlotDataRow("A vs Delink", "", Context.DelinkingStat.Select(x => new Point2D<double>(x.Item1, x.Item3))) { VisualProperties = { PointSize = 6 } });
+      sp.Rows.Add(new ScatterPlotDataRow("B vs Delink", "", Context.DelinkingStat.Select(x => new Point2D<double>(x.Item2, x.Item3))) { VisualProperties = { PointSize = 6 } });
+      if (!Results.TryGetValue("DelinkingStat", out res)) {
+        Results.Add(new Result("DelinkingStat", sp));
+      } else res.Value = sp;
+
+      sp = new ScatterPlot("Sampling Correlation", "");
+      sp.Rows.Add(new ScatterPlotDataRow("AvgFitness vs Sample", "", Context.SamplingStat.Select(x => new Point2D<double>(x.Item1, x.Item2))) { VisualProperties = { PointSize = 6 } });
+      if (!Results.TryGetValue("SampleStat", out res)) {
+        Results.Add(new Result("SampleStat", sp));
+      } else res.Value = sp;
+
+      sp = new ScatterPlot("Hillclimbing Correlation", "");
+      sp.Rows.Add(new ScatterPlotDataRow("Start vs End", "", Context.HillclimbingStat.Select(x => new Point2D<double>(x.Item1, x.Item2))) { VisualProperties = { PointSize = 6 } });
       if (!Results.TryGetValue("HillclimbingStat", out res)) {
         Results.Add(new Result("HillclimbingStat", sp));
       } else res.Value = sp;
 
-      sp = new ScatterPlot("Solution vs Tabu Walk", "");
-      sp.Rows.Add(new ScatterPlotDataRow("corr", "", Context.TabuwalkingStat.Select(x => new Point2D<double>(x.Item1, x.Item2))) { VisualProperties = { PointSize = 6 } });
-      if (!Results.TryGetValue("TabuwalkingStat", out res)) {
-        Results.Add(new Result("TabuwalkingStat", sp));
+      sp = new ScatterPlot("Adaptivewalking Correlation", "");
+      sp.Rows.Add(new ScatterPlotDataRow("Start vs Best", "", Context.AdaptivewalkingStat.Select(x => new Point2D<double>(x.Item1, x.Item2))) { VisualProperties = { PointSize = 6 } });
+      if (!Results.TryGetValue("AdaptivewalkingStat", out res)) {
+        Results.Add(new Result("AdaptivewalkingStat", sp));
       } else res.Value = sp;
 
+      if (Context.BreedingPerformanceModel != null) {
+        var sol = Context.GetSolution(Context.BreedingPerformanceModel, Context.BreedingStat);
+        if (!Results.TryGetValue("Breeding Performance", out res)) {
+          Results.Add(new Result("Breeding Performance", sol));
+        } else res.Value = sol;
+      }
+      if (Context.RelinkingPerformanceModel != null) {
+        var sol = Context.GetSolution(Context.RelinkingPerformanceModel, Context.RelinkingStat);
+        if (!Results.TryGetValue("Relinking Performance", out res)) {
+          Results.Add(new Result("Relinking Performance", sol));
+        } else res.Value = sol;
+      }
+      if (Context.DelinkingPerformanceModel != null) {
+        var sol = Context.GetSolution(Context.DelinkingPerformanceModel, Context.DelinkingStat);
+        if (!Results.TryGetValue("Delinking Performance", out res)) {
+          Results.Add(new Result("Delinking Performance", sol));
+        } else res.Value = sol;
+      }
+      if (Context.SamplingPerformanceModel != null) {
+        var sol = Context.GetSolution(Context.SamplingPerformanceModel, Context.SamplingStat);
+        if (!Results.TryGetValue("Sampling Performance", out res)) {
+          Results.Add(new Result("Sampling Performance", sol));
+        } else res.Value = sol;
+      }
+      if (Context.HillclimbingPerformanceModel != null) {
+        var sol = Context.GetSolution(Context.HillclimbingPerformanceModel, Context.HillclimbingStat);
+        if (!Results.TryGetValue("Hillclimbing Performance", out res)) {
+          Results.Add(new Result("Hillclimbing Performance", sol));
+        } else res.Value = sol;
+      }
+      if (Context.AdaptiveWalkPerformanceModel != null) {
+        var sol = Context.GetSolution(Context.AdaptiveWalkPerformanceModel, Context.AdaptivewalkingStat);
+        if (!Results.TryGetValue("Adaptivewalk Performance", out res)) {
+          Results.Add(new Result("Adaptivewalk Performance", sol));
+        } else res.Value = sol;
+      }
+
       RunOperator(Analyzer, Context.Scope, token);
     }
 
-    protected int Replace(ISingleObjectiveSolutionScope<TSolution> child, CancellationToken token) {
+    protected bool Replace(ISingleObjectiveSolutionScope<TSolution> child, CancellationToken token) {
       if (double.IsNaN(child.Fitness)) {
         Evaluate(child, token);
         Context.IncrementEvaluatedSolutions(1);
       }
-      if (IsBetter(child.Fitness, Context.BestQuality)) {
+      if (Context.IsBetter(child.Fitness, Context.BestQuality)) {
         Context.BestQuality = child.Fitness;
         Context.BestSolution = (TSolution)child.Solution.Clone();
@@ -379,5 +441,5 @@
         if (Context.PopulationCount < popSize) {
           Context.AddToPopulation(child);
-          return Context.PopulationCount - 1;
+          return true;// Context.PopulationCount - 1;
         }
         
@@ -385,6 +447,6 @@
         var candidates = Context.Population.Select((p, i) => new { Index = i, Individual = p })
                                          .Where(x => x.Individual.Fitness == child.Fitness
-                                           || IsBetter(child, x.Individual)).ToList();
-        if (candidates.Count == 0) return -1;
+                                           || Context.IsBetter(child, x.Individual)).ToList();
+        if (candidates.Count == 0) return false;// -1;
 
         var repCand = -1;
@@ -435,5 +497,5 @@
           // a worse solution with smallest distance is chosen
           var minDist = double.MaxValue;
-          foreach (var c in candidates.Where(x => IsBetter(child, x.Individual))) {
+          foreach (var c in candidates.Where(x => Context.IsBetter(child, x.Individual))) {
             var d = Dist(c.Individual, child);
             if (d < minDist) {
@@ -447,21 +509,10 @@
         // no worse solutions and those on the same plateau are all better
         // stretched out than the new one
-        if (repCand < 0) return -1;
+        if (repCand < 0) return false;// -1;
         
         Context.ReplaceAtPopulation(repCand, child);
-        return repCand;
-      }
-      return -1;
-    }
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    protected bool IsBetter(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b) {
-      return IsBetter(a.Fitness, b.Fitness);
-    }
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    protected bool IsBetter(double a, double b) {
-      return double.IsNaN(b) && !double.IsNaN(a)
-        || Problem.Maximization && a > b
-        || !Problem.Maximization && a < b;
+        return true;// repCand;
+      }
+      return false;// -1;
     }
     
@@ -497,9 +548,10 @@
       var after = scope.Fitness;
       Context.HillclimbingStat.Add(Tuple.Create(before, after));
+      if (Context.HillclimbingStat.Count % 10 == 0) Context.RelearnHillclimbingPerformanceModel();
       Context.IncrementEvaluatedSolutions(lscontext.EvaluatedSolutions);
       return lscontext.EvaluatedSolutions;
     }
 
-    protected virtual void PerformTabuWalk(ISingleObjectiveSolutionScope<TSolution> scope, int steps, CancellationToken token, ISolutionSubspace<TSolution> subspace = null) {
+    protected virtual void AdaptiveClimb(ISingleObjectiveSolutionScope<TSolution> scope, int maxEvals, CancellationToken token, ISolutionSubspace<TSolution> subspace = null) {
       if (double.IsNaN(scope.Fitness)) {
         Evaluate(scope, token);
@@ -508,29 +560,16 @@
       var before = scope.Fitness;
       var newScope = (ISingleObjectiveSolutionScope<TSolution>)scope.Clone();
-      var newSteps = TabuWalk(newScope, steps, token, subspace);
-      Context.TabuwalkingStat.Add(Tuple.Create(before, newScope.Fitness));
-      //Context.HcSteps = (int)Math.Ceiling(Context.HcSteps * (1.0 + Context.TabuwalkingStat.Count) / (2.0 + Context.TabuwalkingStat.Count) + newSteps / (2.0 + Context.TabuwalkingStat.Count));
-      if (IsBetter(newScope, scope) || (newScope.Fitness == scope.Fitness && Dist(newScope, scope) > 0))
+      AdaptiveWalk(newScope, maxEvals, token, subspace);
+      Context.AdaptivewalkingStat.Add(Tuple.Create(before, newScope.Fitness));
+      if (Context.AdaptivewalkingStat.Count % 10 == 0) Context.RelearnAdaptiveWalkPerformanceModel();
+      if (Context.IsBetter(newScope, scope))
         scope.Adopt(newScope);
     }
-    protected abstract int TabuWalk(ISingleObjectiveSolutionScope<TSolution> scope, int maxEvals, CancellationToken token, ISolutionSubspace<TSolution> subspace = null);
-    protected virtual void TabuClimb(ISingleObjectiveSolutionScope<TSolution> scope, int steps, CancellationToken token, ISolutionSubspace<TSolution> subspace = null) {
-      if (double.IsNaN(scope.Fitness)) {
-        Evaluate(scope, token);
-        Context.IncrementEvaluatedSolutions(1);
-      }
-      var before = scope.Fitness;
-      var newScope = (ISingleObjectiveSolutionScope<TSolution>)scope.Clone();
-      var newSteps = TabuWalk(newScope, steps, token, subspace);
-      Context.TabuwalkingStat.Add(Tuple.Create(before, newScope.Fitness));
-      //Context.HcSteps = (int)Math.Ceiling(Context.HcSteps * (1.0 + Context.TabuwalkingStat.Count) / (2.0 + Context.TabuwalkingStat.Count) + newSteps / (2.0 + Context.TabuwalkingStat.Count));
-      if (IsBetter(newScope, scope) || (newScope.Fitness == scope.Fitness && Dist(newScope, scope) > 0))
-        scope.Adopt(newScope);
-    }
+    protected abstract void AdaptiveWalk(ISingleObjectiveSolutionScope<TSolution> scope, int maxEvals, CancellationToken token, ISolutionSubspace<TSolution> subspace = null);
+    
     #endregion
-    
+
     #region Breed
-    protected virtual ISingleObjectiveSolutionScope<TSolution> PerformBreeding(CancellationToken token) {
-      if (Context.PopulationCount < 2) throw new InvalidOperationException("Cannot breed from population with less than 2 individuals.");
+    protected virtual ISingleObjectiveSolutionScope<TSolution> Breed(CancellationToken token) {
       var i1 = Context.Random.Next(Context.PopulationCount);
       var i2 = Context.Random.Next(Context.PopulationCount);
@@ -549,33 +588,29 @@
       }
 
-      return BreedAndImprove(p1, p2, token);
-    }
-
-    protected virtual ISingleObjectiveSolutionScope<TSolution> BreedAndImprove(ISingleObjectiveSolutionScope<TSolution> p1, ISingleObjectiveSolutionScope<TSolution> p2, CancellationToken token) {
-      var offspring = Cross(p1, p2, token);
-      var subspace = CalculateSubspace(new[] { p1.Solution, p2.Solution });
-      if (Context.Random.NextDouble() < MutationProbabilityMagicConst) {
-        Mutate(offspring, token, subspace); // mutate the solutions, especially to widen the sub-space
-      }
-      if (double.IsNaN(offspring.Fitness)) {
-        Evaluate(offspring, token);
-        Context.IncrementEvaluatedSolutions(1);
-      }
-      Context.BreedingStat.Add(Tuple.Create(p1.Fitness, p2.Fitness, offspring.Fitness));
-      if ((IsBetter(offspring, p1) && IsBetter(offspring, p2))
-        || Context.Population.Any(p => IsBetter(offspring, p))) return offspring;
-
-      if (HillclimbingSuited(offspring))
-        HillClimb(offspring, token, subspace); // perform hillclimb in the solution sub-space
-      return offspring;
-    }
-
-    protected abstract ISingleObjectiveSolutionScope<TSolution> Cross(ISingleObjectiveSolutionScope<TSolution> p1, ISingleObjectiveSolutionScope<TSolution> p2, CancellationToken token);
-    protected abstract void Mutate(ISingleObjectiveSolutionScope<TSolution> offspring, CancellationToken token, ISolutionSubspace<TSolution> subspace = null);
+      if (Context.BreedingSuited(p1, p2)) {
+        var offspring = Breed(p1, p2, token);
+
+        if (double.IsNaN(offspring.Fitness)) {
+          Evaluate(offspring, token);
+          Context.IncrementEvaluatedSolutions(1);
+        }
+
+        // new best solutions are improved using hill climbing in full solution space
+        if (Context.Population.All(p => Context.IsBetter(offspring, p)))
+          HillClimb(offspring, token);
+        else HillClimb(offspring, token, CalculateSubspace(new[] { p1.Solution, p2.Solution }));
+
+        Context.AddBreedingResult(p1, p2, offspring);
+        if (Context.BreedingStat.Count % 10 == 0) Context.RelearnBreedingPerformanceModel();
+        return offspring;
+      }
+      return null;
+    }
+
+    protected abstract ISingleObjectiveSolutionScope<TSolution> Breed(ISingleObjectiveSolutionScope<TSolution> p1, ISingleObjectiveSolutionScope<TSolution> p2, CancellationToken token);
     #endregion
 
-    #region Relink
-    protected virtual ISingleObjectiveSolutionScope<TSolution> PerformRelinking(CancellationToken token) {
-      if (Context.PopulationCount < 2) throw new InvalidOperationException("Cannot breed from population with less than 2 individuals.");
+    #region Relink/Delink
+    protected virtual ISingleObjectiveSolutionScope<TSolution> Relink(CancellationToken token) {
       var i1 = Context.Random.Next(Context.PopulationCount);
       var i2 = Context.Random.Next(Context.PopulationCount);
@@ -585,108 +620,65 @@
       var p2 = Context.AtPopulation(i2);
 
-      return RelinkAndImprove(p1, p2, token);
-    }
-
-    protected virtual ISingleObjectiveSolutionScope<TSolution> RelinkAndImprove(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b, CancellationToken token) {
-      var child = Relink(a, b, token);
-      if (IsBetter(child, a) && IsBetter(child, b)) return child;
-
-      var dist1 = Dist(child, a);
-      var dist2 = Dist(child, b);
-      if (dist1 > 0 && dist2 > 0) {
-        var subspace = CalculateSubspace(new[] { a.Solution, b.Solution }, inverse: true);
-        if (HillclimbingSuited(child)) {
-          HillClimb(child, token, subspace); // perform hillclimb in solution sub-space
-        }
-      }
-      return child;
-    }
-
-    protected abstract ISingleObjectiveSolutionScope<TSolution> Relink(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b, CancellationToken token);
+      return Context.RelinkSuited(p1, p2) ? PerformRelinking(p1, p2, token, delink: false) : null;
+    }
+
+    protected virtual ISingleObjectiveSolutionScope<TSolution> Delink(CancellationToken token) {
+      var i1 = Context.Random.Next(Context.PopulationCount);
+      var i2 = Context.Random.Next(Context.PopulationCount);
+      while (i1 == i2) i2 = Context.Random.Next(Context.PopulationCount);
+
+      var p1 = Context.AtPopulation(i1);
+      var p2 = Context.AtPopulation(i2);
+
+      return Context.DelinkSuited(p1, p2) ? PerformRelinking(p1, p2, token, delink: true) : null;
+    }
+
+    protected virtual ISingleObjectiveSolutionScope<TSolution> PerformRelinking(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b, CancellationToken token, bool delink = false) {
+      var relink = Link(a, b, token, delink);
+
+      if (double.IsNaN(relink.Fitness)) {
+        Evaluate(relink, token);
+        Context.IncrementEvaluatedSolutions(1);
+      }
+
+      // new best solutions are improved using hill climbing
+      if (Context.Population.All(p => Context.IsBetter(relink, p)))
+        HillClimb(relink, token);
+
+      if (delink) {
+        Context.AddDelinkingResult(a, b, relink);
+        if (Context.DelinkingStat.Count % 10 == 0) Context.RelearnDelinkingPerformanceModel();
+      } else {
+        Context.AddRelinkingResult(a, b, relink);
+        if (context.RelinkingStat.Count % 10 == 0) Context.RelearnRelinkingPerformanceModel();
+      }
+      return relink;
+    }
+
+    protected abstract ISingleObjectiveSolutionScope<TSolution> Link(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b, CancellationToken token, bool delink = false);
     #endregion
 
     #region Sample
-    protected virtual ISingleObjectiveSolutionScope<TSolution> PerformSampling(CancellationToken token) {
-      SolutionModelTrainerParameter.Value.TrainModel(Context);
-      var sample = ToScope(Context.Model.Sample());
-      Evaluate(sample, token);
-      Context.IncrementEvaluatedSolutions(1);
-      if (Context.Population.Any(p => IsBetter(sample, p) || sample.Fitness == p.Fitness)) return sample;
-
-      if (HillclimbingSuited(sample)) {
-        var subspace = CalculateSubspace(Context.Population.Select(x => x.Solution));
-        HillClimb(sample, token, subspace);
-      }
-      return sample;
+    protected virtual ISingleObjectiveSolutionScope<TSolution> Sample(CancellationToken token) {
+      if (Context.PopulationCount == MaximumPopulationSize && Context.SamplingSuited()) {
+        SolutionModelTrainerParameter.Value.TrainModel(Context);
+        ISingleObjectiveSolutionScope<TSolution> bestSample = null;
+        var tries = 1;
+        for (; tries < Context.LocalSearchEvaluations; tries++) {
+          var sample = ToScope(Context.Model.Sample());
+          Evaluate(sample, token);
+          if (bestSample == null || Context.IsBetter(sample, bestSample)) {
+            bestSample = sample;
+          }
+          if (Context.Population.Any(x => !Context.IsBetter(x, bestSample))) break;
+        }
+        Context.IncrementEvaluatedSolutions(tries);
+        Context.AddSamplingResult(bestSample);
+        if (Context.SamplingStat.Count % 10 == 0) Context.RelearnSamplingPerformanceModel();
+        return bestSample;
+      }
+      return null;
     }
     #endregion
-
-    protected bool HillclimbingSuited(ISingleObjectiveSolutionScope<TSolution> scope) {
-      return Context.Random.NextDouble() < ProbabilityAccept(scope, Context.HillclimbingStat);
-    }
-    protected bool HillclimbingSuited(double startingFitness) {
-      return Context.Random.NextDouble() < ProbabilityAccept(startingFitness, Context.HillclimbingStat);
-    }
-    protected bool TabuwalkingSuited(ISingleObjectiveSolutionScope<TSolution> scope) {
-      return Context.Random.NextDouble() < ProbabilityAccept(scope, Context.TabuwalkingStat);
-    }
-    protected bool TabuwalkingSuited(double startingFitness) {
-      return Context.Random.NextDouble() < ProbabilityAccept(startingFitness, Context.TabuwalkingStat);
-    }
-
-    protected double ProbabilityAccept(ISingleObjectiveSolutionScope<TSolution> scope, IList<Tuple<double, double>> data) {
-      if (double.IsNaN(scope.Fitness)) {
-        Evaluate(scope, CancellationToken.None);
-        Context.IncrementEvaluatedSolutions(1);
-      }
-      return ProbabilityAccept(scope.Fitness, data);
-    }
-    protected double ProbabilityAccept(double startingFitness, IList<Tuple<double, double>> data) {
-      if (data.Count < 10) return 1.0;
-      int[] clusterValues;
-      var centroids = CkMeans1D.Cluster(data.Select(x => x.Item1).ToArray(), 2, out clusterValues);
-      var cluster = Math.Abs(startingFitness - centroids.First().Key) < Math.Abs(startingFitness - centroids.Last().Key) ? centroids.First().Value : centroids.Last().Value;
-
-      var samples = 0;
-      double meanStart = 0, meanStartOld = 0, meanEnd = 0, meanEndOld = 0;
-      double varStart = 0, varStartOld = 0, varEnd = 0, varEndOld = 0;
-      for (var i = 0; i < data.Count; i++) {
-        if (clusterValues[i] != cluster) continue;
-
-        samples++;
-        var x = data[i].Item1;
-        var y = data[i].Item2;
-
-        if (samples == 1) {
-          meanStartOld = x;
-          meanEndOld = y;
-        } else {
-          meanStart = meanStartOld + (x - meanStartOld) / samples;
-          meanEnd = meanEndOld + (x - meanEndOld) / samples;
-          varStart = varStartOld + (x - meanStartOld) * (x - meanStart) / (samples - 1);
-          varEnd = varEndOld + (x - meanEndOld) * (x - meanEnd) / (samples - 1);
-
-          meanStartOld = meanStart;
-          meanEndOld = meanEnd;
-          varStartOld = varStart;
-          varEndOld = varEnd;
-        }
-      }
-      if (samples < 5) return 1.0;
-      var cov = data.Select((v, i) => new { Index = i, Value = v }).Where(x => clusterValues[x.Index] == cluster).Select(x => x.Value).Sum(x => (x.Item1 - meanStart) * (x.Item2 - meanEnd)) / data.Count;
-
-      var biasedMean = meanEnd + cov / varStart * (startingFitness - meanStart);
-      var biasedStdev = Math.Sqrt(varEnd - (cov * cov) / varStart);
-
-      if (Problem.Maximization) {
-        var goal = Context.Population.Min(x => x.Fitness);
-        var z = (goal - biasedMean) / biasedStdev;
-        return 1.0 - Phi(z); // P(X >= z)
-      } else {
-        var goal = Context.Population.Max(x => x.Fitness);
-        var z = (goal - biasedMean) / biasedStdev;
-        return Phi(z); // P(X <= z)
-      }
-    }
 
     protected virtual bool Terminate() {
@@ -730,32 +722,4 @@
     }
     #endregion
-
-    #region Math Helper
-    // normal distribution CDF (left of x) for N(0;1) standard normal distribution
-    // from http://www.johndcook.com/blog/csharp_phi/
-    // license: "This code is in the public domain. Do whatever you want with it, no strings attached."
-    // added: 2016-11-19 21:46 CET
-    protected static double Phi(double x) {
-      // constants
-      double a1 = 0.254829592;
-      double a2 = -0.284496736;
-      double a3 = 1.421413741;
-      double a4 = -1.453152027;
-      double a5 = 1.061405429;
-      double p = 0.3275911;
-
-      // Save the sign of x
-      int sign = 1;
-      if (x < 0)
-        sign = -1;
-      x = Math.Abs(x) / Math.Sqrt(2.0);
-
-      // A&S formula 7.1.26
-      double t = 1.0 / (1.0 + p * x);
-      double y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.Exp(-x * x);
-
-      return 0.5 * (1.0 + sign * y);
-    }
-    #endregion
   }
 }
Index: /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/MemPRContext.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/MemPRContext.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/MemPRContext.cs	(revision 14544)
@@ -23,4 +23,7 @@
 using System.Collections.Generic;
 using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using HeuristicLab.Algorithms.DataAnalysis;
 using HeuristicLab.Algorithms.MemPR.Interfaces;
 using HeuristicLab.Common;
@@ -30,5 +33,7 @@
 using HeuristicLab.Parameters;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Problems.DataAnalysis;
 using HeuristicLab.Random;
+using ExecutionContext = HeuristicLab.Core.ExecutionContext;
 
 namespace HeuristicLab.Algorithms.MemPR {
@@ -123,4 +128,11 @@
 
     [Storable]
+    private IValueParameter<IntValue> byDelinking;
+    public int ByDelinking {
+      get { return byDelinking.Value.Value; }
+      set { byDelinking.Value.Value = value; }
+    }
+
+    [Storable]
     private IValueParameter<IntValue> bySampling;
     public int BySampling {
@@ -137,8 +149,8 @@
 
     [Storable]
-    private IValueParameter<IntValue> byTabuwalking;
-    public int ByTabuwalking {
-      get { return byTabuwalking.Value.Value; }
-      set { byTabuwalking.Value.Value = value; }
+    private IValueParameter<IntValue> byAdaptivewalking;
+    public int ByAdaptivewalking {
+      get { return byAdaptivewalking.Value.Value; }
+      set { byAdaptivewalking.Value.Value = value; }
     }
 
@@ -162,8 +174,16 @@
       return scope.SubScopes[index] as ISingleObjectiveSolutionScope<TSolution>;
     }
+    public void SortPopulation() {
+      scope.SubScopes.Replace(scope.SubScopes.OfType<ISingleObjectiveSolutionScope<TSolution>>().OrderBy(x => Problem.Maximization ? -x.Fitness : x.Fitness).ToList());
+    }
     public int PopulationCount {
       get { return scope.SubScopes.Count; }
     }
 
+    [Storable]
+    private IConfidenceRegressionModel breedingPerformanceModel;
+    public IConfidenceRegressionModel BreedingPerformanceModel {
+      get { return breedingPerformanceModel; }
+    }
     [Storable]
     private List<Tuple<double, double, double>> breedingStat;
@@ -172,4 +192,39 @@
     }
     [Storable]
+    private IConfidenceRegressionModel relinkingPerformanceModel;
+    public IConfidenceRegressionModel RelinkingPerformanceModel {
+      get { return relinkingPerformanceModel; }
+    }
+    [Storable]
+    private List<Tuple<double, double, double>> relinkingStat;
+    public List<Tuple<double, double, double>> RelinkingStat {
+      get { return relinkingStat; }
+    }
+    [Storable]
+    private IConfidenceRegressionModel delinkingPerformanceModel;
+    public IConfidenceRegressionModel DelinkingPerformanceModel {
+      get { return delinkingPerformanceModel; }
+    }
+    [Storable]
+    private List<Tuple<double, double, double>> delinkingStat;
+    public List<Tuple<double, double, double>> DelinkingStat {
+      get { return delinkingStat; }
+    }
+    [Storable]
+    private IConfidenceRegressionModel samplingPerformanceModel;
+    public IConfidenceRegressionModel SamplingPerformanceModel {
+      get { return samplingPerformanceModel; }
+    }
+    [Storable]
+    private List<Tuple<double, double>> samplingStat;
+    public List<Tuple<double, double>> SamplingStat {
+      get { return samplingStat; }
+    }
+    [Storable]
+    private IConfidenceRegressionModel hillclimbingPerformanceModel;
+    public IConfidenceRegressionModel HillclimbingPerformanceModel {
+      get { return hillclimbingPerformanceModel; }
+    }
+    [Storable]
     private List<Tuple<double, double>> hillclimbingStat;
     public List<Tuple<double, double>> HillclimbingStat {
@@ -177,7 +232,12 @@
     }
     [Storable]
-    private List<Tuple<double, double>> tabuwalkingStat;
-    public List<Tuple<double, double>> TabuwalkingStat {
-      get { return tabuwalkingStat; }
+    private IConfidenceRegressionModel adaptiveWalkPerformanceModel;
+    public IConfidenceRegressionModel AdaptiveWalkPerformanceModel {
+      get { return adaptiveWalkPerformanceModel; }
+    }
+    [Storable]
+    private List<Tuple<double, double>> adaptivewalkingStat;
+    public List<Tuple<double, double>> AdaptivewalkingStat {
+      get { return adaptivewalkingStat; }
     }
 
@@ -199,12 +259,22 @@
       byBreeding = cloner.Clone(original.byBreeding);
       byRelinking = cloner.Clone(original.byRelinking);
+      byDelinking = cloner.Clone(original.byDelinking);
       bySampling = cloner.Clone(original.bySampling);
       byHillclimbing = cloner.Clone(original.byHillclimbing);
-      byTabuwalking = cloner.Clone(original.byTabuwalking);
+      byAdaptivewalking = cloner.Clone(original.byAdaptivewalking);
       random = cloner.Clone(original.random);
+      breedingPerformanceModel = cloner.Clone(original.breedingPerformanceModel);
       breedingStat = original.breedingStat.Select(x => Tuple.Create(x.Item1, x.Item2, x.Item3)).ToList();
+      relinkingPerformanceModel = cloner.Clone(original.relinkingPerformanceModel);
+      relinkingStat = original.relinkingStat.Select(x => Tuple.Create(x.Item1, x.Item2, x.Item3)).ToList();
+      delinkingPerformanceModel = cloner.Clone(original.delinkingPerformanceModel);
+      delinkingStat = original.delinkingStat.Select(x => Tuple.Create(x.Item1, x.Item2, x.Item3)).ToList();
+      samplingPerformanceModel = cloner.Clone(original.samplingPerformanceModel);
+      samplingStat = original.samplingStat.Select(x => Tuple.Create(x.Item1, x.Item2)).ToList();
+      hillclimbingPerformanceModel = cloner.Clone(original.hillclimbingPerformanceModel);
       hillclimbingStat = original.hillclimbingStat.Select(x => Tuple.Create(x.Item1, x.Item2)).ToList();
-      tabuwalkingStat = original.tabuwalkingStat.Select(x => Tuple.Create(x.Item1, x.Item2)).ToList();
-
+      adaptiveWalkPerformanceModel = cloner.Clone(original.adaptiveWalkPerformanceModel);
+      adaptivewalkingStat = original.adaptivewalkingStat.Select(x => Tuple.Create(x.Item1, x.Item2)).ToList();
+      
       Model = cloner.Clone(original.Model);
     }
@@ -222,12 +292,16 @@
       Parameters.Add(byBreeding = new ValueParameter<IntValue>("ByBreeding", new IntValue(0)));
       Parameters.Add(byRelinking = new ValueParameter<IntValue>("ByRelinking", new IntValue(0)));
+      Parameters.Add(byDelinking = new ValueParameter<IntValue>("ByDelinking", new IntValue(0)));
       Parameters.Add(bySampling = new ValueParameter<IntValue>("BySampling", new IntValue(0)));
       Parameters.Add(byHillclimbing = new ValueParameter<IntValue>("ByHillclimbing", new IntValue(0)));
-      Parameters.Add(byTabuwalking = new ValueParameter<IntValue>("ByTabuwalking", new IntValue(0)));
+      Parameters.Add(byAdaptivewalking = new ValueParameter<IntValue>("ByAdaptivewalking", new IntValue(0)));
       Parameters.Add(random = new ValueParameter<IRandom>("Random", new MersenneTwister()));
 
       breedingStat = new List<Tuple<double, double, double>>();
+      relinkingStat = new List<Tuple<double, double, double>>();
+      delinkingStat = new List<Tuple<double, double, double>>();
+      samplingStat = new List<Tuple<double, double>>();
       hillclimbingStat = new List<Tuple<double, double>>();
-      tabuwalkingStat = new List<Tuple<double, double>>();
+      adaptivewalkingStat = new List<Tuple<double, double>>();
     }
 
@@ -239,4 +313,342 @@
     }
 
+    public void RelearnBreedingPerformanceModel() {
+      breedingPerformanceModel = RunRegression(PrepareRegression(BreedingStat), breedingPerformanceModel).Model;
+    }
+    public bool BreedingSuited(ISingleObjectiveSolutionScope<TSolution> p1, ISingleObjectiveSolutionScope<TSolution> p2) {
+      if (breedingPerformanceModel == null) return true;
+      double minI1 = double.MaxValue, minI2 = double.MaxValue, maxI1 = double.MinValue, maxI2 = double.MinValue;
+      foreach (var d in BreedingStat) {
+        if (d.Item1 < minI1) minI1 = d.Item1;
+        if (d.Item1 > maxI1) maxI1 = d.Item1;
+        if (d.Item2 < minI2) minI2 = d.Item2;
+        if (d.Item2 > maxI2) maxI2 = d.Item2;
+      }
+      if (IsBetter(p1, p2)) {
+        if (p1.Fitness < minI1 || p1.Fitness > maxI1 || p2.Fitness < minI2 || p2.Fitness > maxI2)
+          return true;
+        return Random.NextDouble() < ProbabilityAccept3dModel(p1.Fitness, p2.Fitness, breedingPerformanceModel);
+      }
+      if (p1.Fitness < minI2 || p1.Fitness > maxI2 || p2.Fitness < minI1 || p2.Fitness > maxI1)
+        return true;
+      return Random.NextDouble() < ProbabilityAccept3dModel(p2.Fitness, p1.Fitness, breedingPerformanceModel);
+    }
+
+    public void RelearnRelinkingPerformanceModel() {
+      relinkingPerformanceModel = RunRegression(PrepareRegression(RelinkingStat), relinkingPerformanceModel).Model;
+    }
+    public bool RelinkSuited(ISingleObjectiveSolutionScope<TSolution> p1, ISingleObjectiveSolutionScope<TSolution> p2) {
+      if (relinkingPerformanceModel == null) return true;
+      double minI1 = double.MaxValue, minI2 = double.MaxValue, maxI1 = double.MinValue, maxI2 = double.MinValue;
+      foreach (var d in RelinkingStat) {
+        if (d.Item1 < minI1) minI1 = d.Item1;
+        if (d.Item1 > maxI1) maxI1 = d.Item1;
+        if (d.Item2 < minI2) minI2 = d.Item2;
+        if (d.Item2 > maxI2) maxI2 = d.Item2;
+      }
+      if (IsBetter(p1, p2)) {
+        if (p1.Fitness < minI1 || p1.Fitness > maxI1 || p2.Fitness < minI2 || p2.Fitness > maxI2)
+          return true;
+        return Random.NextDouble() < ProbabilityAccept3dModel(p1.Fitness, p2.Fitness, relinkingPerformanceModel);
+      }
+      if (p1.Fitness < minI2 || p1.Fitness > maxI2 || p2.Fitness < minI1 || p2.Fitness > maxI1)
+        return true;
+      return Random.NextDouble() < ProbabilityAccept3dModel(p2.Fitness, p1.Fitness, relinkingPerformanceModel);
+    }
+
+    public void RelearnDelinkingPerformanceModel() {
+      delinkingPerformanceModel = RunRegression(PrepareRegression(DelinkingStat), delinkingPerformanceModel).Model;
+    }
+    public bool DelinkSuited(ISingleObjectiveSolutionScope<TSolution> p1, ISingleObjectiveSolutionScope<TSolution> p2) {
+      if (delinkingPerformanceModel == null) return true;
+      double minI1 = double.MaxValue, minI2 = double.MaxValue, maxI1 = double.MinValue, maxI2 = double.MinValue;
+      foreach (var d in DelinkingStat) {
+        if (d.Item1 < minI1) minI1 = d.Item1;
+        if (d.Item1 > maxI1) maxI1 = d.Item1;
+        if (d.Item2 < minI2) minI2 = d.Item2;
+        if (d.Item2 > maxI2) maxI2 = d.Item2;
+      }
+      if (IsBetter(p1, p2)) {
+        if (p1.Fitness < minI1 || p1.Fitness > maxI1 || p2.Fitness < minI2 || p2.Fitness > maxI2)
+          return true;
+        return Random.NextDouble() < ProbabilityAccept3dModel(p1.Fitness, p2.Fitness, delinkingPerformanceModel);
+      }
+      if (p1.Fitness < minI2 || p1.Fitness > maxI2 || p2.Fitness < minI1 || p2.Fitness > maxI1)
+        return true;
+      return Random.NextDouble() < ProbabilityAccept3dModel(p2.Fitness, p1.Fitness, delinkingPerformanceModel);
+    }
+
+    public void RelearnSamplingPerformanceModel() {
+      samplingPerformanceModel = RunRegression(PrepareRegression(SamplingStat), samplingPerformanceModel).Model;
+    }
+    public bool SamplingSuited() {
+      if (samplingPerformanceModel == null) return true;
+      return Random.NextDouble() < ProbabilityAccept2dModel(Population.Average(x => x.Fitness), samplingPerformanceModel);
+    }
+
+    public void RelearnHillclimbingPerformanceModel() {
+      hillclimbingPerformanceModel = RunRegression(PrepareRegression(HillclimbingStat), hillclimbingPerformanceModel).Model;
+    }
+    public bool HillclimbingSuited(ISingleObjectiveSolutionScope<TSolution> scope) {
+      if (hillclimbingPerformanceModel == null) return true;
+      if (scope.Fitness < HillclimbingStat.Min(x => x.Item1) || scope.Fitness > HillclimbingStat.Max(x => x.Item1))
+        return true;
+      return Random.NextDouble() < ProbabilityAccept2dModel(scope.Fitness, hillclimbingPerformanceModel);
+    }
+    public bool HillclimbingSuited(double startingFitness) {
+      if (hillclimbingPerformanceModel == null) return true;
+      if (startingFitness < HillclimbingStat.Min(x => x.Item1) || startingFitness > HillclimbingStat.Max(x => x.Item1))
+        return true;
+      return Random.NextDouble() < ProbabilityAccept2dModel(startingFitness, hillclimbingPerformanceModel);
+    }
+
+    public void RelearnAdaptiveWalkPerformanceModel() {
+      adaptiveWalkPerformanceModel = RunRegression(PrepareRegression(AdaptivewalkingStat), adaptiveWalkPerformanceModel).Model;
+    }
+    public bool AdaptivewalkingSuited(ISingleObjectiveSolutionScope<TSolution> scope) {
+      if (adaptiveWalkPerformanceModel == null) return true;
+      if (scope.Fitness < AdaptivewalkingStat.Min(x => x.Item1) || scope.Fitness > AdaptivewalkingStat.Max(x => x.Item1))
+        return true;
+      return Random.NextDouble() < ProbabilityAccept2dModel(scope.Fitness, adaptiveWalkPerformanceModel);
+    }
+    public bool AdaptivewalkingSuited(double startingFitness) {
+      if (adaptiveWalkPerformanceModel == null) return true;
+      if (startingFitness < AdaptivewalkingStat.Min(x => x.Item1) || startingFitness > AdaptivewalkingStat.Max(x => x.Item1))
+        return true;
+      return Random.NextDouble() < ProbabilityAccept2dModel(startingFitness, adaptiveWalkPerformanceModel);
+    }
+
+    public IConfidenceRegressionSolution GetSolution(IConfidenceRegressionModel model, List<Tuple<double, double>> data) {
+      return new ConfidenceRegressionSolution(model, PrepareRegression(data));
+    }
+    public IConfidenceRegressionSolution GetSolution(IConfidenceRegressionModel model, List<Tuple<double, double, double>> data) {
+      return new ConfidenceRegressionSolution(model, PrepareRegression(data));
+    }
+
+    protected RegressionProblemData PrepareRegression(List<Tuple<double, double>> sample) {
+      var inCol = new List<double>();
+      var outCol = new List<double>();
+      foreach (var next in sample.Shuffle(Random)) {
+        inCol.Add(next.Item1);
+        outCol.Add(next.Item2);
+      }
+      var ds = new Dataset(new[] { "in", "out" }, new[] { inCol, outCol });
+      var regPrb = new RegressionProblemData(ds, new[] { "in" }, "out") {
+        TrainingPartition = { Start = 0, End = Math.Min(50, sample.Count) },
+        TestPartition = { Start = Math.Min(50, sample.Count), End = sample.Count }
+      };
+      return regPrb;
+    }
+
+    protected RegressionProblemData PrepareRegression(List<Tuple<double, double, double>> sample) {
+      var in1Col = new List<double>();
+      var in2Col = new List<double>();
+      var outCol = new List<double>();
+      foreach (var next in sample.Shuffle(Random)) {
+        in1Col.Add(next.Item1);
+        in2Col.Add(next.Item2);
+        outCol.Add(next.Item3);
+      }
+      var ds = new Dataset(new[] { "in1", "in2", "out" }, new[] { in1Col, in2Col, outCol });
+      var regPrb = new RegressionProblemData(ds, new[] { "in1", "in2" }, "out") {
+        TrainingPartition = { Start = 0, End = Math.Min(50, sample.Count) },
+        TestPartition = { Start = Math.Min(50, sample.Count), End = sample.Count }
+      };
+      return regPrb;
+    }
+
+    protected static IConfidenceRegressionSolution RunRegression(RegressionProblemData trainingData, IConfidenceRegressionModel baseLineModel = null) {
+      var baseline = baseLineModel != null ? new ConfidenceRegressionSolution(baseLineModel, trainingData) : null;
+      var gpr = new GaussianProcessRegression { Problem = { ProblemData = trainingData } };
+      if (trainingData.InputVariables.CheckedItems.Any(x => alglib.pearsoncorr2(trainingData.Dataset.GetDoubleValues(x.Value.Value).ToArray(), trainingData.TargetVariableValues.ToArray()) > 0.8)) {
+        gpr.MeanFunction = new MeanZero();
+        var cov1 = new CovarianceSum();
+        cov1.Terms.Add(new CovarianceLinearArd());
+        cov1.Terms.Add(new CovarianceConst());
+        gpr.CovarianceFunction = cov1;
+      }
+      IConfidenceRegressionSolution solution = null;
+      var cnt = 0;
+      do {
+        ExecuteAlgorithm(gpr);
+        solution = (IConfidenceRegressionSolution)gpr.Results["Solution"].Value;
+        cnt++;
+      } while (cnt < 10 && (solution == null || solution.TrainingRSquared.IsAlmost(0)));
+      if (baseline == null) return solution;
+      if (trainingData.Dataset.Rows < 60)
+        return solution.TrainingMeanAbsoluteError < baseline.TrainingMeanAbsoluteError ? solution : baseline;
+      return solution.TestMeanAbsoluteError < baseline.TestMeanAbsoluteError ? solution : baseline;
+    }
+
+    protected static void ExecuteAlgorithm(IAlgorithm algorithm) {
+      using (var evt = new AutoResetEvent(false)) {
+        EventHandler exeStateChanged = (o, args) => {
+          if (algorithm.ExecutionState == ExecutionState.Paused || algorithm.ExecutionState == ExecutionState.Stopped)
+            evt.Set();
+        };
+        algorithm.ExecutionStateChanged += exeStateChanged;
+        algorithm.Prepare(true);
+        algorithm.Start();
+        evt.WaitOne();
+        algorithm.ExecutionStateChanged -= exeStateChanged;
+      }
+    }
+
+    protected double ProbabilityAccept(ISingleObjectiveSolutionScope<TSolution> scope, IList<Tuple<double, double>> data) {
+      if (double.IsNaN(scope.Fitness)) throw new ArgumentException("solution not evaluated or quality unknown", "scope");
+      return ProbabilityAccept2d(scope.Fitness, data);
+    }
+
+    private double ProbabilityAccept2dModel(double a, IConfidenceRegressionModel model) {
+      var ds = new Dataset(new[] { "in", "out" }, new[] { new List<double> { a }, new List<double> { double.NaN } });
+      var mean = model.GetEstimatedValues(ds, new[] { 0 }).Single();
+      var sdev = Math.Sqrt(model.GetEstimatedVariances(ds, new[] { 0 }).Single());
+
+      var goal = Problem.Maximization ? Population.Min(x => x.Fitness) : Population.Max(x => x.Fitness);
+      var z = (goal - mean) / sdev;
+      return Problem.Maximization ? 1.0 - Phi(z) /* P(X >= z) */ : Phi(z); // P(X <= z)
+    }
+    protected double ProbabilityAccept2d(double startingFitness, IList<Tuple<double, double>> data) {
+      if (data.Count < 10) return 1.0;
+      var samples = 0;
+      double meanStart = 0, meanStartOld = 0, meanEnd = 0, meanEndOld = 0;
+      double varStart = 0, varStartOld = 0, varEnd = 0, varEndOld = 0;
+      for (var i = 0; i < data.Count; i++) {
+        samples++;
+        var x = data[i].Item1;
+        var y = data[i].Item2;
+
+        if (samples == 1) {
+          meanStartOld = x;
+          meanEndOld = y;
+        } else {
+          meanStart = meanStartOld + (x - meanStartOld) / samples;
+          meanEnd = meanEndOld + (y - meanEndOld) / samples;
+          varStart = varStartOld + (x - meanStartOld) * (x - meanStart) / (samples - 1);
+          varEnd = varEndOld + (y - meanEndOld) * (y - meanEnd) / (samples - 1);
+
+          meanStartOld = meanStart;
+          meanEndOld = meanEnd;
+          varStartOld = varStart;
+          varEndOld = varEnd;
+        }
+      }
+      var cov = data.Select((v, i) => new { Index = i, Value = v }).Select(x => x.Value).Sum(x => (x.Item1 - meanStart) * (x.Item2 - meanEnd)) / data.Count;
+
+      var biasedMean = meanEnd + cov / varStart * (startingFitness - meanStart);
+      var biasedStdev = Math.Sqrt(varEnd - (cov * cov) / varStart);
+
+      if (Problem.Maximization) {
+        var goal = Population.Min(x => x.Fitness);
+        var z = (goal - biasedMean) / biasedStdev;
+        return 1.0 - Phi(z); // P(X >= z)
+      } else {
+        var goal = Population.Max(x => x.Fitness);
+        var z = (goal - biasedMean) / biasedStdev;
+        return Phi(z); // P(X <= z)
+      }
+    }
+
+    private double ProbabilityAccept3dModel(double a, double b, IConfidenceRegressionModel model) {
+      var ds = new Dataset(new[] { "in1", "in2", "out" }, new[] { new List<double> { a }, new List<double> { b }, new List<double> { double.NaN } });
+      var mean = model.GetEstimatedValues(ds, new[] { 0 }).Single();
+      var sdev = Math.Sqrt(model.GetEstimatedVariances(ds, new[] { 0 }).Single());
+
+      var goal = Problem.Maximization ? Population.Min(x => x.Fitness) : Population.Max(x => x.Fitness);
+      var z = (goal - mean) / sdev;
+      return Problem.Maximization ? 1.0 - Phi(z) /* P(X >= z) */ : Phi(z); // P(X <= z)
+    }
+    protected double ProbabilityAccept3d(double startingFitness1, double startingFitness2, IList<Tuple<double, double, double>> data) {
+      if (data.Count < 20) return 1.0;
+      var samples = 0;
+      double meanX1 = 0, meanX1Old = 0, meanX2 = 0, meanX2Old = 0, meanY = 0, meanYOld = 0;
+      double varX1 = 0, varX1Old = 0, varX2 = 0, varX2Old = 0, varY = 0, varYOld = 0;
+      for (var i = 0; i < data.Count; i++) {
+        samples++;
+        var x1 = data[i].Item1;
+        var x2 = data[i].Item2;
+        var y = data[i].Item3;
+
+        if (samples == 1) {
+          meanX1Old = x1;
+          meanX2Old = x2;
+          meanYOld = y;
+        } else {
+          meanX1 = meanX1Old + (x1 - meanX1Old) / samples;
+          meanX2 = meanX2Old + (x2 - meanX2Old) / samples;
+          meanY = meanYOld + (y - meanYOld) / samples;
+          varX1 = varX1Old + (x1 - meanX1Old) * (x1 - meanX1) / (samples - 1);
+          varX2 = varX2Old + (x2 - meanX2Old) * (x2 - meanX2) / (samples - 1);
+          varY = varYOld + (y - meanYOld) * (y - meanY) / (samples - 1);
+
+          meanX1Old = meanX1;
+          meanX2Old = meanX2;
+          meanYOld = meanY;
+          varX1Old = varX1;
+          varX2Old = varX2;
+          varYOld = varY;
+        }
+      }
+      double covX1X2 = 0, covX1Y = 0, covX2Y = 0;
+      for (var i = 0; i < data.Count; i++) {
+        covX1X2 += (data[i].Item1 - meanX1) * (data[i].Item2 - meanX2) / data.Count;
+        covX1Y += (data[i].Item1 - meanX1) * (data[i].Item3 - meanY) / data.Count;
+        covX2Y += (data[i].Item2 - meanX2) * (data[i].Item3 - meanY) / data.Count;
+      }
+
+      var biasedMean = meanY + ((covX1Y * varX2 - covX2Y * covX1X2) * (startingFitness1 - meanX1) - (covX1Y * covX1X2 - covX2Y * varX1) * (startingFitness2 - meanX2)) / (varX1 * varX2 - covX1X2 * covX1X2);
+      var biasedStdev = Math.Sqrt(varY - (covX1Y * covX1Y * varX2 - 2 * covX1Y * covX2Y * covX1X2 + covX2Y * covX2Y * varX1) / (varX1 * varX2 - covX1X2 * covX1X2));
+      if (Problem.Maximization) {
+        var goal = Population.Min(x => x.Fitness);
+        var z = (goal - biasedMean) / biasedStdev;
+        return 1.0 - Phi(z); // P(X >= z)
+      } else {
+        var goal = Population.Max(x => x.Fitness);
+        var z = (goal - biasedMean) / biasedStdev;
+        return Phi(z); // P(X <= z)
+      }
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public bool IsBetter(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b) {
+      return IsBetter(a.Fitness, b.Fitness);
+    }
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public bool IsBetter(double a, double b) {
+      return double.IsNaN(b) && !double.IsNaN(a)
+        || Problem.Maximization && a > b
+        || !Problem.Maximization && a < b;
+    }
+
+    public void AddBreedingResult(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b, ISingleObjectiveSolutionScope<TSolution> child) {
+      if (IsBetter(a, b))
+        BreedingStat.Add(Tuple.Create(a.Fitness, b.Fitness, child.Fitness));
+      else BreedingStat.Add(Tuple.Create(b.Fitness, a.Fitness, child.Fitness));
+    }
+
+    public void AddRelinkingResult(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b, ISingleObjectiveSolutionScope<TSolution> child) {
+      if (IsBetter(a, b))
+        RelinkingStat.Add(Tuple.Create(a.Fitness, b.Fitness, child.Fitness));
+      else RelinkingStat.Add(Tuple.Create(b.Fitness, a.Fitness, child.Fitness));
+    }
+
+    public void AddDelinkingResult(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b, ISingleObjectiveSolutionScope<TSolution> child) {
+      if (IsBetter(a, b))
+        DelinkingStat.Add(Tuple.Create(a.Fitness, b.Fitness, child.Fitness));
+      else DelinkingStat.Add(Tuple.Create(b.Fitness, a.Fitness, child.Fitness));
+    }
+
+    public void AddSamplingResult(ISingleObjectiveSolutionScope<TSolution> sample) {
+      SamplingStat.Add(Tuple.Create(Population.Average(x => x.Fitness), sample.Fitness));
+    }
+
+    public void AddHillclimbingResult(ISingleObjectiveSolutionScope<TSolution> input, ISingleObjectiveSolutionScope<TSolution> outcome) {
+      HillclimbingStat.Add(Tuple.Create(input.Fitness, outcome.Fitness));
+    }
+
+    public void AddTabuwalkingResult(ISingleObjectiveSolutionScope<TSolution> input, ISingleObjectiveSolutionScope<TSolution> outcome) {
+      AdaptivewalkingStat.Add(Tuple.Create(input.Fitness, outcome.Fitness));
+    }
+
     #region IExecutionContext members
     public IAtomicOperation CreateOperation(IOperator op) {
@@ -254,4 +666,32 @@
     public IAtomicOperation CreateChildOperation(IOperator op, IScope s) {
       return new ExecutionContext(this, op, s);
+    }
+    #endregion
+
+    #region Math Helper
+    // normal distribution CDF (left of x) for N(0;1) standard normal distribution
+    // from http://www.johndcook.com/blog/csharp_phi/
+    // license: "This code is in the public domain. Do whatever you want with it, no strings attached."
+    // added: 2016-11-19 21:46 CET
+    protected static double Phi(double x) {
+      // constants
+      double a1 = 0.254829592;
+      double a2 = -0.284496736;
+      double a3 = 1.421413741;
+      double a4 = -1.453152027;
+      double a5 = 1.061405429;
+      double p = 0.3275911;
+
+      // Save the sign of x
+      int sign = 1;
+      if (x < 0)
+        sign = -1;
+      x = Math.Abs(x) / Math.Sqrt(2.0);
+
+      // A&S formula 7.1.26
+      double t = 1.0 / (1.0 + p * x);
+      double y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.Exp(-x * x);
+
+      return 0.5 * (1.0 + sign * y);
     }
     #endregion
Index: /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Permutation/PermutationMemPR.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Permutation/PermutationMemPR.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Permutation/PermutationMemPR.cs	(revision 14544)
@@ -39,6 +39,4 @@
   [Creatable(CreatableAttribute.Categories.PopulationBasedAlgorithms, Priority = 999)]
   public class PermutationMemPR : MemPRAlgorithm<SingleObjectiveBasicProblem<PermutationEncoding>, Encodings.PermutationEncoding.Permutation, PermutationMemPRPopulationContext, PermutationMemPRSolutionContext> {
-    private const double UncommonBitSubsetMutationProbabilityMagicConst = 0.05;
-
 #if DEBUG
     private const bool VALIDATE = true;
@@ -144,9 +142,9 @@
     }
 
-    protected override int TabuWalk(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> scope, int maxEvals, CancellationToken token, ISolutionSubspace<Encodings.PermutationEncoding.Permutation> subspace = null) {
+    protected override void AdaptiveWalk(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> scope, int maxEvals, CancellationToken token, ISolutionSubspace<Encodings.PermutationEncoding.Permutation> subspace = null) {
       var wrapper = new EvaluationWrapper<Encodings.PermutationEncoding.Permutation>(Context.Problem, scope);
       var quality = scope.Fitness;
       try {
-        return TabuWalk(Context.Random, scope.Solution, wrapper.Evaluate, ref quality, maxEvals, subspace != null ? ((PermutationSolutionSubspace)subspace).Subspace : null);
+        TabuWalk(Context.Random, scope.Solution, wrapper.Evaluate, ref quality, maxEvals, subspace != null ? ((PermutationSolutionSubspace)subspace).Subspace : null);
       } finally {
         scope.Fitness = quality;
@@ -154,20 +152,18 @@
     }
 
-    public int TabuWalk(IRandom random, Encodings.PermutationEncoding.Permutation perm, Func<Encodings.PermutationEncoding.Permutation, IRandom, double> eval, ref double quality, int maxEvals = int.MaxValue, bool[,] subspace = null) {
-      int newSteps = 0;
+    public void TabuWalk(IRandom random, Encodings.PermutationEncoding.Permutation perm, Func<Encodings.PermutationEncoding.Permutation, IRandom, double> eval, ref double quality, int maxEvals = int.MaxValue, bool[,] subspace = null) {
       switch (perm.PermutationType) {
         case PermutationTypes.Absolute:
-          newSteps = TabuWalkSwap(random, perm, eval, ref quality, maxEvals, subspace);
+          TabuWalkSwap(random, perm, eval, ref quality, maxEvals, subspace);
           break;
         case PermutationTypes.RelativeDirected:
-          newSteps = TabuWalkShift(random, perm, eval, ref quality, maxEvals, subspace);
+          TabuWalkShift(random, perm, eval, ref quality, maxEvals, subspace);
           break;
         case PermutationTypes.RelativeUndirected:
-          newSteps = TabuWalkOpt(random, perm, eval, ref quality, maxEvals, subspace);
+          TabuWalkOpt(random, perm, eval, ref quality, maxEvals, subspace);
           break;
         default: throw new ArgumentException(string.Format("Permutation type {0} is not known", perm.PermutationType));
       }
       if (VALIDATE && !perm.Validate()) throw new ArgumentException("TabuWalk produced invalid child");
-      return newSteps;
     }
 
@@ -382,116 +378,103 @@
     }
 
-    protected override ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> Cross(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p1, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p2, CancellationToken token) {
-      Encodings.PermutationEncoding.Permutation child = null;
+    protected override ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> Breed(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p1, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p2, CancellationToken token) {
+      ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> child = null;
 
       if (p1.Solution.PermutationType == PermutationTypes.Absolute) {
-        child = CyclicCrossover.Apply(Context.Random, p1.Solution, p2.Solution);
+        child = CrossAbsolute(p1, p2, token);
       } else if (p1.Solution.PermutationType == PermutationTypes.RelativeDirected) {
-        child = PartiallyMatchedCrossover.Apply(Context.Random, p1.Solution, p2.Solution);
+        child = CrossRelativeDirected(p1, p2, token);
       } else if (p1.Solution.PermutationType == PermutationTypes.RelativeUndirected) {
-        child = EdgeRecombinationCrossover.Apply(Context.Random, p1.Solution, p2.Solution);
+        child = CrossRelativeUndirected(p1, p2, token);
       } else throw new ArgumentException(string.Format("Unknown permutation type {0}", p1.Solution.PermutationType));
 
-      if (VALIDATE && !child.Validate()) throw new ArgumentException("Cross produced invalid child");
-      return ToScope(child);
-    }
-
-    protected override void Mutate(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> offspring, CancellationToken token, ISolutionSubspace<Encodings.PermutationEncoding.Permutation> subspace = null) {
-      Mutate(Context.Random, offspring.Solution, UncommonBitSubsetMutationProbabilityMagicConst, subspace != null ? ((PermutationSolutionSubspace)subspace).Subspace : null);
-    }
-
-    public static void Mutate(IRandom random, Encodings.PermutationEncoding.Permutation perm, double p, bool[,] subspace) {
-      switch (perm.PermutationType) {
-        case PermutationTypes.Absolute:
-          MutateSwap(random, perm, p, subspace);
-          break;
-        case PermutationTypes.RelativeDirected:
-          MutateShift(random, perm, p, subspace);
-          break;
-        case PermutationTypes.RelativeUndirected:
-          MutateOpt(random, perm, p, subspace);
-          break;
-        default: throw new ArgumentException(string.Format("Permutation type {0} is not known", perm.PermutationType));
-      }
-      if (VALIDATE && !perm.Validate()) throw new ArgumentException("Mutate produced invalid child");
-    }
-
-    public static void MutateSwap(IRandom random, Encodings.PermutationEncoding.Permutation perm, double p, bool[,] subspace) {
-      //Log("BEFOR: {0}", string.Join(", ", lle));
-      // The goal of the mutation is to disrupt crossover when it's in an agreeing position
-      var options = new List<int>(Enumerable.Range(0, perm.Length).Where(x => subspace == null || !subspace[x, 0]));
-      if (options.Count < 1) return;
-
-      for (var i = options.Count - 1; i > 0; i--) {
-        if (random.NextDouble() < p) {
-          var j = random.Next(0, i);
-          var h = perm[options[i]];
-          perm[options[i]] = perm[options[j]];
-          perm[options[j]] = h;
-          if (subspace != null) {
-            subspace[options[i], 0] = true;
-            subspace[options[j], 0] = true;
-          }
-        }
-      }
-    }
-
-    public static void MutateShift(IRandom random, Encodings.PermutationEncoding.Permutation perm, double p, bool[,] subspace) {
-      //Log("BEFOR: {0}", string.Join(", ", lle));
-      // The goal of the mutation is to disrupt crossover when it's in an agreeing position
-      foreach (var shift in ExhaustiveInsertionMoveGenerator.Generate(perm).Shuffle(random)) {
-        var prev1 = shift.Index1 - 1;
-        var next1 = (shift.Index1 + 1) % perm.Length;
-        if (prev1 < 0) prev1 += perm.Length;
-        var prev3 = shift.Index3 - 1;
-        var next3 = (shift.Index3 + 1) % perm.Length;
-        if (prev3 < 0) prev3 += perm.Length;
-        if (subspace == null || !(subspace[perm[prev1], perm[shift.Index1]] && subspace[perm[shift.Index1], perm[next1]]
-            && subspace[perm[prev3], perm[shift.Index3]] && subspace[perm[shift.Index3], perm[next3]])) {
-          if (random.NextDouble() < p) {
-            if (subspace != null) {
-              subspace[perm[prev1], perm[shift.Index1]] = true;
-              subspace[perm[shift.Index1], perm[next1]] = true;
-              subspace[perm[prev3], perm[shift.Index3]] = true;
-              subspace[perm[shift.Index3], perm[next3]] = true;
-            }
-            TranslocationManipulator.Apply(perm, shift.Index1, shift.Index2, shift.Index3);
-            return;
-          }
-        }
-      }
-    }
-
-    public static void MutateOpt(IRandom random, Encodings.PermutationEncoding.Permutation perm, double p, bool[,] subspace) {
-      //Log("BEFOR: {0}", string.Join(", ", lle));
-      // The goal of the mutation is to disrupt crossover when it's in an agreeing position
-      foreach (var opt in ExhaustiveInversionMoveGenerator.Generate(perm).Shuffle(random)) {
-        var prev = opt.Index1 - 1;
-        var next = (opt.Index2 + 1) % perm.Length;
-        if (prev < 0) prev += perm.Length;
-        if (subspace == null || !(subspace[perm[prev], perm[opt.Index1]] && subspace[perm[opt.Index2], perm[next]])) {
-          if (random.NextDouble() < p) {
-            if (subspace != null) {
-              subspace[perm[prev], perm[opt.Index1]] = true;
-              subspace[perm[opt.Index1], perm[prev]] = true;
-              subspace[perm[opt.Index2], perm[next]] = true;
-              subspace[perm[next], perm[opt.Index2]] = true;
-            }
-            InversionManipulator.Apply(perm, opt.Index1, opt.Index2);
-            return;
-          }
-        }
-      }
-    }
-
-    protected override ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> Relink(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> a, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> b, CancellationToken token) {
+      if (VALIDATE && !child.Solution.Validate()) throw new ArgumentException("Cross produced invalid child");
+      return child;
+    }
+
+    private ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> CrossAbsolute(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p1, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p2, CancellationToken token) {
+      var cache = new HashSet<Encodings.PermutationEncoding.Permutation>(new PermutationEqualityComparer());
+      var cacheHits = 0;
+      var evaluations = 1;
+      ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> offspring = null;
+      for (; evaluations <= Context.LocalSearchEvaluations; evaluations++) {
+        var c = CyclicCrossover2.Apply(Context.Random, p1.Solution, p2.Solution);
+        if (cache.Contains(c)) {
+          cacheHits++;
+          if (cacheHits > 10) break;
+          continue;
+        }
+        var probe = ToScope(c);
+        Evaluate(probe, token);
+        cache.Add(c);
+        if (offspring == null || Context.IsBetter(probe, offspring)) {
+          offspring = probe;
+          if (Context.IsBetter(offspring, p1) && Context.IsBetter(offspring, p2))
+            break;
+        }
+      }
+      Context.IncrementEvaluatedSolutions(evaluations-1);
+      return offspring;
+    }
+
+    private ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> CrossRelativeDirected(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p1, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p2, CancellationToken token) {
+      var cache = new HashSet<Encodings.PermutationEncoding.Permutation>(new PermutationEqualityComparer());
+      var cacheHits = 0;
+      var evaluations = 1;
+      ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> offspring = null;
+      for (; evaluations <= Context.LocalSearchEvaluations; evaluations++) {
+        var c = PartiallyMatchedCrossover.Apply(Context.Random, p1.Solution, p2.Solution);
+        if (cache.Contains(c)) {
+          cacheHits++;
+          if (cacheHits > 10) break;
+          continue;
+        }
+        var probe = ToScope(c);
+        Evaluate(probe, token);
+        cache.Add(c);
+        if (offspring == null || Context.IsBetter(probe, offspring)) {
+          offspring = probe;
+          if (Context.IsBetter(offspring, p1) && Context.IsBetter(offspring, p2))
+            break;
+        }
+      }
+      Context.IncrementEvaluatedSolutions(evaluations-1);
+      return offspring;
+    }
+
+    private ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> CrossRelativeUndirected(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p1, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p2, CancellationToken token) {
+      var cache = new HashSet<Encodings.PermutationEncoding.Permutation>(new PermutationEqualityComparer());
+      var cacheHits = 0;
+      var evaluations = 1;
+      ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> offspring = null;
+      for (; evaluations <= Context.LocalSearchEvaluations; evaluations++) {
+        var c = EdgeRecombinationCrossover.Apply(Context.Random, p1.Solution, p2.Solution);
+        if (cache.Contains(c)) {
+          cacheHits++;
+          if (cacheHits > 10) break;
+          continue;
+        }
+        var probe = ToScope(c);
+        Evaluate(probe, token);
+        cache.Add(c);
+        if (offspring == null || Context.IsBetter(probe, offspring)) {
+          offspring = probe;
+          if (Context.IsBetter(offspring, p1) && Context.IsBetter(offspring, p2))
+            break;
+        }
+      }
+      Context.IncrementEvaluatedSolutions(evaluations-1);
+      return offspring;
+    }
+
+    protected override ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> Link(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> a, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> b, CancellationToken token, bool delink = false) {
       if (double.IsNaN(a.Fitness)) Evaluate(a, token);
       if (double.IsNaN(b.Fitness)) Evaluate(b, token);
       if (Context.Random.NextDouble() < 0.5)
-        return IsBetter(a, b) ? Relink(a, b, token, false) : Relink(b, a, token, true);
-      else return IsBetter(a, b) ? Relink(b, a, token, true) : Relink(a, b, token, false);
-    }
-
-    protected virtual ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> Relink(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> betterScope, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> worseScope, CancellationToken token, bool fromWorseToBetter) {
+        return Context.IsBetter(a, b) ? Relink(a, b, token) : Relink(b, a, token);
+      else return Context.IsBetter(a, b) ? Relink(b, a, token) : Relink(a, b, token);
+    }
+
+    protected virtual ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> Relink(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> betterScope, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> worseScope, CancellationToken token) {
       var wrapper = new EvaluationWrapper<Encodings.PermutationEncoding.Permutation>(Problem, betterScope);
       double quality;
Index: /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Plugin.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Plugin.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Plugin.cs	(revision 14544)
@@ -26,4 +26,5 @@
   [Plugin("HeuristicLab.Algorithms.MemPR", "Provides the MemPR (MEMetic Path Relinking) algorithm.", "3.3.14.0")]
   [PluginFile("HeuristicLab.Algorithms.MemPR-3.3.dll", PluginFileType.Assembly)]
+  [PluginDependency("HeuristicLab.Algorithms.DataAnalysis", "3.4")]
   [PluginDependency("HeuristicLab.Analysis", "3.3")]
   [PluginDependency("HeuristicLab.Collections", "3.3")]
Index: /branches/MemPRAlgorithm/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/HeuristicLab.Encodings.LinearLinkageEncoding-3.4.csproj
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/HeuristicLab.Encodings.LinearLinkageEncoding-3.4.csproj	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/HeuristicLab.Encodings.LinearLinkageEncoding-3.4.csproj	(revision 14544)
@@ -167,4 +167,5 @@
     <Compile Include="Interfaces\ILinearLinkageSwap2MoveOperator.cs" />
     <Compile Include="Interfaces\ILinearLinkageCreator.cs" />
+    <Compile Include="LinearLinkageEqualityComparer.cs" />
     <Compile Include="LinearLinkage.cs" />
     <Compile Include="LinearLinkageCreator.cs" />
Index: /branches/MemPRAlgorithm/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageEqualityComparer.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageEqualityComparer.cs	(revision 14544)
+++ /branches/MemPRAlgorithm/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/LinearLinkageEqualityComparer.cs	(revision 14544)
@@ -0,0 +1,44 @@
+﻿#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;
+
+namespace HeuristicLab.Encodings.LinearLinkageEncoding {
+  public class LinearLinkageEqualityComparer : EqualityComparer<LinearLinkage> {
+    public override bool Equals(LinearLinkage x, LinearLinkage y) {
+      if (x == null && y == null) return true;
+      if (x == null || y == null) return false;
+      if (x.Length != y.Length) return false;
+      for (var i = 0; i < x.Length; i++)
+        if (x[i] != y[i]) return false;
+      return true;
+    }
+
+    public override int GetHashCode(LinearLinkage obj) {
+      unchecked {
+        int hash = 17;
+        foreach (var o in obj)
+          hash = hash * 31 + o.GetHashCode();
+        return hash;
+      }
+    }
+  }
+}
Index: /branches/MemPRAlgorithm/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Move.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Move.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/Move.cs	(revision 14544)
@@ -20,6 +20,4 @@
 #endregion
 
-using HeuristicLab.Collections;
-
 namespace HeuristicLab.Encodings.LinearLinkageEncoding {
   public abstract class Move {
Index: /branches/MemPRAlgorithm/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/ShiftMove.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/ShiftMove.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Encodings.LinearLinkageEncoding/3.4/Moves/ShiftMove.cs	(revision 14544)
@@ -21,5 +21,4 @@
 
 using System;
-using HeuristicLab.Collections;
 
 namespace HeuristicLab.Encodings.LinearLinkageEncoding {
Index: /branches/MemPRAlgorithm/HeuristicLab.Random/3.3/RandomEnumerable.cs
===================================================================
--- /branches/MemPRAlgorithm/HeuristicLab.Random/3.3/RandomEnumerable.cs	(revision 14543)
+++ /branches/MemPRAlgorithm/HeuristicLab.Random/3.3/RandomEnumerable.cs	(revision 14544)
@@ -269,5 +269,6 @@
         // swapped element because we already returned it.
       }
-      yield return elements[0];
+      if (elements.Length > 0)
+        yield return elements[0];
     }
   }
