Index: branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/Analyzers/BestParameterConfigurationAnalyzer.cs
===================================================================
--- branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/Analyzers/BestParameterConfigurationAnalyzer.cs	(revision 5277)
+++ branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/Analyzers/BestParameterConfigurationAnalyzer.cs	(revision 5281)
@@ -14,5 +14,5 @@
   /// TODO An operator for analyzing the best solution of Traveling Salesman Problems given in path representation using city coordinates.
   /// </summary>
-  [Item("BestParameterConfigurationAnalyzer", "TODO An operator for analyzing the best solution of Traveling Salesman Problems given in path representation using city coordinates.")]
+  [Item("BestParameterConfigurationAnalyzer", "")]
   [StorableClass]
   public sealed class BestParameterConfigurationAnalyzer : SingleSuccessorOperator, IAnalyzer {
@@ -39,6 +39,4 @@
       get { return (LookupParameter<RunCollection>)Parameters["Population"]; }
     }
-
-    // stores the medians of the qualities of each problem
     public LookupParameter<DoubleArray> ProblemQualityMediansParameter {
       get { return (LookupParameter<DoubleArray>)Parameters["ProblemQualityMedians"]; }
@@ -50,11 +48,11 @@
     public BestParameterConfigurationAnalyzer()
       : base() {
-      Parameters.Add(new ScopeTreeLookupParameter<ParameterConfigurationTree>("ParameterConfigurationTree", "TODO The TSP solutions given in path representation from which the best solution should be analyzed."));
-      Parameters.Add(new ScopeTreeLookupParameter<DoubleValue>("Quality", "TODO The qualities of the TSP solutions which should be analyzed."));
-      Parameters.Add(new LookupParameter<IRun>("BestSolution", "TODO The best TSP solution."));
-      Parameters.Add(new ValueLookupParameter<ResultCollection>("Results", "TODO The result collection where the best TSP solution should be stored."));
-      Parameters.Add(new LookupParameter<DoubleValue>("BestKnownQuality", "TODO The quality of the best known solution of this TSP instance."));
-      Parameters.Add(new LookupParameter<IRun>("BestKnownSolution", "TODO The best known solution of this TSP instance."));
-      Parameters.Add(new LookupParameter<RunCollection>("Population", "TODO The best known solution of this TSP instance."));
+      Parameters.Add(new ScopeTreeLookupParameter<ParameterConfigurationTree>("ParameterConfigurationTree", ""));
+      Parameters.Add(new ScopeTreeLookupParameter<DoubleValue>("Quality", ""));
+      Parameters.Add(new LookupParameter<IRun>("BestSolution", ""));
+      Parameters.Add(new ValueLookupParameter<ResultCollection>("Results", ""));
+      Parameters.Add(new LookupParameter<DoubleValue>("BestKnownQuality", ""));
+      Parameters.Add(new LookupParameter<IRun>("BestKnownSolution", ""));
+      Parameters.Add(new LookupParameter<RunCollection>("Population", ""));
       Parameters.Add(new LookupParameter<DoubleArray>("ProblemQualityMedians", ""));
       Parameters.Add(new LookupParameter<ConstrainedItemList<ISingleObjectiveProblem>>(MetaOptimizationProblem.ProblemsParameterName));
@@ -114,21 +112,4 @@
       }
 
-      // medians for the problems
-      DoubleArray medians = new DoubleArray(ProblemsParameter.ActualValue.Count);
-      for (int pi = 0; pi < ProblemsParameter.ActualValue.Count; pi++) {
-        double[] values = new double[rc.Count];
-        for (int ri = 0; ri < rc.Count; ri++) {
-          values[ri] = ((DoubleArray)rc.ElementAt(ri).Results["RunsAverageQualities"])[pi];
-        }
-        medians[pi] = values.Median();
-      }
-      ProblemQualityMediansParameter.ActualValue = medians;
-
-      if (results.ContainsKey("ProblemsAverageQualityMedians")) {
-        results["ProblemsAverageQualityMedians"].Value = medians;
-      } else {
-        results.Add(new Result("ProblemsAverageQualityMedians", medians));
-      }
-
       return base.Apply();
     }
