-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
- Loading branch information
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
"use strict"; | ||
var __extends = (this && this.__extends) || (function () { | ||
var extendStatics = function (d, b) { | ||
extendStatics = Object.setPrototypeOf || | ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || | ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
}; | ||
return function (d, b) { | ||
if (typeof b !== "function" && b !== null) | ||
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.CycleError = void 0; | ||
var CycleError = /** @class */ (function (_super) { | ||
__extends(CycleError, _super); | ||
function CycleError(message) { | ||
var _this = _super.call(this, message) || this; | ||
Object.setPrototypeOf(_this, CycleError.prototype); | ||
return _this; | ||
} | ||
return CycleError; | ||
}(Error)); | ||
exports.CycleError = CycleError; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Graph = void 0; | ||
var invariant_js_1 = require("./invariant.js"); | ||
Check failure on line 4 in src/Graph.js
|
||
var Graph = /** @class */ (function () { | ||
function Graph() { | ||
/** | ||
* Contains all the nodes added to the graph. | ||
*/ | ||
this.nodes = new Set(); | ||
/** | ||
* The adjacency list of the graph. | ||
*/ | ||
this.edges = new Map(); | ||
/** | ||
* The weights of edges. | ||
* | ||
* Map<SourceNode, Map<TargetNode, EdgeWeight>> | ||
*/ | ||
this.edgeWeights = new Map(); | ||
/** | ||
* Arbitrary properties of edges. | ||
* Map<SourceNode, Map<TargetNode, EdgeProperties>> | ||
*/ | ||
this.edgeProperties = new Map(); | ||
} | ||
/** | ||
* Adds a node to the graph. | ||
* If node was already added, this function does nothing. | ||
* If node was not already added, this function sets up an empty adjacency list. | ||
*/ | ||
Graph.prototype.addNode = function (node) { | ||
if (!this.nodes.has(node)) { | ||
this.nodes.add(node); | ||
} | ||
if (!this.edges.has(node)) { | ||
this.edges.set(node, new Set()); | ||
} | ||
return this; | ||
}; | ||
/** | ||
* Removes a node from the graph. | ||
* Also removes incoming and outgoing edges. | ||
*/ | ||
Graph.prototype.removeNode = function (node) { | ||
// Remove outgoing edges (and signal that the node no longer exists). | ||
this.edges.delete(node); | ||
this.nodes.delete(node); | ||
// Remove ingoing edges | ||
for (var _i = 0, _a = this.edges.values(); _i < _a.length; _i++) { | ||
var adjacentNodes = _a[_i]; | ||
adjacentNodes.delete(node); | ||
} | ||
return this; | ||
}; | ||
/** | ||
* Gets the adjacent nodes set for the given node. | ||
*/ | ||
Graph.prototype.adjacent = function (node) { | ||
return this.edges.get(node); | ||
}; | ||
/** | ||
* Sets the weight of the given edge. | ||
*/ | ||
Graph.prototype.setEdgeWeight = function (source, target, weight) { | ||
if (!this.edgeWeights.has(source)) { | ||
this.edgeWeights.set(source, new Map()); | ||
} | ||
var weights = this.edgeWeights.get(source); | ||
(0, invariant_js_1.invariant)(weights); | ||
weights.set(target, weight); | ||
return this; | ||
}; | ||
/** | ||
* Gets the weight of the given edge or `1` if not set. | ||
*/ | ||
Graph.prototype.getEdgeWeight = function (source, target) { | ||
var _a, _b; | ||
return (_b = (_a = this.edgeWeights.get(source)) === null || _a === void 0 ? void 0 : _a.get(target)) !== null && _b !== void 0 ? _b : 1; | ||
}; | ||
/** | ||
* Set the properties of the given edge. | ||
*/ | ||
Graph.prototype.setEdgeProperties = function (source, target, props) { | ||
if (!this.edgeProperties.has(source)) { | ||
this.edgeProperties.set(source, new Map()); | ||
} | ||
var propsHolder = this.edgeProperties.get(source); | ||
(0, invariant_js_1.invariant)(propsHolder); | ||
propsHolder.set(target, props); | ||
return this; | ||
}; | ||
/** | ||
* Get the properties of the given edge or undefined if none are set. | ||
*/ | ||
Graph.prototype.getEdgeProperties = function (source, target) { | ||
var _a; | ||
return (_a = this.edgeProperties.get(source)) === null || _a === void 0 ? void 0 : _a.get(target); | ||
}; | ||
/** | ||
* Adds an edge from the `source` node to `target` node. | ||
* This method will create the nodes if they were not already added. | ||
*/ | ||
Graph.prototype.addEdge = function (source, target) { | ||
var opts = []; | ||
for (var _i = 2; _i < arguments.length; _i++) { | ||
opts[_i - 2] = arguments[_i]; | ||
} | ||
var weight = opts[0], linkProps = opts[1]; | ||
this.addNode(source); | ||
this.addNode(target); | ||
var adjacentNodes = this.adjacent(source); | ||
(0, invariant_js_1.invariant)(adjacentNodes); | ||
adjacentNodes.add(target); | ||
if (weight !== undefined) { | ||
this.setEdgeWeight(source, target, weight); | ||
} | ||
if (linkProps !== undefined) { | ||
this.setEdgeProperties(source, target, linkProps); | ||
} | ||
return this; | ||
}; | ||
/** | ||
* Removes the edge from the `source` node to `target` node. | ||
* Does not remove the nodes themselves. | ||
* Does nothing if the edge does not exist. | ||
*/ | ||
Graph.prototype.removeEdge = function (source, target) { | ||
var _a, _b; | ||
(_a = this.edges.get(source)) === null || _a === void 0 ? void 0 : _a.delete(target); | ||
(_b = this.edgeProperties.get(source)) === null || _b === void 0 ? void 0 : _b.delete(target); | ||
return this; | ||
}; | ||
/** | ||
* Returns true if there is an edge from the `source` node to `target` node.. | ||
*/ | ||
Graph.prototype.hasEdge = function (source, target) { | ||
var _a, _b; | ||
return (_b = (_a = this.edges.get(source)) === null || _a === void 0 ? void 0 : _a.has(target)) !== null && _b !== void 0 ? _b : false; | ||
}; | ||
return Graph; | ||
}()); | ||
exports.Graph = Graph; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.depthFirstSearch = depthFirstSearch; | ||
var depthFirstVisit_js_1 = require("./depthFirstVisit.js"); | ||
/** | ||
* Depth First Search algorithm, inspired by | ||
* Cormen et al. "Introduction to Algorithms" 3rd Ed. p. 604 | ||
*/ | ||
function depthFirstSearch(graph, opts) { | ||
var _a; | ||
if (opts === void 0) { opts = {}; } | ||
var _b = opts.sourceNodes, sourceNodes = _b === void 0 ? Array.from(graph.nodes) : _b, _c = opts.includeSourceNodes, includeSourceNodes = _c === void 0 ? true : _c; | ||
var visited = new Set(); | ||
var visiting = new Set(); | ||
var nodeList = []; | ||
if (includeSourceNodes) { | ||
for (var i = 0; i < sourceNodes.length; i++) { | ||
var sourceNode = sourceNodes[i]; | ||
if (!sourceNode) | ||
continue; | ||
(0, depthFirstVisit_js_1.depthFirstVisit)(graph, nodeList, visited, visiting, sourceNode, opts); | ||
} | ||
return nodeList; | ||
} | ||
for (var i = 0; i < sourceNodes.length; i++) { | ||
var sourceNode = sourceNodes[i]; | ||
if (!sourceNode) | ||
continue; | ||
visited.add(sourceNode); | ||
} | ||
for (var i = 0; i < sourceNodes.length; i++) { | ||
var sourceNode = sourceNodes[i]; | ||
if (!sourceNode) | ||
continue; | ||
(_a = graph | ||
.adjacent(sourceNode)) === null || _a === void 0 ? void 0 : _a.forEach(function (n) { return (0, depthFirstVisit_js_1.depthFirstVisit)(graph, nodeList, visited, visiting, n, opts); }); | ||
} | ||
return nodeList; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.depthFirstVisit = depthFirstVisit; | ||
var CycleError_js_1 = require("../../CycleError.js"); | ||
function depthFirstVisit(graph, nodeList, visited, visiting, node, opts) { | ||
var _a; | ||
var _b = opts.errorOnCycle, errorOnCycle = _b === void 0 ? false : _b, shouldFollow = opts.shouldFollow; | ||
if (visiting.has(node) && errorOnCycle) { | ||
throw new CycleError_js_1.CycleError('Cycle found'); | ||
} | ||
if (!visited.has(node)) { | ||
visited.add(node); | ||
visiting.add(node); | ||
(_a = graph.adjacent(node)) === null || _a === void 0 ? void 0 : _a.forEach(function (n) { | ||
var follow = shouldFollow === undefined || shouldFollow({ source: node, target: n, graph: graph }); | ||
if (!follow) | ||
return; | ||
depthFirstVisit(graph, nodeList, visited, visiting, n, opts); | ||
}); | ||
visiting.delete(node); | ||
nodeList.push(node); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.depthFirstSearch = void 0; | ||
var depthFirstSearch_js_1 = require("./depthFirstSearch.js"); | ||
Object.defineProperty(exports, "depthFirstSearch", { enumerable: true, get: function () { return depthFirstSearch_js_1.depthFirstSearch; } }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.lowestCommonAncestors = void 0; | ||
var lowestCommonAncestors_js_1 = require("./lowestCommonAncestors.js"); | ||
Object.defineProperty(exports, "lowestCommonAncestors", { enumerable: true, get: function () { return lowestCommonAncestors_js_1.lowestCommonAncestors; } }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.lowestCommonAncestors = lowestCommonAncestors; | ||
/** | ||
* Return an array containing the lowest common ancestors. | ||
* | ||
* Inspired by https://github.com/relaxedws/lca/blob/master/src/LowestCommonAncestor.php code | ||
* but uses depth search instead of breadth. Also uses some optimizations. | ||
*/ | ||
function lowestCommonAncestors(graph, node1, node2) { | ||
var node1Ancestors = []; | ||
var lcas = []; | ||
if (CA1Visit(graph, node1Ancestors, lcas, new Set(), node1, node2)) { | ||
// No shortcut worked | ||
CA2Visit(graph, node1Ancestors, lcas, new Set(), node2); | ||
} | ||
return lcas; | ||
} | ||
function CA1Visit(graph, node1Ancestors, lcas, visited, node, node2) { | ||
var _a; | ||
if (!visited.has(node)) { | ||
visited.add(node); | ||
node1Ancestors.push(node); | ||
if (node == node2) { | ||
lcas.push(node); | ||
return false; // found - shortcut | ||
} | ||
return Array.from((_a = graph.adjacent(node)) !== null && _a !== void 0 ? _a : []).every(function (node) { | ||
return CA1Visit(graph, node1Ancestors, lcas, visited, node, node2); | ||
}); | ||
} | ||
else { | ||
return true; | ||
} | ||
} | ||
function CA2Visit(graph, node1Ancestors, lcas, visited, node) { | ||
var _a; | ||
if (!visited.has(node)) { | ||
visited.add(node); | ||
if (node1Ancestors.indexOf(node) >= 0) { | ||
lcas.push(node); | ||
} | ||
else if (lcas.length == 0) { | ||
(_a = graph.adjacent(node)) === null || _a === void 0 ? void 0 : _a.forEach(function (node) { | ||
CA2Visit(graph, node1Ancestors, lcas, visited, node); | ||
}); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.dijkstra = dijkstra; | ||
var extractMin_js_1 = require("./extractMin.js"); | ||
var relax_js_1 = require("./relax.js"); | ||
function dijkstra(graph, tracks, source, destination) { | ||
var _a; | ||
var nodes = graph.nodes; | ||
var q = tracks.q; | ||
initializeSingleSource(nodes, tracks, source, destination); | ||
initializePriorityQueue(nodes, tracks); | ||
var _loop_1 = function () { | ||
var u = (0, extractMin_js_1.extractMin)(tracks); | ||
if (u === null) | ||
return { value: void 0 }; | ||
(_a = graph.adjacent(u)) === null || _a === void 0 ? void 0 : _a.forEach(function (v) { | ||
(0, relax_js_1.relax)(graph, tracks, u, v); | ||
}); | ||
}; | ||
while (q.size !== 0) { | ||
var state_1 = _loop_1(); | ||
if (typeof state_1 === "object") | ||
return state_1.value; | ||
} | ||
} | ||
function initializeSingleSource(nodes, _a, source, destination) { | ||
var d = _a.d; | ||
nodes.forEach(function (node) { | ||
d.set(node, Infinity); | ||
}); | ||
if (d.get(source) !== Infinity) { | ||
throw new Error('Source node is not in the graph'); | ||
} | ||
if (d.get(destination) !== Infinity) { | ||
throw new Error('Destination node is not in the graph'); | ||
} | ||
d.set(source, 0); | ||
} | ||
function initializePriorityQueue(nodes, _a) { | ||
var q = _a.q; | ||
nodes.forEach(function (node) { | ||
q.add(node); | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.extractMin = extractMin; | ||
/** | ||
* Remove the node with the minimum weight from the priority queue. | ||
* | ||
* Performs linear search. | ||
*/ | ||
function extractMin(tracks) { | ||
var min = Infinity; | ||
var minNode; | ||
var d = tracks.d, q = tracks.q; | ||
q.forEach(function (node) { | ||
var _a; | ||
var nodeWeight = (_a = d.get(node)) !== null && _a !== void 0 ? _a : Infinity; | ||
if (nodeWeight < min) { | ||
min = nodeWeight; | ||
minNode = node; | ||
} | ||
}); | ||
if (minNode === undefined) { | ||
// If we reach here, there's a disconnected subgraph, and we're done. | ||
q.clear(); | ||
return null; | ||
} | ||
q.delete(minNode); | ||
return minNode; | ||
} |