Index: /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/HeuristicLab.Problems.TSP.Views-3.3.csproj
===================================================================
--- /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/HeuristicLab.Problems.TSP.Views-3.3.csproj	(revision 3150)
+++ /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/HeuristicLab.Problems.TSP.Views-3.3.csproj	(revision 3151)
@@ -92,4 +92,10 @@
     </Compile>
     <Compile Include="HeuristicLabProblemsTSPViewsPlugin.cs" />
+    <Compile Include="TSPLIBImportDialog.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="TSPLIBImportDialog.Designer.cs">
+      <DependentUpon>TSPLIBImportDialog.cs</DependentUpon>
+    </Compile>
     <Compile Include="TSPView.cs">
       <SubType>UserControl</SubType>
@@ -109,4 +115,8 @@
       <Project>{958B43BC-CC5C-4FA2-8628-2B3B01D890B6}</Project>
       <Name>HeuristicLab.Collections-3.3</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.Common.Resources\3.2\HeuristicLab.Common.Resources-3.2.csproj">
+      <Project>{0E27A536-1C4A-4624-A65E-DC4F4F23E3E1}</Project>
+      <Name>HeuristicLab.Common.Resources-3.2</Name>
     </ProjectReference>
     <ProjectReference Include="..\..\HeuristicLab.Core.Views\3.3\HeuristicLab.Core.Views-3.3.csproj">
Index: /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/HeuristicLabProblemsTSPViewsPlugin.cs.frame
===================================================================
--- /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/HeuristicLabProblemsTSPViewsPlugin.cs.frame	(revision 3150)
+++ /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/HeuristicLabProblemsTSPViewsPlugin.cs.frame	(revision 3151)
@@ -29,4 +29,5 @@
   [PluginFile("HeuristicLab.Problems.TSP.Views-3.3.dll", PluginFileType.Assembly)]
   [PluginDependency("HeuristicLab.Collections", "3.3")]
+  [PluginDependency("HeuristicLab.Common.Resources", "3.2")]
   [PluginDependency("HeuristicLab.Core", "3.3")]
   [PluginDependency("HeuristicLab.Core.Views", "3.3")]