Index: branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/Analyzers/MedianQualityAnalyzer.cs
===================================================================
--- branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/Analyzers/MedianQualityAnalyzer.cs	(revision 5281)
+++ branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/Analyzers/MedianQualityAnalyzer.cs	(revision 5281)
@@ -0,0 +1,81 @@
+﻿using System.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Operators;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using System.Collections.Generic;
+using System;
+
+namespace HeuristicLab.Problems.MetaOptimization {
+  /// <summary>
+  /// TODO An operator for analyzing the best solution of Traveling Salesman Problems given in path representation using city coordinates.
+  /// </summary>
+  [Item("MedianQualityAnalyzer", "")]
+  [StorableClass]
+  public sealed class MedianQualityAnalyzer : SingleSuccessorOperator, IAnalyzer {
+    
+    public ValueLookupParameter<ResultCollection> ResultsParameter {
+      get { return (ValueLookupParameter<ResultCollection>)Parameters["Results"]; }
+    }
+    public ScopeTreeLookupParameter<ParameterConfigurationTree> ParameterConfigurationParameter {
+      get { return (ScopeTreeLookupParameter<ParameterConfigurationTree>)Parameters["ParameterConfigurationTree"]; }
+    }
+    public ScopeTreeLookupParameter<DoubleValue> QualityParameter {
+      get { return (ScopeTreeLookupParameter<DoubleValue>)Parameters["Quality"]; }
+    }
+    public LookupParameter<DoubleArray> ProblemQualityMediansParameter {
+      get { return (LookupParameter<DoubleArray>)Parameters["ProblemQualityMedians"]; }
+    }
+    public LookupParameter<ConstrainedItemList<ISingleObjectiveProblem>> ProblemsParameter {
+      get { return (LookupParameter<ConstrainedItemList<ISingleObjectiveProblem>>)Parameters[MetaOptimizationProblem.ProblemsParameterName]; }
+    }
+
+    public MedianQualityAnalyzer()
+      : base() {
+        Parameters.Add(new ScopeTreeLookupParameter<DoubleValue>("Quality", ""));
+      Parameters.Add(new ValueLookupParameter<ResultCollection>("Results", ""));
+      Parameters.Add(new ScopeTreeLookupParameter<ParameterConfigurationTree>("ParameterConfigurationTree", ""));
+      Parameters.Add(new LookupParameter<DoubleArray>("ProblemQualityMedians", ""));
+      Parameters.Add(new LookupParameter<ConstrainedItemList<ISingleObjectiveProblem>>(MetaOptimizationProblem.ProblemsParameterName, ""));
+    }
+
+    [StorableConstructor]
+    private MedianQualityAnalyzer(bool deserializing) : base(deserializing) { }
+    private MedianQualityAnalyzer(MedianQualityAnalyzer original, Cloner cloner) : base(original, cloner) { }
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new MedianQualityAnalyzer(this, cloner);
+    }
+
+    public override IOperation Apply() {
+      ResultCollection results = ResultsParameter.ActualValue;
+      ItemArray<ParameterConfigurationTree> solutions = ParameterConfigurationParameter.ActualValue;
+      ItemArray<DoubleValue> qualities = QualityParameter.ActualValue;
+
+      if (ProblemQualityMediansParameter.ActualValue == null) {
+        DoubleArray medians = CalculateMedians(solutions);
+        ProblemQualityMediansParameter.ActualValue = medians;
+        NormalizePopulation(solutions, qualities, medians);
+        results.Add(new Result("ProblemsAverageQualityMedians", medians));
+      }
+
+      return base.Apply();
+    }
+
+    private DoubleArray CalculateMedians(ItemArray<ParameterConfigurationTree> solutions) {
+      DoubleArray medians = new DoubleArray(ProblemsParameter.ActualValue.Count);
+      for (int pi = 0; pi < ProblemsParameter.ActualValue.Count; pi++) {
+        medians[pi] = solutions.Select(x => x.AverageQualities[pi]).Median();
+      }
+      return medians;
+    }
+
+    private void NormalizePopulation(ItemArray<ParameterConfigurationTree> solutions, ItemArray<DoubleValue> qualities, DoubleArray medians) {
+      for (int i = 0; i < solutions.Length; i++) {
+        qualities[i].Value = ParameterConfigurationEvaluator.NormalizeQualities(solutions[i], medians.ToArray());
+      }
+    }
+  }
+}
Index: branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/Evaluators/AlgorithmExecutor.cs
===================================================================
--- branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/Evaluators/AlgorithmExecutor.cs	(revision 5281)
+++ branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/Evaluators/AlgorithmExecutor.cs	(revision 5281)
@@ -0,0 +1,38 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using HeuristicLab.Optimization;
+using System.Threading;
+
+namespace HeuristicLab.Problems.MetaOptimization {
+  /// <summary>
+  /// Can execute an algorithm synchronously
+  /// </summary>
+  public class AlgorithmExecutor {
+    private EngineAlgorithm algorithm;
+    private AutoResetEvent waitHandle = new AutoResetEvent(false);
+
+    public AlgorithmExecutor(EngineAlgorithm algorithm) {
+      this.algorithm = algorithm;
+    }
+
+    public void StartSync() {
+      algorithm.Stopped += new EventHandler(algorithm_Stopped);
+      algorithm.Paused += new EventHandler(algorithm_Paused);
+      algorithm.Start();
+      waitHandle.WaitOne();
+      waitHandle.Dispose();
+      algorithm.Stopped -= new EventHandler(algorithm_Stopped);
+      algorithm.Paused -= new EventHandler(algorithm_Paused);
+    }
+
+    void algorithm_Paused(object sender, EventArgs e) {
+      waitHandle.Set();
+    }
+
+    void algorithm_Stopped(object sender, EventArgs e) {
+      waitHandle.Set();
+    }
+  }
+}
Index: branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/Evaluators/ParameterConfigurationEvaluator.cs
===================================================================
--- branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/Evaluators/ParameterConfigurationEvaluator.cs	(revision 5277)
+++ branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/Evaluators/ParameterConfigurationEvaluator.cs	(revision 5281)
@@ -70,9 +70,10 @@
       EngineAlgorithm algorithm = (EngineAlgorithm)AlgorithmParameter.ActualValue.Clone();
       IItemList<ISingleObjectiveProblem> problems = ProblemsParameter.ActualValue;
