Index: src/globals.h
===================================================================
--- src/globals.h	(revision b5c9bcb585676ba581afb851fefa70eca0341262)
+++ src/globals.h	(revision 430bd7f7e921daaebe01fd919c3125800872eceb)
@@ -1,4 +1,4 @@
 /*
- *  TSPSG - TSP Solver and Generator
+ *  TSPSG: TSP Solver and Generator
  *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
@@ -29,4 +29,6 @@
 #include <QtGui>
 
+// Version info
+#include "version.h"
 // OS detection
 #include "os.h"
@@ -38,5 +40,5 @@
 #define DEF_OFFSET 100
 #define DEF_FONT_FAMILY "Courier New"
-#define DEF_FONT_SIZE 12
+#define DEF_FONT_SIZE 10
 #define DEF_FONT_COLOR Qt::black
 
@@ -54,9 +56,4 @@
 #define ZKT_VERSION quint8(1)
 
-// Decided, that static array with 100 of cities maximum hard limit
-// will be enough for most cases, but the code will be simplier than
-// when using dynamic lists. If you need more, just change this value
-// and recompile the program ;-)
-#define MAX_CITIES 100
 // This value means infinity :-)
 #ifndef INFINITY
@@ -64,5 +61,5 @@
 #endif
 // This is string, which represents infinite value in table
-#define INFSTR "-----"
+#define INFSTR "---"
 
 #endif // GLOBALS_H
Index: src/main.cpp
===================================================================
--- src/main.cpp	(revision b5c9bcb585676ba581afb851fefa70eca0341262)
+++ src/main.cpp	(revision 430bd7f7e921daaebe01fd919c3125800872eceb)
@@ -1,4 +1,4 @@
 /*
- *  TSPSG - TSP Solver and Generator
+ *  TSPSG: TSP Solver and Generator
  *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
Index: src/mainwindow.cpp
===================================================================
--- src/mainwindow.cpp	(revision b5c9bcb585676ba581afb851fefa70eca0341262)
+++ src/mainwindow.cpp	(revision 430bd7f7e921daaebe01fd919c3125800872eceb)
@@ -1,4 +1,4 @@
-/*
- *  TSPSG - TSP Solver and Generator
+	/*
+ *  TSPSG: TSP Solver and Generator
  *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
@@ -30,4 +30,8 @@
 	loadLanguage();
 	setupUi(this);
+	initDocStyleSheet();
+	solutionText->document()->setDefaultFont(settings->value("Output/Font",QFont(DEF_FONT_FAMILY,DEF_FONT_SIZE)).value<QFont>());
+	solutionText->setTextColor(settings->value("Output/Color",DEF_FONT_COLOR).value<QColor>());
+	solutionText->setWordWrapMode(QTextOption::WordWrap);
 #ifdef Q_OS_WINCE
 	// A little hack for toolbar icons to have sane size.
@@ -46,5 +50,6 @@
 	connect(actionFileNew,SIGNAL(triggered()),this,SLOT(actionFileNewTriggered()));
 	connect(actionFileOpen,SIGNAL(triggered()),this,SLOT(actionFileOpenTriggered()));
-	connect(actionFileSaveTask,SIGNAL(triggered()),this,SLOT(actionFileSaveTaskTriggered()));
+	connect(actionFileSaveAsTask,SIGNAL(triggered()),this,SLOT(actionFileSaveAsTaskTriggered()));
+	connect(actionFileSaveAsSolution,SIGNAL(triggered()),this,SLOT(actionFileSaveAsSolutionTriggered()));
 	connect(actionSettingsPreferences,SIGNAL(triggered()),this,SLOT(actionSettingsPreferencesTriggered()));
 	connect(actionSettingsLanguageAutodetect,SIGNAL(triggered(bool)),this,SLOT(actionSettingsLanguageAutodetectTriggered(bool)));
@@ -59,12 +64,6 @@
 	connect(spinCities,SIGNAL(valueChanged(int)),this,SLOT(spinCitiesValueChanged(int)));
 QRect rect = geometry();
-#ifdef Q_OS_WINCE
-	// HACK: Fix for all tabWidget elements becoming "unclickable" if making it central widget.
-/*	rect.setSize(QApplication::desktop()->availableGeometry().size());
-	rect.setHeight(rect.height() - (QApplication::desktop()->screenGeometry().height() - QApplication::desktop()->availableGeometry().height()));
-	tabWidget->resize(rect.width(),rect.height() - toolBar->iconSize().height());*/
-	// Somehow, this works now. No more "unclickable" elements :-\ 
 	setCentralWidget(tabWidget);