Index: /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/TSPLIBImportDialog.Designer.cs
===================================================================
--- /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/TSPLIBImportDialog.Designer.cs	(revision 3151)
+++ /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/TSPLIBImportDialog.Designer.cs	(revision 3151)
@@ -0,0 +1,250 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2010 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
+
+namespace HeuristicLab.Problems.TSP.Views {
+  partial class TSPLIBImportDialog {
+    /// <summary>
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary>
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Windows Form Designer generated code
+
+    /// <summary>
+    /// Required method for Designer support - do not modify
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.okButton = new System.Windows.Forms.Button();
+      this.cancelButton = new System.Windows.Forms.Button();
+      this.openTSPFileDialog = new System.Windows.Forms.OpenFileDialog();
+      this.tspFileLabel = new System.Windows.Forms.Label();
+      this.openTSPFileButton = new System.Windows.Forms.Button();
+      this.tspFileTextBox = new System.Windows.Forms.TextBox();
+      this.tourFileLabel = new System.Windows.Forms.Label();
+      this.openTourFileButton = new System.Windows.Forms.Button();
+      this.tourFileTextBox = new System.Windows.Forms.TextBox();
+      this.qualityLabel = new System.Windows.Forms.Label();
+      this.qualityTextBox = new System.Windows.Forms.TextBox();
+      this.openTourFileDialog = new System.Windows.Forms.OpenFileDialog();
+      this.errorProvider = new System.Windows.Forms.ErrorProvider(this.components);
+      this.clearTourFileButton = new System.Windows.Forms.Button();
+      ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).BeginInit();
+      this.SuspendLayout();
+      // 
+      // okButton
+      // 
+      this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+      this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK;
+      this.okButton.Enabled = false;
+      this.okButton.Location = new System.Drawing.Point(418, 97);
+      this.okButton.Name = "okButton";
+      this.okButton.Size = new System.Drawing.Size(75, 23);
+      this.okButton.TabIndex = 9;
+      this.okButton.Text = "&OK";
+      this.okButton.UseVisualStyleBackColor = true;
+      // 
+      // cancelButton
+      // 
+      this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+      this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+      this.cancelButton.Location = new System.Drawing.Point(499, 97);
+      this.cancelButton.Name = "cancelButton";
+      this.cancelButton.Size = new System.Drawing.Size(75, 23);
+      this.cancelButton.TabIndex = 10;
+      this.cancelButton.Text = "&Cancel";
+      this.cancelButton.UseVisualStyleBackColor = true;
+      // 
+      // openTSPFileDialog
+      // 
+      this.openTSPFileDialog.DefaultExt = "tsp";
+      this.openTSPFileDialog.FileName = "tsp";
+      this.openTSPFileDialog.Filter = "TSP Files|*.tsp|All Files|*.*";
+      this.openTSPFileDialog.Title = "Open TSP File";
+      // 
+      // tspFileLabel
+      // 
+      this.tspFileLabel.AutoSize = true;
+      this.tspFileLabel.Location = new System.Drawing.Point(12, 15);
+      this.tspFileLabel.Name = "tspFileLabel";
+      this.tspFileLabel.Size = new System.Drawing.Size(50, 13);
+      this.tspFileLabel.TabIndex = 0;
+      this.tspFileLabel.Text = "&TSP File:";
+      // 
+      // openTSPFileButton
+      // 
+      this.openTSPFileButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+      this.openTSPFileButton.Image = HeuristicLab.Common.Resources.VS2008ImageLibrary.Add;
+      this.openTSPFileButton.Location = new System.Drawing.Point(520, 10);
+      this.openTSPFileButton.Name = "openTSPFileButton";
+      this.openTSPFileButton.Size = new System.Drawing.Size(24, 24);
+      this.openTSPFileButton.TabIndex = 2;
+      this.openTSPFileButton.UseVisualStyleBackColor = true;
+      this.openTSPFileButton.Click += new System.EventHandler(this.openTSPFileButton_Click);
+      // 
+      // tspFileTextBox
+      // 
+      this.tspFileTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.tspFileTextBox.Enabled = false;
+      this.tspFileTextBox.Location = new System.Drawing.Point(136, 12);
+      this.tspFileTextBox.Name = "tspFileTextBox";
+      this.tspFileTextBox.ReadOnly = true;
+      this.tspFileTextBox.Size = new System.Drawing.Size(378, 20);
+      this.tspFileTextBox.TabIndex = 1;
+      // 
+      // tourFileLabel
+      // 
+      this.tourFileLabel.AutoSize = true;
+      this.tourFileLabel.Location = new System.Drawing.Point(12, 67);
+      this.tourFileLabel.Name = "tourFileLabel";
+      this.tourFileLabel.Size = new System.Drawing.Size(113, 13);
+      this.tourFileLabel.TabIndex = 5;
+      this.tourFileLabel.Text = "&Optimal TSP Tour File:";
+      // 
+      // openTourFileButton
+      // 
+      this.openTourFileButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+      this.openTourFileButton.Image = HeuristicLab.Common.Resources.VS2008ImageLibrary.Add;
+      this.openTourFileButton.Location = new System.Drawing.Point(520, 62);
+      this.openTourFileButton.Name = "openTourFileButton";
+      this.openTourFileButton.Size = new System.Drawing.Size(24, 24);
+      this.openTourFileButton.TabIndex = 7;
+      this.openTourFileButton.UseVisualStyleBackColor = true;
+      this.openTourFileButton.Click += new System.EventHandler(this.openTourFileButton_Click);
+      // 
+      // tourFileTextBox
+      // 
+      this.tourFileTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.tourFileTextBox.Enabled = false;
+      this.tourFileTextBox.Location = new System.Drawing.Point(136, 64);
+      this.tourFileTextBox.Name = "tourFileTextBox";
+      this.tourFileTextBox.ReadOnly = true;
+      this.tourFileTextBox.Size = new System.Drawing.Size(378, 20);
+      this.tourFileTextBox.TabIndex = 6;
+      // 
+      // qualityLabel
+      // 
+      this.qualityLabel.AutoSize = true;
+      this.qualityLabel.Location = new System.Drawing.Point(12, 41);
+      this.qualityLabel.Name = "qualityLabel";
+      this.qualityLabel.Size = new System.Drawing.Size(102, 13);
+      this.qualityLabel.TabIndex = 3;
+      this.qualityLabel.Text = "&Best Known Quality:";
+      // 
+      // qualityTextBox
+      // 
+      this.qualityTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.qualityTextBox.Location = new System.Drawing.Point(136, 38);
+      this.qualityTextBox.Name = "qualityTextBox";
+      this.qualityTextBox.Size = new System.Drawing.Size(378, 20);
+      this.qualityTextBox.TabIndex = 4;
+      this.qualityTextBox.Validated += new System.EventHandler(this.qualityTextBox_Validated);
+      this.qualityTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.qualityTextBox_KeyDown);
+      this.qualityTextBox.Validating += new System.ComponentModel.CancelEventHandler(this.qualityTextBox_Validating);
+      // 
+      // openTourFileDialog
+      // 
+      this.openTourFileDialog.DefaultExt = "opt.tour";
+      this.openTourFileDialog.FileName = "tour";
+      this.openTourFileDialog.Filter = "Optimal TSP Tour Files|*.opt.tour|All Files|*.*";
+      this.openTourFileDialog.SupportMultiDottedExtensions = true;
+      this.openTourFileDialog.Title = "Open Optimal TSP Tour File";
+      // 
+      // errorProvider
+      // 
+      this.errorProvider.BlinkStyle = System.Windows.Forms.ErrorBlinkStyle.NeverBlink;
+      this.errorProvider.ContainerControl = this;
+      // 
+      // clearTourFileButton
+      // 
+      this.clearTourFileButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+      this.clearTourFileButton.Image = HeuristicLab.Common.Resources.VS2008ImageLibrary.Remove;
+      this.clearTourFileButton.Location = new System.Drawing.Point(550, 62);
+      this.clearTourFileButton.Name = "clearTourFileButton";
+      this.clearTourFileButton.Size = new System.Drawing.Size(24, 24);
+      this.clearTourFileButton.TabIndex = 8;
+      this.clearTourFileButton.UseVisualStyleBackColor = true;
+      this.clearTourFileButton.Click += new System.EventHandler(this.clearTourFileButton_Click);
+      // 
+      // TSPLIBImportDialog
+      // 
+      this.AcceptButton = this.okButton;
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.CancelButton = this.cancelButton;
+      this.ClientSize = new System.Drawing.Size(586, 132);
+      this.Controls.Add(this.qualityTextBox);
+      this.Controls.Add(this.tourFileTextBox);
+      this.Controls.Add(this.tspFileTextBox);
+      this.Controls.Add(this.clearTourFileButton);
+      this.Controls.Add(this.openTourFileButton);
+      this.Controls.Add(this.openTSPFileButton);
+      this.Controls.Add(this.qualityLabel);
+      this.Controls.Add(this.tourFileLabel);
+      this.Controls.Add(this.tspFileLabel);
+      this.Controls.Add(this.cancelButton);
+      this.Controls.Add(this.okButton);
+      this.MaximizeBox = false;
+      this.MinimizeBox = false;
+      this.Name = "TSPLIBImportDialog";
+      this.ShowIcon = false;
+      this.ShowInTaskbar = false;
+      this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+      this.Text = "Import TSP from TSPLIB";
+      ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).EndInit();
+      this.ResumeLayout(false);
+      this.PerformLayout();
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.Button okButton;
+    private System.Windows.Forms.Button cancelButton;
+    private System.Windows.Forms.OpenFileDialog openTSPFileDialog;
+    private System.Windows.Forms.Label tspFileLabel;
+    private System.Windows.Forms.Button openTSPFileButton;
+    private System.Windows.Forms.TextBox tspFileTextBox;
+    private System.Windows.Forms.Label tourFileLabel;
+    private System.Windows.Forms.Button openTourFileButton;
+    private System.Windows.Forms.TextBox tourFileTextBox;
+    private System.Windows.Forms.Label qualityLabel;
+    private System.Windows.Forms.TextBox qualityTextBox;
+    private System.Windows.Forms.OpenFileDialog openTourFileDialog;
+    private System.Windows.Forms.ErrorProvider errorProvider;
+    private System.Windows.Forms.Button clearTourFileButton;
+  }
+}
Index: /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/TSPLIBImportDialog.cs
===================================================================
--- /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/TSPLIBImportDialog.cs	(revision 3151)
+++ /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/TSPLIBImportDialog.cs	(revision 3151)
@@ -0,0 +1,99 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2010 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.ComponentModel;
+using System.Text;
+using System.Windows.Forms;
+using HeuristicLab.Data;
+
+namespace HeuristicLab.Problems.TSP.Views {
+  public partial class TSPLIBImportDialog : Form {
+    private string tspFileName;
+    public string TSPFileName {
+      get { return tspFileName; }
+    }
+    private double? quality;
+    public double? Quality {
+      get { return quality; }
+    }
+    private string tourFileName;
+    public string TourFileName {
+      get { return tourFileName; }
+    }
+
+    public TSPLIBImportDialog() {
+      InitializeComponent();
+      tspFileName = string.Empty;
+      tourFileName = string.Empty;
+      quality = null;
+      errorProvider.SetIconAlignment(qualityTextBox, ErrorIconAlignment.MiddleLeft);
+      errorProvider.SetIconPadding(qualityTextBox, 2);
+    }
+
+    private void openTSPFileButton_Click(object sender, EventArgs e) {
+      if (openTSPFileDialog.ShowDialog(this) == DialogResult.OK) {
+        tspFileTextBox.Text = openTSPFileDialog.FileName;
+        tspFileTextBox.Enabled = true;
+        tspFileName = openTSPFileDialog.FileName;
+        okButton.Enabled = true;
+      }
+    }
+    private void openTourFileButton_Click(object sender, EventArgs e) {
+      if (openTourFileDialog.ShowDialog(this) == DialogResult.OK) {
+        tourFileTextBox.Text = openTourFileDialog.FileName;
+        tourFileTextBox.Enabled = true;
+        tourFileName = openTourFileDialog.FileName;
+      }
+    }
+    private void clearTourFileButton_Click(object sender, EventArgs e) {
+      tourFileTextBox.Text = string.Empty;
+      tourFileTextBox.Enabled = false;
+      tourFileName = string.Empty;
+    }
+
+    private void qualityTextBox_Validating(object sender, CancelEventArgs e) {
+      double val;
+      if ((!string.IsNullOrEmpty(qualityTextBox.Text)) && (!double.TryParse(qualityTextBox.Text, out val))) {
+        e.Cancel = true;
+        StringBuilder sb = new StringBuilder();
+        sb.Append("Invalid Value (Valid Value Format: \"");
+        sb.Append(FormatPatterns.GetDoubleFormatPattern());
+        sb.Append("\")");
+        errorProvider.SetError(qualityTextBox, sb.ToString());
+        qualityTextBox.SelectAll();
+        CancelButton = null;
+      }
+    }
+    private void qualityTextBox_Validated(object sender, EventArgs e) {
+      if (string.IsNullOrEmpty(qualityTextBox.Text)) quality = null;
+      else quality = double.Parse(qualityTextBox.Text);
+      errorProvider.SetError(qualityTextBox, string.Empty);
+      CancelButton = cancelButton;
+    }
+    private void qualityTextBox_KeyDown(object sender, KeyEventArgs e) {
+      if (e.KeyCode == Keys.Escape) {
+        qualityTextBox.Text = quality == null ? string.Empty : quality.ToString();
+        qualityLabel.Focus();  // set focus on label to validate data
+      }
+    }
+  }
+}
Index: /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/TSPView.Designer.cs
===================================================================
--- /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/TSPView.Designer.cs	(revision 3150)
+++ /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/TSPView.Designer.cs	(revision 3151)
@@ -33,4 +33,5 @@
     protected override void Dispose(bool disposing) {
       if (disposing) {
+        if (tsplibImportDialog != null) tsplibImportDialog.Dispose();
         if (components != null) components.Dispose();
       }
@@ -45,5 +46,4 @@
     /// </summary>
     private void InitializeComponent() {
-      this.openFileDialog = new System.Windows.Forms.OpenFileDialog();
       this.importButton = new System.Windows.Forms.Button();
       ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).BeginInit();
@@ -63,11 +63,4 @@
       // 
       this.descriptionTextBox.Size = new System.Drawing.Size(457, 20);
-      // 
-      // openFileDialog
-      // 
-      this.openFileDialog.DefaultExt = "tsp";
-      this.openFileDialog.FileName = "tspdata";
-      this.openFileDialog.Filter = "TSPLIB Files|*.tsp|All Files|*.*";
-      this.openFileDialog.Title = "Import TSP Data";
       // 
       // importButton
@@ -104,5 +97,4 @@
     #endregion
 
-    private System.Windows.Forms.OpenFileDialog openFileDialog;
     private System.Windows.Forms.Button importButton;
 
Index: /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/TSPView.cs
===================================================================
--- /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/TSPView.cs	(revision 3150)
+++ /trunk/sources/HeuristicLab.Problems.TSP.Views/3.3/TSPView.cs	(revision 3151)
@@ -33,4 +33,6 @@
   [Content(typeof(TSP), true)]
   public sealed partial class TSPView : ProblemView {
+    private TSPLIBImportDialog tsplibImportDialog;
+
     public new TSP Content {
       get { return (TSP)base.Content; }
@@ -63,7 +65,12 @@
 
     private void importButton_Click(object sender, System.EventArgs e) {
-      if (openFileDialog.ShowDialog(this) == DialogResult.OK) {
+      if (tsplibImportDialog == null) tsplibImportDialog = new TSPLIBImportDialog();
+
+      if (tsplibImportDialog.ShowDialog(this) == DialogResult.OK) {
         try {
-          Content.ImportFromTSPLIB(openFileDialog.FileName);
+          if (tsplibImportDialog.Quality == null)
+            Content.ImportFromTSPLIB(tsplibImportDialog.TSPFileName, tsplibImportDialog.TourFileName);
+          else
+            Content.ImportFromTSPLIB(tsplibImportDialog.TSPFileName, tsplibImportDialog.TourFileName, (double)tsplibImportDialog.Quality);
         }
         catch (Exception ex) {
Index: /trunk/sources/HeuristicLab.Problems.TSP/3.3/HeuristicLab.Problems.TSP-3.3.csproj
===================================================================
--- /trunk/sources/HeuristicLab.Problems.TSP/3.3/HeuristicLab.Problems.TSP-3.3.csproj	(revision 3150)
+++ /trunk/sources/HeuristicLab.Problems.TSP/3.3/HeuristicLab.Problems.TSP-3.3.csproj	(revision 3151)
@@ -84,4 +84,5 @@
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="TSPLIBTourParser.cs" />
     <Compile Include="Interfaces\ICoordinatesTSPSolutionsVisualizer.cs" />
     <Compile Include="Interfaces\IPathCoordinatesTSPSolutionsVisualizer.cs" />
Index: /trunk/sources/HeuristicLab.Problems.TSP/3.3/TSP.cs
===================================================================
--- /trunk/sources/HeuristicLab.Problems.TSP/3.3/TSP.cs	(revision 3150)
+++ /trunk/sources/HeuristicLab.Problems.TSP/3.3/TSP.cs	(revision 3151)
@@ -82,4 +82,7 @@
       get { return BestKnownQualityParameter; }
     }
+    public OptionalValueParameter<Permutation> BestKnownSolutionParameter {
+      get { return (OptionalValueParameter<Permutation>)Parameters["BestKnownSolution"]; }
+    }
     #endregion
 
@@ -124,4 +127,8 @@
       get { return BestKnownQualityParameter.Value; }
       set { BestKnownQualityParameter.Value = value; }
+    }
+    public Permutation BestKnownSolution {
+      get { return BestKnownSolutionParameter.Value; }
+      set { BestKnownSolutionParameter.Value = value; }
     }
     private List<IPermutationOperator> operators;
@@ -145,4 +152,5 @@
       Parameters.Add(new OptionalValueParameter<ITSPSolutionsVisualizer>("Visualizer", "The operator which should be used to visualize TSP solutions.", visualizer));
       Parameters.Add(new OptionalValueParameter<DoubleValue>("BestKnownQuality", "The quality of the best known solution of this TSP instance."));
+      Parameters.Add(new OptionalValueParameter<Permutation>("BestKnownSolution", "The best known solution of this TSP instance."));
 
       creator.PermutationParameter.ActualName = "TSPTour";
@@ -163,9 +171,21 @@
     }
 
-    public void ImportFromTSPLIB(string filename) {
-      TSPLIBParser parser = new TSPLIBParser(filename);
-      parser.Parse();
-      Name = parser.Name + " TSP (imported from TSPLIB)";
-      Coordinates = new DoubleMatrix(parser.Vertices);
+    public void ImportFromTSPLIB(string tspFileName, string optimalTourFileName) {
+      TSPLIBParser tspParser = new TSPLIBParser(tspFileName);
+      tspParser.Parse();
+      Name = tspParser.Name + " TSP (imported from TSPLIB)";
+      if (!string.IsNullOrEmpty(tspParser.Comment)) Description = tspParser.Comment;
+      Coordinates = new DoubleMatrix(tspParser.Vertices);
+      BestKnownQuality = null;
+
+      if (!string.IsNullOrEmpty(optimalTourFileName)) {
+        TSPLIBTourParser tourParser = new TSPLIBTourParser(optimalTourFileName);
+        tourParser.Parse();
+        BestKnownSolution = new Permutation(tourParser.Tour);
+      }
+    }
+    public void ImportFromTSPLIB(string tspFileName, string optimalTourFileName, double bestKnownQuality) {
+      ImportFromTSPLIB(tspFileName, optimalTourFileName);
+      BestKnownQuality = new DoubleValue(bestKnownQuality);
     }
 
Index: /trunk/sources/HeuristicLab.Problems.TSP/3.3/TSPLIBParser.cs
===================================================================
--- /trunk/sources/HeuristicLab.Problems.TSP/3.3/TSPLIBParser.cs	(revision 3150)
+++ /trunk/sources/HeuristicLab.Problems.TSP/3.3/TSPLIBParser.cs	(revision 3151)
@@ -26,5 +26,5 @@
 namespace HeuristicLab.Problems.TSP {
   /// <summary>
-  /// Parses a *.tsp file in the TSPLIB format and extracts its information about a TSP.
+  /// Parses a *.tsp file in TSPLIB format and extracts its information about a TSP.
   /// </summary>
   public class TSPLIBParser {
@@ -32,53 +32,62 @@
     private const int NAME = 1;
     private const int TYPE = 2;
-    private const int DIM = 3;
-    private const int WEIGHTTYPE = 4;
-    private const int NODETYPE = 5;
-    private const int NODESECTION = 6;
+    private const int COMMENT = 3;
+    private const int DIM = 4;
+    private const int WEIGHTTYPE = 5;
+    private const int NODETYPE = 6;
+    private const int NODESECTION = 7;
 
     private StreamReader source;
 
-    private string myName;
+    private string name;
     /// <summary>
     /// Gets the name of the parsed TSP.
     /// </summary>
     public string Name {
-      get { return myName; }
-    }
-    private double[,] myVertices;
+      get { return name; }
+    }
+    private string comment;
+    /// <summary>
+    /// Gets the comment of the parsed TSP.
+    /// </summary>
+    public string Comment {
+      get { return comment; }
+    }
+    private double[,] vertices;
     /// <summary>
     /// Gets the vertices of the parsed TSP.
     /// </summary>
     public double[,] Vertices {
-      get { return myVertices; }
-    }
-    private int myWeightType;
+      get { return vertices; }
+    }
+    private int weightType;
     /// <summary>
     /// Gets the weight type of the parsed TSP.
     /// </summary>
     public int WeightType {
-      get { return myWeightType; }
-    }
-
-    /// <summary>
-    /// Initializes a new instance of <see cref="TSPParser"/> with the given <paramref name="path"/>.
-    /// </summary>
-    /// <exception cref="ArgumentException">Thrown when the input file name is not in TSP format (*.tsp)
+      get { return weightType; }
+    }
+
+    /// <summary>
+    /// Initializes a new instance of <see cref="TSPLIBParser"/> with the given <paramref name="path"/>.
+    /// </summary>
+    /// <exception cref="ArgumentException">Thrown if the input file is not a TSPLIB TSP file (*.tsp)
     /// </exception>
     /// <param name="path">The path where the TSP is stored.</param>
     public TSPLIBParser(String path) {
       if (!path.EndsWith(".tsp"))
-        throw new ArgumentException("Input file name has to be in TSP format (*.tsp)");
+        throw new ArgumentException("Input file has to be a TSPLIB TSP file (*.tsp).");
 
       source = new StreamReader(path);
-      myName = path;
-      myVertices = null;
-      myWeightType = -1;
-    }
-
-    /// <summary>
-    /// Reads the TSP file and parses the elements.
-    /// </summary>
-    /// <exception cref="InvalidDataException">Thrown when file contains unknown (edge) types.</exception>
+      name = path;
+      comment = string.Empty;
+      vertices = null;
+      weightType = -1;
+    }
+
+    /// <summary>
+    /// Reads the TSPLIB TSP file and parses the elements.
+    /// </summary>
+    /// <exception cref="InvalidDataException">Thrown if the file has an invalid format or contains invalid data.</exception>
     public void Parse() {
       int section = -1;
@@ -100,4 +109,7 @@
               typeIsChecked = true;
               break;
+            case COMMENT:
+              ReadComment(str);
+              break;
             case DIM:
               InitVerticesArray(str);
@@ -118,5 +130,5 @@
 
       if (!(typeIsChecked && weightTypeIsChecked))
-        throw new InvalidDataException("File contains unknown (edge) types");
+        throw new InvalidDataException("Input file does not contain type or edge weight type information.");
     }
 
@@ -136,4 +148,6 @@
       if (token.Equals("type", StringComparison.OrdinalIgnoreCase))
         return TYPE;
+      if (token.Equals("comment", StringComparison.OrdinalIgnoreCase))
+        return COMMENT;
       if (token.Equals("dimension", StringComparison.OrdinalIgnoreCase))
         return DIM;
@@ -150,5 +164,5 @@
     private void ReadName(string str) {
       string[] tokens = str.Split(new string[] { ":" }, StringSplitOptions.None);
-      myName = tokens[tokens.Length - 1].Trim();
+      name = tokens[tokens.Length - 1].Trim();
     }
 
@@ -158,5 +172,10 @@
       string type = tokens[tokens.Length - 1].Trim();
       if (!type.Equals("tsp", StringComparison.OrdinalIgnoreCase))
-        throw new InvalidDataException("Input data format is not \"TSP\"");
+        throw new InvalidDataException("Input file type is not \"TSP\"");
+    }
+
+    private void ReadComment(string str) {
+      string[] tokens = str.Split(new string[] { ":" }, StringSplitOptions.None);
+      comment = tokens[tokens.Length - 1].Trim();
     }
 
@@ -166,5 +185,5 @@
 
       int dim = Int32.Parse(dimension);
-      myVertices = new double[dim, 2];
+      vertices = new double[dim, 2];
     }
 
@@ -174,9 +193,9 @@
 
       if (type.Equals("euc_2d", StringComparison.OrdinalIgnoreCase))
-        myWeightType = 0;
+        weightType = 0;
       else if (type.Equals("geo", StringComparison.OrdinalIgnoreCase))
-        myWeightType = 1;
+        weightType = 1;
       else
-        throw new InvalidDataException("Unsupported type of edge weights");
+        throw new InvalidDataException("Input file contains an unsupported edge weight type (only \"EUC_2D\" and \"GEO\" are supported).");
     }
 