+      ParameterConfigurationTree parameterConfiguration = ParameterConfigurationParameter.ActualValue;
 
       double[] medians;
       if (ProblemQualityMediansParameter.ActualValue == null) {
-        // this is generation zero. no reference qualities for scaling have been calculated yet. calculate a random individuum to get reference values
-        medians = new double[problems.Count]; // todo
+        // this is generation zero. no reference qualities for normalization have been calculated yet.
+        medians = new double[problems.Count];
         for (int i = 0; i < medians.Length; i++) {
           medians[i] = 1;
@@ -83,5 +84,5 @@
 
       // set parameters
-      ParameterConfigurationParameter.ActualValue.Parameterize(algorithm);
+      parameterConfiguration.Parameterize(algorithm);
       algorithm.StoreAlgorithmInEachRun = false;
 
@@ -121,24 +122,26 @@
       algorithm.Prepare();
 
-      ParameterConfigurationParameter.ActualValue.AverageExecutionTimes = new ItemList<TimeSpanValue>(executionTimes.Select(t => new TimeSpanValue(TimeSpan.FromMilliseconds(t.Average(ts => ts.TotalMilliseconds)))));
-      ParameterConfigurationParameter.ActualValue.Repetitions = (IntValue)Repetitions.Clone();
-      ParameterConfigurationParameter.ActualValue.BestQualities = new DoubleArray(qualities.Select(q => q.Min()).ToArray()); // todo: respect Maximization:true/false
-      ParameterConfigurationParameter.ActualValue.AverageQualities = new DoubleArray(qualities.Select(q => q.Average()).ToArray());
-      ParameterConfigurationParameter.ActualValue.WorstQualities = new DoubleArray(qualities.Select(q => q.Max()).ToArray()); // todo: respect Maximization:true/false
-      ParameterConfigurationParameter.ActualValue.QualityVariances = new DoubleArray(qualities.Select(q => q.Variance()).ToArray());
-      ParameterConfigurationParameter.ActualValue.QualityStandardDeviations = new DoubleArray(qualities.Select(q => q.StandardDeviation()).ToArray());
-      ParameterConfigurationParameter.ActualValue.Runs = (RunCollection)algorithm.Runs.Clone();
+      parameterConfiguration.AverageExecutionTimes = new ItemList<TimeSpanValue>(executionTimes.Select(t => new TimeSpanValue(TimeSpan.FromMilliseconds(t.Average(ts => ts.TotalMilliseconds)))));
+      parameterConfiguration.Repetitions = (IntValue)Repetitions.Clone();
+      parameterConfiguration.BestQualities = new DoubleArray(qualities.Select(q => q.Min()).ToArray()); // todo: respect Maximization:true/false
+      parameterConfiguration.AverageQualities = new DoubleArray(qualities.Select(q => q.Average()).ToArray());
+      parameterConfiguration.WorstQualities = new DoubleArray(qualities.Select(q => q.Max()).ToArray()); // todo: respect Maximization:true/false
+      parameterConfiguration.QualityVariances = new DoubleArray(qualities.Select(q => q.Variance()).ToArray());
+      parameterConfiguration.QualityStandardDeviations = new DoubleArray(qualities.Select(q => q.StandardDeviation()).ToArray());
+      parameterConfiguration.Runs = (RunCollection)algorithm.Runs.Clone();
 
-      // normalize qualities
-      double[] qualitiesNormalized = new double[problems.Count];
-      for (int i = 0; i < problems.Count; i++) {
-        qualitiesNormalized[i] = qualities[i].Average() / medians[i];
-      }
-      ParameterConfigurationParameter.ActualValue.QualitiesNormalized = new DoubleArray(qualitiesNormalized);
-      ParameterConfigurationParameter.ActualValue.AverageQualityNormalized = new DoubleValue(qualitiesNormalized.Average());
-
-      this.QualityParameter.ActualValue = ParameterConfigurationParameter.ActualValue.AverageQualityNormalized;
+      this.QualityParameter.ActualValue = new DoubleValue(NormalizeQualities(parameterConfiguration, medians));
 
       return base.Apply();
+    }
+
+    public static double NormalizeQualities(ParameterConfigurationTree parameterConfigurationTree, double[] medians) {
+      double[] qualitiesNormalized = new double[medians.Length];
+      for (int i = 0; i < medians.Length; i++) {
+        qualitiesNormalized[i] = parameterConfigurationTree.AverageQualities[i] / medians[i];
+      }
+      parameterConfigurationTree.QualitiesNormalized = new DoubleArray(qualitiesNormalized);
+      parameterConfigurationTree.AverageQualityNormalized = new DoubleValue(qualitiesNormalized.Average());
+      return parameterConfigurationTree.AverageQualityNormalized.Value;
     }
 
@@ -153,33 +156,3 @@
     }
   }
