#region License Information
/* HeuristicLab
* Copyright (C) 2002-2011 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 .
*/
#endregion
using System;
using System.Globalization;
using System.IO;
namespace HeuristicLab.Problems.TravelingSalesman {
///
/// Parses a *.opt.tour file in TSPLIB format and extracts its information about an optimal tour of a TSP.
///
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;
///
/// Gets the name of the parsed tour.
///
public string Name {
get { return name; }
}
private string comment;
///
/// Gets the comment of the parsed tour.
///
public string Comment {
get { return comment; }
}
private int[] tour;
///
/// Gets the parsed tour.
///
public int[] Tour {
get { return tour; }
}
///
/// Initializes a new instance of with the given .
///
/// Thrown if the input file is not a TSPLIB optimal tour file (*.opt.tour)
///
/// The path where the optimal tour is stored.
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;
}
///
/// Reads the TSPLIB optimal tour file and parses its elements.
///
/// Thrown if the file has an invalid format or contains invalid data.
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) - 1;
}
}
}
}