Index: /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problem.ProgramSynthesis.BenchmarkSuite/BenchmarkSuiteInstanceProvider.cs
===================================================================
--- /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problem.ProgramSynthesis.BenchmarkSuite/BenchmarkSuiteInstanceProvider.cs	(revision 15344)
+++ /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problem.ProgramSynthesis.BenchmarkSuite/BenchmarkSuiteInstanceProvider.cs	(revision 15345)
@@ -45,4 +45,5 @@
       yield return new LastIndexOfZero();
       yield return new Median();
+      yield return new MedianIntegerError();
       yield return new MirrorImage();
       yield return new NegativeToZero();
Index: /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problem.ProgramSynthesis.BenchmarkSuite/HeuristicLab.BenchmarkSuite.csproj
===================================================================
--- /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problem.ProgramSynthesis.BenchmarkSuite/HeuristicLab.BenchmarkSuite.csproj	(revision 15344)
+++ /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problem.ProgramSynthesis.BenchmarkSuite/HeuristicLab.BenchmarkSuite.csproj	(revision 15345)
@@ -107,4 +107,5 @@
     <Compile Include="CsvParser.cs" />
     <Compile Include="DataTypes.cs" />
+    <Compile Include="Problems\MedianIntegerError.cs" />
     <Compile Include="Problems\ScrabbleScore.cs" />
     <Compile Include="ExampleArgumentConverter.cs" />
Index: /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problem.ProgramSynthesis.BenchmarkSuite/ProblemType.cs
===================================================================
--- /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problem.ProgramSynthesis.BenchmarkSuite/ProblemType.cs	(revision 15344)
+++ /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problem.ProgramSynthesis.BenchmarkSuite/ProblemType.cs	(revision 15345)
@@ -12,4 +12,5 @@
     LastIndexOfZero,
     Median,
+    MedianIntegerError,
     MirrorImage,
     NegativeToZero,
Index: /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problem.ProgramSynthesis.BenchmarkSuite/Problems/MedianIntegerError.cs
===================================================================
--- /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problem.ProgramSynthesis.BenchmarkSuite/Problems/MedianIntegerError.cs	(revision 15345)
+++ /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problem.ProgramSynthesis.BenchmarkSuite/Problems/MedianIntegerError.cs	(revision 15345)
@@ -0,0 +1,48 @@
+﻿namespace HeuristicLab.BenchmarkSuite.Problems {
+  using HeuristicLab.Problems.ProgramSynthesis.Base.Erc.Integer;
+
+  public class MedianIntegerError : BenchmarkSuiteDataDescriptor {
+    private const string name = "Median Integer Error - Medium";
+    private const string fileName = "Median.csv";
+    private const string description = "Given 3 integers, print their median.";
+
+    protected override string FileName { get { return fileName; } }
+    public override string Name { get { return name; } }
+    public override string Description { get { return description; } }
+    protected override int InputArgumentCount { get { return 3; } }
+    protected override int OutputArgumentCount { get { return 1; } }
+
+    public override ProblemData CreateProblemData() {
+      return new ProblemData(ProblemType.MedianIntegerError) {
+        Name = Name,
+        Description = Description,
+        ProgramExecutionBudget = 20000000,
+        Examples = CloneExamples(),
+        BestResult = 0,
+        WorstResult = 5000,
+        InputArgumentTypes = new[] { ExampleArgumentType.Integer, ExampleArgumentType.Integer, ExampleArgumentType.Integer },
+        OutputArgumentTypes = new[] { ExampleArgumentType.Print },
+        TrainingCount = 100,
+        TestCount = 1000,
+        EnabledDataTypes = DataTypes.Exec | DataTypes.Integer | DataTypes.Boolean | DataTypes.Print,
+        MaxSize = 200,
+        EvalLimit = 200,
+        ErcOptions = {
+          ErcProbability = 0.05,
+          IntegerErcOptions = new IntegerErcOptions(
+            new IntegerRangeErc(-100, 100))
+        }
+      };
+    }
+
+    protected override Example ParseExample(string[] input, string[] output) {
+      return new Example {
+        InputArgs = input,
+        OutputArgs = output,
+        InputInteger = ExampleArgumentConverter.ConvertIntegers(input),
+        OutputPrint = output[0],
+        OutputInteger = new[] { long.Parse(output[0]) }
+      };
+    }
+  }
+}
Index: /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problems.ProgramSynthesis/Push/Evaluator/PushBenchmarkSuiteEvaluator.cs
===================================================================
--- /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problems.ProgramSynthesis/Push/Evaluator/PushBenchmarkSuiteEvaluator.cs	(revision 15344)
+++ /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problems.ProgramSynthesis/Push/Evaluator/PushBenchmarkSuiteEvaluator.cs	(revision 15345)
@@ -149,4 +149,5 @@
         case ProblemType.Smallest:
         case ProblemType.Median: return PrintedStringRight(interpreter, example);