-
-  /// <summary>
-  /// Can execute an algorithm synchronously
-  /// </summary>
-  public class AlgorithmExecutor {
-    private EngineAlgorithm algorithm;
-    private AutoResetEvent waitHandle = new AutoResetEvent(false);
-
-    public AlgorithmExecutor(EngineAlgorithm algorithm) {
-      this.algorithm = algorithm;
-    }
-
-    public void StartSync() {
-      algorithm.Stopped += new EventHandler(algorithm_Stopped);
-      algorithm.Paused += new EventHandler(algorithm_Paused);
-      algorithm.Start();
-      waitHandle.WaitOne();
-      waitHandle.Dispose();
-      algorithm.Stopped -= new EventHandler(algorithm_Stopped);
-      algorithm.Paused -= new EventHandler(algorithm_Paused);
-    }
-
-    void algorithm_Paused(object sender, EventArgs e) {
-      waitHandle.Set();
-    }
-
-    void algorithm_Stopped(object sender, EventArgs e) {
-      waitHandle.Set();
-    }
-  }
 }
Index: branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/HeuristicLab.Problems.MetaOptimization-3.3.csproj
===================================================================
--- branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/HeuristicLab.Problems.MetaOptimization-3.3.csproj	(revision 5277)
+++ branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/HeuristicLab.Problems.MetaOptimization-3.3.csproj	(revision 5281)
@@ -132,4 +132,5 @@
     <None Include="Properties\AssemblyInfo.cs.frame" />
     <None Include="HeuristicLabProblemsMetaOptimizationPlugin.cs.frame" />
+    <Compile Include="Analyzers\MedianQualityAnalyzer.cs" />
     <Compile Include="ConstrainedItemList.cs" />
     <Compile Include="Encoding\NullValue.cs" />
@@ -147,4 +148,5 @@
     <Compile Include="Encoding\ValueConfigurations\ValueConfiguration.cs" />
     <Compile Include="Encoding\ValueConfigurations\CheckedValueConfigurationCollection.cs" />
+    <Compile Include="Evaluators\AlgorithmExecutor.cs" />
     <Compile Include="Interfaces\IParameterConfigurationCrossover.cs" />
     <Compile Include="Interfaces\ICheckedValueConfigurationCollection.cs" />
Index: branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/MetaOptimizationProblem.cs
===================================================================
--- branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/MetaOptimizationProblem.cs	(revision 5277)
+++ branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/MetaOptimizationProblem.cs	(revision 5281)
@@ -98,4 +98,7 @@
       get { return Operators.OfType<BestParameterConfigurationAnalyzer>().FirstOrDefault(); }
     }
+    private MedianQualityAnalyzer MedianQualityAnalyzer {
+      get { return Operators.OfType<MedianQualityAnalyzer>().FirstOrDefault(); }
+    }
     #endregion
 
@@ -158,4 +161,5 @@
       Operators.AddRange(ApplicationManager.Manager.GetInstances<IParameterConfigurationOperator>().Cast<IOperator>());
       Operators.Add(new BestParameterConfigurationAnalyzer());
+      Operators.Add(new MedianQualityAnalyzer());
     }
     private void ParameterizeSolutionCreator() {
@@ -167,4 +171,7 @@
       if (BestParameterConfigurationAnalyzer != null) {
         BestParameterConfigurationAnalyzer.ParameterConfigurationParameter.ActualName = ((RandomParameterConfigurationCreator)SolutionCreator).ParameterConfigurationParameter.ActualName;
+      }
+      if (MedianQualityAnalyzer != null) {
+        MedianQualityAnalyzer.ParameterConfigurationParameter.ActualName = ((RandomParameterConfigurationCreator)SolutionCreator).ParameterConfigurationParameter.ActualName;
       }
     }
