From 1d21cc372715bbea59e00682b122e65387e6fdad Mon Sep 17 00:00:00 2001 From: Oleg Sh Date: Sun, 20 Mar 2022 19:59:40 +0200 Subject: [PATCH] Add longest path algorithm. --- lang/bg/home.php | 3 + lang/ch/home.php | 3 + lang/de/home.php | 3 + lang/el/home.php | 3 + lang/en/home.php | 3 + lang/es/home.php | 3 + lang/fr/home.php | 3 + lang/nl/home.php | 3 + lang/pt/home.php | 3 + lang/ru/home.php | 3 + lang/sv/home.php | 3 + script/Graph.js | 21 +++ script/plugins/FindLongestPath.js | 229 ++++++++++++++++++++++++++++++ script/texts.js | 6 + tpl/home.php | 2 + 15 files changed, 291 insertions(+) create mode 100644 script/plugins/FindLongestPath.js diff --git a/lang/bg/home.php b/lang/bg/home.php index 2af3ea1..f4b58ef 100644 --- a/lang/bg/home.php +++ b/lang/bg/home.php @@ -277,4 +277,7 @@ We have added Dutch translation 🇳🇱. Thank you Willie de Wit"; $g_lang['other_algorithms'] = "Other algorithms"; $g_lang['use_context_menu'] = "Use context menu for additional actions."; + + $g_lang['find_longest_path'] = "Find the longest path"; + $g_lang['length_of_longest_path_from'] = "Length of the longest path from "; ?> diff --git a/lang/ch/home.php b/lang/ch/home.php index 9964873..9e707df 100644 --- a/lang/ch/home.php +++ b/lang/ch/home.php @@ -277,4 +277,7 @@ $g_lang['other_algorithms'] = "Other algorithms"; $g_lang['use_context_menu'] = "Use context menu for additional actions."; + + $g_lang['find_longest_path'] = "Find the longest path"; + $g_lang['length_of_longest_path_from'] = "Length of the longest path from "; ?> diff --git a/lang/de/home.php b/lang/de/home.php index bc0a22c..f6efe5d 100644 --- a/lang/de/home.php +++ b/lang/de/home.php @@ -240,4 +240,7 @@ $g_lang['other_algorithms'] = "Other algorithms"; $g_lang['use_context_menu'] = "Use context menu for additional actions."; + + $g_lang['find_longest_path'] = "Find the longest path"; + $g_lang['length_of_longest_path_from'] = "Length of the longest path from "; ?> diff --git a/lang/el/home.php b/lang/el/home.php index 555fec1..e32b86d 100644 --- a/lang/el/home.php +++ b/lang/el/home.php @@ -278,4 +278,7 @@ $g_lang['other_algorithms'] = "Other algorithms"; $g_lang['use_context_menu'] = "Use context menu for additional actions."; + + $g_lang['find_longest_path'] = "Find the longest path"; + $g_lang['length_of_longest_path_from'] = "Length of the longest path from "; ?> diff --git a/lang/en/home.php b/lang/en/home.php index 6448e01..a70f5f6 100755 --- a/lang/en/home.php +++ b/lang/en/home.php @@ -277,4 +277,7 @@ We have added Dutch translation 🇳🇱. Thank you Willie de Wit"; $g_lang['other_algorithms'] = "Other algorithms"; $g_lang['use_context_menu'] = "Use context menu for additional actions."; + + $g_lang['find_longest_path'] = "Find the longest path"; + $g_lang['length_of_longest_path_from'] = "Length of the longest path from "; ?> diff --git a/lang/es/home.php b/lang/es/home.php index 8d1d755..96e4acf 100755 --- a/lang/es/home.php +++ b/lang/es/home.php @@ -277,4 +277,7 @@ Tenemos traducciones en griego 🇬🇷. diff --git a/lang/fr/home.php b/lang/fr/home.php index 1c0092b..386f949 100644 --- a/lang/fr/home.php +++ b/lang/fr/home.php @@ -245,4 +245,7 @@ $g_lang['other_algorithms'] = "Other algorithms"; $g_lang['use_context_menu'] = "Use context menu for additional actions."; + + $g_lang['find_longest_path'] = "Find the longest path"; + $g_lang['length_of_longest_path_from'] = "Length of the longest path from "; ?> diff --git a/lang/nl/home.php b/lang/nl/home.php index f6996e1..2f7cfc1 100644 --- a/lang/nl/home.php +++ b/lang/nl/home.php @@ -242,4 +242,7 @@ $g_lang['other_algorithms'] = "Other algorithms"; $g_lang['use_context_menu'] = "Use context menu for additional actions."; + + $g_lang['find_longest_path'] = "Find the longest path"; + $g_lang['length_of_longest_path_from'] = "Length of the longest path from "; ?> diff --git a/lang/pt/home.php b/lang/pt/home.php index 75f949f..2c676c5 100644 --- a/lang/pt/home.php +++ b/lang/pt/home.php @@ -243,4 +243,7 @@ $g_lang['other_algorithms'] = "Other algorithms"; $g_lang['use_context_menu'] = "Use context menu for additional actions."; + + $g_lang['find_longest_path'] = "Find the longest path"; + $g_lang['length_of_longest_path_from'] = "Length of the longest path from "; ?> diff --git a/lang/ru/home.php b/lang/ru/home.php index dc78e9a..b3ab72f 100755 --- a/lang/ru/home.php +++ b/lang/ru/home.php @@ -278,4 +278,7 @@ $g_lang['other_algorithms'] = "Другие алгоритмы"; $g_lang['use_context_menu'] = "Используйте контекстное меню для дополнительных действий."; + + $g_lang['find_longest_path'] = "Поиск самого длинного пути"; + $g_lang['length_of_longest_path_from'] = "Длина самого длинного пути ровна "; ?> diff --git a/lang/sv/home.php b/lang/sv/home.php index 58facff..672815b 100644 --- a/lang/sv/home.php +++ b/lang/sv/home.php @@ -239,4 +239,7 @@ $g_lang['other_algorithms'] = "Other algorithms"; $g_lang['use_context_menu'] = "Use context menu for additional actions."; + + $g_lang['find_longest_path'] = "Find the longest path"; + $g_lang['length_of_longest_path_from'] = "Length of the longest path from "; ?> diff --git a/script/Graph.js b/script/Graph.js index e68c8d0..e7b8127 100644 --- a/script/Graph.js +++ b/script/Graph.js @@ -196,6 +196,27 @@ Graph.prototype.FindEdgeMin = function(id1, id2) return res; } +Graph.prototype.FindEdgeMax = function(id1, id2) +{ + var res = null; + var maxWeight = 0; + for (var i = 0; i < this.edges.length; i++) + { + var edge = this.edges[i]; + if ((edge.vertex1.id == id1 && edge.vertex2.id == id2) + || (!edge.isDirect && edge.vertex1.id == id2 && edge.vertex2.id == id1)) + { + if (edge.weight > maxWeight) + { + res = edge; + maxWeight = edge.weight; + } + } + } + + return res; +} + Graph.prototype.FindEdgeMinIgnoreDirection = function(id1, id2) { var res = null; diff --git a/script/plugins/FindLongestPath.js b/script/plugins/FindLongestPath.js new file mode 100644 index 0000000..8ac3857 --- /dev/null +++ b/script/plugins/FindLongestPath.js @@ -0,0 +1,229 @@ +/** + * Find short path. + * + */ +function FindLongestPath(graph, app) +{ + BaseAlgorithmEx.apply(this, arguments); + this.message = g_selectStartVertex; + this.selectedObjects = {}; + this.foundSubGraphs = {}; + this.nSubgraphIndex = 0; + this.nSubGraphCount = 0; + this.foundPaths = {}; + this.maxPathLength = 0; +} + + +// inheritance. +FindLongestPath.prototype = Object.create(BaseAlgorithmEx.prototype); +// First selected. +FindLongestPath.prototype.firstObject = null; +// Second selected. +FindLongestPath.prototype.secondObject = null; +// Path +FindLongestPath.prototype.pathObjects = null; +// Infinity +FindLongestPath.prototype.infinityValue = 1E9 - 1; + +FindLongestPath.prototype.getName = function(local) +{ + return g_findLongestPath; +} + +FindLongestPath.prototype.getId = function() +{ + return "OlegSh.FindLongestPath"; +} + +// @return message for user. +FindLongestPath.prototype.getMessage = function(local) +{ + return this.message; +} + +FindLongestPath.prototype.getCategory = function() +{ + return 1; +} + +FindLongestPath.prototype.result = function(resultCallback) +{ + if (this.firstObject && this.secondObject) + { + this.outResultCallback = function (result ) { resultCallback(result); }; + self = this; + this.CalculateAlgorithm("prnpaths", [ + {name: "start", value: this.firstObject.id}, + {name: "finish", value: this.secondObject.id} + ], + function (pathObjects, properties, results) + { + self.resultCallback(pathObjects, properties, results); + }); + } + return null; +} + +FindLongestPath.prototype.setResultMessage = function() +{ + if (this.nSubGraphCount > 0) + { + var currentPath = ""; + var first = true; + this.foundPaths[this.nSubgraphIndex].forEach((nodeId) => { + currentPath += (first ? "" : "⇒") + this.graph.FindVertex(nodeId).mainText; + first = false; + }); + + this.message = g_LengthOfLongestPathFrom + this.firstObject.mainText + + g_to + this.secondObject.mainText + g_are + + this.maxPathLength + ": " + currentPath; + } + else + { + this.message = g_pathNotExists; + } +} + +FindLongestPath.prototype.resultCallback = function(pathObjects, properties, results) +{ + var outputResult = {}; + outputResult["version"] = 1; + outputResult["minPath"] = true; + + this.pathObjects = pathObjects; + this.properties = properties; + + var bFound = results.length > 0 && results[0].value < this.infinityValue && (results[0].type == 1 || results[0].type == 2); + + if (bFound) + { + this.nSubGraphCount = results.length > 0 && results[0].type == 1 ? results[0].value : 0; + + this.foundSubGraphs = {}; + this.foundPaths = {}; + this.maxPathLength = 0; + var maxPathIndex = 0; + var currentLength = 0; + for (var i = 0; i < this.nSubGraphCount; i++) + { + this.foundSubGraphs[i] = {}; + this.foundPaths[i] = []; + } + + var subGraphIndex = 0; + var prevNodeId = -1; + for (var i = 0; i < results.length; i++) + { + if (results[i].type == 6) + { + if (currentLength > this.maxPathLength) { + this.maxPathLength = currentLength; + maxPathIndex = subGraphIndex; + } + currentLength = 0; + subGraphIndex++; + prevNodeId = -1; + } + + if (results[i].type == 4) + { + var nodeId = parseInt(results[i].value); + var index = subGraphIndex; + var subgGraph = this.foundSubGraphs[index]; + subgGraph[nodeId] = true; + + this.foundPaths[index].push(nodeId); + + if (prevNodeId >= 0) + { + var edgeObject = this.graph.FindEdgeMax(prevNodeId, nodeId); + subgGraph[edgeObject.id] = true; + currentLength += edgeObject.GetWeight(); + } + prevNodeId = nodeId; + } + } + if (currentLength > this.maxPathLength) { + this.maxPathLength = currentLength; + maxPathIndex = subGraphIndex; + } + this.nSubgraphIndex = maxPathIndex; + + this.setResultMessage(); + + this.firstObject = null; + this.secondObject = null; + } + else + { + this.secondObject = null; + this.firstObject = null; + this.message = g_pathNotExists; + } + + this.outResultCallback(outputResult); +} + +FindLongestPath.prototype.selectVertex = function(vertex) +{ + this.pathObjects = null; + this.shortDist = null; + + if (this.firstObject) + { + this.message = g_processing; + this.secondObject = vertex; + this.selectedObjects = []; + } + else + { + this.deselectAll(); + this.firstObject = vertex; + this.secondObject = null; + this.selectedObjects = {}; + this.message = g_selectFinishVertex; + } + + return true; +} + +FindLongestPath.prototype.deselectAll = function() +{ + this.firstObject = null; + this.secondObject = null; + this.selectedObjects = {}; + this.foundSubGraphs = {}; + this.nSubgraphIndex = 0; + this.nSubGraphCount = 0; + this.message = g_selectStartVertex; + this.maxPathLength = 0; + return true; +} + +FindLongestPath.prototype.instance = function() +{ + return false; +} + +FindLongestPath.prototype.getObjectSelectedGroup = function(object) +{ + return (this.nSubgraphIndex in this.foundSubGraphs && object.id in this.foundSubGraphs[this.nSubgraphIndex]) ? 1 : + (object.id in this.selectedObjects) ? this.selectedObjects[object.id] : ((object == this.firstObject || object == object.secondObject) ? 1 : 0); +} + +FindLongestPath.prototype.getPriority = function() +{ + return -9.4; +} + + +// Factory for connected components. +function CreateFindLongestPath(graph, app) +{ + return new FindLongestPath(graph, app) +} + +// Gerister connected component. +RegisterAlgorithm (CreateFindLongestPath); diff --git a/script/texts.js b/script/texts.js index 6d4f01a..bf55230 100644 --- a/script/texts.js +++ b/script/texts.js @@ -186,6 +186,9 @@ var g_pathTo = "Path to "; var g_useContextMenuText = "Use context menu for addition actions." +var g_findLongestPath = "Find the longest path"; +var g_LengthOfLongestPathFrom = "Length of the longest path from "; + function loadTexts() { g_textsSelectAndMove = document.getElementById("SelectAndMoveObject").innerHTML; @@ -379,4 +382,7 @@ function loadTexts() g_pathTo = document.getElementById("pathTo").innerHTML; g_useContextMenuText = document.getElementById("UseContextMenuText").innerHTML; + + g_findLongestPath = document.getElementById("findLongestPath").innerHTML; + g_LengthOfLongestPathFrom = document.getElementById("LengthOfLongestPathFrom").innerHTML; } diff --git a/tpl/home.php b/tpl/home.php index 98cdae9..c55c882 100755 --- a/tpl/home.php +++ b/tpl/home.php @@ -837,6 +837,8 @@

+

+