diff --git a/lib/alg/extract-path.js b/lib/alg/extract-path.js new file mode 100644 index 00000000..e4e5eba0 --- /dev/null +++ b/lib/alg/extract-path.js @@ -0,0 +1,27 @@ +module.exports = extractPath; + +function extractPath(shortestPaths, source, destination) { + if (shortestPaths[source].predecessor !== undefined) { + throw new Error("Invalid source vertex"); + } + if (shortestPaths[destination].predecessor === undefined && destination !== source) { + throw new Error("Invalid destination vertex"); + } + + return { + weight: shortestPaths[destination].distance, + path: runExtractPath(shortestPaths, source, destination) + }; +} + +function runExtractPath(shortestPaths, source, destination) { + var path = []; + var currentNode = destination; + + while(currentNode !== source) { + path.push(currentNode); + currentNode = shortestPaths[currentNode].predecessor; + } + path.push(source); + return path.reverse(); +} diff --git a/lib/alg/index.js b/lib/alg/index.js index 2c3d76f2..46eab8c7 100644 --- a/lib/alg/index.js +++ b/lib/alg/index.js @@ -2,6 +2,7 @@ module.exports = { components: require("./components"), dijkstra: require("./dijkstra"), dijkstraAll: require("./dijkstra-all"), + extractPath: require("./extract-path"), findCycles: require("./find-cycles"), floydWarshall: require("./floyd-warshall"), isAcyclic: require("./is-acyclic"), diff --git a/test/alg/extract-path-tests.js b/test/alg/extract-path-tests.js new file mode 100644 index 00000000..972e36cf --- /dev/null +++ b/test/alg/extract-path-tests.js @@ -0,0 +1,49 @@ +var expect = require("../chai").expect, + extractPath = require("../..").alg.extractPath; + +describe("alg.extractPath", function() { + it("returns weight: 0 and path: [source], from source to source", function() { + var shortestPaths = { + "a": { distance: 0 }, + "b": { distance: 73, predecessor: "a" } + }; + expect(extractPath(shortestPaths, "a", "a")).to.eql( + { weight: 0, + path: ["a"] + }); + }); + + it("returns weight and path from source to destination", function() { + var shortestPaths = { + "a": { distance: 0 }, + "b": { distance: 25, predecessor: "a" }, + "c": { distance: 55, predecessor: "b" }, + "d": { distance: 44, predecessor: "b" }, + "e": { distance: 73, predecessor: "c" }, + "f": { distance: 65, predecessor: "d" }, + "g": { distance: 67, predecessor: "b" }, + }; + expect(extractPath(shortestPaths, "a", "e")).to.eql( + { weight: 73, + path: ["a", "b", "c", "e"] + }); + }); + + it("throws an error when provided with an invalid source vertex", function() { + var shortestPaths = { + "a": { distance: 0 }, + "b": { distance: 17, predecessor: "c" }, + "c": { distance: 42, predecessor: "a" } + }; + expect(function() { extractPath(shortestPaths, "b", "c"); }).to.throw(); + }); + + it("throws an error when given an invalid destination vertex", function() { + var shortestPaths = { + "a": { distance: 0 }, + "b": { distance: 99, predecessor: "a" }, + "c": { distance: 100 } + }; + expect(function() { extractPath(shortestPaths, "a", "c"); }).to.throw(); + }); +});