@@ -186,21 +205,21 @@
 
       if (!type.Equals("twod_coords", StringComparison.OrdinalIgnoreCase))
-        throw new InvalidDataException("Unsupported node type");
+        throw new InvalidDataException("Input file contains an unsupported node coordinates type (only \"TWOD_COORDS\" is supported).");
     }
 
     private void ReadVertices() {
-      if (myVertices == null)
-        throw new InvalidDataException("Dimension not found");
-
-      for (int i = 0; i < (myVertices.Length / 2); i++) {
+      if (vertices == null)
+        throw new InvalidDataException("Input file does not contain dimension information.");
+
+      for (int i = 0; i < (vertices.Length / 2); i++) {
         string str = source.ReadLine();
         string[] tokens = str.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
 
         if (tokens.Length != 3)
-          throw new InvalidDataException("Invalid node format");
+          throw new InvalidDataException("Input file contains invalid node coordinates.");
 
         CultureInfo culture = new CultureInfo("en-US");
-        myVertices[i, 0] = Double.Parse(tokens[1], culture.NumberFormat);
-        myVertices[i, 1] = Double.Parse(tokens[2], culture.NumberFormat);
+        vertices[i, 0] = double.Parse(tokens[1], culture.NumberFormat);
+        vertices[i, 1] = double.Parse(tokens[2], culture.NumberFormat);
       }
     }
Index: /trunk/sources/HeuristicLab.Problems.TSP/3.3/TSPLIBTourParser.cs
===================================================================
--- /trunk/sources/HeuristicLab.Problems.TSP/3.3/TSPLIBTourParser.cs	(revision 3151)
+++ /trunk/sources/HeuristicLab.Problems.TSP/3.3/TSPLIBTourParser.cs	(revision 3151)
@@ -0,0 +1,180 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2010 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Globalization;
+using System.IO;
+
+namespace HeuristicLab.Problems.TSP {
+  /// <summary>
+  /// Parses a *.opt.tour file in TSPLIB format and extracts its information about an optimal tour of a TSP.
+  /// </summary>
+  public class TSPLIBTourParser {
+    private const int EOF = 0;
+    private const int NAME = 1;
+    private const int TYPE = 2;
+    private const int COMMENT = 3;
+    private const int DIM = 4;
+    private const int TOURSECTION = 5;
+
+    private StreamReader source;
+
+    private string name;
+    /// <summary>
+    /// Gets the name of the parsed tour.
+    /// </summary>
+    public string Name {
+      get { return name; }
+    }
+    private string comment;
+    /// <summary>
+    /// Gets the comment of the parsed tour.
+    /// </summary>
+    public string Comment {
+      get { return comment; }
+    }
+    private int[] tour;
+    /// <summary>
+    /// Gets the parsed tour.
+    /// </summary>
+    public int[] Tour {
+      get { return tour; }
+    }
+
+    /// <summary>
+    /// Initializes a new instance of <see cref="TSPLIBTourParser"/> with the given <paramref name="path"/>.
+    /// </summary>
+    /// <exception cref="ArgumentException">Thrown if the input file is not a TSPLIB optimal tour file (*.opt.tour)
+    /// </exception>
+    /// <param name="path">The path where the optimal tour is stored.</param>
+    public TSPLIBTourParser(String path) {
+      if (!path.EndsWith(".opt.tour"))
+        throw new ArgumentException("Input file has to be a TSPLIB optimal tour file (*.opt.tour).");
+
+      source = new StreamReader(path);
+      name = path;
+      comment = string.Empty;
+      tour = null;
+    }
+
+    /// <summary>
+    /// Reads the TSPLIB optimal tour file and parses its elements.
+    /// </summary>
+    /// <exception cref="InvalidDataException">Thrown if the file has an invalid format or contains invalid data.</exception>
+    public void Parse() {
+      int section = -1;
+      string str = null;
+      bool typeIsChecked = false;
+
+      do {
+        str = source.ReadLine();
+        section = GetSection(str);
+
+        if (section != -1) {
+          switch (section) {
+            case NAME:
+              ReadName(str);
+              break;
+            case TYPE:
+              CheckType(str);
+              typeIsChecked = true;
+              break;
+            case COMMENT:
+              ReadComment(str);
+              break;
+            case DIM:
+              InitTour(str);
+              break;
+            case TOURSECTION:
+              ReadTour();
+              break;
+          }
+        }
+      } while (!((section == EOF) || (str == null)));
+
+      if (!typeIsChecked)
+        throw new InvalidDataException("Input file does not contain type information.");
+    }
+
+    private int GetSection(string str) {
+      if (str == null)
+        return EOF;
+
+      string[] tokens = str.Split(new string[] { ":" }, StringSplitOptions.None);
+      if (tokens.Length == 0)
+        return -1;
+
+      string token = tokens[0].Trim();
+      if (token.Equals("eof", StringComparison.OrdinalIgnoreCase))
+        return EOF;
+      if (token.Equals("name", StringComparison.OrdinalIgnoreCase))
+        return NAME;
+      if (token.Equals("type", StringComparison.OrdinalIgnoreCase))
+        return TYPE;
+      if (token.Equals("comment", StringComparison.OrdinalIgnoreCase))
+        return COMMENT;
+      if (token.Equals("dimension", StringComparison.OrdinalIgnoreCase))
+        return DIM;
+      if (token.Equals("tour_section", StringComparison.OrdinalIgnoreCase))
+        return TOURSECTION;
+
+      return -1;
+    }
+
+    private void ReadName(string str) {
+      string[] tokens = str.Split(new string[] { ":" }, StringSplitOptions.None);
+      name = tokens[tokens.Length - 1].Trim();
+    }
+
+    private void CheckType(string str) {
+      string[] tokens = str.Split(new string[] { ":" }, StringSplitOptions.None);
+
+      string type = tokens[tokens.Length - 1].Trim();
+      if (!type.Equals("tour", StringComparison.OrdinalIgnoreCase))
+        throw new InvalidDataException("Input data format is not \"TOUR\"");
+    }
+
+    private void ReadComment(string str) {
+      string[] tokens = str.Split(new string[] { ":" }, StringSplitOptions.None);
+      comment = tokens[tokens.Length - 1].Trim();
+    }
+
+    private void InitTour(string str) {
+      string[] tokens = str.Split(new string[] { ":" }, StringSplitOptions.None);
+      string dimension = tokens[tokens.Length - 1].Trim();
+
+      int dim = Int32.Parse(dimension);
+      tour = new int[dim];
+    }
+
+    private void ReadTour() {
+      if (tour == null)
+        throw new InvalidDataException("Input file does not contain dimension information.");
+
+      for (int i = 0; i < (tour.Length); i++) {
+        string str = source.ReadLine();
+
+        CultureInfo culture = new CultureInfo("en-US");
+        tour[i] = int.Parse(str, culture.NumberFormat);
+      }
+    }
+  }
+}
Index: /trunk/sources/HeuristicLab.Problems.TSP/3.3/ch130.opt.tour
===================================================================
--- /trunk/sources/HeuristicLab.Problems.TSP/3.3/ch130.opt.tour	(revision 3151)
+++ /trunk/sources/HeuristicLab.Problems.TSP/3.3/ch130.opt.tour	(revision 3151)
@@ -0,0 +1,136 @@
+NAME : ch130.opt.tour
+COMMENT : Length 6110
+TYPE : TOUR
+DIMENSION : 130
+TOUR_SECTION
+1
+41
+39
+117
+112
+115
+28
+62
+105
+128
+16
+45
+5
+11
+76
+109
+61
+129
+124
+64
+69
+86
+88
+26
+7
+97
+70
+107
+127
+104
+43
+34
+17
+31
+27
+19
+100
+15
+29
+24
+116
+95
+79
+87
+12
+81
+103
+77
+94
+89
+110
+98
+68
+63
+48
+25
+113
+32
+36
+84
+119
+111
+123
+101
+82
+57
+9
+56
+65
+52
+75
+74
+99
+73
+92
+38
+106
+53
+120
+58
+49
+72
+91
+6
+102
+10
+14
+67
+13
+96
+122
+55
+60
+51
+42
+44
+93
+37
+22
+47
+40
+23
+33
+21
+126
+121
+78
+66
+85
+125
+90
+59
+30
+83
+3
+114
+108
+8
+18
+46
+80
+118
+20
+4
+35
+54
+2
+50
+130
+71
+-1