-#else
+#ifndef Q_OS_WINCE
 	if (settings->value("SavePos",false).toBool()) {
 		// Loading of saved window state
@@ -92,4 +91,12 @@
 	taskView->resizeRowsToContents();
 #endif // Q_OS_WINCE
+}
+
+void MainWindow::enableSolutionActions(bool enable)
+{
+	actionFileSaveAsSolution->setEnabled(enable);
+	solutionText->setEnabled(enable);
+	if (!enable)
+		output.clear();
 }
 
@@ -141,4 +148,16 @@
 }
 
+void MainWindow::initDocStyleSheet()
+{
+QColor color = settings->value("Output/Color",DEF_FONT_COLOR).value<QColor>();
+QColor hilight;
+	if (color.value() < 192)
+		hilight.setHsv(color.hue(),color.saturation(),127 + qRound(color.value() / 2));
+	else
+		hilight.setHsv(color.hue(),color.saturation(),color.value() / 2);
+	solutionText->document()->setDefaultStyleSheet("* {color: " + color.name() +";} p {margin: 0px 10px;} table {margin: 5px;} td {padding: 1px 5px;} .hasalts {color: " + hilight.name() + ";} .selected {color: #A00000; font-weight: bold;} .alternate {color: #008000; font-weight: bold;}");
+	solutionText->document()->setDefaultFont(settings->value("Output/Font",QFont(DEF_FONT_FAMILY,DEF_FONT_SIZE)).value<QFont>());
+}
+
 void MainWindow::spinCitiesValueChanged(int n)
 {
@@ -166,4 +185,7 @@
 	tspmodel->clear();
 	setWindowModified(false);
+	tabWidget->setCurrentIndex(0);
+	solutionText->clear();
+	enableSolutionActions(false);
 }
 
@@ -190,9 +212,35 @@
 	tspmodel->loadTask(files.first());
 	setWindowModified(false);
-}
-
-void MainWindow::actionFileSaveTaskTriggered()
+	solutionText->clear();
+	enableSolutionActions(false);
+}
+
+void MainWindow::actionFileSaveAsTaskTriggered()
 {
 	saveTask();
+}
+
+void MainWindow::actionFileSaveAsSolutionTriggered()
+{
+static QString selectedFile;
+	if (selectedFile.isEmpty())
+		selectedFile = "solution.html";
+QFileDialog sd(this);
+	sd.setAcceptMode(QFileDialog::AcceptSave);
+QStringList filters(trUtf8("HTML Files") + " (*.html *.htm)");
+	filters.append(trUtf8("OpenDocument Files") + " (*.odt)");
+	filters.append(trUtf8("All Files") + " (*)");
+	sd.setNameFilters(filters);
+	sd.selectFile(selectedFile);
+	if (sd.exec() != QDialog::Accepted)
+		return;
+QStringList files = sd.selectedFiles();
+	if (files.empty())
+		return;
+	selectedFile = files.first();
+QTextDocumentWriter dw(selectedFile);
+	if (!(selectedFile.endsWith(".htm",Qt::CaseInsensitive) || selectedFile.endsWith(".html",Qt::CaseInsensitive) || selectedFile.endsWith(".odt",Qt::CaseInsensitive) || selectedFile.endsWith(".txt",Qt::CaseInsensitive)))
+		dw.setFormat("plaintext");
+	dw.write(solutionText->document());
 }
 
@@ -219,5 +267,15 @@
 {
 SettingsDialog sd(this);
-	sd.exec();
+	if (sd.exec() != QDialog::Accepted)
+		return;
+	if (sd.colorChanged() || sd.fontChanged()) {
+		initDocStyleSheet();
+		if (!output.isEmpty() && sd.colorChanged() && (QMessageBox(QMessageBox::Question,trUtf8("Settings Changed"),trUtf8("You have changed color settings.\nDo you wish to apply them to current solution text?"),QMessageBox::Yes | QMessageBox::No,this).exec() == QMessageBox::Yes)) {
+			QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+			solutionText->clear();
+			solutionText->setHtml(output.join(""));
+			QApplication::restoreOverrideCursor();
+		}
+	}
 }
 