+        case ProblemType.MedianIntegerError: return MedianIntegerError(interpreter, example);
 
         // boolean error
@@ -214,4 +215,17 @@
     }
 
+    private double MedianIntegerError(IPushInterpreter interpreter, Example example) {
+      var printResult = interpreter.PrintStack.ToString();
+
+      long result;
+      if (long.TryParse(printResult, out result)) {
+        var diff = Math.Abs(result - example.OutputInteger[0]);
+
+        return Math.Min(Data.WorstResult, diff);
+      }
+
+      return Data.WorstResult;
+    }
+
     private double NumberIo(IPushInterpreter interpreter, Example example) {
       if (!string.IsNullOrEmpty(example.OutputPrint) && interpreter.PrintStack.IsEmpty)
@@ -289,5 +303,5 @@
       var printLines = interpreter.PrintStack.AsStrings().ToArray();
       var lineCountWithCorrectFormat = printLines.Count(line => line.IsNumeric());
-      var lineCountWithInvalidFormat = example.OutputPrintLineCount - lineCountWithCorrectFormat;
+      var lineCountWithInvalidFormat = Math.Max(0, example.OutputPrintLineCount - lineCountWithCorrectFormat);
 
       lineCountWithInvalidFormat = Math.Abs(Math.Min(lineCountWithInvalidFormat, example.OutputPrintLineCount));
Index: /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problems.ProgramSynthesis/Push/Selector/LexicaseSelector.cs
===================================================================
--- /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problems.ProgramSynthesis/Push/Selector/LexicaseSelector.cs	(revision 15344)
+++ /branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problems.ProgramSynthesis/Push/Selector/LexicaseSelector.cs	(revision 15345)
@@ -30,8 +30,7 @@
   using HeuristicLab.Parameters;
   using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
-  using HeuristicLab.Problems.ProgramSynthesis.Push.Extensions;
   using HeuristicLab.Problems.ProgramSynthesis.Push.Problem;
+  using HeuristicLab.Random;
   using HeuristicLab.Selection;
-  using Random;
 
   /// <summary>
@@ -59,12 +58,25 @@
     }
 
-    protected override IScope[] Select(List<IScope> population) {
+
+    protected override IScope[] Select(List<IScope> scopes) {
+      var count = NumberOfSelectedSubScopesParameter.ActualValue.Value;
+      var copy = CopySelectedParameter.Value.Value;
+      var random = RandomParameter.ActualValue;
+      var maximization = MaximizationParameter.ActualValue.Value;
+
+      //var qualities = QualityParameter.ActualValue;
+      //.Where(x => IsValidQuality(x.Value))
+      //.Select(x => x.Value)
+      //.ToList();
+
+      var caseQualities = CaseQualitiesParameter.ActualValue.ToList();
+
       var selected = Apply(
-        population,
-        NumberOfSelectedSubScopesParameter.ActualValue.Value,
-        CopySelectedParameter.Value.Value,
-        MaximizationParameter.ActualValue.Value,
-        RandomParameter.ActualValue,
-        CaseQualitiesParameter.ActualValue);
+        scopes,
+        count,
+        copy,
+        maximization,
+        random,
+        caseQualities);
 
       return selected;
@@ -72,38 +84,40 @@
 
     public static IScope[] Apply(
-      List<IScope> population,
+      List<IScope> scopes,
       int count,
       bool copy,
       bool maximization,
       IRandom random,
-      ItemArray<DoubleArray> caseQualities) {
+      List<DoubleArray> caseQualities) {
+
+      for (var i = 0; i < caseQualities.Count; i++) {
+        if (caseQualities[i].Length == 0) {
+          scopes.RemoveAt(i);
+          caseQualities.RemoveAt(i);
+        }
+      }
+
+      if (caseQualities.Any(x => x.Length != caseQualities[0].Length)) {
+        throw new ArgumentException("Not all case qualities have the same length");
+      }
+
       var selected = new IScope[count];
-      var repeats = (int)Math.Ceiling(count / (double)population.Count);
-      var caseCount = caseQualities[0].Length;
-      var source = Enumerable.Range(0, population.Count).ToList();
+      var candidates = Enumerable.Range(0, caseQualities.Count).ToList();
+      var orderSource = Enumerable.Range(0, caseQualities[0].Length).ToList();
 
-      for (var k = 0; k < repeats; k++) {
-        // The fitness cases are shuffled.
-        var fitnessCaseIndexes = Enumerable.Range(0, caseCount).Shuffle(random).ToList();
+      for (var i = 0; i < count; i++) {
+        var index = LexicaseSelect(
+          caseQualities,
+          candidates,
+          orderSource.Shuffle(random),
+          random,
+          maximization);
 
-        // copy list if required
-        var pool = repeats == 1 ? source : new List<int>(source);
-        var countLimit = Math.Min(count - k * population.Count, population.Count);
-
-        for (var i = 0; i < countLimit; i++) {
-          var candidates = pool;
-
-          for (var j = 0; j < fitnessCaseIndexes.Count && candidates.Count > 1; j++) {
-            candidates = GetBestIndividuals(maximization, caseQualities, candidates, fitnessCaseIndexes[j]);
-          }
-
-          /*  If only one individual remains, it is the chosen parent. If no more fitness cases are left, a parent is
-              chosen randomly from the remaining individuals */
-          var bestIndividualIndex = candidates.Count == 1 ? candidates[0] : candidates.Random(random);
-          var bestIndividual = population[bestIndividualIndex];
-
-          selected[k * population.Count + i] = copy ? (IScope)bestIndividual.Clone() : bestIndividual;
-
-          pool.Remove(bestIndividualIndex);
+        if (copy) {
+          selected[i] = (IScope)scopes[index].Clone();
+        } else {
+          selected[i] = scopes[index];
+          scopes.RemoveAt(index);
+          caseQualities.RemoveAt(index);
         }
       }
@@ -112,25 +126,48 @@
     }
 
-    private static List<int> GetBestIndividuals(bool maximization, ItemArray<DoubleArray> caseQualities, List<int> bestIndividuals, int index) {
-      var bestFitness = maximization ? double.NegativeInfinity : double.PositiveInfinity;
-      var result = new List<int>();
+    private static int LexicaseSelect(
+      List<DoubleArray> caseQualities,
+      List<int> candidates,
+      IEnumerable<int> order,
+      IRandom random,
+      bool maximization) {
 
-      for (var l = 0; l < bestIndividuals.Count; l++) {
-        var individual = bestIndividuals[l];
-        var caseQuality = caseQualities[individual][index];
+      foreach (var curCase in order) {
+        var nextCandidates = new List<int>();
 
-        if (bestFitness.IsAlmost(caseQuality)) {
-          result.Add(individual);
-        } else if (maximization && bestFitness < caseQuality ||
-                  !maximization && bestFitness > caseQuality) {
-          bestFitness = caseQuality;
-          result.Clear();
-          result.Add(individual);
+        var best = maximization
+          ? double.NegativeInfinity
+          : double.PositiveInfinity;
+
+        foreach (var candidate in candidates) {
+          if (caseQualities[candidate][curCase].IsAlmost(best)) {
+            // if the individuals is as good as the best one, add it
+            nextCandidates.Add(candidate);
+          } else if (
+             (maximization && (caseQualities[candidate][curCase] > best)) ||
+             (!maximization && (caseQualities[candidate][curCase] < best))) {
+            // if the individuals is better than the best one, remove all previous candidates and add the new one
+            nextCandidates.Clear();
+            nextCandidates.Add(candidate);
+            // also set the next best quality value
+            best = caseQualities[candidate][curCase];
+          }
+          // else {do nothing}
         }
 
-        bestIndividuals = result;
+        if (nextCandidates.Count == 1) {
+          return nextCandidates[0];
+        }
+
+        if (nextCandidates.Count < 1) {
+          return candidates.SampleRandom(random);
+        }
+
+        candidates = nextCandidates;
       }
 
-      return bestIndividuals;
+      return candidates.Count == 1
+        ? candidates[0]
+        : candidates.SampleRandom(random);
     }
   }
Index: /branches/PushGP/HeuristicLab.PushGP/Push.Thesis/Program.cs
===================================================================
--- /branches/PushGP/HeuristicLab.PushGP/Push.Thesis/Program.cs	(revision 15344)
+++ /branches/PushGP/HeuristicLab.PushGP/Push.Thesis/Program.cs	(revision 15345)
@@ -30,5 +30,5 @@
     static void Main(string[] args) {
       //BuildListOfInstructions();
-      GetProblemNames();
+      //GetProblemNames();
 
       //GenerateRuns();
@@ -49,4 +49,39 @@
 
       //Console.ReadKey(false);
+
+      var grouper = new RunGrouper();
+
+      grouper.WriteJobResults(
+        @"C:\tmp\",
+        @"C:\tmp\Total Results.txt");
+
+      //WriteProblemOverviewTable();
+    }
+
+    static void WriteProblemOverviewTable() {
+      var provider = new BenchmarkSuiteInstanceProvider();
+      var problems = provider
+        .GetDataDescriptors()
+        .Cast<BenchmarkSuiteDataDescriptor>()
+        .Select(d => d.CreateProblemData())
+        .OrderBy(prob => prob.Name);
+
+      var lines = problems.Select(d => string.Format(
+        @"{0} & {1} & {2} & {3} & {4} & {5} \\",
+        GetProblemNameWithoutDifficullty(d).PadRight(29),
+        d.TestCount.ToString().PadLeft(5),
+        d.TrainingCount.ToString().PadLeft(8),
+        d.MaxSize.ToString().PadLeft(15),
+        GetMaxGenerations(d).ToString().PadLeft(8),
+        d.EvalLimit.ToString().PadLeft(7)
+        ));
+
+      File.WriteAllLines(@"C:\tmp\ProblemOverviewTable.txt", lines);
+    }
+
+    static string GetProblemNameWithoutDifficullty(ProblemData data) {
+      var end = data.Name.LastIndexOf('-');
+
+      return data.Name.Substring(0, end - 1).Trim();
     }
 
@@ -189,4 +224,6 @@
       ga.Problem = pushProblem;
 
+      pushProblem.Config.DisableExpression<ExecYExpression>();
+
       var crossover = ga.CrossoverParameter.ValidValues.OfType<AlternationCrossover>().First();
 
@@ -219,5 +256,5 @@
       ga.PopulationSize.Value = 1000;
 
-      ga.MaximumGenerations.Value = problemData.ProgramExecutionBudget / problemData.TrainingCount / 1000;
+      ga.MaximumGenerations.Value = GetMaxGenerations(problemData);
 
       var zeroErrorAnalyzer = pushProblem.OperatorsParameter.Value
@@ -236,4 +273,7 @@
     }
 
+    private static int GetMaxGenerations(ProblemData problemData) {
+      return problemData.ProgramExecutionBudget / problemData.TrainingCount / 1000;
+    }
 
     static IAlgorithm GetOffspringGA(IBenchmarkSuiteDataDescriptor descriptor, bool parallel = true) {
@@ -245,4 +285,6 @@
       pushProblem.Load(problemData);
       ga.Name = "[Offspring GA]" + pushProblem.Name;
+
+      pushProblem.Config.DisableExpression<ExecYExpression>();
 
       ga.Problem = pushProblem;
Index: /branches/PushGP/HeuristicLab.PushGP/Push.Thesis/Push.Thesis.csproj
===================================================================
--- /branches/PushGP/HeuristicLab.PushGP/Push.Thesis/Push.Thesis.csproj	(revision 15344)
+++ /branches/PushGP/HeuristicLab.PushGP/Push.Thesis/Push.Thesis.csproj	(revision 15345)
@@ -111,4 +111,5 @@
     <Compile Include="Program.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="RunGrouper.cs" />
   </ItemGroup>
   <ItemGroup>
Index: /branches/PushGP/HeuristicLab.PushGP/Push.Thesis/RunGrouper.cs
===================================================================
--- /branches/PushGP/HeuristicLab.PushGP/Push.Thesis/RunGrouper.cs	(revision 15345)
+++ /branches/PushGP/HeuristicLab.PushGP/Push.Thesis/RunGrouper.cs	(revision 15345)
@@ -0,0 +1,215 @@
+﻿using System.Linq;
+
+namespace Push.Thesis {
+  using System;
+  using System.Collections.Generic;
+  using System.Globalization;
+  using System.IO;
+  using System.Threading.Tasks;
+
+  using HeuristicLab.Common;
+  using HeuristicLab.Data;
+  using HeuristicLab.Optimization;
+  using HeuristicLab.Persistence.Default.Xml;
+
+  public class JobResult {
+    public bool IsWithOffspringSelection { get; set; }
+    public string Name { get; set; }
+    public double Best { get; set; }
+    public double AverageBest { get; set; }
+    public double StandardDeviation { get; set; }
+    public int SolvedCount { get; set; }
+    public double SolvedRelative { get; set; }
+    public int TotalCount { get; set; }
+  }
+
+  public class RunGrouper {
+
+    public void WriteJobResults(string jobRoot, string outputPath) {
+      Console.WriteLine("Write job results from {0} to {1}", jobRoot, outputPath);
+
+      var jobResults = GetJobResults(jobRoot);
+
+      var lines = jobResults
+        .Select(GetJobResultLine)
+        .ToList();
+
+      File.WriteAllLines(outputPath, lines);
+      Console.WriteLine("JobResults written");
+    }
+
+    public void WriteJobResult(string jobRoot, string outputPath) {
+      var jobResult = GetJobResult(jobRoot);
+      var line = GetJobResultLine(jobResult);
+
+      File.WriteAllText(outputPath, line);
+    }
+
+    private static string GetJobResultLine(JobResult jobResult) {
+      var solvedPercentage = Math.Round(jobResult.SolvedRelative * 100, 2);
+
+      var prefix = jobResult.IsWithOffspringSelection
+        ? "[OSGA]"
+        : "[GA]  ";
+
+      return string.Format(
+                @"{7} {0} {1} {2} {3} {4} {5} {6}\%",
+                jobResult.Name.PadRight(28),
+                jobResult.Best.ToString("N4", CultureInfo.InvariantCulture).PadLeft(12),
+                jobResult.AverageBest.ToString("N4", CultureInfo.InvariantCulture).PadLeft(12),
+                jobResult.StandardDeviation.ToString("N4", CultureInfo.InvariantCulture).PadLeft(12),
+                jobResult.SolvedCount.ToString().PadLeft(4),
+                jobResult.TotalCount.ToString().PadLeft(4),
+                solvedPercentage.ToString("N2").PadLeft(6),
+                prefix
+              );
+    }
+
+    protected virtual bool CanFileBeRead(string path) {
+      return !IsFileLocked(path);
+    }
+
+    protected virtual bool IsFileLocked(string path) {
+      var fileInfo = new FileInfo(path);
+
+      return IsFileLocked(fileInfo);
+    }
+
+    protected virtual bool IsFileLocked(FileInfo file) {
+      FileStream stream = null;
+
+      try {
+        stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
+      }
+      catch (IOException) {
+        //the file is unavailable because it is:
+        //still being written to
+        //or being processed by another thread
+        //or does not exist (has already been processed)
+        return true;
+      }
+      finally {
+        if (stream != null)
+          stream.Close();
+      }
+
+      //file is not locked
+      return false;
+    }
+
+    public IReadOnlyList<JobResult> GetJobResults(string rootPath) {
+      Console.WriteLine("GetJobResults from {0}", rootPath);
+
+      if (!Directory.Exists(rootPath)) {
+        Console.WriteLine("Directory {0} not found", rootPath);
+        throw new FileNotFoundException(rootPath);
+      }
+
+      var jobResults = Directory
+        .EnumerateDirectories(rootPath)
+        .AsParallel()
+        .Select(GetJobResult)
+        .OrderBy(jr => jr.Name)
+        .ToList();
+
+      Console.WriteLine("JobResults read");
+      return jobResults;
+    }
+
+    public JobResult GetJobResult(string rootPath) {
+      if (!Directory.Exists(rootPath)) {
+        Console.WriteLine("Directory {0} not found\n", rootPath);
+        throw new FileNotFoundException(rootPath);
+      }
+
+      var directoryInfo = new DirectoryInfo(rootPath);
+      var name = GetJobName(directoryInfo.Name);
+
+      Console.WriteLine("Get job {0}", name);
+
+      var runs = this.GetRuns(rootPath);
+
+      var bestResults = runs.Select(r => (r.Results["BestQuality"] as DoubleValue).Value).ToList();
+      var best = bestResults.Min();
+      var avg = bestResults.Average();
+      var stdDeviation = StandardDeviation(bestResults);
+      var solvedCount = bestResults.Count(x => x.IsAlmost(0));
+      var totalCount = runs.Count;
+      var solvedRelative = solvedCount / (double)totalCount;
+      var isWithOffspringSelection = directoryInfo.Name.Contains("[Offspring GA]");
+
+      return new JobResult {
+        Name = name,
+        Best = best,
+        AverageBest = avg,
+        StandardDeviation = stdDeviation,
+        SolvedCount = solvedCount,
+        SolvedRelative = solvedRelative,
+        TotalCount = totalCount,
+        IsWithOffspringSelection = isWithOffspringSelection
+      };
+    }
+
+    private static string GetJobName(string totalName) {
+      var start = totalName.LastIndexOf(']') + 2;
+      var end = totalName.IndexOf('-') - 1;
+      var length = end - start;
+
+      var name = length > 0
+        ? totalName.Substring(start, length)
+        : totalName;
+
+      return name;
+    }
+
+    public static double StandardDeviation(IEnumerable<double> values) {
+      var avg = values.Average();
+      return Math.Sqrt(values.Average(v => Math.Pow(v - avg, 2)));
+    }
+
+    public void DoAllJobs(string rootPath) {
+      if (!Directory.Exists(rootPath)) {
+        throw new FileNotFoundException(rootPath);
+      }
+
+      var jobDirs = Directory.EnumerateDirectories(rootPath);
+      Parallel.ForEach(jobDirs, DoSingleJob);
+    }
+
+    public void DoSingleJob(string rootPath) {
+      if (!Directory.Exists(rootPath)) {
+        throw new FileNotFoundException(rootPath);
+      }
+
+      var directoryInfo = new DirectoryInfo(rootPath);
+      var fileName = "Grouped - " + directoryInfo.Name;
+      var outputPath = Path.Combine(directoryInfo.Parent.FullName, fileName + ".hl");
+
+      if (File.Exists(outputPath)) {
+        Console.WriteLine(@"Experiment {0} already exists", fileName);
+        return;
+      }
+
+      var runs = GetRuns(rootPath);
+
+      var runCollection = new RunCollection();
+      runCollection.AddRange(runs);
+
+      ContentManager.Save(runCollection, outputPath, true);
+    }
+
+    public IReadOnlyList<Run> GetRuns(string rootPath) {
+      var runs = Directory
+        .EnumerateFiles(rootPath)
+        .AsParallel()
+        .Where(CanFileBeRead)
+        .Select(XmlParser.Deserialize)
+        .Where(x => x is IAlgorithm)
+        .Cast<IAlgorithm>()
+        .Select(alg => new Run(alg))
+        .ToList();
+
+      return runs;
+    }
+  }
+}