@@ -245,15 +303,35 @@
 }
 
+void MainWindow::outputMatrix(tMatrix matrix, QStringList &output, int nRow, int nCol)
+{
+int n = spinCities->value();
+QString line="";
+	output.append("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">");
+	for (int r = 0; r < n; r++) {
+		line = "<tr>";
+		for (int c = 0; c < n; c++) {
+			if (matrix[r][c] == INFINITY)
+				line += "<td align=\"center\">"INFSTR"</td>";
+			else if ((r == nRow) && (c == nCol))
+				line += "<td align=\"center\" class=\"selected\">" + QVariant(matrix[r][c]).toString() + "</td>";
+			else
+				line += "<td align=\"center\">" + QVariant(matrix[r][c]).toString() + "</td>";
+		}
+		line += "</tr>";
+		output.append(line);
+	}
+	output.append("</table>");
+}
+
 void MainWindow::buttonSolveClicked()
 {
-	// TODO: Task solving goes here :-)
 tMatrix matrix;
-double *row;
+QList<double> row;
 int n = spinCities->value();
 bool ok;
 	for (int r = 0; r < n; r++) {
-		row = new double[n];
+		row.clear();
 		for (int c = 0; c < n; c++) {
-			row[c] = tspmodel->index(r,c).data(Qt::UserRole).toDouble(&ok);
+			row.append(tspmodel->index(r,c).data(Qt::UserRole).toDouble(&ok));
 			if (!ok) {
 				QMessageBox(QMessageBox::Critical,trUtf8("Data error"),QString(trUtf8("Error in cell [Row %1; Column %2]: Invalid data format.")).arg(r + 1).arg(c + 1),QMessageBox::Ok,this).exec();
@@ -266,6 +344,42 @@
 sStep *root = solver.solve(spinCities->value(),matrix);
 	if (!root)
-		QMessageBox(QMessageBox::Critical,trUtf8("Solution error"),trUtf8("There was an error while solving the task."),QMessageBox::Ok,this).exec();
-	// tabWidget->setCurrentIndex(1);
+		return;
+	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+QColor color = settings->value("Output/Color",DEF_FONT_COLOR).value<QColor>();
+	output.clear();
+	output.append("<p>" + trUtf8("Variant #%1").arg(spinVariant->value()) + "</p>");
+	output.append("<p>" + trUtf8("Task:") + "</p>");
+	outputMatrix(matrix,output);
+	output.append("<hr>");
+	output.append("<p>" + trUtf8("Solution of Variant #%1 task").arg(spinVariant->value()) + "</p>");
+sStep *step = root;
+	n = 1;
+QString path = "";
+	while (n <= spinCities->value()) {
+		if (step->prNode->prNode != NULL || (step->prNode->prNode == NULL && step->plNode->prNode == NULL)) {
+			if (n != spinCities->value()) {
+				output.append("<p>" + trUtf8("Step #%1").arg(n++) + "</p>");
+				outputMatrix(step->matrix,output,step->candidate.nRow,step->candidate.nCol);
+				if (step->alts)
+					output.append("<p class=\"hasalts\">" + trUtf8("This step has alternate candidates for branching.") + "</p>");
+				output.append("<p>&nbsp;</p>");
+			}
+			path += QString(" (%1,%2)").arg(step->candidate.nRow + 1).arg(step->candidate.nCol + 1);
+		}
+		if (step->prNode->prNode != NULL)
+			step = step->prNode;
+		else if (step->plNode->prNode != NULL)
+			step = step->plNode;
+		else
+			break;
+	}
+	output.append("<p>" + trUtf8("Optimal path:") + "</p>");
+	output.append("<p>&nbsp;&nbsp;" + path + "</p>");
+	output.append("<p>" + trUtf8("The price is <b>%1</b> units.").arg(step->price) + "</p>");
+	solutionText->setHtml(output.join(""));
+	solutionText->setDocumentTitle(trUtf8("Solution of Variant #%1 task").arg(spinVariant->value()));
+	enableSolutionActions();
+	tabWidget->setCurrentIndex(1);
+	QApplication::restoreOverrideCursor();
 }
 
@@ -274,5 +388,6 @@
 	// TODO: Normal about window :-)
 QString about = QString::fromUtf8("TSPSG - TSP Solver and Generator\n");
-about += QString::fromUtf8("    Copyright (C) 2007-%1 Lёppa <contacts[at]oleksii[dot]name>\n").arg(QDate::currentDate().toString("yyyy"));
+	about += QString::fromUtf8("    Version: "BUILD_VERSION" ("BUILD_STATUS")\n");
+	about += QString::fromUtf8("    Copyright (C) 2007-%1 Lёppa <contacts[at]oleksii[dot]name>\n").arg(QDate::currentDate().toString("yyyy"));
 	about += QString::fromUtf8("Target OS: %1\n").arg(OS);
 	about += "Qt library:\n";
Index: src/mainwindow.h
===================================================================
--- src/mainwindow.h	(revision b5c9bcb585676ba581afb851fefa70eca0341262)
+++ src/mainwindow.h	(revision 430bd7f7e921daaebe01fd919c3125800872eceb)
@@ -1,4 +1,4 @@
 /*
- *  TSPSG - TSP Solver and Generator
+ *  TSPSG: TSP Solver and Generator
  *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
@@ -44,5 +44,6 @@
 	void actionFileNewTriggered();
 	void actionFileOpenTriggered();
-	void actionFileSaveTaskTriggered();
+	void actionFileSaveAsTaskTriggered();
+	void actionFileSaveAsSolutionTriggered();
 	void actionSettingsPreferencesTriggered();
 	void actionSettingsLanguageAutodetectTriggered(bool);
@@ -62,7 +63,11 @@
 	CTSPModel *tspmodel;
 	QActionGroup *groupSettingsLanguageList;
+	QStringList output;
 	bool loadLanguage(QString lang = "");
 	void loadLangList();
+	void initDocStyleSheet();
 	bool saveTask();
+	void outputMatrix(tMatrix, QStringList &, int nRow = -1, int nCol = -1);
+	void enableSolutionActions(bool enable = true);
 };
 
Index: src/os.h
===================================================================
--- src/os.h	(revision b5c9bcb585676ba581afb851fefa70eca0341262)
+++ src/os.h	(revision 430bd7f7e921daaebe01fd919c3125800872eceb)
@@ -1,4 +1,4 @@
 /*
- *  TSPSG - TSP Solver and Generator
+ *  TSPSG: TSP Solver and Generator
  *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
Index: src/resource.h
===================================================================
--- src/resource.h	(revision b5c9bcb585676ba581afb851fefa70eca0341262)
+++ src/resource.h	(revision 430bd7f7e921daaebe01fd919c3125800872eceb)
@@ -1,4 +1,4 @@
 /*
- *  TSPSG - TSP Solver and Generator
+ *  TSPSG: TSP Solver and Generator
  *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
@@ -9,3 +9,6 @@
  */
 
+#include "version.h"
+
+#define VS_VERSION_INFO 1
 #define IDI_APPICON 101
Index: src/settingsdialog.cpp
===================================================================
--- src/settingsdialog.cpp	(revision b5c9bcb585676ba581afb851fefa70eca0341262)
+++ src/settingsdialog.cpp	(revision 430bd7f7e921daaebe01fd919c3125800872eceb)
@@ -1,4 +1,4 @@
 /*
- *  TSPSG - TSP Solver and Generator
+ *  TSPSG: TSP Solver and Generator
  *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
@@ -25,5 +25,5 @@
 
 SettingsDialog::SettingsDialog(QWidget *parent)
-	: QDialog(parent)
+	: QDialog(parent), newFont(false), newColor(false)
 {
 	setupUi(this);
@@ -35,13 +35,8 @@
 //	setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::MSWindowsFixedSizeDialogHint);
 	setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint);
-	layout()->setSizeConstraint(layout()->SetFixedSize);
 #ifndef Q_OS_WINCE
 	// Setting initial text of dialog hint label to own status tip
 	// text.
 	labelHint->setText(labelHint->statusTip());
-	// HACK: Do not resize label hint (and dialog) when text changes
-	// from one-line to two-line and vice versa. Any better solution?
-	labelHint->setMaximumHeight(labelHint->height());
-	labelHint->setMinimumHeight(labelHint->height());
 #endif // Q_OS_WINCE
 	settings = new QSettings(QSettings::IniFormat,QSettings::UserScope,"TSPSG","tspsg");
@@ -51,11 +46,53 @@
 	cbSaveState->setChecked(settings->value("SavePos",false).toBool());
 #endif // Q_OS_WINCE
-	settings->beginGroup("Print");
+	settings->beginGroup("Output");
 	font = settings->value("Font",QFont(DEF_FONT_FAMILY,DEF_FONT_SIZE)).value<QFont>();
 	color = settings->value("Color",DEF_FONT_COLOR).value<QColor>();
+	settings->endGroup();
+}
+
+void SettingsDialog::accept()
+{
 #ifndef Q_OS_WINCE
-	spinLeftMargin->setValue(settings->value("Offset",DEF_OFFSET).toInt());
+	settings->setValue("SavePos",cbSaveState->isChecked());
 #endif // Q_OS_WINCE
+	settings->setValue("MinCost",spinRandMin->value());
+	settings->setValue("MaxCost",spinRandMax->value());
+	settings->beginGroup("Output");
+	if (newFont)
+		settings->setValue("Font",font);
+	if (newColor)
+		settings->setValue("Color",color);
 	settings->endGroup();
+	QDialog::accept();
+}
+
+void SettingsDialog::buttonFontClicked()
+{
+bool ok;
+QFont font = QFontDialog::getFont(&ok,this->font,this);
+	if (ok && (this->font != font)) {
+		this->font = font;
+		newFont = true;
+	}
+}
+
+void SettingsDialog::buttonColorClicked()
+{
+QColor color = QColorDialog::getColor(this->color,this);
+	if (color.isValid() && (this->color != color)) {
+		this->color = color;
+		newColor = true;
+	}
+}
+
+bool SettingsDialog::colorChanged() const
+{
+	return newColor;
+}
+
+bool SettingsDialog::fontChanged() const
+{
+	return newFont;
 }
 
@@ -77,34 +114,2 @@
 }
 #endif // Q_OS_WINCE
-
-void SettingsDialog::buttonFontClicked()
-{
-bool ok;
-QFont font = QFontDialog::getFont(&ok,this->font,this);
-	if (ok)
-		this->font = font;
-}
-
-void SettingsDialog::buttonColorClicked()
-{
-QColor color = QColorDialog::getColor(this->color,this);
-	if (color.isValid())
-		this->color = color;
-}
-
-void SettingsDialog::accept()
-{
-#ifndef Q_OS_WINCE
-	settings->setValue("SavePos",cbSaveState->isChecked());
-#endif // Q_OS_WINCE
-	settings->setValue("MinCost",spinRandMin->value());
-	settings->setValue("MaxCost",spinRandMax->value());
-	settings->beginGroup("Print");
-	settings->setValue("Font",font);
-	settings->setValue("Color",color);
-#ifndef Q_OS_WINCE
-	settings->setValue("Offset",spinLeftMargin->value());
-#endif // Q_OS_WINCE
-	settings->endGroup();
-	QDialog::accept();
-}
Index: src/settingsdialog.h
===================================================================
--- src/settingsdialog.h	(revision b5c9bcb585676ba581afb851fefa70eca0341262)
+++ src/settingsdialog.h	(revision 430bd7f7e921daaebe01fd919c3125800872eceb)
@@ -1,4 +1,4 @@
 /*
- *  TSPSG - TSP Solver and Generator
+ *  TSPSG: TSP Solver and Generator
  *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
@@ -37,4 +37,6 @@
 public:
 	SettingsDialog(QWidget *parent = 0);
+	bool fontChanged() const;
+	bool colorChanged() const;
 
 private:
@@ -42,4 +44,6 @@
 	QFont font;
 	QColor color;
+	bool newFont;
+	bool newColor;
 #ifndef Q_OS_WINCE
 	bool event(QEvent *);
Index: src/tspmodel.cpp
===================================================================
--- src/tspmodel.cpp	(revision b5c9bcb585676ba581afb851fefa70eca0341262)
+++ src/tspmodel.cpp	(revision 430bd7f7e921daaebe01fd919c3125800872eceb)
@@ -1,4 +1,4 @@
 /*
- *  TSPSG - TSP Solver and Generator
+ *  TSPSG: TSP Solver and Generator
  *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
@@ -119,20 +119,11 @@
 		return;
 	emit layoutAboutToBeChanged();
-	if (n > nCities) {
-		for (int r = 0; r < nCities; r++) {
-			for (int c = nCities; c < n; c++)
-				if (r == c)
-					table[r][c] = INFINITY;
-				else
-					table[r][c] = 0;
-		}
-		for (int r = nCities; r < n; r++) {
-			for (int c = 0; c < n; c++)
-				if (r == c)
-					table[r][c] = INFINITY;
-				else
-					table[r][c] = 0;
-		}
-	}
+	table.resize(n);
+	for (int k = 0; k < n; k++) {
+		table[k].resize(n);
+	}
+	if (n > nCities)
+		for (int k = nCities; k < n; k++)
+			table[k][k] = INFINITY;
 	nCities = n;
 	emit layoutChanged();
@@ -148,5 +139,5 @@
 }
 
-inline bool CTSPModel::loadError(QDataStream::Status status) const
+inline bool CTSPModel::loadError(QDataStream::Status status)
 {
 QString err;
@@ -257,7 +248,7 @@
 	// Costs
 double val;
-	for (int r = 0; r < size; r++)
-		for (int c = 0; c < size; c++)
-			if (r != c) {
+	for (int r = 0; r < 5; r++)
+		for (int c = 0; c < 5; c++)
+			if ((r != c) && (r < size)) {
 				ds->readRawData(reinterpret_cast<char *>(&val),8);
 				if (loadError(ds->status())) {
Index: src/tspmodel.h
===================================================================
--- src/tspmodel.h	(revision b5c9bcb585676ba581afb851fefa70eca0341262)
+++ src/tspmodel.h	(revision 430bd7f7e921daaebe01fd919c3125800872eceb)
@@ -1,4 +1,4 @@
 /*
- *  TSPSG - TSP Solver and Generator
+ *  TSPSG: TSP Solver and Generator
  *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
@@ -48,8 +48,8 @@
 private:
 	QSettings *settings;
-	double table[MAX_CITIES][MAX_CITIES];
+	QVector<QVector<double>> table;
 	quint16 nCities;
 	int rand(int, int) const;
-	bool loadError(QDataStream::Status) const;
+	bool loadError(QDataStream::Status);
 	void loadZKT(QDataStream *);
 	void loadTSPT(QDataStream *);
Index: src/tspsolver.cpp
===================================================================
--- src/tspsolver.cpp	(revision b5c9bcb585676ba581afb851fefa70eca0341262)
+++ src/tspsolver.cpp	(revision 430bd7f7e921daaebe01fd919c3125800872eceb)
@@ -1,4 +1,4 @@
 /*
- *  TSPSG - TSP Solver and Generator
+ *  TSPSG: TSP Solver and Generator
  *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
@@ -26,34 +26,189 @@
 
 CTSPSolver::CTSPSolver()
-{
-}
-
-double CTSPSolver::findMinInRow(int nRow, tMatrix matrix)
+	: nCities(0)
+{
+}
+
+void CTSPSolver::cleanup()
+{
+	route.clear();
+}
+
+double CTSPSolver::findMinInRow(int nRow, tMatrix matrix, int exc)
 {
 double min = INFINITY;
 	for (int k = 0; k < nCities; k++)
-		if (min > matrix[nRow][k])
+		if (((k != exc)) && (min > matrix[nRow][k]))
 			min = matrix[nRow][k];
 	return min == INFINITY ? 0 : min;
 }
 
-double CTSPSolver::findMinInCol(int nCol, tMatrix matrix)
+double CTSPSolver::findMinInCol(int nCol, tMatrix matrix, int exr)
 {
 double min = INFINITY;
 	for (int k = 0; k < nCities; k++)
-		if (min > matrix[k][nCol])
+		if ((k != exr) && (min > matrix[k][nCol]))
 			min = matrix[k][nCol];
 	return min == INFINITY ? 0 : min;
 }
 
-sStep *CTSPSolver::solve(int numCities, tMatrix task)
+void CTSPSolver::subRow(tMatrix &matrix, int nRow, double val)
+{
+	for (int k = 0; k < nCities; k++)
+		if (k != nRow)
+			matrix[nRow][k] -= val;
+}
+
+void CTSPSolver::subCol(tMatrix &matrix, int nCol, double val)
+{
+	for (int k = 0; k < nCities; k++)
+		if (k != nCol)
+			matrix[k][nCol] -= val;
+}
+
+double CTSPSolver::align(tMatrix &matrix)
+{
+double r = 0;
+double min;
+	for (int k = 0; k < nCities; k++) {
+		min = findMinInRow(k,matrix);
+		if (min > 0) {
+			r += min;
+			subRow(matrix,k,min);
+		}
+	}
+	for (int k = 0; k < nCities; k++) {
+		min = findMinInCol(k,matrix);
+		if (min > 0) {
+			r += min;
+			subCol(matrix,k,min);
+		}
+	}
+	return r;
+}
+
+bool CTSPSolver::findCandidate(tMatrix matrix, int &nRow, int &nCol, double &h)
+{
+	h = -1;
+	nRow = -1;
+	nCol = -1;
+bool alts = false;
+double sum;
+	for (int r = 0; r < nCities; r++)
+		for (int c = 0; c < nCities; c++)
+			if ((matrix[r][c] == 0) && !forbidden.values(r).contains(c)) {
+				sum = findMinInRow(r,matrix,c) + findMinInCol(c,matrix,r);
+				if (sum > h) {
+					h = sum;
+					nRow = r;
+					nCol = c;
+					alts = false;
+				} else if (sum == h)
+					alts = true;
+			}
+	return alts;
+}
+
+bool CTSPSolver::hasSubCycles(int nRow, int nCol)
+{
+	if ((nRow < 0) || (nCol < 0) || route.isEmpty() || !(route.size() < nCities - 1) || !route.contains(nCol))
+		return false;
+int i = nCol;
+	while (true) {
+		if ((i = route[i]) == nRow)
+			return true;
+		if (!route.contains(i))
+			return false;
+	}
+	return false;
+}
+
+// TODO: Comment the algorithm
+sStep *CTSPSolver::solve(int numCities, tMatrix task, QWidget *parent)
 {
 	if (numCities <= 1)
 		return NULL;
+	cleanup();
 	nCities = numCities;
+double s;
+QProgressDialog pd(parent);
+QProgressBar *pb = new QProgressBar(&pd);
+	pb->setAlignment(Qt::AlignCenter);
+	pb->setFormat(trUtf8("%v of %m parts found"));
+	pd.setBar(pb);
+	pd.setMaximum(nCities);
+	pd.setMinimumDuration(1000);
+	pd.setLabelText(trUtf8("Calculating optimal route..."));
+	pd.setWindowTitle(trUtf8("Solution Progress"));
+	pd.setWindowModality(Qt::ApplicationModal);
+	pd.setValue(0);
+
 sStep *step = new sStep();
 	step->matrix = task;
+
+	s = align(step->matrix);
+	step->price = s;
 	root = step;
 
-	return step;
-}
+sStep *left, *right;
+int nRow, nCol;
+	while (route.size() < nCities) {
+		forbidden.clear();
+		step->alts = findCandidate(step->matrix,nRow,nCol,s);
+		while (hasSubCycles(nRow,nCol)) {
+			forbidden[nRow] = nCol;
+			step->matrix[nRow][nCol] = INFINITY;
+			step->price += align(step->matrix);
+			step->alts = findCandidate(step->matrix,nRow,nCol,s);
+		}
+		if ((nRow == -1) || (nCol == -1) || pd.wasCanceled()) {
+			root = NULL;
+			break;
+		}
+
+		// Route with (nRow,nCol) path
+		right = new sStep();
+		right->matrix = step->matrix;
+		for (int k = 0; k < nCities; k++) {
+			if (k != nCol)
+				right->matrix[nRow][k] = INFINITY;
+			if (k != nRow)
+				right->matrix[k][nCol] = INFINITY;
+		}
+		right->price = step->price + align(right->matrix);
+		// Forbid selected route to exclude its reuse in next steps.
+		right->matrix[nCol][nRow] = INFINITY;
+		right->matrix[nRow][nCol] = INFINITY;
+
+		// Route without (nRow,nCol) path
+		left = new sStep();
+		left->matrix = step->matrix;
+		left->matrix[nRow][nCol] = INFINITY;
+ 		left->price = step->price + align(left->matrix);
+
+		step->candidate.nRow = nRow;
+		step->candidate.nCol = nCol;
+		step->plNode = left;
+		step->prNode = right;
+
+		if (right->price <= left->price) {
+			// Route with (nRow,nCol) path is cheaper
+			step = right;
+			route[nRow] = nCol;
+			pd.setValue(route.size());
+		} else {
+			// Route without (nRow,nCol) path is cheaper
+			step = left;
+			qApp->processEvents();
+		}
+	}
+
+	pd.reset();
+	qApp->processEvents();
+
+	if (!root && !pd.wasCanceled()) {
+		QMessageBox(QMessageBox::Warning,trUtf8("Solution Result"),trUtf8("This task has no solution."),QMessageBox::Ok,parent).exec();
+	}
+
+	return root;
+}
Index: src/tspsolver.h
===================================================================
--- src/tspsolver.h	(revision b5c9bcb585676ba581afb851fefa70eca0341262)
+++ src/tspsolver.h	(revision 430bd7f7e921daaebe01fd919c3125800872eceb)
@@ -1,4 +1,4 @@
 /*
- *  TSPSG - TSP Solver and Generator
+ *  TSPSG: TSP Solver and Generator
  *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
@@ -27,14 +27,15 @@
 #include "globals.h"
 
-typedef QList<double *> tMatrix;
+typedef QList<QList<double>> tMatrix;
 
 // This structure represents one step of solving
 // The tree of such elements will represent the solving process
 struct sStep {
-	tMatrix matrix;
-	double price;
-	struct {unsigned int x; unsigned int y;} pos;
-	sStep *plNode, *prNode;
-	sStep() { price = pos.x = pos.y = 0; plNode = prNode = NULL; }
+	tMatrix matrix; // Steps's matrix
+	double price; // Price of travel to this step
+	struct {unsigned int nRow; unsigned int nCol;} candidate; // Candiadate for branching in current matrix
+	bool alts; // This matrix has alternative candidates
+	sStep *plNode, *prNode; // Pointers to left and right branch steps
+	sStep() { price = candidate.nRow = candidate.nCol = -1; alts = false; plNode = prNode = NULL; } // Default values
 };
 
@@ -42,12 +43,23 @@
 class CTSPSolver
 {
+	Q_DECLARE_TR_FUNCTIONS(CTSPSolver)
+
 public:
 	CTSPSolver();
-	sStep *solve(int, tMatrix);
+	sStep *solve(int, tMatrix, QWidget *parent = 0);
+
 private:
 	int nCities;
 	sStep *root;
-	double findMinInRow(int, tMatrix);
-	double findMinInCol(int, tMatrix);
+	QHash<int,int> route;
+	QHash<int,int> forbidden;
+	double align(tMatrix &);
+	void cleanup();
+	bool findCandidate(tMatrix, int &, int &, double &);
+	double findMinInRow(int, tMatrix, int exc = -1);
+	double findMinInCol(int, tMatrix, int exr = -1);
+	bool hasSubCycles(int, int);
+	void subCol(tMatrix &, int, double);
+	void subRow(tMatrix &, int, double);
 };
 
Index: src/version.h
===================================================================
--- src/version.h	(revision 430bd7f7e921daaebe01fd919c3125800872eceb)
+++ src/version.h	(revision 430bd7f7e921daaebe01fd919c3125800872eceb)
@@ -0,0 +1,40 @@
+/*
+ *  TSPSG: TSP Solver and Generator
+ *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
+ *
+ *  $Id$
+ *  $URL$
+ *
+ *  This file is part of TSPSG.
+ *
+ *  TSPSG 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.
+ *
+ *  TSPSG 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 TSPSG.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef VERSION_H
+#define VERSION_H
+
+#define BUILD_VERSION_MAJOR 0
+#define BUILD_VERSION_MINOR 1
+#define BUILD_RELEASE 1
+// Will stick this to revision
+#define BUILD_NUMBER 42
+#define BUILD_STATUS "alpha 1"
+
+// "Converting" number to string
+#define QUOTE_X(x) #x
+#define QUOTE(x) QUOTE_X(x)
+
+#define BUILD_VERSION QUOTE(BUILD_VERSION_MAJOR.BUILD_VERSION_MINOR.BUILD_RELEASE)
+
+#endif // VERSION_H
