@dagrejs/graphlib
Advanced tools
Comparing version 2.1.10 to 2.1.11
@@ -77,3 +77,3 @@ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.graphlib = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ | ||
* | ||
* Order must be one of "pre" or "post". | ||
* If the order is not "post", it will be treated as "pre". | ||
*/ | ||
@@ -85,7 +85,8 @@ function dfs(g, vs, order) { | ||
var navigation = (g.isDirected() ? g.successors : g.neighbors).bind(g); | ||
var navigation = g.isDirected() ? v => g.successors(v) : v => g.neighbors(v); | ||
var orderFunc = order === "post" ? postOrderDfs : preOrderDfs; | ||
var acc = []; | ||
var visited = {}; | ||
vs.forEach(function(v) { | ||
vs.forEach(v => { | ||
if (!g.hasNode(v)) { | ||
@@ -95,19 +96,45 @@ throw new Error("Graph does not have node: " + v); | ||
doDfs(g, v, order === "post", visited, navigation, acc); | ||
orderFunc(v, navigation, visited, acc); | ||
}); | ||
return acc; | ||
} | ||
function doDfs(g, v, postorder, visited, navigation, acc) { | ||
if (!visited.hasOwnProperty(v)) { | ||
visited[v] = true; | ||
function postOrderDfs(v, navigation, visited, acc) { | ||
var stack = [[v, false]]; | ||
while (stack.length > 0) { | ||
var curr = stack.pop(); | ||
if (curr[1]) { | ||
acc.push(curr[0]); | ||
} else { | ||
if (!visited.hasOwnProperty(curr[0])) { | ||
visited[curr[0]] = true; | ||
stack.push([curr[0], true]); | ||
forEachRight(navigation(curr[0]), w => stack.push([w, false])); | ||
} | ||
} | ||
} | ||
} | ||
if (!postorder) { acc.push(v); } | ||
navigation(v).forEach(function(w) { | ||
doDfs(g, w, postorder, visited, navigation, acc); | ||
}); | ||
if (postorder) { acc.push(v); } | ||
function preOrderDfs(v, navigation, visited, acc) { | ||
var stack = [v]; | ||
while (stack.length > 0) { | ||
var curr = stack.pop(); | ||
if (!visited.hasOwnProperty(curr)) { | ||
visited[curr] = true; | ||
acc.push(curr); | ||
forEachRight(navigation(curr), w => stack.push(w)); | ||
} | ||
} | ||
} | ||
function forEachRight(array, iteratee) { | ||
var length = array.length; | ||
while (length--) { | ||
iteratee(array[length], length, array); | ||
} | ||
return array; | ||
} | ||
},{}],4:[function(require,module,exports){ | ||
@@ -392,5 +419,2 @@ var dijkstra = require("./dijkstra"); | ||
},{}],14:[function(require,module,exports){ | ||
module.exports = topsort; | ||
topsort.CycleException = CycleException; | ||
function topsort(g) { | ||
@@ -424,8 +448,12 @@ var visited = {}; | ||
function CycleException() {} | ||
CycleException.prototype = new Error(); // must be an instance of Error to pass testing | ||
class CycleException extends Error { | ||
constructor() { | ||
super(...arguments); | ||
} | ||
} | ||
module.exports = topsort; | ||
topsort.CycleException = CycleException; | ||
},{}],15:[function(require,module,exports){ | ||
module.exports = PriorityQueue; | ||
/** | ||
@@ -438,149 +466,149 @@ * A min-priority queue data structure. This algorithm is derived from Cormen, | ||
*/ | ||
function PriorityQueue() { | ||
this._arr = []; | ||
this._keyIndices = {}; | ||
} | ||
class PriorityQueue { | ||
#arr = []; | ||
#keyIndices = {}; | ||
/** | ||
* Returns the number of elements in the queue. Takes `O(1)` time. | ||
*/ | ||
PriorityQueue.prototype.size = function() { | ||
return this._arr.length; | ||
}; | ||
/** | ||
* Returns the number of elements in the queue. Takes `O(1)` time. | ||
*/ | ||
size() { | ||
return this.#arr.length; | ||
} | ||
/** | ||
* Returns the keys that are in the queue. Takes `O(n)` time. | ||
*/ | ||
PriorityQueue.prototype.keys = function() { | ||
return this._arr.map(function(x) { return x.key; }); | ||
}; | ||
/** | ||
* Returns the keys that are in the queue. Takes `O(n)` time. | ||
*/ | ||
keys() { | ||
return this.#arr.map(function(x) { return x.key; }); | ||
} | ||
/** | ||
* Returns `true` if **key** is in the queue and `false` if not. | ||
*/ | ||
PriorityQueue.prototype.has = function(key) { | ||
return this._keyIndices.hasOwnProperty(key); | ||
}; | ||
/** | ||
* Returns `true` if **key** is in the queue and `false` if not. | ||
*/ | ||
has(key) { | ||
return this.#keyIndices.hasOwnProperty(key); | ||
} | ||
/** | ||
* Returns the priority for **key**. If **key** is not present in the queue | ||
* then this function returns `undefined`. Takes `O(1)` time. | ||
* | ||
* @param {Object} key | ||
*/ | ||
PriorityQueue.prototype.priority = function(key) { | ||
var index = this._keyIndices[key]; | ||
if (index !== undefined) { | ||
return this._arr[index].priority; | ||
/** | ||
* Returns the priority for **key**. If **key** is not present in the queue | ||
* then this function returns `undefined`. Takes `O(1)` time. | ||
* | ||
* @param {Object} key | ||
*/ | ||
priority(key) { | ||
var index = this.#keyIndices[key]; | ||
if (index !== undefined) { | ||
return this.#arr[index].priority; | ||
} | ||
} | ||
}; | ||
/** | ||
* Returns the key for the minimum element in this queue. If the queue is | ||
* empty this function throws an Error. Takes `O(1)` time. | ||
*/ | ||
PriorityQueue.prototype.min = function() { | ||
if (this.size() === 0) { | ||
throw new Error("Queue underflow"); | ||
/** | ||
* Returns the key for the minimum element in this queue. If the queue is | ||
* empty this function throws an Error. Takes `O(1)` time. | ||
*/ | ||
min() { | ||
if (this.size() === 0) { | ||
throw new Error("Queue underflow"); | ||
} | ||
return this.#arr[0].key; | ||
} | ||
return this._arr[0].key; | ||
}; | ||
/** | ||
* Inserts a new key into the priority queue. If the key already exists in | ||
* the queue this function returns `false`; otherwise it will return `true`. | ||
* Takes `O(n)` time. | ||
* | ||
* @param {Object} key the key to add | ||
* @param {Number} priority the initial priority for the key | ||
*/ | ||
PriorityQueue.prototype.add = function(key, priority) { | ||
var keyIndices = this._keyIndices; | ||
key = String(key); | ||
if (!keyIndices.hasOwnProperty(key)) { | ||
var arr = this._arr; | ||
var index = arr.length; | ||
keyIndices[key] = index; | ||
arr.push({key: key, priority: priority}); | ||
this._decrease(index); | ||
return true; | ||
/** | ||
* Inserts a new key into the priority queue. If the key already exists in | ||
* the queue this function returns `false`; otherwise it will return `true`. | ||
* Takes `O(n)` time. | ||
* | ||
* @param {Object} key the key to add | ||
* @param {Number} priority the initial priority for the key | ||
*/ | ||
add(key, priority) { | ||
var keyIndices = this.#keyIndices; | ||
key = String(key); | ||
if (!keyIndices.hasOwnProperty(key)) { | ||
var arr = this.#arr; | ||
var index = arr.length; | ||
keyIndices[key] = index; | ||
arr.push({key: key, priority: priority}); | ||
this.#decrease(index); | ||
return true; | ||
} | ||
return false; | ||
} | ||
return false; | ||
}; | ||
/** | ||
* Removes and returns the smallest key in the queue. Takes `O(log n)` time. | ||
*/ | ||
PriorityQueue.prototype.removeMin = function() { | ||
this._swap(0, this._arr.length - 1); | ||
var min = this._arr.pop(); | ||
delete this._keyIndices[min.key]; | ||
this._heapify(0); | ||
return min.key; | ||
}; | ||
/** | ||
* Removes and returns the smallest key in the queue. Takes `O(log n)` time. | ||
*/ | ||
removeMin() { | ||
this.#swap(0, this.#arr.length - 1); | ||
var min = this.#arr.pop(); | ||
delete this.#keyIndices[min.key]; | ||
this.#heapify(0); | ||
return min.key; | ||
} | ||
/** | ||
* Decreases the priority for **key** to **priority**. If the new priority is | ||
* greater than the previous priority, this function will throw an Error. | ||
* | ||
* @param {Object} key the key for which to raise priority | ||
* @param {Number} priority the new priority for the key | ||
*/ | ||
PriorityQueue.prototype.decrease = function(key, priority) { | ||
var index = this._keyIndices[key]; | ||
if (priority > this._arr[index].priority) { | ||
throw new Error("New priority is greater than current priority. " + | ||
"Key: " + key + " Old: " + this._arr[index].priority + " New: " + priority); | ||
/** | ||
* Decreases the priority for **key** to **priority**. If the new priority is | ||
* greater than the previous priority, this function will throw an Error. | ||
* | ||
* @param {Object} key the key for which to raise priority | ||
* @param {Number} priority the new priority for the key | ||
*/ | ||
decrease(key, priority) { | ||
var index = this.#keyIndices[key]; | ||
if (priority > this.#arr[index].priority) { | ||
throw new Error("New priority is greater than current priority. " + | ||
"Key: " + key + " Old: " + this.#arr[index].priority + " New: " + priority); | ||
} | ||
this.#arr[index].priority = priority; | ||
this.#decrease(index); | ||
} | ||
this._arr[index].priority = priority; | ||
this._decrease(index); | ||
}; | ||
PriorityQueue.prototype._heapify = function(i) { | ||
var arr = this._arr; | ||
var l = 2 * i; | ||
var r = l + 1; | ||
var largest = i; | ||
if (l < arr.length) { | ||
largest = arr[l].priority < arr[largest].priority ? l : largest; | ||
if (r < arr.length) { | ||
largest = arr[r].priority < arr[largest].priority ? r : largest; | ||
#heapify(i) { | ||
var arr = this.#arr; | ||
var l = 2 * i; | ||
var r = l + 1; | ||
var largest = i; | ||
if (l < arr.length) { | ||
largest = arr[l].priority < arr[largest].priority ? l : largest; | ||
if (r < arr.length) { | ||
largest = arr[r].priority < arr[largest].priority ? r : largest; | ||
} | ||
if (largest !== i) { | ||
this.#swap(i, largest); | ||
this.#heapify(largest); | ||
} | ||
} | ||
if (largest !== i) { | ||
this._swap(i, largest); | ||
this._heapify(largest); | ||
} | ||
} | ||
}; | ||
PriorityQueue.prototype._decrease = function(index) { | ||
var arr = this._arr; | ||
var priority = arr[index].priority; | ||
var parent; | ||
while (index !== 0) { | ||
parent = index >> 1; | ||
if (arr[parent].priority < priority) { | ||
break; | ||
#decrease(index) { | ||
var arr = this.#arr; | ||
var priority = arr[index].priority; | ||
var parent; | ||
while (index !== 0) { | ||
parent = index >> 1; | ||
if (arr[parent].priority < priority) { | ||
break; | ||
} | ||
this.#swap(index, parent); | ||
index = parent; | ||
} | ||
this._swap(index, parent); | ||
index = parent; | ||
} | ||
}; | ||
PriorityQueue.prototype._swap = function(i, j) { | ||
var arr = this._arr; | ||
var keyIndices = this._keyIndices; | ||
var origArrI = arr[i]; | ||
var origArrJ = arr[j]; | ||
arr[i] = origArrJ; | ||
arr[j] = origArrI; | ||
keyIndices[origArrJ.key] = i; | ||
keyIndices[origArrI.key] = j; | ||
}; | ||
#swap(i, j) { | ||
var arr = this.#arr; | ||
var keyIndices = this.#keyIndices; | ||
var origArrI = arr[i]; | ||
var origArrJ = arr[j]; | ||
arr[i] = origArrJ; | ||
arr[j] = origArrI; | ||
keyIndices[origArrJ.key] = i; | ||
keyIndices[origArrI.key] = j; | ||
} | ||
} | ||
module.exports = PriorityQueue; | ||
},{}],16:[function(require,module,exports){ | ||
"use strict"; | ||
module.exports = Graph; | ||
var DEFAULT_EDGE_NAME = "\x00"; | ||
@@ -600,622 +628,625 @@ var GRAPH_NODE = "\x00"; | ||
function Graph(opts) { | ||
this._isDirected = true; | ||
this._isMultigraph = false; | ||
this._isCompound = false; | ||
class Graph { | ||
#isDirected = true; | ||
#isMultigraph = false; | ||
#isCompound = false; | ||
if (opts) { | ||
this._isDirected = opts.hasOwnProperty("directed") ? opts.directed : true; | ||
this._isMultigraph = opts.hasOwnProperty("multigraph") ? opts.multigraph : false; | ||
this._isCompound = opts.hasOwnProperty("compound") ? opts.compound : false; | ||
} | ||
// Label for the graph itself | ||
this._label = undefined; | ||
#label; | ||
// Defaults to be set when creating a new node | ||
this._defaultNodeLabelFn = () => undefined; | ||
#defaultNodeLabelFn = () => undefined; | ||
// Defaults to be set when creating a new edge | ||
this._defaultEdgeLabelFn = () => undefined; | ||
#defaultEdgeLabelFn = () => undefined; | ||
// v -> label | ||
this._nodes = {}; | ||
#nodes = {}; | ||
if (this._isCompound) { | ||
// v -> parent | ||
this._parent = {}; | ||
// v -> children | ||
this._children = {}; | ||
this._children[GRAPH_NODE] = {}; | ||
} | ||
// v -> edgeObj | ||
this._in = {}; | ||
#in = {}; | ||
// u -> v -> Number | ||
this._preds = {}; | ||
#preds = {}; | ||
// v -> edgeObj | ||
this._out = {}; | ||
#out = {}; | ||
// v -> w -> Number | ||
this._sucs = {}; | ||
#sucs = {}; | ||
// e -> edgeObj | ||
this._edgeObjs = {}; | ||
#edgeObjs = {}; | ||
// e -> label | ||
this._edgeLabels = {}; | ||
} | ||
#edgeLabels = {}; | ||
/* Number of nodes in the graph. Should only be changed by the implementation. */ | ||
Graph.prototype._nodeCount = 0; | ||
/* Number of nodes in the graph. Should only be changed by the implementation. */ | ||
#nodeCount = 0; | ||
/* Number of edges in the graph. Should only be changed by the implementation. */ | ||
Graph.prototype._edgeCount = 0; | ||
/* Number of edges in the graph. Should only be changed by the implementation. */ | ||
#edgeCount = 0; | ||
#parent; | ||
/* === Graph functions ========= */ | ||
#children; | ||
/** | ||
* Whether graph was created with 'directed' flag set to true or not. | ||
*/ | ||
Graph.prototype.isDirected = function() { | ||
return this._isDirected; | ||
}; | ||
constructor(opts) { | ||
if (opts) { | ||
this.#isDirected = opts.hasOwnProperty("directed") ? opts.directed : true; | ||
this.#isMultigraph = opts.hasOwnProperty("multigraph") ? opts.multigraph : false; | ||
this.#isCompound = opts.hasOwnProperty("compound") ? opts.compound : false; | ||
} | ||
/** | ||
* Whether graph was created with 'multigraph' flag set to true or not. | ||
*/ | ||
Graph.prototype.isMultigraph = function() { | ||
return this._isMultigraph; | ||
}; | ||
if (this.#isCompound) { | ||
// v -> parent | ||
this.#parent = {}; | ||
/** | ||
* Whether graph was created with 'compound' flag set to true or not. | ||
*/ | ||
Graph.prototype.isCompound = function() { | ||
return this._isCompound; | ||
}; | ||
// v -> children | ||
this.#children = {}; | ||
this.#children[GRAPH_NODE] = {}; | ||
} | ||
} | ||
/** | ||
* Sets the label of the graph. | ||
*/ | ||
Graph.prototype.setGraph = function(label) { | ||
this._label = label; | ||
return this; | ||
}; | ||
/* === Graph functions ========= */ | ||
/** | ||
* Gets the graph label. | ||
*/ | ||
Graph.prototype.graph = function() { | ||
return this._label; | ||
}; | ||
/** | ||
* Whether graph was created with 'directed' flag set to true or not. | ||
*/ | ||
isDirected() { | ||
return this.#isDirected; | ||
} | ||
/** | ||
* Whether graph was created with 'multigraph' flag set to true or not. | ||
*/ | ||
isMultigraph() { | ||
return this.#isMultigraph; | ||
} | ||
/* === Node functions ========== */ | ||
/** | ||
* Whether graph was created with 'compound' flag set to true or not. | ||
*/ | ||
isCompound() { | ||
return this.#isCompound; | ||
} | ||
/** | ||
* Sets the default node label. If newDefault is a function, it will be | ||
* invoked ach time when setting a label for a node. Otherwise, this label | ||
* will be assigned as default label in case if no label was specified while | ||
* setting a node. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.setDefaultNodeLabel = function(newDefault) { | ||
this._defaultNodeLabelFn = newDefault; | ||
if (typeof newDefault !== 'function') { | ||
this._defaultNodeLabelFn = () => newDefault; | ||
/** | ||
* Sets the label of the graph. | ||
*/ | ||
setGraph(label) { | ||
this.#label = label; | ||
return this; | ||
} | ||
return this; | ||
}; | ||
/** | ||
* Gets the graph label. | ||
*/ | ||
graph() { | ||
return this.#label; | ||
} | ||
/** | ||
* Gets the number of nodes in the graph. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.nodeCount = function() { | ||
return this._nodeCount; | ||
}; | ||
/** | ||
* Gets all nodes of the graph. Note, the in case of compound graph subnodes are | ||
* not included in list. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.nodes = function() { | ||
return Object.keys(this._nodes); | ||
}; | ||
/* === Node functions ========== */ | ||
/** | ||
* Gets list of nodes without in-edges. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.sources = function() { | ||
var self = this; | ||
return this.nodes().filter(v => Object.keys(self._in[v]).length === 0); | ||
}; | ||
/** | ||
* Gets list of nodes without out-edges. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.sinks = function() { | ||
var self = this; | ||
return this.nodes().filter(v => Object.keys(self._out[v]).length === 0); | ||
}; | ||
/** | ||
* Invokes setNode method for each node in names list. | ||
* Complexity: O(|names|). | ||
*/ | ||
Graph.prototype.setNodes = function(vs, value) { | ||
var args = arguments; | ||
var self = this; | ||
vs.forEach(function(v) { | ||
if (args.length > 1) { | ||
self.setNode(v, value); | ||
} else { | ||
self.setNode(v); | ||
/** | ||
* Sets the default node label. If newDefault is a function, it will be | ||
* invoked ach time when setting a label for a node. Otherwise, this label | ||
* will be assigned as default label in case if no label was specified while | ||
* setting a node. | ||
* Complexity: O(1). | ||
*/ | ||
setDefaultNodeLabel(newDefault) { | ||
this.#defaultNodeLabelFn = newDefault; | ||
if (typeof newDefault !== 'function') { | ||
this.#defaultNodeLabelFn = () => newDefault; | ||
} | ||
}); | ||
return this; | ||
}; | ||
/** | ||
* Creates or updates the value for the node v in the graph. If label is supplied | ||
* it is set as the value for the node. If label is not supplied and the node was | ||
* created by this call then the default node label will be assigned. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.setNode = function(v, value) { | ||
if (this._nodes.hasOwnProperty(v)) { | ||
if (arguments.length > 1) { | ||
this._nodes[v] = value; | ||
} | ||
return this; | ||
} | ||
this._nodes[v] = arguments.length > 1 ? value : this._defaultNodeLabelFn(v); | ||
if (this._isCompound) { | ||
this._parent[v] = GRAPH_NODE; | ||
this._children[v] = {}; | ||
this._children[GRAPH_NODE][v] = true; | ||
/** | ||
* Gets the number of nodes in the graph. | ||
* Complexity: O(1). | ||
*/ | ||
nodeCount() { | ||
return this.#nodeCount; | ||
} | ||
this._in[v] = {}; | ||
this._preds[v] = {}; | ||
this._out[v] = {}; | ||
this._sucs[v] = {}; | ||
++this._nodeCount; | ||
return this; | ||
}; | ||
/** | ||
* Gets the label of node with specified name. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.node = function(v) { | ||
return this._nodes[v]; | ||
}; | ||
/** | ||
* Gets all nodes of the graph. Note, the in case of compound graph subnodes are | ||
* not included in list. | ||
* Complexity: O(1). | ||
*/ | ||
nodes() { | ||
return Object.keys(this.#nodes); | ||
} | ||
/** | ||
* Detects whether graph has a node with specified name or not. | ||
*/ | ||
Graph.prototype.hasNode = function(v) { | ||
return this._nodes.hasOwnProperty(v); | ||
}; | ||
/** | ||
* Gets list of nodes without in-edges. | ||
* Complexity: O(|V|). | ||
*/ | ||
sources() { | ||
var self = this; | ||
return this.nodes().filter(v => Object.keys(self.#in[v]).length === 0); | ||
} | ||
/** | ||
* Remove the node with the name from the graph or do nothing if the node is not in | ||
* the graph. If the node was removed this function also removes any incident | ||
* edges. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.removeNode = function(v) { | ||
var self = this; | ||
if (this._nodes.hasOwnProperty(v)) { | ||
var removeEdge = e => self.removeEdge(self._edgeObjs[e]); | ||
delete this._nodes[v]; | ||
if (this._isCompound) { | ||
this._removeFromParentsChildList(v); | ||
delete this._parent[v]; | ||
this.children(v).forEach(function(child) { | ||
self.setParent(child); | ||
}); | ||
delete this._children[v]; | ||
} | ||
Object.keys(this._in[v]).forEach(removeEdge); | ||
delete this._in[v]; | ||
delete this._preds[v]; | ||
Object.keys(this._out[v]).forEach(removeEdge); | ||
delete this._out[v]; | ||
delete this._sucs[v]; | ||
--this._nodeCount; | ||
/** | ||
* Gets list of nodes without out-edges. | ||
* Complexity: O(|V|). | ||
*/ | ||
sinks() { | ||
var self = this; | ||
return this.nodes().filter(v => Object.keys(self.#out[v]).length === 0); | ||
} | ||
return this; | ||
}; | ||
/** | ||
* Sets node p as a parent for node v if it is defined, or removes the | ||
* parent for v if p is undefined. Method throws an exception in case of | ||
* invoking it in context of noncompound graph. | ||
* Average-case complexity: O(1). | ||
*/ | ||
Graph.prototype.setParent = function(v, parent) { | ||
if (!this._isCompound) { | ||
throw new Error("Cannot set parent in a non-compound graph"); | ||
/** | ||
* Invokes setNode method for each node in names list. | ||
* Complexity: O(|names|). | ||
*/ | ||
setNodes(vs, value) { | ||
var args = arguments; | ||
var self = this; | ||
vs.forEach(function(v) { | ||
if (args.length > 1) { | ||
self.setNode(v, value); | ||
} else { | ||
self.setNode(v); | ||
} | ||
}); | ||
return this; | ||
} | ||
if (parent === undefined) { | ||
parent = GRAPH_NODE; | ||
} else { | ||
// Coerce parent to string | ||
parent += ""; | ||
for (var ancestor = parent; | ||
ancestor !== undefined; | ||
ancestor = this.parent(ancestor)) { | ||
if (ancestor === v) { | ||
throw new Error("Setting " + parent+ " as parent of " + v + | ||
" would create a cycle"); | ||
/** | ||
* Creates or updates the value for the node v in the graph. If label is supplied | ||
* it is set as the value for the node. If label is not supplied and the node was | ||
* created by this call then the default node label will be assigned. | ||
* Complexity: O(1). | ||
*/ | ||
setNode(v, value) { | ||
if (this.#nodes.hasOwnProperty(v)) { | ||
if (arguments.length > 1) { | ||
this.#nodes[v] = value; | ||
} | ||
return this; | ||
} | ||
this.setNode(parent); | ||
this.#nodes[v] = arguments.length > 1 ? value : this.#defaultNodeLabelFn(v); | ||
if (this.#isCompound) { | ||
this.#parent[v] = GRAPH_NODE; | ||
this.#children[v] = {}; | ||
this.#children[GRAPH_NODE][v] = true; | ||
} | ||
this.#in[v] = {}; | ||
this.#preds[v] = {}; | ||
this.#out[v] = {}; | ||
this.#sucs[v] = {}; | ||
++this.#nodeCount; | ||
return this; | ||
} | ||
this.setNode(v); | ||
this._removeFromParentsChildList(v); | ||
this._parent[v] = parent; | ||
this._children[parent][v] = true; | ||
return this; | ||
}; | ||
/** | ||
* Gets the label of node with specified name. | ||
* Complexity: O(|V|). | ||
*/ | ||
node(v) { | ||
return this.#nodes[v]; | ||
} | ||
Graph.prototype._removeFromParentsChildList = function(v) { | ||
delete this._children[this._parent[v]][v]; | ||
}; | ||
/** | ||
* Detects whether graph has a node with specified name or not. | ||
*/ | ||
hasNode(v) { | ||
return this.#nodes.hasOwnProperty(v); | ||
} | ||
/** | ||
* Gets parent node for node v. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.parent = function(v) { | ||
if (this._isCompound) { | ||
var parent = this._parent[v]; | ||
if (parent !== GRAPH_NODE) { | ||
return parent; | ||
/** | ||
* Remove the node with the name from the graph or do nothing if the node is not in | ||
* the graph. If the node was removed this function also removes any incident | ||
* edges. | ||
* Complexity: O(1). | ||
*/ | ||
removeNode(v) { | ||
var self = this; | ||
if (this.#nodes.hasOwnProperty(v)) { | ||
var removeEdge = e => self.removeEdge(self.#edgeObjs[e]); | ||
delete this.#nodes[v]; | ||
if (this.#isCompound) { | ||
this.#removeFromParentsChildList(v); | ||
delete this.#parent[v]; | ||
this.children(v).forEach(function(child) { | ||
self.setParent(child); | ||
}); | ||
delete this.#children[v]; | ||
} | ||
Object.keys(this.#in[v]).forEach(removeEdge); | ||
delete this.#in[v]; | ||
delete this.#preds[v]; | ||
Object.keys(this.#out[v]).forEach(removeEdge); | ||
delete this.#out[v]; | ||
delete this.#sucs[v]; | ||
--this.#nodeCount; | ||
} | ||
return this; | ||
} | ||
}; | ||
/** | ||
* Gets list of direct children of node v. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.children = function(v = GRAPH_NODE) { | ||
if (this._isCompound) { | ||
var children = this._children[v]; | ||
if (children) { | ||
return Object.keys(children); | ||
/** | ||
* Sets node p as a parent for node v if it is defined, or removes the | ||
* parent for v if p is undefined. Method throws an exception in case of | ||
* invoking it in context of noncompound graph. | ||
* Average-case complexity: O(1). | ||
*/ | ||
setParent(v, parent) { | ||
if (!this.#isCompound) { | ||
throw new Error("Cannot set parent in a non-compound graph"); | ||
} | ||
} else if (v === GRAPH_NODE) { | ||
return this.nodes(); | ||
} else if (this.hasNode(v)) { | ||
return []; | ||
if (parent === undefined) { | ||
parent = GRAPH_NODE; | ||
} else { | ||
// Coerce parent to string | ||
parent += ""; | ||
for (var ancestor = parent; ancestor !== undefined; ancestor = this.parent(ancestor)) { | ||
if (ancestor === v) { | ||
throw new Error("Setting " + parent+ " as parent of " + v + | ||
" would create a cycle"); | ||
} | ||
} | ||
this.setNode(parent); | ||
} | ||
this.setNode(v); | ||
this.#removeFromParentsChildList(v); | ||
this.#parent[v] = parent; | ||
this.#children[parent][v] = true; | ||
return this; | ||
} | ||
}; | ||
/** | ||
* Return all nodes that are predecessors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.predecessors = function(v) { | ||
var predsV = this._preds[v]; | ||
if (predsV) { | ||
return Object.keys(predsV); | ||
#removeFromParentsChildList(v) { | ||
delete this.#children[this.#parent[v]][v]; | ||
} | ||
}; | ||
/** | ||
* Return all nodes that are successors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.successors = function(v) { | ||
var sucsV = this._sucs[v]; | ||
if (sucsV) { | ||
return Object.keys(sucsV); | ||
/** | ||
* Gets parent node for node v. | ||
* Complexity: O(1). | ||
*/ | ||
parent(v) { | ||
if (this.#isCompound) { | ||
var parent = this.#parent[v]; | ||
if (parent !== GRAPH_NODE) { | ||
return parent; | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* Return all nodes that are predecessors or successors of the specified node or undefined if | ||
* node v is not in the graph. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.neighbors = function(v) { | ||
var preds = this.predecessors(v); | ||
if (preds) { | ||
const union = new Set(preds); | ||
for (var succ of this.successors(v)) { | ||
union.add(succ); | ||
/** | ||
* Gets list of direct children of node v. | ||
* Complexity: O(1). | ||
*/ | ||
children(v = GRAPH_NODE) { | ||
if (this.#isCompound) { | ||
var children = this.#children[v]; | ||
if (children) { | ||
return Object.keys(children); | ||
} | ||
} else if (v === GRAPH_NODE) { | ||
return this.nodes(); | ||
} else if (this.hasNode(v)) { | ||
return []; | ||
} | ||
} | ||
return Array.from(union.values()); | ||
/** | ||
* Return all nodes that are predecessors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/ | ||
predecessors(v) { | ||
var predsV = this.#preds[v]; | ||
if (predsV) { | ||
return Object.keys(predsV); | ||
} | ||
} | ||
}; | ||
Graph.prototype.isLeaf = function (v) { | ||
var neighbors; | ||
if (this.isDirected()) { | ||
neighbors = this.successors(v); | ||
} else { | ||
neighbors = this.neighbors(v); | ||
/** | ||
* Return all nodes that are successors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/ | ||
successors(v) { | ||
var sucsV = this.#sucs[v]; | ||
if (sucsV) { | ||
return Object.keys(sucsV); | ||
} | ||
} | ||
return neighbors.length === 0; | ||
}; | ||
/** | ||
* Creates new graph with nodes filtered via filter. Edges incident to rejected node | ||
* are also removed. In case of compound graph, if parent is rejected by filter, | ||
* than all its children are rejected too. | ||
* Average-case complexity: O(|E|+|V|). | ||
*/ | ||
Graph.prototype.filterNodes = function(filter) { | ||
var copy = new this.constructor({ | ||
directed: this._isDirected, | ||
multigraph: this._isMultigraph, | ||
compound: this._isCompound | ||
}); | ||
/** | ||
* Return all nodes that are predecessors or successors of the specified node or undefined if | ||
* node v is not in the graph. | ||
* Complexity: O(|V|). | ||
*/ | ||
neighbors(v) { | ||
var preds = this.predecessors(v); | ||
if (preds) { | ||
const union = new Set(preds); | ||
for (var succ of this.successors(v)) { | ||
union.add(succ); | ||
} | ||
copy.setGraph(this.graph()); | ||
var self = this; | ||
Object.entries(this._nodes).forEach(function([v, value]) { | ||
if (filter(v)) { | ||
copy.setNode(v, value); | ||
return Array.from(union.values()); | ||
} | ||
}); | ||
} | ||
Object.values(this._edgeObjs).forEach(function(e) { | ||
if (copy.hasNode(e.v) && copy.hasNode(e.w)) { | ||
copy.setEdge(e, self.edge(e)); | ||
} | ||
}); | ||
var parents = {}; | ||
function findParent(v) { | ||
var parent = self.parent(v); | ||
if (parent === undefined || copy.hasNode(parent)) { | ||
parents[v] = parent; | ||
return parent; | ||
} else if (parent in parents) { | ||
return parents[parent]; | ||
isLeaf(v) { | ||
var neighbors; | ||
if (this.isDirected()) { | ||
neighbors = this.successors(v); | ||
} else { | ||
return findParent(parent); | ||
neighbors = this.neighbors(v); | ||
} | ||
return neighbors.length === 0; | ||
} | ||
if (this._isCompound) { | ||
copy.nodes().forEach(v => copy.setParent(v, findParent(v))); | ||
} | ||
/** | ||
* Creates new graph with nodes filtered via filter. Edges incident to rejected node | ||
* are also removed. In case of compound graph, if parent is rejected by filter, | ||
* than all its children are rejected too. | ||
* Average-case complexity: O(|E|+|V|). | ||
*/ | ||
filterNodes(filter) { | ||
var copy = new this.constructor({ | ||
directed: this.#isDirected, | ||
multigraph: this.#isMultigraph, | ||
compound: this.#isCompound | ||
}); | ||
return copy; | ||
}; | ||
copy.setGraph(this.graph()); | ||
/* === Edge functions ========== */ | ||
var self = this; | ||
Object.entries(this.#nodes).forEach(function([v, value]) { | ||
if (filter(v)) { | ||
copy.setNode(v, value); | ||
} | ||
}); | ||
/** | ||
* Sets the default edge label or factory function. This label will be | ||
* assigned as default label in case if no label was specified while setting | ||
* an edge or this function will be invoked each time when setting an edge | ||
* with no label specified and returned value * will be used as a label for edge. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.setDefaultEdgeLabel = function(newDefault) { | ||
this._defaultEdgeLabelFn = newDefault; | ||
if (typeof newDefault !== 'function') { | ||
this._defaultEdgeLabelFn = () => newDefault; | ||
} | ||
Object.values(this.#edgeObjs).forEach(function(e) { | ||
if (copy.hasNode(e.v) && copy.hasNode(e.w)) { | ||
copy.setEdge(e, self.edge(e)); | ||
} | ||
}); | ||
return this; | ||
}; | ||
var parents = {}; | ||
function findParent(v) { | ||
var parent = self.parent(v); | ||
if (parent === undefined || copy.hasNode(parent)) { | ||
parents[v] = parent; | ||
return parent; | ||
} else if (parent in parents) { | ||
return parents[parent]; | ||
} else { | ||
return findParent(parent); | ||
} | ||
} | ||
/** | ||
* Gets the number of edges in the graph. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.edgeCount = function() { | ||
return this._edgeCount; | ||
}; | ||
if (this.#isCompound) { | ||
copy.nodes().forEach(v => copy.setParent(v, findParent(v))); | ||
} | ||
/** | ||
* Gets edges of the graph. In case of compound graph subgraphs are not considered. | ||
* Complexity: O(|E|). | ||
*/ | ||
Graph.prototype.edges = function() { | ||
return Object.values(this._edgeObjs); | ||
}; | ||
return copy; | ||
} | ||
/** | ||
* Establish an edges path over the nodes in nodes list. If some edge is already | ||
* exists, it will update its label, otherwise it will create an edge between pair | ||
* of nodes with label provided or default label if no label provided. | ||
* Complexity: O(|nodes|). | ||
*/ | ||
Graph.prototype.setPath = function(vs, value) { | ||
var self = this; | ||
var args = arguments; | ||
vs.reduce(function(v, w) { | ||
if (args.length > 1) { | ||
self.setEdge(v, w, value); | ||
} else { | ||
self.setEdge(v, w); | ||
/* === Edge functions ========== */ | ||
/** | ||
* Sets the default edge label or factory function. This label will be | ||
* assigned as default label in case if no label was specified while setting | ||
* an edge or this function will be invoked each time when setting an edge | ||
* with no label specified and returned value * will be used as a label for edge. | ||
* Complexity: O(1). | ||
*/ | ||
setDefaultEdgeLabel(newDefault) { | ||
this.#defaultEdgeLabelFn = newDefault; | ||
if (typeof newDefault !== 'function') { | ||
this.#defaultEdgeLabelFn = () => newDefault; | ||
} | ||
return w; | ||
}); | ||
return this; | ||
}; | ||
/** | ||
* Creates or updates the label for the edge (v, w) with the optionally supplied | ||
* name. If label is supplied it is set as the value for the edge. If label is not | ||
* supplied and the edge was created by this call then the default edge label will | ||
* be assigned. The name parameter is only useful with multigraphs. | ||
*/ | ||
Graph.prototype.setEdge = function() { | ||
var v, w, name, value; | ||
var valueSpecified = false; | ||
var arg0 = arguments[0]; | ||
return this; | ||
} | ||
if (typeof arg0 === "object" && arg0 !== null && "v" in arg0) { | ||
v = arg0.v; | ||
w = arg0.w; | ||
name = arg0.name; | ||
if (arguments.length === 2) { | ||
value = arguments[1]; | ||
valueSpecified = true; | ||
} | ||
} else { | ||
v = arg0; | ||
w = arguments[1]; | ||
name = arguments[3]; | ||
if (arguments.length > 2) { | ||
value = arguments[2]; | ||
valueSpecified = true; | ||
} | ||
/** | ||
* Gets the number of edges in the graph. | ||
* Complexity: O(1). | ||
*/ | ||
edgeCount() { | ||
return this.#edgeCount; | ||
} | ||
v = "" + v; | ||
w = "" + w; | ||
if (name !== undefined) { | ||
name = "" + name; | ||
/** | ||
* Gets edges of the graph. In case of compound graph subgraphs are not considered. | ||
* Complexity: O(|E|). | ||
*/ | ||
edges() { | ||
return Object.values(this.#edgeObjs); | ||
} | ||
var e = edgeArgsToId(this._isDirected, v, w, name); | ||
if (this._edgeLabels.hasOwnProperty(e)) { | ||
if (valueSpecified) { | ||
this._edgeLabels[e] = value; | ||
} | ||
/** | ||
* Establish an edges path over the nodes in nodes list. If some edge is already | ||
* exists, it will update its label, otherwise it will create an edge between pair | ||
* of nodes with label provided or default label if no label provided. | ||
* Complexity: O(|nodes|). | ||
*/ | ||
setPath(vs, value) { | ||
var self = this; | ||
var args = arguments; | ||
vs.reduce(function(v, w) { | ||
if (args.length > 1) { | ||
self.setEdge(v, w, value); | ||
} else { | ||
self.setEdge(v, w); | ||
} | ||
return w; | ||
}); | ||
return this; | ||
} | ||
if (name !== undefined && !this._isMultigraph) { | ||
throw new Error("Cannot set a named edge when isMultigraph = false"); | ||
} | ||
/** | ||
* Creates or updates the label for the edge (v, w) with the optionally supplied | ||
* name. If label is supplied it is set as the value for the edge. If label is not | ||
* supplied and the edge was created by this call then the default edge label will | ||
* be assigned. The name parameter is only useful with multigraphs. | ||
*/ | ||
setEdge() { | ||
var v, w, name, value; | ||
var valueSpecified = false; | ||
var arg0 = arguments[0]; | ||
// It didn't exist, so we need to create it. | ||
// First ensure the nodes exist. | ||
this.setNode(v); | ||
this.setNode(w); | ||
if (typeof arg0 === "object" && arg0 !== null && "v" in arg0) { | ||
v = arg0.v; | ||
w = arg0.w; | ||
name = arg0.name; | ||
if (arguments.length === 2) { | ||
value = arguments[1]; | ||
valueSpecified = true; | ||
} | ||
} else { | ||
v = arg0; | ||
w = arguments[1]; | ||
name = arguments[3]; | ||
if (arguments.length > 2) { | ||
value = arguments[2]; | ||
valueSpecified = true; | ||
} | ||
} | ||
this._edgeLabels[e] = valueSpecified ? value : this._defaultEdgeLabelFn(v, w, name); | ||
v = "" + v; | ||
w = "" + w; | ||
if (name !== undefined) { | ||
name = "" + name; | ||
} | ||
var edgeObj = edgeArgsToObj(this._isDirected, v, w, name); | ||
// Ensure we add undirected edges in a consistent way. | ||
v = edgeObj.v; | ||
w = edgeObj.w; | ||
var e = edgeArgsToId(this.#isDirected, v, w, name); | ||
if (this.#edgeLabels.hasOwnProperty(e)) { | ||
if (valueSpecified) { | ||
this.#edgeLabels[e] = value; | ||
} | ||
return this; | ||
} | ||
Object.freeze(edgeObj); | ||
this._edgeObjs[e] = edgeObj; | ||
incrementOrInitEntry(this._preds[w], v); | ||
incrementOrInitEntry(this._sucs[v], w); | ||
this._in[w][e] = edgeObj; | ||
this._out[v][e] = edgeObj; | ||
this._edgeCount++; | ||
return this; | ||
}; | ||
if (name !== undefined && !this.#isMultigraph) { | ||
throw new Error("Cannot set a named edge when isMultigraph = false"); | ||
} | ||
/** | ||
* Gets the label for the specified edge. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.edge = function(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this._isDirected, arguments[0]) | ||
: edgeArgsToId(this._isDirected, v, w, name)); | ||
return this._edgeLabels[e]; | ||
}; | ||
// It didn't exist, so we need to create it. | ||
// First ensure the nodes exist. | ||
this.setNode(v); | ||
this.setNode(w); | ||
/** | ||
* Detects whether the graph contains specified edge or not. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.hasEdge = function(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this._isDirected, arguments[0]) | ||
: edgeArgsToId(this._isDirected, v, w, name)); | ||
return this._edgeLabels.hasOwnProperty(e); | ||
}; | ||
this.#edgeLabels[e] = valueSpecified ? value : this.#defaultEdgeLabelFn(v, w, name); | ||
/** | ||
* Removes the specified edge from the graph. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.removeEdge = function(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this._isDirected, arguments[0]) | ||
: edgeArgsToId(this._isDirected, v, w, name)); | ||
var edge = this._edgeObjs[e]; | ||
if (edge) { | ||
v = edge.v; | ||
w = edge.w; | ||
delete this._edgeLabels[e]; | ||
delete this._edgeObjs[e]; | ||
decrementOrRemoveEntry(this._preds[w], v); | ||
decrementOrRemoveEntry(this._sucs[v], w); | ||
delete this._in[w][e]; | ||
delete this._out[v][e]; | ||
this._edgeCount--; | ||
var edgeObj = edgeArgsToObj(this.#isDirected, v, w, name); | ||
// Ensure we add undirected edges in a consistent way. | ||
v = edgeObj.v; | ||
w = edgeObj.w; | ||
Object.freeze(edgeObj); | ||
this.#edgeObjs[e] = edgeObj; | ||
incrementOrInitEntry(this.#preds[w], v); | ||
incrementOrInitEntry(this.#sucs[v], w); | ||
this.#in[w][e] = edgeObj; | ||
this.#out[v][e] = edgeObj; | ||
this.#edgeCount++; | ||
return this; | ||
} | ||
return this; | ||
}; | ||
/** | ||
* Return all edges that point to the node v. Optionally filters those edges down to just those | ||
* coming from node u. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/ | ||
Graph.prototype.inEdges = function(v, u) { | ||
var inV = this._in[v]; | ||
if (inV) { | ||
var edges = Object.values(inV); | ||
if (!u) { | ||
return edges; | ||
/** | ||
* Gets the label for the specified edge. | ||
* Complexity: O(1). | ||
*/ | ||
edge(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this.#isDirected, arguments[0]) | ||
: edgeArgsToId(this.#isDirected, v, w, name)); | ||
return this.#edgeLabels[e]; | ||
} | ||
/** | ||
* Detects whether the graph contains specified edge or not. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/ | ||
hasEdge(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this.#isDirected, arguments[0]) | ||
: edgeArgsToId(this.#isDirected, v, w, name)); | ||
return this.#edgeLabels.hasOwnProperty(e); | ||
} | ||
/** | ||
* Removes the specified edge from the graph. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/ | ||
removeEdge(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this.#isDirected, arguments[0]) | ||
: edgeArgsToId(this.#isDirected, v, w, name)); | ||
var edge = this.#edgeObjs[e]; | ||
if (edge) { | ||
v = edge.v; | ||
w = edge.w; | ||
delete this.#edgeLabels[e]; | ||
delete this.#edgeObjs[e]; | ||
decrementOrRemoveEntry(this.#preds[w], v); | ||
decrementOrRemoveEntry(this.#sucs[v], w); | ||
delete this.#in[w][e]; | ||
delete this.#out[v][e]; | ||
this.#edgeCount--; | ||
} | ||
return edges.filter(edge => edge.v === u); | ||
return this; | ||
} | ||
}; | ||
/** | ||
* Return all edges that are pointed at by node v. Optionally filters those edges down to just | ||
* those point to w. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/ | ||
Graph.prototype.outEdges = function(v, w) { | ||
var outV = this._out[v]; | ||
if (outV) { | ||
var edges = Object.values(outV); | ||
if (!w) { | ||
return edges; | ||
/** | ||
* Return all edges that point to the node v. Optionally filters those edges down to just those | ||
* coming from node u. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/ | ||
inEdges(v, u) { | ||
var inV = this.#in[v]; | ||
if (inV) { | ||
var edges = Object.values(inV); | ||
if (!u) { | ||
return edges; | ||
} | ||
return edges.filter(edge => edge.v === u); | ||
} | ||
return edges.filter(edge => edge.w === w); | ||
} | ||
}; | ||
/** | ||
* Returns all edges to or from node v regardless of direction. Optionally filters those edges | ||
* down to just those between nodes v and w regardless of direction. | ||
* Complexity: O(|E|). | ||
*/ | ||
Graph.prototype.nodeEdges = function(v, w) { | ||
var inEdges = this.inEdges(v, w); | ||
if (inEdges) { | ||
return inEdges.concat(this.outEdges(v, w)); | ||
/** | ||
* Return all edges that are pointed at by node v. Optionally filters those edges down to just | ||
* those point to w. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/ | ||
outEdges(v, w) { | ||
var outV = this.#out[v]; | ||
if (outV) { | ||
var edges = Object.values(outV); | ||
if (!w) { | ||
return edges; | ||
} | ||
return edges.filter(edge => edge.w === w); | ||
} | ||
} | ||
}; | ||
/** | ||
* Returns all edges to or from node v regardless of direction. Optionally filters those edges | ||
* down to just those between nodes v and w regardless of direction. | ||
* Complexity: O(|E|). | ||
*/ | ||
nodeEdges(v, w) { | ||
var inEdges = this.inEdges(v, w); | ||
if (inEdges) { | ||
return inEdges.concat(this.outEdges(v, w)); | ||
} | ||
} | ||
} | ||
function incrementOrInitEntry(map, k) { | ||
@@ -1264,2 +1295,4 @@ if (map[k]) { | ||
module.exports = Graph; | ||
},{}],17:[function(require,module,exports){ | ||
@@ -1355,5 +1388,5 @@ // Includes only the "core" of graphlib | ||
},{"./graph":16}],19:[function(require,module,exports){ | ||
module.exports = '2.1.9'; | ||
module.exports = '2.1.11'; | ||
},{}]},{},[1])(1) | ||
}); |
@@ -38,8 +38,7 @@ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.graphlib=f()}})(function(){var define,module,exports;return function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r}()({1:[function(require,module,exports){ | ||
* | ||
* Order must be one of "pre" or "post". | ||
*/function dfs(g,vs,order){if(!Array.isArray(vs)){vs=[vs]}var navigation=(g.isDirected()?g.successors:g.neighbors).bind(g);var acc=[];var visited={};vs.forEach(function(v){if(!g.hasNode(v)){throw new Error("Graph does not have node: "+v)}doDfs(g,v,order==="post",visited,navigation,acc)});return acc}function doDfs(g,v,postorder,visited,navigation,acc){if(!visited.hasOwnProperty(v)){visited[v]=true;if(!postorder){acc.push(v)}navigation(v).forEach(function(w){doDfs(g,w,postorder,visited,navigation,acc)});if(postorder){acc.push(v)}}}},{}],4:[function(require,module,exports){var dijkstra=require("./dijkstra");module.exports=dijkstraAll;function dijkstraAll(g,weightFunc,edgeFunc){return g.nodes().reduce(function(acc,v){acc[v]=dijkstra(g,v,weightFunc,edgeFunc);return acc},{})}},{"./dijkstra":5}],5:[function(require,module,exports){var PriorityQueue=require("../data/priority-queue");module.exports=dijkstra;var DEFAULT_WEIGHT_FUNC=()=>1;function dijkstra(g,source,weightFn,edgeFn){return runDijkstra(g,String(source),weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runDijkstra(g,source,weightFn,edgeFn){var results={};var pq=new PriorityQueue;var v,vEntry;var updateNeighbors=function(edge){var w=edge.v!==v?edge.v:edge.w;var wEntry=results[w];var weight=weightFn(edge);var distance=vEntry.distance+weight;if(weight<0){throw new Error("dijkstra does not allow negative edge weights. "+"Bad edge: "+edge+" Weight: "+weight)}if(distance<wEntry.distance){wEntry.distance=distance;wEntry.predecessor=v;pq.decrease(w,distance)}};g.nodes().forEach(function(v){var distance=v===source?0:Number.POSITIVE_INFINITY;results[v]={distance:distance};pq.add(v,distance)});while(pq.size()>0){v=pq.removeMin();vEntry=results[v];if(vEntry.distance===Number.POSITIVE_INFINITY){break}edgeFn(v).forEach(updateNeighbors)}return results}},{"../data/priority-queue":15}],6:[function(require,module,exports){var tarjan=require("./tarjan");module.exports=findCycles;function findCycles(g){return tarjan(g).filter(function(cmpt){return cmpt.length>1||cmpt.length===1&&g.hasEdge(cmpt[0],cmpt[0])})}},{"./tarjan":13}],7:[function(require,module,exports){module.exports=floydWarshall;var DEFAULT_WEIGHT_FUNC=()=>1;function floydWarshall(g,weightFn,edgeFn){return runFloydWarshall(g,weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runFloydWarshall(g,weightFn,edgeFn){var results={};var nodes=g.nodes();nodes.forEach(function(v){results[v]={};results[v][v]={distance:0};nodes.forEach(function(w){if(v!==w){results[v][w]={distance:Number.POSITIVE_INFINITY}}});edgeFn(v).forEach(function(edge){var w=edge.v===v?edge.w:edge.v;var d=weightFn(edge);results[v][w]={distance:d,predecessor:v}})});nodes.forEach(function(k){var rowK=results[k];nodes.forEach(function(i){var rowI=results[i];nodes.forEach(function(j){var ik=rowI[k];var kj=rowK[j];var ij=rowI[j];var altDistance=ik.distance+kj.distance;if(altDistance<ij.distance){ij.distance=altDistance;ij.predecessor=kj.predecessor}})})});return results}},{}],8:[function(require,module,exports){module.exports={components:require("./components"),dijkstra:require("./dijkstra"),dijkstraAll:require("./dijkstra-all"),findCycles:require("./find-cycles"),floydWarshall:require("./floyd-warshall"),isAcyclic:require("./is-acyclic"),postorder:require("./postorder"),preorder:require("./preorder"),prim:require("./prim"),tarjan:require("./tarjan"),topsort:require("./topsort")}},{"./components":2,"./dijkstra":5,"./dijkstra-all":4,"./find-cycles":6,"./floyd-warshall":7,"./is-acyclic":9,"./postorder":10,"./preorder":11,"./prim":12,"./tarjan":13,"./topsort":14}],9:[function(require,module,exports){var topsort=require("./topsort");module.exports=isAcyclic;function isAcyclic(g){try{topsort(g)}catch(e){if(e instanceof topsort.CycleException){return false}throw e}return true}},{"./topsort":14}],10:[function(require,module,exports){var dfs=require("./dfs");module.exports=postorder;function postorder(g,vs){return dfs(g,vs,"post")}},{"./dfs":3}],11:[function(require,module,exports){var dfs=require("./dfs");module.exports=preorder;function preorder(g,vs){return dfs(g,vs,"pre")}},{"./dfs":3}],12:[function(require,module,exports){var Graph=require("../graph");var PriorityQueue=require("../data/priority-queue");module.exports=prim;function prim(g,weightFunc){var result=new Graph;var parents={};var pq=new PriorityQueue;var v;function updateNeighbors(edge){var w=edge.v===v?edge.w:edge.v;var pri=pq.priority(w);if(pri!==undefined){var edgeWeight=weightFunc(edge);if(edgeWeight<pri){parents[w]=v;pq.decrease(w,edgeWeight)}}}if(g.nodeCount()===0){return result}g.nodes().forEach(function(v){pq.add(v,Number.POSITIVE_INFINITY);result.setNode(v)}); | ||
* If the order is not "post", it will be treated as "pre". | ||
*/function dfs(g,vs,order){if(!Array.isArray(vs)){vs=[vs]}var navigation=g.isDirected()?v=>g.successors(v):v=>g.neighbors(v);var orderFunc=order==="post"?postOrderDfs:preOrderDfs;var acc=[];var visited={};vs.forEach(v=>{if(!g.hasNode(v)){throw new Error("Graph does not have node: "+v)}orderFunc(v,navigation,visited,acc)});return acc}function postOrderDfs(v,navigation,visited,acc){var stack=[[v,false]];while(stack.length>0){var curr=stack.pop();if(curr[1]){acc.push(curr[0])}else{if(!visited.hasOwnProperty(curr[0])){visited[curr[0]]=true;stack.push([curr[0],true]);forEachRight(navigation(curr[0]),w=>stack.push([w,false]))}}}}function preOrderDfs(v,navigation,visited,acc){var stack=[v];while(stack.length>0){var curr=stack.pop();if(!visited.hasOwnProperty(curr)){visited[curr]=true;acc.push(curr);forEachRight(navigation(curr),w=>stack.push(w))}}}function forEachRight(array,iteratee){var length=array.length;while(length--){iteratee(array[length],length,array)}return array}},{}],4:[function(require,module,exports){var dijkstra=require("./dijkstra");module.exports=dijkstraAll;function dijkstraAll(g,weightFunc,edgeFunc){return g.nodes().reduce(function(acc,v){acc[v]=dijkstra(g,v,weightFunc,edgeFunc);return acc},{})}},{"./dijkstra":5}],5:[function(require,module,exports){var PriorityQueue=require("../data/priority-queue");module.exports=dijkstra;var DEFAULT_WEIGHT_FUNC=()=>1;function dijkstra(g,source,weightFn,edgeFn){return runDijkstra(g,String(source),weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runDijkstra(g,source,weightFn,edgeFn){var results={};var pq=new PriorityQueue;var v,vEntry;var updateNeighbors=function(edge){var w=edge.v!==v?edge.v:edge.w;var wEntry=results[w];var weight=weightFn(edge);var distance=vEntry.distance+weight;if(weight<0){throw new Error("dijkstra does not allow negative edge weights. "+"Bad edge: "+edge+" Weight: "+weight)}if(distance<wEntry.distance){wEntry.distance=distance;wEntry.predecessor=v;pq.decrease(w,distance)}};g.nodes().forEach(function(v){var distance=v===source?0:Number.POSITIVE_INFINITY;results[v]={distance:distance};pq.add(v,distance)});while(pq.size()>0){v=pq.removeMin();vEntry=results[v];if(vEntry.distance===Number.POSITIVE_INFINITY){break}edgeFn(v).forEach(updateNeighbors)}return results}},{"../data/priority-queue":15}],6:[function(require,module,exports){var tarjan=require("./tarjan");module.exports=findCycles;function findCycles(g){return tarjan(g).filter(function(cmpt){return cmpt.length>1||cmpt.length===1&&g.hasEdge(cmpt[0],cmpt[0])})}},{"./tarjan":13}],7:[function(require,module,exports){module.exports=floydWarshall;var DEFAULT_WEIGHT_FUNC=()=>1;function floydWarshall(g,weightFn,edgeFn){return runFloydWarshall(g,weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runFloydWarshall(g,weightFn,edgeFn){var results={};var nodes=g.nodes();nodes.forEach(function(v){results[v]={};results[v][v]={distance:0};nodes.forEach(function(w){if(v!==w){results[v][w]={distance:Number.POSITIVE_INFINITY}}});edgeFn(v).forEach(function(edge){var w=edge.v===v?edge.w:edge.v;var d=weightFn(edge);results[v][w]={distance:d,predecessor:v}})});nodes.forEach(function(k){var rowK=results[k];nodes.forEach(function(i){var rowI=results[i];nodes.forEach(function(j){var ik=rowI[k];var kj=rowK[j];var ij=rowI[j];var altDistance=ik.distance+kj.distance;if(altDistance<ij.distance){ij.distance=altDistance;ij.predecessor=kj.predecessor}})})});return results}},{}],8:[function(require,module,exports){module.exports={components:require("./components"),dijkstra:require("./dijkstra"),dijkstraAll:require("./dijkstra-all"),findCycles:require("./find-cycles"),floydWarshall:require("./floyd-warshall"),isAcyclic:require("./is-acyclic"),postorder:require("./postorder"),preorder:require("./preorder"),prim:require("./prim"),tarjan:require("./tarjan"),topsort:require("./topsort")}},{"./components":2,"./dijkstra":5,"./dijkstra-all":4,"./find-cycles":6,"./floyd-warshall":7,"./is-acyclic":9,"./postorder":10,"./preorder":11,"./prim":12,"./tarjan":13,"./topsort":14}],9:[function(require,module,exports){var topsort=require("./topsort");module.exports=isAcyclic;function isAcyclic(g){try{topsort(g)}catch(e){if(e instanceof topsort.CycleException){return false}throw e}return true}},{"./topsort":14}],10:[function(require,module,exports){var dfs=require("./dfs");module.exports=postorder;function postorder(g,vs){return dfs(g,vs,"post")}},{"./dfs":3}],11:[function(require,module,exports){var dfs=require("./dfs");module.exports=preorder;function preorder(g,vs){return dfs(g,vs,"pre")}},{"./dfs":3}],12:[function(require,module,exports){var Graph=require("../graph");var PriorityQueue=require("../data/priority-queue");module.exports=prim;function prim(g,weightFunc){var result=new Graph;var parents={};var pq=new PriorityQueue;var v;function updateNeighbors(edge){var w=edge.v===v?edge.w:edge.v;var pri=pq.priority(w);if(pri!==undefined){var edgeWeight=weightFunc(edge);if(edgeWeight<pri){parents[w]=v;pq.decrease(w,edgeWeight)}}}if(g.nodeCount()===0){return result}g.nodes().forEach(function(v){pq.add(v,Number.POSITIVE_INFINITY);result.setNode(v)}); | ||
// Start from an arbitrary node | ||
pq.decrease(g.nodes()[0],0);var init=false;while(pq.size()>0){v=pq.removeMin();if(parents.hasOwnProperty(v)){result.setEdge(v,parents[v])}else if(init){throw new Error("Input graph is not connected: "+g)}else{init=true}g.nodeEdges(v).forEach(updateNeighbors)}return result}},{"../data/priority-queue":15,"../graph":16}],13:[function(require,module,exports){module.exports=tarjan;function tarjan(g){var index=0;var stack=[];var visited={};// node id -> { onStack, lowlink, index } | ||
var results=[];function dfs(v){var entry=visited[v]={onStack:true,lowlink:index,index:index++};stack.push(v);g.successors(v).forEach(function(w){if(!visited.hasOwnProperty(w)){dfs(w);entry.lowlink=Math.min(entry.lowlink,visited[w].lowlink)}else if(visited[w].onStack){entry.lowlink=Math.min(entry.lowlink,visited[w].index)}});if(entry.lowlink===entry.index){var cmpt=[];var w;do{w=stack.pop();visited[w].onStack=false;cmpt.push(w)}while(v!==w);results.push(cmpt)}}g.nodes().forEach(function(v){if(!visited.hasOwnProperty(v)){dfs(v)}});return results}},{}],14:[function(require,module,exports){module.exports=topsort;topsort.CycleException=CycleException;function topsort(g){var visited={};var stack={};var results=[];function visit(node){if(stack.hasOwnProperty(node)){throw new CycleException}if(!visited.hasOwnProperty(node)){stack[node]=true;visited[node]=true;g.predecessors(node).forEach(visit);delete stack[node];results.push(node)}}g.sinks().forEach(visit);if(Object.keys(visited).length!==g.nodeCount()){throw new CycleException}return results}function CycleException(){}CycleException.prototype=new Error;// must be an instance of Error to pass testing | ||
},{}],15:[function(require,module,exports){module.exports=PriorityQueue; | ||
var results=[];function dfs(v){var entry=visited[v]={onStack:true,lowlink:index,index:index++};stack.push(v);g.successors(v).forEach(function(w){if(!visited.hasOwnProperty(w)){dfs(w);entry.lowlink=Math.min(entry.lowlink,visited[w].lowlink)}else if(visited[w].onStack){entry.lowlink=Math.min(entry.lowlink,visited[w].index)}});if(entry.lowlink===entry.index){var cmpt=[];var w;do{w=stack.pop();visited[w].onStack=false;cmpt.push(w)}while(v!==w);results.push(cmpt)}}g.nodes().forEach(function(v){if(!visited.hasOwnProperty(v)){dfs(v)}});return results}},{}],14:[function(require,module,exports){function topsort(g){var visited={};var stack={};var results=[];function visit(node){if(stack.hasOwnProperty(node)){throw new CycleException}if(!visited.hasOwnProperty(node)){stack[node]=true;visited[node]=true;g.predecessors(node).forEach(visit);delete stack[node];results.push(node)}}g.sinks().forEach(visit);if(Object.keys(visited).length!==g.nodeCount()){throw new CycleException}return results}class CycleException extends Error{constructor(){super(...arguments)}}module.exports=topsort;topsort.CycleException=CycleException},{}],15:[function(require,module,exports){ | ||
/** | ||
@@ -51,40 +50,41 @@ * A min-priority queue data structure. This algorithm is derived from Cormen, | ||
* have its priority decreased in O(log n) time. | ||
*/function PriorityQueue(){this._arr=[];this._keyIndices={}} | ||
*/ | ||
class PriorityQueue{#arr=[];#keyIndices={}; | ||
/** | ||
* Returns the number of elements in the queue. Takes `O(1)` time. | ||
*/PriorityQueue.prototype.size=function(){return this._arr.length}; | ||
* Returns the number of elements in the queue. Takes `O(1)` time. | ||
*/size(){return this.#arr.length} | ||
/** | ||
* Returns the keys that are in the queue. Takes `O(n)` time. | ||
*/PriorityQueue.prototype.keys=function(){return this._arr.map(function(x){return x.key})}; | ||
* Returns the keys that are in the queue. Takes `O(n)` time. | ||
*/keys(){return this.#arr.map(function(x){return x.key})} | ||
/** | ||
* Returns `true` if **key** is in the queue and `false` if not. | ||
*/PriorityQueue.prototype.has=function(key){return this._keyIndices.hasOwnProperty(key)}; | ||
* Returns `true` if **key** is in the queue and `false` if not. | ||
*/has(key){return this.#keyIndices.hasOwnProperty(key)} | ||
/** | ||
* Returns the priority for **key**. If **key** is not present in the queue | ||
* then this function returns `undefined`. Takes `O(1)` time. | ||
* | ||
* @param {Object} key | ||
*/PriorityQueue.prototype.priority=function(key){var index=this._keyIndices[key];if(index!==undefined){return this._arr[index].priority}}; | ||
* Returns the priority for **key**. If **key** is not present in the queue | ||
* then this function returns `undefined`. Takes `O(1)` time. | ||
* | ||
* @param {Object} key | ||
*/priority(key){var index=this.#keyIndices[key];if(index!==undefined){return this.#arr[index].priority}} | ||
/** | ||
* Returns the key for the minimum element in this queue. If the queue is | ||
* empty this function throws an Error. Takes `O(1)` time. | ||
*/PriorityQueue.prototype.min=function(){if(this.size()===0){throw new Error("Queue underflow")}return this._arr[0].key}; | ||
* Returns the key for the minimum element in this queue. If the queue is | ||
* empty this function throws an Error. Takes `O(1)` time. | ||
*/min(){if(this.size()===0){throw new Error("Queue underflow")}return this.#arr[0].key} | ||
/** | ||
* Inserts a new key into the priority queue. If the key already exists in | ||
* the queue this function returns `false`; otherwise it will return `true`. | ||
* Takes `O(n)` time. | ||
* | ||
* @param {Object} key the key to add | ||
* @param {Number} priority the initial priority for the key | ||
*/PriorityQueue.prototype.add=function(key,priority){var keyIndices=this._keyIndices;key=String(key);if(!keyIndices.hasOwnProperty(key)){var arr=this._arr;var index=arr.length;keyIndices[key]=index;arr.push({key:key,priority:priority});this._decrease(index);return true}return false}; | ||
* Inserts a new key into the priority queue. If the key already exists in | ||
* the queue this function returns `false`; otherwise it will return `true`. | ||
* Takes `O(n)` time. | ||
* | ||
* @param {Object} key the key to add | ||
* @param {Number} priority the initial priority for the key | ||
*/add(key,priority){var keyIndices=this.#keyIndices;key=String(key);if(!keyIndices.hasOwnProperty(key)){var arr=this.#arr;var index=arr.length;keyIndices[key]=index;arr.push({key:key,priority:priority});this.#decrease(index);return true}return false} | ||
/** | ||
* Removes and returns the smallest key in the queue. Takes `O(log n)` time. | ||
*/PriorityQueue.prototype.removeMin=function(){this._swap(0,this._arr.length-1);var min=this._arr.pop();delete this._keyIndices[min.key];this._heapify(0);return min.key}; | ||
* Removes and returns the smallest key in the queue. Takes `O(log n)` time. | ||
*/removeMin(){this.#swap(0,this.#arr.length-1);var min=this.#arr.pop();delete this.#keyIndices[min.key];this.#heapify(0);return min.key} | ||
/** | ||
* Decreases the priority for **key** to **priority**. If the new priority is | ||
* greater than the previous priority, this function will throw an Error. | ||
* | ||
* @param {Object} key the key for which to raise priority | ||
* @param {Number} priority the new priority for the key | ||
*/PriorityQueue.prototype.decrease=function(key,priority){var index=this._keyIndices[key];if(priority>this._arr[index].priority){throw new Error("New priority is greater than current priority. "+"Key: "+key+" Old: "+this._arr[index].priority+" New: "+priority)}this._arr[index].priority=priority;this._decrease(index)};PriorityQueue.prototype._heapify=function(i){var arr=this._arr;var l=2*i;var r=l+1;var largest=i;if(l<arr.length){largest=arr[l].priority<arr[largest].priority?l:largest;if(r<arr.length){largest=arr[r].priority<arr[largest].priority?r:largest}if(largest!==i){this._swap(i,largest);this._heapify(largest)}}};PriorityQueue.prototype._decrease=function(index){var arr=this._arr;var priority=arr[index].priority;var parent;while(index!==0){parent=index>>1;if(arr[parent].priority<priority){break}this._swap(index,parent);index=parent}};PriorityQueue.prototype._swap=function(i,j){var arr=this._arr;var keyIndices=this._keyIndices;var origArrI=arr[i];var origArrJ=arr[j];arr[i]=origArrJ;arr[j]=origArrI;keyIndices[origArrJ.key]=i;keyIndices[origArrI.key]=j}},{}],16:[function(require,module,exports){"use strict";module.exports=Graph;var DEFAULT_EDGE_NAME="\0";var GRAPH_NODE="\0";var EDGE_KEY_DELIM=""; | ||
* Decreases the priority for **key** to **priority**. If the new priority is | ||
* greater than the previous priority, this function will throw an Error. | ||
* | ||
* @param {Object} key the key for which to raise priority | ||
* @param {Number} priority the new priority for the key | ||
*/decrease(key,priority){var index=this.#keyIndices[key];if(priority>this.#arr[index].priority){throw new Error("New priority is greater than current priority. "+"Key: "+key+" Old: "+this.#arr[index].priority+" New: "+priority)}this.#arr[index].priority=priority;this.#decrease(index)}#heapify(i){var arr=this.#arr;var l=2*i;var r=l+1;var largest=i;if(l<arr.length){largest=arr[l].priority<arr[largest].priority?l:largest;if(r<arr.length){largest=arr[r].priority<arr[largest].priority?r:largest}if(largest!==i){this.#swap(i,largest);this.#heapify(largest)}}}#decrease(index){var arr=this.#arr;var priority=arr[index].priority;var parent;while(index!==0){parent=index>>1;if(arr[parent].priority<priority){break}this.#swap(index,parent);index=parent}}#swap(i,j){var arr=this.#arr;var keyIndices=this.#keyIndices;var origArrI=arr[i];var origArrJ=arr[j];arr[i]=origArrJ;arr[j]=origArrI;keyIndices[origArrJ.key]=i;keyIndices[origArrI.key]=j}}module.exports=PriorityQueue},{}],16:[function(require,module,exports){"use strict";var DEFAULT_EDGE_NAME="\0";var GRAPH_NODE="\0";var EDGE_KEY_DELIM=""; | ||
// Implementation notes: | ||
@@ -99,190 +99,190 @@ // | ||
// we're going to get to a performant hashtable in JavaScript. | ||
function Graph(opts){this._isDirected=true;this._isMultigraph=false;this._isCompound=false;if(opts){this._isDirected=opts.hasOwnProperty("directed")?opts.directed:true;this._isMultigraph=opts.hasOwnProperty("multigraph")?opts.multigraph:false;this._isCompound=opts.hasOwnProperty("compound")?opts.compound:false} | ||
class Graph{#isDirected=true;#isMultigraph=false;#isCompound=false; | ||
// Label for the graph itself | ||
this._label=undefined; | ||
#label; | ||
// Defaults to be set when creating a new node | ||
this._defaultNodeLabelFn=()=>undefined; | ||
#defaultNodeLabelFn=()=>undefined; | ||
// Defaults to be set when creating a new edge | ||
this._defaultEdgeLabelFn=()=>undefined; | ||
#defaultEdgeLabelFn=()=>undefined; | ||
// v -> label | ||
this._nodes={};if(this._isCompound){ | ||
// v -> parent | ||
this._parent={}; | ||
// v -> children | ||
this._children={};this._children[GRAPH_NODE]={}} | ||
#nodes={}; | ||
// v -> edgeObj | ||
this._in={}; | ||
#in={}; | ||
// u -> v -> Number | ||
this._preds={}; | ||
#preds={}; | ||
// v -> edgeObj | ||
this._out={}; | ||
#out={}; | ||
// v -> w -> Number | ||
this._sucs={}; | ||
#sucs={}; | ||
// e -> edgeObj | ||
this._edgeObjs={}; | ||
#edgeObjs={}; | ||
// e -> label | ||
this._edgeLabels={}} | ||
/* Number of nodes in the graph. Should only be changed by the implementation. */Graph.prototype._nodeCount=0; | ||
/* Number of edges in the graph. Should only be changed by the implementation. */Graph.prototype._edgeCount=0; | ||
#edgeLabels={}; | ||
/* Number of nodes in the graph. Should only be changed by the implementation. */#nodeCount=0; | ||
/* Number of edges in the graph. Should only be changed by the implementation. */#edgeCount=0;#parent;#children;constructor(opts){if(opts){this.#isDirected=opts.hasOwnProperty("directed")?opts.directed:true;this.#isMultigraph=opts.hasOwnProperty("multigraph")?opts.multigraph:false;this.#isCompound=opts.hasOwnProperty("compound")?opts.compound:false}if(this.#isCompound){ | ||
// v -> parent | ||
this.#parent={}; | ||
// v -> children | ||
this.#children={};this.#children[GRAPH_NODE]={}}} | ||
/* === Graph functions ========= */ | ||
/** | ||
* Whether graph was created with 'directed' flag set to true or not. | ||
*/Graph.prototype.isDirected=function(){return this._isDirected}; | ||
* Whether graph was created with 'directed' flag set to true or not. | ||
*/isDirected(){return this.#isDirected} | ||
/** | ||
* Whether graph was created with 'multigraph' flag set to true or not. | ||
*/Graph.prototype.isMultigraph=function(){return this._isMultigraph}; | ||
* Whether graph was created with 'multigraph' flag set to true or not. | ||
*/isMultigraph(){return this.#isMultigraph} | ||
/** | ||
* Whether graph was created with 'compound' flag set to true or not. | ||
*/Graph.prototype.isCompound=function(){return this._isCompound}; | ||
* Whether graph was created with 'compound' flag set to true or not. | ||
*/isCompound(){return this.#isCompound} | ||
/** | ||
* Sets the label of the graph. | ||
*/Graph.prototype.setGraph=function(label){this._label=label;return this}; | ||
* Sets the label of the graph. | ||
*/setGraph(label){this.#label=label;return this} | ||
/** | ||
* Gets the graph label. | ||
*/Graph.prototype.graph=function(){return this._label}; | ||
* Gets the graph label. | ||
*/graph(){return this.#label} | ||
/* === Node functions ========== */ | ||
/** | ||
* Sets the default node label. If newDefault is a function, it will be | ||
* invoked ach time when setting a label for a node. Otherwise, this label | ||
* will be assigned as default label in case if no label was specified while | ||
* setting a node. | ||
* Complexity: O(1). | ||
*/Graph.prototype.setDefaultNodeLabel=function(newDefault){this._defaultNodeLabelFn=newDefault;if(typeof newDefault!=="function"){this._defaultNodeLabelFn=()=>newDefault}return this}; | ||
* Sets the default node label. If newDefault is a function, it will be | ||
* invoked ach time when setting a label for a node. Otherwise, this label | ||
* will be assigned as default label in case if no label was specified while | ||
* setting a node. | ||
* Complexity: O(1). | ||
*/setDefaultNodeLabel(newDefault){this.#defaultNodeLabelFn=newDefault;if(typeof newDefault!=="function"){this.#defaultNodeLabelFn=()=>newDefault}return this} | ||
/** | ||
* Gets the number of nodes in the graph. | ||
* Complexity: O(1). | ||
*/Graph.prototype.nodeCount=function(){return this._nodeCount}; | ||
* Gets the number of nodes in the graph. | ||
* Complexity: O(1). | ||
*/nodeCount(){return this.#nodeCount} | ||
/** | ||
* Gets all nodes of the graph. Note, the in case of compound graph subnodes are | ||
* not included in list. | ||
* Complexity: O(1). | ||
*/Graph.prototype.nodes=function(){return Object.keys(this._nodes)}; | ||
* Gets all nodes of the graph. Note, the in case of compound graph subnodes are | ||
* not included in list. | ||
* Complexity: O(1). | ||
*/nodes(){return Object.keys(this.#nodes)} | ||
/** | ||
* Gets list of nodes without in-edges. | ||
* Complexity: O(|V|). | ||
*/Graph.prototype.sources=function(){var self=this;return this.nodes().filter(v=>Object.keys(self._in[v]).length===0)}; | ||
* Gets list of nodes without in-edges. | ||
* Complexity: O(|V|). | ||
*/sources(){var self=this;return this.nodes().filter(v=>Object.keys(self.#in[v]).length===0)} | ||
/** | ||
* Gets list of nodes without out-edges. | ||
* Complexity: O(|V|). | ||
*/Graph.prototype.sinks=function(){var self=this;return this.nodes().filter(v=>Object.keys(self._out[v]).length===0)}; | ||
* Gets list of nodes without out-edges. | ||
* Complexity: O(|V|). | ||
*/sinks(){var self=this;return this.nodes().filter(v=>Object.keys(self.#out[v]).length===0)} | ||
/** | ||
* Invokes setNode method for each node in names list. | ||
* Complexity: O(|names|). | ||
*/Graph.prototype.setNodes=function(vs,value){var args=arguments;var self=this;vs.forEach(function(v){if(args.length>1){self.setNode(v,value)}else{self.setNode(v)}});return this}; | ||
* Invokes setNode method for each node in names list. | ||
* Complexity: O(|names|). | ||
*/setNodes(vs,value){var args=arguments;var self=this;vs.forEach(function(v){if(args.length>1){self.setNode(v,value)}else{self.setNode(v)}});return this} | ||
/** | ||
* Creates or updates the value for the node v in the graph. If label is supplied | ||
* it is set as the value for the node. If label is not supplied and the node was | ||
* created by this call then the default node label will be assigned. | ||
* Complexity: O(1). | ||
*/Graph.prototype.setNode=function(v,value){if(this._nodes.hasOwnProperty(v)){if(arguments.length>1){this._nodes[v]=value}return this}this._nodes[v]=arguments.length>1?value:this._defaultNodeLabelFn(v);if(this._isCompound){this._parent[v]=GRAPH_NODE;this._children[v]={};this._children[GRAPH_NODE][v]=true}this._in[v]={};this._preds[v]={};this._out[v]={};this._sucs[v]={};++this._nodeCount;return this}; | ||
* Creates or updates the value for the node v in the graph. If label is supplied | ||
* it is set as the value for the node. If label is not supplied and the node was | ||
* created by this call then the default node label will be assigned. | ||
* Complexity: O(1). | ||
*/setNode(v,value){if(this.#nodes.hasOwnProperty(v)){if(arguments.length>1){this.#nodes[v]=value}return this}this.#nodes[v]=arguments.length>1?value:this.#defaultNodeLabelFn(v);if(this.#isCompound){this.#parent[v]=GRAPH_NODE;this.#children[v]={};this.#children[GRAPH_NODE][v]=true}this.#in[v]={};this.#preds[v]={};this.#out[v]={};this.#sucs[v]={};++this.#nodeCount;return this} | ||
/** | ||
* Gets the label of node with specified name. | ||
* Complexity: O(|V|). | ||
*/Graph.prototype.node=function(v){return this._nodes[v]}; | ||
* Gets the label of node with specified name. | ||
* Complexity: O(|V|). | ||
*/node(v){return this.#nodes[v]} | ||
/** | ||
* Detects whether graph has a node with specified name or not. | ||
*/Graph.prototype.hasNode=function(v){return this._nodes.hasOwnProperty(v)}; | ||
* Detects whether graph has a node with specified name or not. | ||
*/hasNode(v){return this.#nodes.hasOwnProperty(v)} | ||
/** | ||
* Remove the node with the name from the graph or do nothing if the node is not in | ||
* the graph. If the node was removed this function also removes any incident | ||
* edges. | ||
* Complexity: O(1). | ||
*/Graph.prototype.removeNode=function(v){var self=this;if(this._nodes.hasOwnProperty(v)){var removeEdge=e=>self.removeEdge(self._edgeObjs[e]);delete this._nodes[v];if(this._isCompound){this._removeFromParentsChildList(v);delete this._parent[v];this.children(v).forEach(function(child){self.setParent(child)});delete this._children[v]}Object.keys(this._in[v]).forEach(removeEdge);delete this._in[v];delete this._preds[v];Object.keys(this._out[v]).forEach(removeEdge);delete this._out[v];delete this._sucs[v];--this._nodeCount}return this}; | ||
* Remove the node with the name from the graph or do nothing if the node is not in | ||
* the graph. If the node was removed this function also removes any incident | ||
* edges. | ||
* Complexity: O(1). | ||
*/removeNode(v){var self=this;if(this.#nodes.hasOwnProperty(v)){var removeEdge=e=>self.removeEdge(self.#edgeObjs[e]);delete this.#nodes[v];if(this.#isCompound){this.#removeFromParentsChildList(v);delete this.#parent[v];this.children(v).forEach(function(child){self.setParent(child)});delete this.#children[v]}Object.keys(this.#in[v]).forEach(removeEdge);delete this.#in[v];delete this.#preds[v];Object.keys(this.#out[v]).forEach(removeEdge);delete this.#out[v];delete this.#sucs[v];--this.#nodeCount}return this} | ||
/** | ||
* Sets node p as a parent for node v if it is defined, or removes the | ||
* parent for v if p is undefined. Method throws an exception in case of | ||
* invoking it in context of noncompound graph. | ||
* Average-case complexity: O(1). | ||
*/Graph.prototype.setParent=function(v,parent){if(!this._isCompound){throw new Error("Cannot set parent in a non-compound graph")}if(parent===undefined){parent=GRAPH_NODE}else{ | ||
* Sets node p as a parent for node v if it is defined, or removes the | ||
* parent for v if p is undefined. Method throws an exception in case of | ||
* invoking it in context of noncompound graph. | ||
* Average-case complexity: O(1). | ||
*/setParent(v,parent){if(!this.#isCompound){throw new Error("Cannot set parent in a non-compound graph")}if(parent===undefined){parent=GRAPH_NODE}else{ | ||
// Coerce parent to string | ||
parent+="";for(var ancestor=parent;ancestor!==undefined;ancestor=this.parent(ancestor)){if(ancestor===v){throw new Error("Setting "+parent+" as parent of "+v+" would create a cycle")}}this.setNode(parent)}this.setNode(v);this._removeFromParentsChildList(v);this._parent[v]=parent;this._children[parent][v]=true;return this};Graph.prototype._removeFromParentsChildList=function(v){delete this._children[this._parent[v]][v]}; | ||
parent+="";for(var ancestor=parent;ancestor!==undefined;ancestor=this.parent(ancestor)){if(ancestor===v){throw new Error("Setting "+parent+" as parent of "+v+" would create a cycle")}}this.setNode(parent)}this.setNode(v);this.#removeFromParentsChildList(v);this.#parent[v]=parent;this.#children[parent][v]=true;return this}#removeFromParentsChildList(v){delete this.#children[this.#parent[v]][v]} | ||
/** | ||
* Gets parent node for node v. | ||
* Complexity: O(1). | ||
*/Graph.prototype.parent=function(v){if(this._isCompound){var parent=this._parent[v];if(parent!==GRAPH_NODE){return parent}}}; | ||
* Gets parent node for node v. | ||
* Complexity: O(1). | ||
*/parent(v){if(this.#isCompound){var parent=this.#parent[v];if(parent!==GRAPH_NODE){return parent}}} | ||
/** | ||
* Gets list of direct children of node v. | ||
* Complexity: O(1). | ||
*/Graph.prototype.children=function(v=GRAPH_NODE){if(this._isCompound){var children=this._children[v];if(children){return Object.keys(children)}}else if(v===GRAPH_NODE){return this.nodes()}else if(this.hasNode(v)){return[]}}; | ||
* Gets list of direct children of node v. | ||
* Complexity: O(1). | ||
*/children(v=GRAPH_NODE){if(this.#isCompound){var children=this.#children[v];if(children){return Object.keys(children)}}else if(v===GRAPH_NODE){return this.nodes()}else if(this.hasNode(v)){return[]}} | ||
/** | ||
* Return all nodes that are predecessors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/Graph.prototype.predecessors=function(v){var predsV=this._preds[v];if(predsV){return Object.keys(predsV)}}; | ||
* Return all nodes that are predecessors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/predecessors(v){var predsV=this.#preds[v];if(predsV){return Object.keys(predsV)}} | ||
/** | ||
* Return all nodes that are successors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/Graph.prototype.successors=function(v){var sucsV=this._sucs[v];if(sucsV){return Object.keys(sucsV)}}; | ||
* Return all nodes that are successors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/successors(v){var sucsV=this.#sucs[v];if(sucsV){return Object.keys(sucsV)}} | ||
/** | ||
* Return all nodes that are predecessors or successors of the specified node or undefined if | ||
* node v is not in the graph. | ||
* Complexity: O(|V|). | ||
*/Graph.prototype.neighbors=function(v){var preds=this.predecessors(v);if(preds){const union=new Set(preds);for(var succ of this.successors(v)){union.add(succ)}return Array.from(union.values())}};Graph.prototype.isLeaf=function(v){var neighbors;if(this.isDirected()){neighbors=this.successors(v)}else{neighbors=this.neighbors(v)}return neighbors.length===0}; | ||
* Return all nodes that are predecessors or successors of the specified node or undefined if | ||
* node v is not in the graph. | ||
* Complexity: O(|V|). | ||
*/neighbors(v){var preds=this.predecessors(v);if(preds){const union=new Set(preds);for(var succ of this.successors(v)){union.add(succ)}return Array.from(union.values())}}isLeaf(v){var neighbors;if(this.isDirected()){neighbors=this.successors(v)}else{neighbors=this.neighbors(v)}return neighbors.length===0} | ||
/** | ||
* Creates new graph with nodes filtered via filter. Edges incident to rejected node | ||
* are also removed. In case of compound graph, if parent is rejected by filter, | ||
* than all its children are rejected too. | ||
* Average-case complexity: O(|E|+|V|). | ||
*/Graph.prototype.filterNodes=function(filter){var copy=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});copy.setGraph(this.graph());var self=this;Object.entries(this._nodes).forEach(function([v,value]){if(filter(v)){copy.setNode(v,value)}});Object.values(this._edgeObjs).forEach(function(e){if(copy.hasNode(e.v)&©.hasNode(e.w)){copy.setEdge(e,self.edge(e))}});var parents={};function findParent(v){var parent=self.parent(v);if(parent===undefined||copy.hasNode(parent)){parents[v]=parent;return parent}else if(parent in parents){return parents[parent]}else{return findParent(parent)}}if(this._isCompound){copy.nodes().forEach(v=>copy.setParent(v,findParent(v)))}return copy}; | ||
* Creates new graph with nodes filtered via filter. Edges incident to rejected node | ||
* are also removed. In case of compound graph, if parent is rejected by filter, | ||
* than all its children are rejected too. | ||
* Average-case complexity: O(|E|+|V|). | ||
*/filterNodes(filter){var copy=new this.constructor({directed:this.#isDirected,multigraph:this.#isMultigraph,compound:this.#isCompound});copy.setGraph(this.graph());var self=this;Object.entries(this.#nodes).forEach(function([v,value]){if(filter(v)){copy.setNode(v,value)}});Object.values(this.#edgeObjs).forEach(function(e){if(copy.hasNode(e.v)&©.hasNode(e.w)){copy.setEdge(e,self.edge(e))}});var parents={};function findParent(v){var parent=self.parent(v);if(parent===undefined||copy.hasNode(parent)){parents[v]=parent;return parent}else if(parent in parents){return parents[parent]}else{return findParent(parent)}}if(this.#isCompound){copy.nodes().forEach(v=>copy.setParent(v,findParent(v)))}return copy} | ||
/* === Edge functions ========== */ | ||
/** | ||
* Sets the default edge label or factory function. This label will be | ||
* assigned as default label in case if no label was specified while setting | ||
* an edge or this function will be invoked each time when setting an edge | ||
* with no label specified and returned value * will be used as a label for edge. | ||
* Complexity: O(1). | ||
*/Graph.prototype.setDefaultEdgeLabel=function(newDefault){this._defaultEdgeLabelFn=newDefault;if(typeof newDefault!=="function"){this._defaultEdgeLabelFn=()=>newDefault}return this}; | ||
* Sets the default edge label or factory function. This label will be | ||
* assigned as default label in case if no label was specified while setting | ||
* an edge or this function will be invoked each time when setting an edge | ||
* with no label specified and returned value * will be used as a label for edge. | ||
* Complexity: O(1). | ||
*/setDefaultEdgeLabel(newDefault){this.#defaultEdgeLabelFn=newDefault;if(typeof newDefault!=="function"){this.#defaultEdgeLabelFn=()=>newDefault}return this} | ||
/** | ||
* Gets the number of edges in the graph. | ||
* Complexity: O(1). | ||
*/Graph.prototype.edgeCount=function(){return this._edgeCount}; | ||
* Gets the number of edges in the graph. | ||
* Complexity: O(1). | ||
*/edgeCount(){return this.#edgeCount} | ||
/** | ||
* Gets edges of the graph. In case of compound graph subgraphs are not considered. | ||
* Complexity: O(|E|). | ||
*/Graph.prototype.edges=function(){return Object.values(this._edgeObjs)}; | ||
* Gets edges of the graph. In case of compound graph subgraphs are not considered. | ||
* Complexity: O(|E|). | ||
*/edges(){return Object.values(this.#edgeObjs)} | ||
/** | ||
* Establish an edges path over the nodes in nodes list. If some edge is already | ||
* exists, it will update its label, otherwise it will create an edge between pair | ||
* of nodes with label provided or default label if no label provided. | ||
* Complexity: O(|nodes|). | ||
*/Graph.prototype.setPath=function(vs,value){var self=this;var args=arguments;vs.reduce(function(v,w){if(args.length>1){self.setEdge(v,w,value)}else{self.setEdge(v,w)}return w});return this}; | ||
* Establish an edges path over the nodes in nodes list. If some edge is already | ||
* exists, it will update its label, otherwise it will create an edge between pair | ||
* of nodes with label provided or default label if no label provided. | ||
* Complexity: O(|nodes|). | ||
*/setPath(vs,value){var self=this;var args=arguments;vs.reduce(function(v,w){if(args.length>1){self.setEdge(v,w,value)}else{self.setEdge(v,w)}return w});return this} | ||
/** | ||
* Creates or updates the label for the edge (v, w) with the optionally supplied | ||
* name. If label is supplied it is set as the value for the edge. If label is not | ||
* supplied and the edge was created by this call then the default edge label will | ||
* be assigned. The name parameter is only useful with multigraphs. | ||
*/Graph.prototype.setEdge=function(){var v,w,name,value;var valueSpecified=false;var arg0=arguments[0];if(typeof arg0==="object"&&arg0!==null&&"v"in arg0){v=arg0.v;w=arg0.w;name=arg0.name;if(arguments.length===2){value=arguments[1];valueSpecified=true}}else{v=arg0;w=arguments[1];name=arguments[3];if(arguments.length>2){value=arguments[2];valueSpecified=true}}v=""+v;w=""+w;if(name!==undefined){name=""+name}var e=edgeArgsToId(this._isDirected,v,w,name);if(this._edgeLabels.hasOwnProperty(e)){if(valueSpecified){this._edgeLabels[e]=value}return this}if(name!==undefined&&!this._isMultigraph){throw new Error("Cannot set a named edge when isMultigraph = false")} | ||
* Creates or updates the label for the edge (v, w) with the optionally supplied | ||
* name. If label is supplied it is set as the value for the edge. If label is not | ||
* supplied and the edge was created by this call then the default edge label will | ||
* be assigned. The name parameter is only useful with multigraphs. | ||
*/setEdge(){var v,w,name,value;var valueSpecified=false;var arg0=arguments[0];if(typeof arg0==="object"&&arg0!==null&&"v"in arg0){v=arg0.v;w=arg0.w;name=arg0.name;if(arguments.length===2){value=arguments[1];valueSpecified=true}}else{v=arg0;w=arguments[1];name=arguments[3];if(arguments.length>2){value=arguments[2];valueSpecified=true}}v=""+v;w=""+w;if(name!==undefined){name=""+name}var e=edgeArgsToId(this.#isDirected,v,w,name);if(this.#edgeLabels.hasOwnProperty(e)){if(valueSpecified){this.#edgeLabels[e]=value}return this}if(name!==undefined&&!this.#isMultigraph){throw new Error("Cannot set a named edge when isMultigraph = false")} | ||
// It didn't exist, so we need to create it. | ||
// First ensure the nodes exist. | ||
this.setNode(v);this.setNode(w);this._edgeLabels[e]=valueSpecified?value:this._defaultEdgeLabelFn(v,w,name);var edgeObj=edgeArgsToObj(this._isDirected,v,w,name); | ||
this.setNode(v);this.setNode(w);this.#edgeLabels[e]=valueSpecified?value:this.#defaultEdgeLabelFn(v,w,name);var edgeObj=edgeArgsToObj(this.#isDirected,v,w,name); | ||
// Ensure we add undirected edges in a consistent way. | ||
v=edgeObj.v;w=edgeObj.w;Object.freeze(edgeObj);this._edgeObjs[e]=edgeObj;incrementOrInitEntry(this._preds[w],v);incrementOrInitEntry(this._sucs[v],w);this._in[w][e]=edgeObj;this._out[v][e]=edgeObj;this._edgeCount++;return this}; | ||
v=edgeObj.v;w=edgeObj.w;Object.freeze(edgeObj);this.#edgeObjs[e]=edgeObj;incrementOrInitEntry(this.#preds[w],v);incrementOrInitEntry(this.#sucs[v],w);this.#in[w][e]=edgeObj;this.#out[v][e]=edgeObj;this.#edgeCount++;return this} | ||
/** | ||
* Gets the label for the specified edge. | ||
* Complexity: O(1). | ||
*/Graph.prototype.edge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);return this._edgeLabels[e]}; | ||
* Gets the label for the specified edge. | ||
* Complexity: O(1). | ||
*/edge(v,w,name){var e=arguments.length===1?edgeObjToId(this.#isDirected,arguments[0]):edgeArgsToId(this.#isDirected,v,w,name);return this.#edgeLabels[e]} | ||
/** | ||
* Detects whether the graph contains specified edge or not. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/Graph.prototype.hasEdge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);return this._edgeLabels.hasOwnProperty(e)}; | ||
* Detects whether the graph contains specified edge or not. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/hasEdge(v,w,name){var e=arguments.length===1?edgeObjToId(this.#isDirected,arguments[0]):edgeArgsToId(this.#isDirected,v,w,name);return this.#edgeLabels.hasOwnProperty(e)} | ||
/** | ||
* Removes the specified edge from the graph. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/Graph.prototype.removeEdge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);var edge=this._edgeObjs[e];if(edge){v=edge.v;w=edge.w;delete this._edgeLabels[e];delete this._edgeObjs[e];decrementOrRemoveEntry(this._preds[w],v);decrementOrRemoveEntry(this._sucs[v],w);delete this._in[w][e];delete this._out[v][e];this._edgeCount--}return this}; | ||
* Removes the specified edge from the graph. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/removeEdge(v,w,name){var e=arguments.length===1?edgeObjToId(this.#isDirected,arguments[0]):edgeArgsToId(this.#isDirected,v,w,name);var edge=this.#edgeObjs[e];if(edge){v=edge.v;w=edge.w;delete this.#edgeLabels[e];delete this.#edgeObjs[e];decrementOrRemoveEntry(this.#preds[w],v);decrementOrRemoveEntry(this.#sucs[v],w);delete this.#in[w][e];delete this.#out[v][e];this.#edgeCount--}return this} | ||
/** | ||
* Return all edges that point to the node v. Optionally filters those edges down to just those | ||
* coming from node u. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/Graph.prototype.inEdges=function(v,u){var inV=this._in[v];if(inV){var edges=Object.values(inV);if(!u){return edges}return edges.filter(edge=>edge.v===u)}}; | ||
* Return all edges that point to the node v. Optionally filters those edges down to just those | ||
* coming from node u. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/inEdges(v,u){var inV=this.#in[v];if(inV){var edges=Object.values(inV);if(!u){return edges}return edges.filter(edge=>edge.v===u)}} | ||
/** | ||
* Return all edges that are pointed at by node v. Optionally filters those edges down to just | ||
* those point to w. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/Graph.prototype.outEdges=function(v,w){var outV=this._out[v];if(outV){var edges=Object.values(outV);if(!w){return edges}return edges.filter(edge=>edge.w===w)}}; | ||
* Return all edges that are pointed at by node v. Optionally filters those edges down to just | ||
* those point to w. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/outEdges(v,w){var outV=this.#out[v];if(outV){var edges=Object.values(outV);if(!w){return edges}return edges.filter(edge=>edge.w===w)}} | ||
/** | ||
* Returns all edges to or from node v regardless of direction. Optionally filters those edges | ||
* down to just those between nodes v and w regardless of direction. | ||
* Complexity: O(|E|). | ||
*/Graph.prototype.nodeEdges=function(v,w){var inEdges=this.inEdges(v,w);if(inEdges){return inEdges.concat(this.outEdges(v,w))}};function incrementOrInitEntry(map,k){if(map[k]){map[k]++}else{map[k]=1}}function decrementOrRemoveEntry(map,k){if(!--map[k]){delete map[k]}}function edgeArgsToId(isDirected,v_,w_,name){var v=""+v_;var w=""+w_;if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}return v+EDGE_KEY_DELIM+w+EDGE_KEY_DELIM+(name===undefined?DEFAULT_EDGE_NAME:name)}function edgeArgsToObj(isDirected,v_,w_,name){var v=""+v_;var w=""+w_;if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}var edgeObj={v:v,w:w};if(name){edgeObj.name=name}return edgeObj}function edgeObjToId(isDirected,edgeObj){return edgeArgsToId(isDirected,edgeObj.v,edgeObj.w,edgeObj.name)}},{}],17:[function(require,module,exports){ | ||
* Returns all edges to or from node v regardless of direction. Optionally filters those edges | ||
* down to just those between nodes v and w regardless of direction. | ||
* Complexity: O(|E|). | ||
*/nodeEdges(v,w){var inEdges=this.inEdges(v,w);if(inEdges){return inEdges.concat(this.outEdges(v,w))}}}function incrementOrInitEntry(map,k){if(map[k]){map[k]++}else{map[k]=1}}function decrementOrRemoveEntry(map,k){if(!--map[k]){delete map[k]}}function edgeArgsToId(isDirected,v_,w_,name){var v=""+v_;var w=""+w_;if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}return v+EDGE_KEY_DELIM+w+EDGE_KEY_DELIM+(name===undefined?DEFAULT_EDGE_NAME:name)}function edgeArgsToObj(isDirected,v_,w_,name){var v=""+v_;var w=""+w_;if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}var edgeObj={v:v,w:w};if(name){edgeObj.name=name}return edgeObj}function edgeObjToId(isDirected,edgeObj){return edgeArgsToId(isDirected,edgeObj.v,edgeObj.w,edgeObj.name)}module.exports=Graph},{}],17:[function(require,module,exports){ | ||
// Includes only the "core" of graphlib | ||
@@ -303,2 +303,2 @@ module.exports={Graph:require("./graph"),version:require("./version")}},{"./graph":16,"./version":19}],18:[function(require,module,exports){var Graph=require("./graph");module.exports={write:write,read:read}; | ||
* // [ { v: 'a', w: 'b' } ] | ||
*/function read(json){var g=new Graph(json.options).setGraph(json.value);json.nodes.forEach(function(entry){g.setNode(entry.v,entry.value);if(entry.parent){g.setParent(entry.v,entry.parent)}});json.edges.forEach(function(entry){g.setEdge({v:entry.v,w:entry.w,name:entry.name},entry.value)});return g}},{"./graph":16}],19:[function(require,module,exports){module.exports="2.1.9"},{}]},{},[1])(1)}); | ||
*/function read(json){var g=new Graph(json.options).setGraph(json.value);json.nodes.forEach(function(entry){g.setNode(entry.v,entry.value);if(entry.parent){g.setParent(entry.v,entry.parent)}});json.edges.forEach(function(entry){g.setEdge({v:entry.v,w:entry.w,name:entry.name},entry.value)});return g}},{"./graph":16}],19:[function(require,module,exports){module.exports="2.1.11"},{}]},{},[1])(1)}); |
1333
dist/graphlib.js
@@ -77,3 +77,3 @@ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.graphlib = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ | ||
* | ||
* Order must be one of "pre" or "post". | ||
* If the order is not "post", it will be treated as "pre". | ||
*/ | ||
@@ -85,7 +85,8 @@ function dfs(g, vs, order) { | ||
var navigation = (g.isDirected() ? g.successors : g.neighbors).bind(g); | ||
var navigation = g.isDirected() ? v => g.successors(v) : v => g.neighbors(v); | ||
var orderFunc = order === "post" ? postOrderDfs : preOrderDfs; | ||
var acc = []; | ||
var visited = {}; | ||
vs.forEach(function(v) { | ||
vs.forEach(v => { | ||
if (!g.hasNode(v)) { | ||
@@ -95,19 +96,45 @@ throw new Error("Graph does not have node: " + v); | ||
doDfs(g, v, order === "post", visited, navigation, acc); | ||
orderFunc(v, navigation, visited, acc); | ||
}); | ||
return acc; | ||
} | ||
function doDfs(g, v, postorder, visited, navigation, acc) { | ||
if (!visited.hasOwnProperty(v)) { | ||
visited[v] = true; | ||
function postOrderDfs(v, navigation, visited, acc) { | ||
var stack = [[v, false]]; | ||
while (stack.length > 0) { | ||
var curr = stack.pop(); | ||
if (curr[1]) { | ||
acc.push(curr[0]); | ||
} else { | ||
if (!visited.hasOwnProperty(curr[0])) { | ||
visited[curr[0]] = true; | ||
stack.push([curr[0], true]); | ||
forEachRight(navigation(curr[0]), w => stack.push([w, false])); | ||
} | ||
} | ||
} | ||
} | ||
if (!postorder) { acc.push(v); } | ||
navigation(v).forEach(function(w) { | ||
doDfs(g, w, postorder, visited, navigation, acc); | ||
}); | ||
if (postorder) { acc.push(v); } | ||
function preOrderDfs(v, navigation, visited, acc) { | ||
var stack = [v]; | ||
while (stack.length > 0) { | ||
var curr = stack.pop(); | ||
if (!visited.hasOwnProperty(curr)) { | ||
visited[curr] = true; | ||
acc.push(curr); | ||
forEachRight(navigation(curr), w => stack.push(w)); | ||
} | ||
} | ||
} | ||
function forEachRight(array, iteratee) { | ||
var length = array.length; | ||
while (length--) { | ||
iteratee(array[length], length, array); | ||
} | ||
return array; | ||
} | ||
},{}],4:[function(require,module,exports){ | ||
@@ -392,5 +419,2 @@ var dijkstra = require("./dijkstra"); | ||
},{}],14:[function(require,module,exports){ | ||
module.exports = topsort; | ||
topsort.CycleException = CycleException; | ||
function topsort(g) { | ||
@@ -424,8 +448,12 @@ var visited = {}; | ||
function CycleException() {} | ||
CycleException.prototype = new Error(); // must be an instance of Error to pass testing | ||
class CycleException extends Error { | ||
constructor() { | ||
super(...arguments); | ||
} | ||
} | ||
module.exports = topsort; | ||
topsort.CycleException = CycleException; | ||
},{}],15:[function(require,module,exports){ | ||
module.exports = PriorityQueue; | ||
/** | ||
@@ -438,149 +466,149 @@ * A min-priority queue data structure. This algorithm is derived from Cormen, | ||
*/ | ||
function PriorityQueue() { | ||
this._arr = []; | ||
this._keyIndices = {}; | ||
} | ||
class PriorityQueue { | ||
#arr = []; | ||
#keyIndices = {}; | ||
/** | ||
* Returns the number of elements in the queue. Takes `O(1)` time. | ||
*/ | ||
PriorityQueue.prototype.size = function() { | ||
return this._arr.length; | ||
}; | ||
/** | ||
* Returns the number of elements in the queue. Takes `O(1)` time. | ||
*/ | ||
size() { | ||
return this.#arr.length; | ||
} | ||
/** | ||
* Returns the keys that are in the queue. Takes `O(n)` time. | ||
*/ | ||
PriorityQueue.prototype.keys = function() { | ||
return this._arr.map(function(x) { return x.key; }); | ||
}; | ||
/** | ||
* Returns the keys that are in the queue. Takes `O(n)` time. | ||
*/ | ||
keys() { | ||
return this.#arr.map(function(x) { return x.key; }); | ||
} | ||
/** | ||
* Returns `true` if **key** is in the queue and `false` if not. | ||
*/ | ||
PriorityQueue.prototype.has = function(key) { | ||
return this._keyIndices.hasOwnProperty(key); | ||
}; | ||
/** | ||
* Returns `true` if **key** is in the queue and `false` if not. | ||
*/ | ||
has(key) { | ||
return this.#keyIndices.hasOwnProperty(key); | ||
} | ||
/** | ||
* Returns the priority for **key**. If **key** is not present in the queue | ||
* then this function returns `undefined`. Takes `O(1)` time. | ||
* | ||
* @param {Object} key | ||
*/ | ||
PriorityQueue.prototype.priority = function(key) { | ||
var index = this._keyIndices[key]; | ||
if (index !== undefined) { | ||
return this._arr[index].priority; | ||
/** | ||
* Returns the priority for **key**. If **key** is not present in the queue | ||
* then this function returns `undefined`. Takes `O(1)` time. | ||
* | ||
* @param {Object} key | ||
*/ | ||
priority(key) { | ||
var index = this.#keyIndices[key]; | ||
if (index !== undefined) { | ||
return this.#arr[index].priority; | ||
} | ||
} | ||
}; | ||
/** | ||
* Returns the key for the minimum element in this queue. If the queue is | ||
* empty this function throws an Error. Takes `O(1)` time. | ||
*/ | ||
PriorityQueue.prototype.min = function() { | ||
if (this.size() === 0) { | ||
throw new Error("Queue underflow"); | ||
/** | ||
* Returns the key for the minimum element in this queue. If the queue is | ||
* empty this function throws an Error. Takes `O(1)` time. | ||
*/ | ||
min() { | ||
if (this.size() === 0) { | ||
throw new Error("Queue underflow"); | ||
} | ||
return this.#arr[0].key; | ||
} | ||
return this._arr[0].key; | ||
}; | ||
/** | ||
* Inserts a new key into the priority queue. If the key already exists in | ||
* the queue this function returns `false`; otherwise it will return `true`. | ||
* Takes `O(n)` time. | ||
* | ||
* @param {Object} key the key to add | ||
* @param {Number} priority the initial priority for the key | ||
*/ | ||
PriorityQueue.prototype.add = function(key, priority) { | ||
var keyIndices = this._keyIndices; | ||
key = String(key); | ||
if (!keyIndices.hasOwnProperty(key)) { | ||
var arr = this._arr; | ||
var index = arr.length; | ||
keyIndices[key] = index; | ||
arr.push({key: key, priority: priority}); | ||
this._decrease(index); | ||
return true; | ||
/** | ||
* Inserts a new key into the priority queue. If the key already exists in | ||
* the queue this function returns `false`; otherwise it will return `true`. | ||
* Takes `O(n)` time. | ||
* | ||
* @param {Object} key the key to add | ||
* @param {Number} priority the initial priority for the key | ||
*/ | ||
add(key, priority) { | ||
var keyIndices = this.#keyIndices; | ||
key = String(key); | ||
if (!keyIndices.hasOwnProperty(key)) { | ||
var arr = this.#arr; | ||
var index = arr.length; | ||
keyIndices[key] = index; | ||
arr.push({key: key, priority: priority}); | ||
this.#decrease(index); | ||
return true; | ||
} | ||
return false; | ||
} | ||
return false; | ||
}; | ||
/** | ||
* Removes and returns the smallest key in the queue. Takes `O(log n)` time. | ||
*/ | ||
PriorityQueue.prototype.removeMin = function() { | ||
this._swap(0, this._arr.length - 1); | ||
var min = this._arr.pop(); | ||
delete this._keyIndices[min.key]; | ||
this._heapify(0); | ||
return min.key; | ||
}; | ||
/** | ||
* Removes and returns the smallest key in the queue. Takes `O(log n)` time. | ||
*/ | ||
removeMin() { | ||
this.#swap(0, this.#arr.length - 1); | ||
var min = this.#arr.pop(); | ||
delete this.#keyIndices[min.key]; | ||
this.#heapify(0); | ||
return min.key; | ||
} | ||
/** | ||
* Decreases the priority for **key** to **priority**. If the new priority is | ||
* greater than the previous priority, this function will throw an Error. | ||
* | ||
* @param {Object} key the key for which to raise priority | ||
* @param {Number} priority the new priority for the key | ||
*/ | ||
PriorityQueue.prototype.decrease = function(key, priority) { | ||
var index = this._keyIndices[key]; | ||
if (priority > this._arr[index].priority) { | ||
throw new Error("New priority is greater than current priority. " + | ||
"Key: " + key + " Old: " + this._arr[index].priority + " New: " + priority); | ||
/** | ||
* Decreases the priority for **key** to **priority**. If the new priority is | ||
* greater than the previous priority, this function will throw an Error. | ||
* | ||
* @param {Object} key the key for which to raise priority | ||
* @param {Number} priority the new priority for the key | ||
*/ | ||
decrease(key, priority) { | ||
var index = this.#keyIndices[key]; | ||
if (priority > this.#arr[index].priority) { | ||
throw new Error("New priority is greater than current priority. " + | ||
"Key: " + key + " Old: " + this.#arr[index].priority + " New: " + priority); | ||
} | ||
this.#arr[index].priority = priority; | ||
this.#decrease(index); | ||
} | ||
this._arr[index].priority = priority; | ||
this._decrease(index); | ||
}; | ||
PriorityQueue.prototype._heapify = function(i) { | ||
var arr = this._arr; | ||
var l = 2 * i; | ||
var r = l + 1; | ||
var largest = i; | ||
if (l < arr.length) { | ||
largest = arr[l].priority < arr[largest].priority ? l : largest; | ||
if (r < arr.length) { | ||
largest = arr[r].priority < arr[largest].priority ? r : largest; | ||
#heapify(i) { | ||
var arr = this.#arr; | ||
var l = 2 * i; | ||
var r = l + 1; | ||
var largest = i; | ||
if (l < arr.length) { | ||
largest = arr[l].priority < arr[largest].priority ? l : largest; | ||
if (r < arr.length) { | ||
largest = arr[r].priority < arr[largest].priority ? r : largest; | ||
} | ||
if (largest !== i) { | ||
this.#swap(i, largest); | ||
this.#heapify(largest); | ||
} | ||
} | ||
if (largest !== i) { | ||
this._swap(i, largest); | ||
this._heapify(largest); | ||
} | ||
} | ||
}; | ||
PriorityQueue.prototype._decrease = function(index) { | ||
var arr = this._arr; | ||
var priority = arr[index].priority; | ||
var parent; | ||
while (index !== 0) { | ||
parent = index >> 1; | ||
if (arr[parent].priority < priority) { | ||
break; | ||
#decrease(index) { | ||
var arr = this.#arr; | ||
var priority = arr[index].priority; | ||
var parent; | ||
while (index !== 0) { | ||
parent = index >> 1; | ||
if (arr[parent].priority < priority) { | ||
break; | ||
} | ||
this.#swap(index, parent); | ||
index = parent; | ||
} | ||
this._swap(index, parent); | ||
index = parent; | ||
} | ||
}; | ||
PriorityQueue.prototype._swap = function(i, j) { | ||
var arr = this._arr; | ||
var keyIndices = this._keyIndices; | ||
var origArrI = arr[i]; | ||
var origArrJ = arr[j]; | ||
arr[i] = origArrJ; | ||
arr[j] = origArrI; | ||
keyIndices[origArrJ.key] = i; | ||
keyIndices[origArrI.key] = j; | ||
}; | ||
#swap(i, j) { | ||
var arr = this.#arr; | ||
var keyIndices = this.#keyIndices; | ||
var origArrI = arr[i]; | ||
var origArrJ = arr[j]; | ||
arr[i] = origArrJ; | ||
arr[j] = origArrI; | ||
keyIndices[origArrJ.key] = i; | ||
keyIndices[origArrI.key] = j; | ||
} | ||
} | ||
module.exports = PriorityQueue; | ||
},{}],16:[function(require,module,exports){ | ||
"use strict"; | ||
module.exports = Graph; | ||
var DEFAULT_EDGE_NAME = "\x00"; | ||
@@ -600,622 +628,625 @@ var GRAPH_NODE = "\x00"; | ||
function Graph(opts) { | ||
this._isDirected = true; | ||
this._isMultigraph = false; | ||
this._isCompound = false; | ||
class Graph { | ||
#isDirected = true; | ||
#isMultigraph = false; | ||
#isCompound = false; | ||
if (opts) { | ||
this._isDirected = opts.hasOwnProperty("directed") ? opts.directed : true; | ||
this._isMultigraph = opts.hasOwnProperty("multigraph") ? opts.multigraph : false; | ||
this._isCompound = opts.hasOwnProperty("compound") ? opts.compound : false; | ||
} | ||
// Label for the graph itself | ||
this._label = undefined; | ||
#label; | ||
// Defaults to be set when creating a new node | ||
this._defaultNodeLabelFn = () => undefined; | ||
#defaultNodeLabelFn = () => undefined; | ||
// Defaults to be set when creating a new edge | ||
this._defaultEdgeLabelFn = () => undefined; | ||
#defaultEdgeLabelFn = () => undefined; | ||
// v -> label | ||
this._nodes = {}; | ||
#nodes = {}; | ||
if (this._isCompound) { | ||
// v -> parent | ||
this._parent = {}; | ||
// v -> children | ||
this._children = {}; | ||
this._children[GRAPH_NODE] = {}; | ||
} | ||
// v -> edgeObj | ||
this._in = {}; | ||
#in = {}; | ||
// u -> v -> Number | ||
this._preds = {}; | ||
#preds = {}; | ||
// v -> edgeObj | ||
this._out = {}; | ||
#out = {}; | ||
// v -> w -> Number | ||
this._sucs = {}; | ||
#sucs = {}; | ||
// e -> edgeObj | ||
this._edgeObjs = {}; | ||
#edgeObjs = {}; | ||
// e -> label | ||
this._edgeLabels = {}; | ||
} | ||
#edgeLabels = {}; | ||
/* Number of nodes in the graph. Should only be changed by the implementation. */ | ||
Graph.prototype._nodeCount = 0; | ||
/* Number of nodes in the graph. Should only be changed by the implementation. */ | ||
#nodeCount = 0; | ||
/* Number of edges in the graph. Should only be changed by the implementation. */ | ||
Graph.prototype._edgeCount = 0; | ||
/* Number of edges in the graph. Should only be changed by the implementation. */ | ||
#edgeCount = 0; | ||
#parent; | ||
/* === Graph functions ========= */ | ||
#children; | ||
/** | ||
* Whether graph was created with 'directed' flag set to true or not. | ||
*/ | ||
Graph.prototype.isDirected = function() { | ||
return this._isDirected; | ||
}; | ||
constructor(opts) { | ||
if (opts) { | ||
this.#isDirected = opts.hasOwnProperty("directed") ? opts.directed : true; | ||
this.#isMultigraph = opts.hasOwnProperty("multigraph") ? opts.multigraph : false; | ||
this.#isCompound = opts.hasOwnProperty("compound") ? opts.compound : false; | ||
} | ||
/** | ||
* Whether graph was created with 'multigraph' flag set to true or not. | ||
*/ | ||
Graph.prototype.isMultigraph = function() { | ||
return this._isMultigraph; | ||
}; | ||
if (this.#isCompound) { | ||
// v -> parent | ||
this.#parent = {}; | ||
/** | ||
* Whether graph was created with 'compound' flag set to true or not. | ||
*/ | ||
Graph.prototype.isCompound = function() { | ||
return this._isCompound; | ||
}; | ||
// v -> children | ||
this.#children = {}; | ||
this.#children[GRAPH_NODE] = {}; | ||
} | ||
} | ||
/** | ||
* Sets the label of the graph. | ||
*/ | ||
Graph.prototype.setGraph = function(label) { | ||
this._label = label; | ||
return this; | ||
}; | ||
/* === Graph functions ========= */ | ||
/** | ||
* Gets the graph label. | ||
*/ | ||
Graph.prototype.graph = function() { | ||
return this._label; | ||
}; | ||
/** | ||
* Whether graph was created with 'directed' flag set to true or not. | ||
*/ | ||
isDirected() { | ||
return this.#isDirected; | ||
} | ||
/** | ||
* Whether graph was created with 'multigraph' flag set to true or not. | ||
*/ | ||
isMultigraph() { | ||
return this.#isMultigraph; | ||
} | ||
/* === Node functions ========== */ | ||
/** | ||
* Whether graph was created with 'compound' flag set to true or not. | ||
*/ | ||
isCompound() { | ||
return this.#isCompound; | ||
} | ||
/** | ||
* Sets the default node label. If newDefault is a function, it will be | ||
* invoked ach time when setting a label for a node. Otherwise, this label | ||
* will be assigned as default label in case if no label was specified while | ||
* setting a node. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.setDefaultNodeLabel = function(newDefault) { | ||
this._defaultNodeLabelFn = newDefault; | ||
if (typeof newDefault !== 'function') { | ||
this._defaultNodeLabelFn = () => newDefault; | ||
/** | ||
* Sets the label of the graph. | ||
*/ | ||
setGraph(label) { | ||
this.#label = label; | ||
return this; | ||
} | ||
return this; | ||
}; | ||
/** | ||
* Gets the graph label. | ||
*/ | ||
graph() { | ||
return this.#label; | ||
} | ||
/** | ||
* Gets the number of nodes in the graph. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.nodeCount = function() { | ||
return this._nodeCount; | ||
}; | ||
/** | ||
* Gets all nodes of the graph. Note, the in case of compound graph subnodes are | ||
* not included in list. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.nodes = function() { | ||
return Object.keys(this._nodes); | ||
}; | ||
/* === Node functions ========== */ | ||
/** | ||
* Gets list of nodes without in-edges. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.sources = function() { | ||
var self = this; | ||
return this.nodes().filter(v => Object.keys(self._in[v]).length === 0); | ||
}; | ||
/** | ||
* Gets list of nodes without out-edges. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.sinks = function() { | ||
var self = this; | ||
return this.nodes().filter(v => Object.keys(self._out[v]).length === 0); | ||
}; | ||
/** | ||
* Invokes setNode method for each node in names list. | ||
* Complexity: O(|names|). | ||
*/ | ||
Graph.prototype.setNodes = function(vs, value) { | ||
var args = arguments; | ||
var self = this; | ||
vs.forEach(function(v) { | ||
if (args.length > 1) { | ||
self.setNode(v, value); | ||
} else { | ||
self.setNode(v); | ||
/** | ||
* Sets the default node label. If newDefault is a function, it will be | ||
* invoked ach time when setting a label for a node. Otherwise, this label | ||
* will be assigned as default label in case if no label was specified while | ||
* setting a node. | ||
* Complexity: O(1). | ||
*/ | ||
setDefaultNodeLabel(newDefault) { | ||
this.#defaultNodeLabelFn = newDefault; | ||
if (typeof newDefault !== 'function') { | ||
this.#defaultNodeLabelFn = () => newDefault; | ||
} | ||
}); | ||
return this; | ||
}; | ||
/** | ||
* Creates or updates the value for the node v in the graph. If label is supplied | ||
* it is set as the value for the node. If label is not supplied and the node was | ||
* created by this call then the default node label will be assigned. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.setNode = function(v, value) { | ||
if (this._nodes.hasOwnProperty(v)) { | ||
if (arguments.length > 1) { | ||
this._nodes[v] = value; | ||
} | ||
return this; | ||
} | ||
this._nodes[v] = arguments.length > 1 ? value : this._defaultNodeLabelFn(v); | ||
if (this._isCompound) { | ||
this._parent[v] = GRAPH_NODE; | ||
this._children[v] = {}; | ||
this._children[GRAPH_NODE][v] = true; | ||
/** | ||
* Gets the number of nodes in the graph. | ||
* Complexity: O(1). | ||
*/ | ||
nodeCount() { | ||
return this.#nodeCount; | ||
} | ||
this._in[v] = {}; | ||
this._preds[v] = {}; | ||
this._out[v] = {}; | ||
this._sucs[v] = {}; | ||
++this._nodeCount; | ||
return this; | ||
}; | ||
/** | ||
* Gets the label of node with specified name. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.node = function(v) { | ||
return this._nodes[v]; | ||
}; | ||
/** | ||
* Gets all nodes of the graph. Note, the in case of compound graph subnodes are | ||
* not included in list. | ||
* Complexity: O(1). | ||
*/ | ||
nodes() { | ||
return Object.keys(this.#nodes); | ||
} | ||
/** | ||
* Detects whether graph has a node with specified name or not. | ||
*/ | ||
Graph.prototype.hasNode = function(v) { | ||
return this._nodes.hasOwnProperty(v); | ||
}; | ||
/** | ||
* Gets list of nodes without in-edges. | ||
* Complexity: O(|V|). | ||
*/ | ||
sources() { | ||
var self = this; | ||
return this.nodes().filter(v => Object.keys(self.#in[v]).length === 0); | ||
} | ||
/** | ||
* Remove the node with the name from the graph or do nothing if the node is not in | ||
* the graph. If the node was removed this function also removes any incident | ||
* edges. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.removeNode = function(v) { | ||
var self = this; | ||
if (this._nodes.hasOwnProperty(v)) { | ||
var removeEdge = e => self.removeEdge(self._edgeObjs[e]); | ||
delete this._nodes[v]; | ||
if (this._isCompound) { | ||
this._removeFromParentsChildList(v); | ||
delete this._parent[v]; | ||
this.children(v).forEach(function(child) { | ||
self.setParent(child); | ||
}); | ||
delete this._children[v]; | ||
} | ||
Object.keys(this._in[v]).forEach(removeEdge); | ||
delete this._in[v]; | ||
delete this._preds[v]; | ||
Object.keys(this._out[v]).forEach(removeEdge); | ||
delete this._out[v]; | ||
delete this._sucs[v]; | ||
--this._nodeCount; | ||
/** | ||
* Gets list of nodes without out-edges. | ||
* Complexity: O(|V|). | ||
*/ | ||
sinks() { | ||
var self = this; | ||
return this.nodes().filter(v => Object.keys(self.#out[v]).length === 0); | ||
} | ||
return this; | ||
}; | ||
/** | ||
* Sets node p as a parent for node v if it is defined, or removes the | ||
* parent for v if p is undefined. Method throws an exception in case of | ||
* invoking it in context of noncompound graph. | ||
* Average-case complexity: O(1). | ||
*/ | ||
Graph.prototype.setParent = function(v, parent) { | ||
if (!this._isCompound) { | ||
throw new Error("Cannot set parent in a non-compound graph"); | ||
/** | ||
* Invokes setNode method for each node in names list. | ||
* Complexity: O(|names|). | ||
*/ | ||
setNodes(vs, value) { | ||
var args = arguments; | ||
var self = this; | ||
vs.forEach(function(v) { | ||
if (args.length > 1) { | ||
self.setNode(v, value); | ||
} else { | ||
self.setNode(v); | ||
} | ||
}); | ||
return this; | ||
} | ||
if (parent === undefined) { | ||
parent = GRAPH_NODE; | ||
} else { | ||
// Coerce parent to string | ||
parent += ""; | ||
for (var ancestor = parent; | ||
ancestor !== undefined; | ||
ancestor = this.parent(ancestor)) { | ||
if (ancestor === v) { | ||
throw new Error("Setting " + parent+ " as parent of " + v + | ||
" would create a cycle"); | ||
/** | ||
* Creates or updates the value for the node v in the graph. If label is supplied | ||
* it is set as the value for the node. If label is not supplied and the node was | ||
* created by this call then the default node label will be assigned. | ||
* Complexity: O(1). | ||
*/ | ||
setNode(v, value) { | ||
if (this.#nodes.hasOwnProperty(v)) { | ||
if (arguments.length > 1) { | ||
this.#nodes[v] = value; | ||
} | ||
return this; | ||
} | ||
this.setNode(parent); | ||
this.#nodes[v] = arguments.length > 1 ? value : this.#defaultNodeLabelFn(v); | ||
if (this.#isCompound) { | ||
this.#parent[v] = GRAPH_NODE; | ||
this.#children[v] = {}; | ||
this.#children[GRAPH_NODE][v] = true; | ||
} | ||
this.#in[v] = {}; | ||
this.#preds[v] = {}; | ||
this.#out[v] = {}; | ||
this.#sucs[v] = {}; | ||
++this.#nodeCount; | ||
return this; | ||
} | ||
this.setNode(v); | ||
this._removeFromParentsChildList(v); | ||
this._parent[v] = parent; | ||
this._children[parent][v] = true; | ||
return this; | ||
}; | ||
/** | ||
* Gets the label of node with specified name. | ||
* Complexity: O(|V|). | ||
*/ | ||
node(v) { | ||
return this.#nodes[v]; | ||
} | ||
Graph.prototype._removeFromParentsChildList = function(v) { | ||
delete this._children[this._parent[v]][v]; | ||
}; | ||
/** | ||
* Detects whether graph has a node with specified name or not. | ||
*/ | ||
hasNode(v) { | ||
return this.#nodes.hasOwnProperty(v); | ||
} | ||
/** | ||
* Gets parent node for node v. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.parent = function(v) { | ||
if (this._isCompound) { | ||
var parent = this._parent[v]; | ||
if (parent !== GRAPH_NODE) { | ||
return parent; | ||
/** | ||
* Remove the node with the name from the graph or do nothing if the node is not in | ||
* the graph. If the node was removed this function also removes any incident | ||
* edges. | ||
* Complexity: O(1). | ||
*/ | ||
removeNode(v) { | ||
var self = this; | ||
if (this.#nodes.hasOwnProperty(v)) { | ||
var removeEdge = e => self.removeEdge(self.#edgeObjs[e]); | ||
delete this.#nodes[v]; | ||
if (this.#isCompound) { | ||
this.#removeFromParentsChildList(v); | ||
delete this.#parent[v]; | ||
this.children(v).forEach(function(child) { | ||
self.setParent(child); | ||
}); | ||
delete this.#children[v]; | ||
} | ||
Object.keys(this.#in[v]).forEach(removeEdge); | ||
delete this.#in[v]; | ||
delete this.#preds[v]; | ||
Object.keys(this.#out[v]).forEach(removeEdge); | ||
delete this.#out[v]; | ||
delete this.#sucs[v]; | ||
--this.#nodeCount; | ||
} | ||
return this; | ||
} | ||
}; | ||
/** | ||
* Gets list of direct children of node v. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.children = function(v = GRAPH_NODE) { | ||
if (this._isCompound) { | ||
var children = this._children[v]; | ||
if (children) { | ||
return Object.keys(children); | ||
/** | ||
* Sets node p as a parent for node v if it is defined, or removes the | ||
* parent for v if p is undefined. Method throws an exception in case of | ||
* invoking it in context of noncompound graph. | ||
* Average-case complexity: O(1). | ||
*/ | ||
setParent(v, parent) { | ||
if (!this.#isCompound) { | ||
throw new Error("Cannot set parent in a non-compound graph"); | ||
} | ||
} else if (v === GRAPH_NODE) { | ||
return this.nodes(); | ||
} else if (this.hasNode(v)) { | ||
return []; | ||
if (parent === undefined) { | ||
parent = GRAPH_NODE; | ||
} else { | ||
// Coerce parent to string | ||
parent += ""; | ||
for (var ancestor = parent; ancestor !== undefined; ancestor = this.parent(ancestor)) { | ||
if (ancestor === v) { | ||
throw new Error("Setting " + parent+ " as parent of " + v + | ||
" would create a cycle"); | ||
} | ||
} | ||
this.setNode(parent); | ||
} | ||
this.setNode(v); | ||
this.#removeFromParentsChildList(v); | ||
this.#parent[v] = parent; | ||
this.#children[parent][v] = true; | ||
return this; | ||
} | ||
}; | ||
/** | ||
* Return all nodes that are predecessors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.predecessors = function(v) { | ||
var predsV = this._preds[v]; | ||
if (predsV) { | ||
return Object.keys(predsV); | ||
#removeFromParentsChildList(v) { | ||
delete this.#children[this.#parent[v]][v]; | ||
} | ||
}; | ||
/** | ||
* Return all nodes that are successors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.successors = function(v) { | ||
var sucsV = this._sucs[v]; | ||
if (sucsV) { | ||
return Object.keys(sucsV); | ||
/** | ||
* Gets parent node for node v. | ||
* Complexity: O(1). | ||
*/ | ||
parent(v) { | ||
if (this.#isCompound) { | ||
var parent = this.#parent[v]; | ||
if (parent !== GRAPH_NODE) { | ||
return parent; | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* Return all nodes that are predecessors or successors of the specified node or undefined if | ||
* node v is not in the graph. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.neighbors = function(v) { | ||
var preds = this.predecessors(v); | ||
if (preds) { | ||
const union = new Set(preds); | ||
for (var succ of this.successors(v)) { | ||
union.add(succ); | ||
/** | ||
* Gets list of direct children of node v. | ||
* Complexity: O(1). | ||
*/ | ||
children(v = GRAPH_NODE) { | ||
if (this.#isCompound) { | ||
var children = this.#children[v]; | ||
if (children) { | ||
return Object.keys(children); | ||
} | ||
} else if (v === GRAPH_NODE) { | ||
return this.nodes(); | ||
} else if (this.hasNode(v)) { | ||
return []; | ||
} | ||
} | ||
return Array.from(union.values()); | ||
/** | ||
* Return all nodes that are predecessors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/ | ||
predecessors(v) { | ||
var predsV = this.#preds[v]; | ||
if (predsV) { | ||
return Object.keys(predsV); | ||
} | ||
} | ||
}; | ||
Graph.prototype.isLeaf = function (v) { | ||
var neighbors; | ||
if (this.isDirected()) { | ||
neighbors = this.successors(v); | ||
} else { | ||
neighbors = this.neighbors(v); | ||
/** | ||
* Return all nodes that are successors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/ | ||
successors(v) { | ||
var sucsV = this.#sucs[v]; | ||
if (sucsV) { | ||
return Object.keys(sucsV); | ||
} | ||
} | ||
return neighbors.length === 0; | ||
}; | ||
/** | ||
* Creates new graph with nodes filtered via filter. Edges incident to rejected node | ||
* are also removed. In case of compound graph, if parent is rejected by filter, | ||
* than all its children are rejected too. | ||
* Average-case complexity: O(|E|+|V|). | ||
*/ | ||
Graph.prototype.filterNodes = function(filter) { | ||
var copy = new this.constructor({ | ||
directed: this._isDirected, | ||
multigraph: this._isMultigraph, | ||
compound: this._isCompound | ||
}); | ||
/** | ||
* Return all nodes that are predecessors or successors of the specified node or undefined if | ||
* node v is not in the graph. | ||
* Complexity: O(|V|). | ||
*/ | ||
neighbors(v) { | ||
var preds = this.predecessors(v); | ||
if (preds) { | ||
const union = new Set(preds); | ||
for (var succ of this.successors(v)) { | ||
union.add(succ); | ||
} | ||
copy.setGraph(this.graph()); | ||
var self = this; | ||
Object.entries(this._nodes).forEach(function([v, value]) { | ||
if (filter(v)) { | ||
copy.setNode(v, value); | ||
return Array.from(union.values()); | ||
} | ||
}); | ||
} | ||
Object.values(this._edgeObjs).forEach(function(e) { | ||
if (copy.hasNode(e.v) && copy.hasNode(e.w)) { | ||
copy.setEdge(e, self.edge(e)); | ||
} | ||
}); | ||
var parents = {}; | ||
function findParent(v) { | ||
var parent = self.parent(v); | ||
if (parent === undefined || copy.hasNode(parent)) { | ||
parents[v] = parent; | ||
return parent; | ||
} else if (parent in parents) { | ||
return parents[parent]; | ||
isLeaf(v) { | ||
var neighbors; | ||
if (this.isDirected()) { | ||
neighbors = this.successors(v); | ||
} else { | ||
return findParent(parent); | ||
neighbors = this.neighbors(v); | ||
} | ||
return neighbors.length === 0; | ||
} | ||
if (this._isCompound) { | ||
copy.nodes().forEach(v => copy.setParent(v, findParent(v))); | ||
} | ||
/** | ||
* Creates new graph with nodes filtered via filter. Edges incident to rejected node | ||
* are also removed. In case of compound graph, if parent is rejected by filter, | ||
* than all its children are rejected too. | ||
* Average-case complexity: O(|E|+|V|). | ||
*/ | ||
filterNodes(filter) { | ||
var copy = new this.constructor({ | ||
directed: this.#isDirected, | ||
multigraph: this.#isMultigraph, | ||
compound: this.#isCompound | ||
}); | ||
return copy; | ||
}; | ||
copy.setGraph(this.graph()); | ||
/* === Edge functions ========== */ | ||
var self = this; | ||
Object.entries(this.#nodes).forEach(function([v, value]) { | ||
if (filter(v)) { | ||
copy.setNode(v, value); | ||
} | ||
}); | ||
/** | ||
* Sets the default edge label or factory function. This label will be | ||
* assigned as default label in case if no label was specified while setting | ||
* an edge or this function will be invoked each time when setting an edge | ||
* with no label specified and returned value * will be used as a label for edge. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.setDefaultEdgeLabel = function(newDefault) { | ||
this._defaultEdgeLabelFn = newDefault; | ||
if (typeof newDefault !== 'function') { | ||
this._defaultEdgeLabelFn = () => newDefault; | ||
} | ||
Object.values(this.#edgeObjs).forEach(function(e) { | ||
if (copy.hasNode(e.v) && copy.hasNode(e.w)) { | ||
copy.setEdge(e, self.edge(e)); | ||
} | ||
}); | ||
return this; | ||
}; | ||
var parents = {}; | ||
function findParent(v) { | ||
var parent = self.parent(v); | ||
if (parent === undefined || copy.hasNode(parent)) { | ||
parents[v] = parent; | ||
return parent; | ||
} else if (parent in parents) { | ||
return parents[parent]; | ||
} else { | ||
return findParent(parent); | ||
} | ||
} | ||
/** | ||
* Gets the number of edges in the graph. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.edgeCount = function() { | ||
return this._edgeCount; | ||
}; | ||
if (this.#isCompound) { | ||
copy.nodes().forEach(v => copy.setParent(v, findParent(v))); | ||
} | ||
/** | ||
* Gets edges of the graph. In case of compound graph subgraphs are not considered. | ||
* Complexity: O(|E|). | ||
*/ | ||
Graph.prototype.edges = function() { | ||
return Object.values(this._edgeObjs); | ||
}; | ||
return copy; | ||
} | ||
/** | ||
* Establish an edges path over the nodes in nodes list. If some edge is already | ||
* exists, it will update its label, otherwise it will create an edge between pair | ||
* of nodes with label provided or default label if no label provided. | ||
* Complexity: O(|nodes|). | ||
*/ | ||
Graph.prototype.setPath = function(vs, value) { | ||
var self = this; | ||
var args = arguments; | ||
vs.reduce(function(v, w) { | ||
if (args.length > 1) { | ||
self.setEdge(v, w, value); | ||
} else { | ||
self.setEdge(v, w); | ||
/* === Edge functions ========== */ | ||
/** | ||
* Sets the default edge label or factory function. This label will be | ||
* assigned as default label in case if no label was specified while setting | ||
* an edge or this function will be invoked each time when setting an edge | ||
* with no label specified and returned value * will be used as a label for edge. | ||
* Complexity: O(1). | ||
*/ | ||
setDefaultEdgeLabel(newDefault) { | ||
this.#defaultEdgeLabelFn = newDefault; | ||
if (typeof newDefault !== 'function') { | ||
this.#defaultEdgeLabelFn = () => newDefault; | ||
} | ||
return w; | ||
}); | ||
return this; | ||
}; | ||
/** | ||
* Creates or updates the label for the edge (v, w) with the optionally supplied | ||
* name. If label is supplied it is set as the value for the edge. If label is not | ||
* supplied and the edge was created by this call then the default edge label will | ||
* be assigned. The name parameter is only useful with multigraphs. | ||
*/ | ||
Graph.prototype.setEdge = function() { | ||
var v, w, name, value; | ||
var valueSpecified = false; | ||
var arg0 = arguments[0]; | ||
return this; | ||
} | ||
if (typeof arg0 === "object" && arg0 !== null && "v" in arg0) { | ||
v = arg0.v; | ||
w = arg0.w; | ||
name = arg0.name; | ||
if (arguments.length === 2) { | ||
value = arguments[1]; | ||
valueSpecified = true; | ||
} | ||
} else { | ||
v = arg0; | ||
w = arguments[1]; | ||
name = arguments[3]; | ||
if (arguments.length > 2) { | ||
value = arguments[2]; | ||
valueSpecified = true; | ||
} | ||
/** | ||
* Gets the number of edges in the graph. | ||
* Complexity: O(1). | ||
*/ | ||
edgeCount() { | ||
return this.#edgeCount; | ||
} | ||
v = "" + v; | ||
w = "" + w; | ||
if (name !== undefined) { | ||
name = "" + name; | ||
/** | ||
* Gets edges of the graph. In case of compound graph subgraphs are not considered. | ||
* Complexity: O(|E|). | ||
*/ | ||
edges() { | ||
return Object.values(this.#edgeObjs); | ||
} | ||
var e = edgeArgsToId(this._isDirected, v, w, name); | ||
if (this._edgeLabels.hasOwnProperty(e)) { | ||
if (valueSpecified) { | ||
this._edgeLabels[e] = value; | ||
} | ||
/** | ||
* Establish an edges path over the nodes in nodes list. If some edge is already | ||
* exists, it will update its label, otherwise it will create an edge between pair | ||
* of nodes with label provided or default label if no label provided. | ||
* Complexity: O(|nodes|). | ||
*/ | ||
setPath(vs, value) { | ||
var self = this; | ||
var args = arguments; | ||
vs.reduce(function(v, w) { | ||
if (args.length > 1) { | ||
self.setEdge(v, w, value); | ||
} else { | ||
self.setEdge(v, w); | ||
} | ||
return w; | ||
}); | ||
return this; | ||
} | ||
if (name !== undefined && !this._isMultigraph) { | ||
throw new Error("Cannot set a named edge when isMultigraph = false"); | ||
} | ||
/** | ||
* Creates or updates the label for the edge (v, w) with the optionally supplied | ||
* name. If label is supplied it is set as the value for the edge. If label is not | ||
* supplied and the edge was created by this call then the default edge label will | ||
* be assigned. The name parameter is only useful with multigraphs. | ||
*/ | ||
setEdge() { | ||
var v, w, name, value; | ||
var valueSpecified = false; | ||
var arg0 = arguments[0]; | ||
// It didn't exist, so we need to create it. | ||
// First ensure the nodes exist. | ||
this.setNode(v); | ||
this.setNode(w); | ||
if (typeof arg0 === "object" && arg0 !== null && "v" in arg0) { | ||
v = arg0.v; | ||
w = arg0.w; | ||
name = arg0.name; | ||
if (arguments.length === 2) { | ||
value = arguments[1]; | ||
valueSpecified = true; | ||
} | ||
} else { | ||
v = arg0; | ||
w = arguments[1]; | ||
name = arguments[3]; | ||
if (arguments.length > 2) { | ||
value = arguments[2]; | ||
valueSpecified = true; | ||
} | ||
} | ||
this._edgeLabels[e] = valueSpecified ? value : this._defaultEdgeLabelFn(v, w, name); | ||
v = "" + v; | ||
w = "" + w; | ||
if (name !== undefined) { | ||
name = "" + name; | ||
} | ||
var edgeObj = edgeArgsToObj(this._isDirected, v, w, name); | ||
// Ensure we add undirected edges in a consistent way. | ||
v = edgeObj.v; | ||
w = edgeObj.w; | ||
var e = edgeArgsToId(this.#isDirected, v, w, name); | ||
if (this.#edgeLabels.hasOwnProperty(e)) { | ||
if (valueSpecified) { | ||
this.#edgeLabels[e] = value; | ||
} | ||
return this; | ||
} | ||
Object.freeze(edgeObj); | ||
this._edgeObjs[e] = edgeObj; | ||
incrementOrInitEntry(this._preds[w], v); | ||
incrementOrInitEntry(this._sucs[v], w); | ||
this._in[w][e] = edgeObj; | ||
this._out[v][e] = edgeObj; | ||
this._edgeCount++; | ||
return this; | ||
}; | ||
if (name !== undefined && !this.#isMultigraph) { | ||
throw new Error("Cannot set a named edge when isMultigraph = false"); | ||
} | ||
/** | ||
* Gets the label for the specified edge. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.edge = function(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this._isDirected, arguments[0]) | ||
: edgeArgsToId(this._isDirected, v, w, name)); | ||
return this._edgeLabels[e]; | ||
}; | ||
// It didn't exist, so we need to create it. | ||
// First ensure the nodes exist. | ||
this.setNode(v); | ||
this.setNode(w); | ||
/** | ||
* Detects whether the graph contains specified edge or not. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.hasEdge = function(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this._isDirected, arguments[0]) | ||
: edgeArgsToId(this._isDirected, v, w, name)); | ||
return this._edgeLabels.hasOwnProperty(e); | ||
}; | ||
this.#edgeLabels[e] = valueSpecified ? value : this.#defaultEdgeLabelFn(v, w, name); | ||
/** | ||
* Removes the specified edge from the graph. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.removeEdge = function(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this._isDirected, arguments[0]) | ||
: edgeArgsToId(this._isDirected, v, w, name)); | ||
var edge = this._edgeObjs[e]; | ||
if (edge) { | ||
v = edge.v; | ||
w = edge.w; | ||
delete this._edgeLabels[e]; | ||
delete this._edgeObjs[e]; | ||
decrementOrRemoveEntry(this._preds[w], v); | ||
decrementOrRemoveEntry(this._sucs[v], w); | ||
delete this._in[w][e]; | ||
delete this._out[v][e]; | ||
this._edgeCount--; | ||
var edgeObj = edgeArgsToObj(this.#isDirected, v, w, name); | ||
// Ensure we add undirected edges in a consistent way. | ||
v = edgeObj.v; | ||
w = edgeObj.w; | ||
Object.freeze(edgeObj); | ||
this.#edgeObjs[e] = edgeObj; | ||
incrementOrInitEntry(this.#preds[w], v); | ||
incrementOrInitEntry(this.#sucs[v], w); | ||
this.#in[w][e] = edgeObj; | ||
this.#out[v][e] = edgeObj; | ||
this.#edgeCount++; | ||
return this; | ||
} | ||
return this; | ||
}; | ||
/** | ||
* Return all edges that point to the node v. Optionally filters those edges down to just those | ||
* coming from node u. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/ | ||
Graph.prototype.inEdges = function(v, u) { | ||
var inV = this._in[v]; | ||
if (inV) { | ||
var edges = Object.values(inV); | ||
if (!u) { | ||
return edges; | ||
/** | ||
* Gets the label for the specified edge. | ||
* Complexity: O(1). | ||
*/ | ||
edge(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this.#isDirected, arguments[0]) | ||
: edgeArgsToId(this.#isDirected, v, w, name)); | ||
return this.#edgeLabels[e]; | ||
} | ||
/** | ||
* Detects whether the graph contains specified edge or not. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/ | ||
hasEdge(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this.#isDirected, arguments[0]) | ||
: edgeArgsToId(this.#isDirected, v, w, name)); | ||
return this.#edgeLabels.hasOwnProperty(e); | ||
} | ||
/** | ||
* Removes the specified edge from the graph. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/ | ||
removeEdge(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this.#isDirected, arguments[0]) | ||
: edgeArgsToId(this.#isDirected, v, w, name)); | ||
var edge = this.#edgeObjs[e]; | ||
if (edge) { | ||
v = edge.v; | ||
w = edge.w; | ||
delete this.#edgeLabels[e]; | ||
delete this.#edgeObjs[e]; | ||
decrementOrRemoveEntry(this.#preds[w], v); | ||
decrementOrRemoveEntry(this.#sucs[v], w); | ||
delete this.#in[w][e]; | ||
delete this.#out[v][e]; | ||
this.#edgeCount--; | ||
} | ||
return edges.filter(edge => edge.v === u); | ||
return this; | ||
} | ||
}; | ||
/** | ||
* Return all edges that are pointed at by node v. Optionally filters those edges down to just | ||
* those point to w. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/ | ||
Graph.prototype.outEdges = function(v, w) { | ||
var outV = this._out[v]; | ||
if (outV) { | ||
var edges = Object.values(outV); | ||
if (!w) { | ||
return edges; | ||
/** | ||
* Return all edges that point to the node v. Optionally filters those edges down to just those | ||
* coming from node u. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/ | ||
inEdges(v, u) { | ||
var inV = this.#in[v]; | ||
if (inV) { | ||
var edges = Object.values(inV); | ||
if (!u) { | ||
return edges; | ||
} | ||
return edges.filter(edge => edge.v === u); | ||
} | ||
return edges.filter(edge => edge.w === w); | ||
} | ||
}; | ||
/** | ||
* Returns all edges to or from node v regardless of direction. Optionally filters those edges | ||
* down to just those between nodes v and w regardless of direction. | ||
* Complexity: O(|E|). | ||
*/ | ||
Graph.prototype.nodeEdges = function(v, w) { | ||
var inEdges = this.inEdges(v, w); | ||
if (inEdges) { | ||
return inEdges.concat(this.outEdges(v, w)); | ||
/** | ||
* Return all edges that are pointed at by node v. Optionally filters those edges down to just | ||
* those point to w. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/ | ||
outEdges(v, w) { | ||
var outV = this.#out[v]; | ||
if (outV) { | ||
var edges = Object.values(outV); | ||
if (!w) { | ||
return edges; | ||
} | ||
return edges.filter(edge => edge.w === w); | ||
} | ||
} | ||
}; | ||
/** | ||
* Returns all edges to or from node v regardless of direction. Optionally filters those edges | ||
* down to just those between nodes v and w regardless of direction. | ||
* Complexity: O(|E|). | ||
*/ | ||
nodeEdges(v, w) { | ||
var inEdges = this.inEdges(v, w); | ||
if (inEdges) { | ||
return inEdges.concat(this.outEdges(v, w)); | ||
} | ||
} | ||
} | ||
function incrementOrInitEntry(map, k) { | ||
@@ -1264,2 +1295,4 @@ if (map[k]) { | ||
module.exports = Graph; | ||
},{}],17:[function(require,module,exports){ | ||
@@ -1355,5 +1388,5 @@ // Includes only the "core" of graphlib | ||
},{"./graph":16}],19:[function(require,module,exports){ | ||
module.exports = '2.1.9'; | ||
module.exports = '2.1.11'; | ||
},{}]},{},[1])(1) | ||
}); |
@@ -38,8 +38,7 @@ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.graphlib=f()}})(function(){var define,module,exports;return function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r}()({1:[function(require,module,exports){ | ||
* | ||
* Order must be one of "pre" or "post". | ||
*/function dfs(g,vs,order){if(!Array.isArray(vs)){vs=[vs]}var navigation=(g.isDirected()?g.successors:g.neighbors).bind(g);var acc=[];var visited={};vs.forEach(function(v){if(!g.hasNode(v)){throw new Error("Graph does not have node: "+v)}doDfs(g,v,order==="post",visited,navigation,acc)});return acc}function doDfs(g,v,postorder,visited,navigation,acc){if(!visited.hasOwnProperty(v)){visited[v]=true;if(!postorder){acc.push(v)}navigation(v).forEach(function(w){doDfs(g,w,postorder,visited,navigation,acc)});if(postorder){acc.push(v)}}}},{}],4:[function(require,module,exports){var dijkstra=require("./dijkstra");module.exports=dijkstraAll;function dijkstraAll(g,weightFunc,edgeFunc){return g.nodes().reduce(function(acc,v){acc[v]=dijkstra(g,v,weightFunc,edgeFunc);return acc},{})}},{"./dijkstra":5}],5:[function(require,module,exports){var PriorityQueue=require("../data/priority-queue");module.exports=dijkstra;var DEFAULT_WEIGHT_FUNC=()=>1;function dijkstra(g,source,weightFn,edgeFn){return runDijkstra(g,String(source),weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runDijkstra(g,source,weightFn,edgeFn){var results={};var pq=new PriorityQueue;var v,vEntry;var updateNeighbors=function(edge){var w=edge.v!==v?edge.v:edge.w;var wEntry=results[w];var weight=weightFn(edge);var distance=vEntry.distance+weight;if(weight<0){throw new Error("dijkstra does not allow negative edge weights. "+"Bad edge: "+edge+" Weight: "+weight)}if(distance<wEntry.distance){wEntry.distance=distance;wEntry.predecessor=v;pq.decrease(w,distance)}};g.nodes().forEach(function(v){var distance=v===source?0:Number.POSITIVE_INFINITY;results[v]={distance:distance};pq.add(v,distance)});while(pq.size()>0){v=pq.removeMin();vEntry=results[v];if(vEntry.distance===Number.POSITIVE_INFINITY){break}edgeFn(v).forEach(updateNeighbors)}return results}},{"../data/priority-queue":15}],6:[function(require,module,exports){var tarjan=require("./tarjan");module.exports=findCycles;function findCycles(g){return tarjan(g).filter(function(cmpt){return cmpt.length>1||cmpt.length===1&&g.hasEdge(cmpt[0],cmpt[0])})}},{"./tarjan":13}],7:[function(require,module,exports){module.exports=floydWarshall;var DEFAULT_WEIGHT_FUNC=()=>1;function floydWarshall(g,weightFn,edgeFn){return runFloydWarshall(g,weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runFloydWarshall(g,weightFn,edgeFn){var results={};var nodes=g.nodes();nodes.forEach(function(v){results[v]={};results[v][v]={distance:0};nodes.forEach(function(w){if(v!==w){results[v][w]={distance:Number.POSITIVE_INFINITY}}});edgeFn(v).forEach(function(edge){var w=edge.v===v?edge.w:edge.v;var d=weightFn(edge);results[v][w]={distance:d,predecessor:v}})});nodes.forEach(function(k){var rowK=results[k];nodes.forEach(function(i){var rowI=results[i];nodes.forEach(function(j){var ik=rowI[k];var kj=rowK[j];var ij=rowI[j];var altDistance=ik.distance+kj.distance;if(altDistance<ij.distance){ij.distance=altDistance;ij.predecessor=kj.predecessor}})})});return results}},{}],8:[function(require,module,exports){module.exports={components:require("./components"),dijkstra:require("./dijkstra"),dijkstraAll:require("./dijkstra-all"),findCycles:require("./find-cycles"),floydWarshall:require("./floyd-warshall"),isAcyclic:require("./is-acyclic"),postorder:require("./postorder"),preorder:require("./preorder"),prim:require("./prim"),tarjan:require("./tarjan"),topsort:require("./topsort")}},{"./components":2,"./dijkstra":5,"./dijkstra-all":4,"./find-cycles":6,"./floyd-warshall":7,"./is-acyclic":9,"./postorder":10,"./preorder":11,"./prim":12,"./tarjan":13,"./topsort":14}],9:[function(require,module,exports){var topsort=require("./topsort");module.exports=isAcyclic;function isAcyclic(g){try{topsort(g)}catch(e){if(e instanceof topsort.CycleException){return false}throw e}return true}},{"./topsort":14}],10:[function(require,module,exports){var dfs=require("./dfs");module.exports=postorder;function postorder(g,vs){return dfs(g,vs,"post")}},{"./dfs":3}],11:[function(require,module,exports){var dfs=require("./dfs");module.exports=preorder;function preorder(g,vs){return dfs(g,vs,"pre")}},{"./dfs":3}],12:[function(require,module,exports){var Graph=require("../graph");var PriorityQueue=require("../data/priority-queue");module.exports=prim;function prim(g,weightFunc){var result=new Graph;var parents={};var pq=new PriorityQueue;var v;function updateNeighbors(edge){var w=edge.v===v?edge.w:edge.v;var pri=pq.priority(w);if(pri!==undefined){var edgeWeight=weightFunc(edge);if(edgeWeight<pri){parents[w]=v;pq.decrease(w,edgeWeight)}}}if(g.nodeCount()===0){return result}g.nodes().forEach(function(v){pq.add(v,Number.POSITIVE_INFINITY);result.setNode(v)}); | ||
* If the order is not "post", it will be treated as "pre". | ||
*/function dfs(g,vs,order){if(!Array.isArray(vs)){vs=[vs]}var navigation=g.isDirected()?v=>g.successors(v):v=>g.neighbors(v);var orderFunc=order==="post"?postOrderDfs:preOrderDfs;var acc=[];var visited={};vs.forEach(v=>{if(!g.hasNode(v)){throw new Error("Graph does not have node: "+v)}orderFunc(v,navigation,visited,acc)});return acc}function postOrderDfs(v,navigation,visited,acc){var stack=[[v,false]];while(stack.length>0){var curr=stack.pop();if(curr[1]){acc.push(curr[0])}else{if(!visited.hasOwnProperty(curr[0])){visited[curr[0]]=true;stack.push([curr[0],true]);forEachRight(navigation(curr[0]),w=>stack.push([w,false]))}}}}function preOrderDfs(v,navigation,visited,acc){var stack=[v];while(stack.length>0){var curr=stack.pop();if(!visited.hasOwnProperty(curr)){visited[curr]=true;acc.push(curr);forEachRight(navigation(curr),w=>stack.push(w))}}}function forEachRight(array,iteratee){var length=array.length;while(length--){iteratee(array[length],length,array)}return array}},{}],4:[function(require,module,exports){var dijkstra=require("./dijkstra");module.exports=dijkstraAll;function dijkstraAll(g,weightFunc,edgeFunc){return g.nodes().reduce(function(acc,v){acc[v]=dijkstra(g,v,weightFunc,edgeFunc);return acc},{})}},{"./dijkstra":5}],5:[function(require,module,exports){var PriorityQueue=require("../data/priority-queue");module.exports=dijkstra;var DEFAULT_WEIGHT_FUNC=()=>1;function dijkstra(g,source,weightFn,edgeFn){return runDijkstra(g,String(source),weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runDijkstra(g,source,weightFn,edgeFn){var results={};var pq=new PriorityQueue;var v,vEntry;var updateNeighbors=function(edge){var w=edge.v!==v?edge.v:edge.w;var wEntry=results[w];var weight=weightFn(edge);var distance=vEntry.distance+weight;if(weight<0){throw new Error("dijkstra does not allow negative edge weights. "+"Bad edge: "+edge+" Weight: "+weight)}if(distance<wEntry.distance){wEntry.distance=distance;wEntry.predecessor=v;pq.decrease(w,distance)}};g.nodes().forEach(function(v){var distance=v===source?0:Number.POSITIVE_INFINITY;results[v]={distance:distance};pq.add(v,distance)});while(pq.size()>0){v=pq.removeMin();vEntry=results[v];if(vEntry.distance===Number.POSITIVE_INFINITY){break}edgeFn(v).forEach(updateNeighbors)}return results}},{"../data/priority-queue":15}],6:[function(require,module,exports){var tarjan=require("./tarjan");module.exports=findCycles;function findCycles(g){return tarjan(g).filter(function(cmpt){return cmpt.length>1||cmpt.length===1&&g.hasEdge(cmpt[0],cmpt[0])})}},{"./tarjan":13}],7:[function(require,module,exports){module.exports=floydWarshall;var DEFAULT_WEIGHT_FUNC=()=>1;function floydWarshall(g,weightFn,edgeFn){return runFloydWarshall(g,weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runFloydWarshall(g,weightFn,edgeFn){var results={};var nodes=g.nodes();nodes.forEach(function(v){results[v]={};results[v][v]={distance:0};nodes.forEach(function(w){if(v!==w){results[v][w]={distance:Number.POSITIVE_INFINITY}}});edgeFn(v).forEach(function(edge){var w=edge.v===v?edge.w:edge.v;var d=weightFn(edge);results[v][w]={distance:d,predecessor:v}})});nodes.forEach(function(k){var rowK=results[k];nodes.forEach(function(i){var rowI=results[i];nodes.forEach(function(j){var ik=rowI[k];var kj=rowK[j];var ij=rowI[j];var altDistance=ik.distance+kj.distance;if(altDistance<ij.distance){ij.distance=altDistance;ij.predecessor=kj.predecessor}})})});return results}},{}],8:[function(require,module,exports){module.exports={components:require("./components"),dijkstra:require("./dijkstra"),dijkstraAll:require("./dijkstra-all"),findCycles:require("./find-cycles"),floydWarshall:require("./floyd-warshall"),isAcyclic:require("./is-acyclic"),postorder:require("./postorder"),preorder:require("./preorder"),prim:require("./prim"),tarjan:require("./tarjan"),topsort:require("./topsort")}},{"./components":2,"./dijkstra":5,"./dijkstra-all":4,"./find-cycles":6,"./floyd-warshall":7,"./is-acyclic":9,"./postorder":10,"./preorder":11,"./prim":12,"./tarjan":13,"./topsort":14}],9:[function(require,module,exports){var topsort=require("./topsort");module.exports=isAcyclic;function isAcyclic(g){try{topsort(g)}catch(e){if(e instanceof topsort.CycleException){return false}throw e}return true}},{"./topsort":14}],10:[function(require,module,exports){var dfs=require("./dfs");module.exports=postorder;function postorder(g,vs){return dfs(g,vs,"post")}},{"./dfs":3}],11:[function(require,module,exports){var dfs=require("./dfs");module.exports=preorder;function preorder(g,vs){return dfs(g,vs,"pre")}},{"./dfs":3}],12:[function(require,module,exports){var Graph=require("../graph");var PriorityQueue=require("../data/priority-queue");module.exports=prim;function prim(g,weightFunc){var result=new Graph;var parents={};var pq=new PriorityQueue;var v;function updateNeighbors(edge){var w=edge.v===v?edge.w:edge.v;var pri=pq.priority(w);if(pri!==undefined){var edgeWeight=weightFunc(edge);if(edgeWeight<pri){parents[w]=v;pq.decrease(w,edgeWeight)}}}if(g.nodeCount()===0){return result}g.nodes().forEach(function(v){pq.add(v,Number.POSITIVE_INFINITY);result.setNode(v)}); | ||
// Start from an arbitrary node | ||
pq.decrease(g.nodes()[0],0);var init=false;while(pq.size()>0){v=pq.removeMin();if(parents.hasOwnProperty(v)){result.setEdge(v,parents[v])}else if(init){throw new Error("Input graph is not connected: "+g)}else{init=true}g.nodeEdges(v).forEach(updateNeighbors)}return result}},{"../data/priority-queue":15,"../graph":16}],13:[function(require,module,exports){module.exports=tarjan;function tarjan(g){var index=0;var stack=[];var visited={};// node id -> { onStack, lowlink, index } | ||
var results=[];function dfs(v){var entry=visited[v]={onStack:true,lowlink:index,index:index++};stack.push(v);g.successors(v).forEach(function(w){if(!visited.hasOwnProperty(w)){dfs(w);entry.lowlink=Math.min(entry.lowlink,visited[w].lowlink)}else if(visited[w].onStack){entry.lowlink=Math.min(entry.lowlink,visited[w].index)}});if(entry.lowlink===entry.index){var cmpt=[];var w;do{w=stack.pop();visited[w].onStack=false;cmpt.push(w)}while(v!==w);results.push(cmpt)}}g.nodes().forEach(function(v){if(!visited.hasOwnProperty(v)){dfs(v)}});return results}},{}],14:[function(require,module,exports){module.exports=topsort;topsort.CycleException=CycleException;function topsort(g){var visited={};var stack={};var results=[];function visit(node){if(stack.hasOwnProperty(node)){throw new CycleException}if(!visited.hasOwnProperty(node)){stack[node]=true;visited[node]=true;g.predecessors(node).forEach(visit);delete stack[node];results.push(node)}}g.sinks().forEach(visit);if(Object.keys(visited).length!==g.nodeCount()){throw new CycleException}return results}function CycleException(){}CycleException.prototype=new Error;// must be an instance of Error to pass testing | ||
},{}],15:[function(require,module,exports){module.exports=PriorityQueue; | ||
var results=[];function dfs(v){var entry=visited[v]={onStack:true,lowlink:index,index:index++};stack.push(v);g.successors(v).forEach(function(w){if(!visited.hasOwnProperty(w)){dfs(w);entry.lowlink=Math.min(entry.lowlink,visited[w].lowlink)}else if(visited[w].onStack){entry.lowlink=Math.min(entry.lowlink,visited[w].index)}});if(entry.lowlink===entry.index){var cmpt=[];var w;do{w=stack.pop();visited[w].onStack=false;cmpt.push(w)}while(v!==w);results.push(cmpt)}}g.nodes().forEach(function(v){if(!visited.hasOwnProperty(v)){dfs(v)}});return results}},{}],14:[function(require,module,exports){function topsort(g){var visited={};var stack={};var results=[];function visit(node){if(stack.hasOwnProperty(node)){throw new CycleException}if(!visited.hasOwnProperty(node)){stack[node]=true;visited[node]=true;g.predecessors(node).forEach(visit);delete stack[node];results.push(node)}}g.sinks().forEach(visit);if(Object.keys(visited).length!==g.nodeCount()){throw new CycleException}return results}class CycleException extends Error{constructor(){super(...arguments)}}module.exports=topsort;topsort.CycleException=CycleException},{}],15:[function(require,module,exports){ | ||
/** | ||
@@ -51,40 +50,41 @@ * A min-priority queue data structure. This algorithm is derived from Cormen, | ||
* have its priority decreased in O(log n) time. | ||
*/function PriorityQueue(){this._arr=[];this._keyIndices={}} | ||
*/ | ||
class PriorityQueue{#arr=[];#keyIndices={}; | ||
/** | ||
* Returns the number of elements in the queue. Takes `O(1)` time. | ||
*/PriorityQueue.prototype.size=function(){return this._arr.length}; | ||
* Returns the number of elements in the queue. Takes `O(1)` time. | ||
*/size(){return this.#arr.length} | ||
/** | ||
* Returns the keys that are in the queue. Takes `O(n)` time. | ||
*/PriorityQueue.prototype.keys=function(){return this._arr.map(function(x){return x.key})}; | ||
* Returns the keys that are in the queue. Takes `O(n)` time. | ||
*/keys(){return this.#arr.map(function(x){return x.key})} | ||
/** | ||
* Returns `true` if **key** is in the queue and `false` if not. | ||
*/PriorityQueue.prototype.has=function(key){return this._keyIndices.hasOwnProperty(key)}; | ||
* Returns `true` if **key** is in the queue and `false` if not. | ||
*/has(key){return this.#keyIndices.hasOwnProperty(key)} | ||
/** | ||
* Returns the priority for **key**. If **key** is not present in the queue | ||
* then this function returns `undefined`. Takes `O(1)` time. | ||
* | ||
* @param {Object} key | ||
*/PriorityQueue.prototype.priority=function(key){var index=this._keyIndices[key];if(index!==undefined){return this._arr[index].priority}}; | ||
* Returns the priority for **key**. If **key** is not present in the queue | ||
* then this function returns `undefined`. Takes `O(1)` time. | ||
* | ||
* @param {Object} key | ||
*/priority(key){var index=this.#keyIndices[key];if(index!==undefined){return this.#arr[index].priority}} | ||
/** | ||
* Returns the key for the minimum element in this queue. If the queue is | ||
* empty this function throws an Error. Takes `O(1)` time. | ||
*/PriorityQueue.prototype.min=function(){if(this.size()===0){throw new Error("Queue underflow")}return this._arr[0].key}; | ||
* Returns the key for the minimum element in this queue. If the queue is | ||
* empty this function throws an Error. Takes `O(1)` time. | ||
*/min(){if(this.size()===0){throw new Error("Queue underflow")}return this.#arr[0].key} | ||
/** | ||
* Inserts a new key into the priority queue. If the key already exists in | ||
* the queue this function returns `false`; otherwise it will return `true`. | ||
* Takes `O(n)` time. | ||
* | ||
* @param {Object} key the key to add | ||
* @param {Number} priority the initial priority for the key | ||
*/PriorityQueue.prototype.add=function(key,priority){var keyIndices=this._keyIndices;key=String(key);if(!keyIndices.hasOwnProperty(key)){var arr=this._arr;var index=arr.length;keyIndices[key]=index;arr.push({key:key,priority:priority});this._decrease(index);return true}return false}; | ||
* Inserts a new key into the priority queue. If the key already exists in | ||
* the queue this function returns `false`; otherwise it will return `true`. | ||
* Takes `O(n)` time. | ||
* | ||
* @param {Object} key the key to add | ||
* @param {Number} priority the initial priority for the key | ||
*/add(key,priority){var keyIndices=this.#keyIndices;key=String(key);if(!keyIndices.hasOwnProperty(key)){var arr=this.#arr;var index=arr.length;keyIndices[key]=index;arr.push({key:key,priority:priority});this.#decrease(index);return true}return false} | ||
/** | ||
* Removes and returns the smallest key in the queue. Takes `O(log n)` time. | ||
*/PriorityQueue.prototype.removeMin=function(){this._swap(0,this._arr.length-1);var min=this._arr.pop();delete this._keyIndices[min.key];this._heapify(0);return min.key}; | ||
* Removes and returns the smallest key in the queue. Takes `O(log n)` time. | ||
*/removeMin(){this.#swap(0,this.#arr.length-1);var min=this.#arr.pop();delete this.#keyIndices[min.key];this.#heapify(0);return min.key} | ||
/** | ||
* Decreases the priority for **key** to **priority**. If the new priority is | ||
* greater than the previous priority, this function will throw an Error. | ||
* | ||
* @param {Object} key the key for which to raise priority | ||
* @param {Number} priority the new priority for the key | ||
*/PriorityQueue.prototype.decrease=function(key,priority){var index=this._keyIndices[key];if(priority>this._arr[index].priority){throw new Error("New priority is greater than current priority. "+"Key: "+key+" Old: "+this._arr[index].priority+" New: "+priority)}this._arr[index].priority=priority;this._decrease(index)};PriorityQueue.prototype._heapify=function(i){var arr=this._arr;var l=2*i;var r=l+1;var largest=i;if(l<arr.length){largest=arr[l].priority<arr[largest].priority?l:largest;if(r<arr.length){largest=arr[r].priority<arr[largest].priority?r:largest}if(largest!==i){this._swap(i,largest);this._heapify(largest)}}};PriorityQueue.prototype._decrease=function(index){var arr=this._arr;var priority=arr[index].priority;var parent;while(index!==0){parent=index>>1;if(arr[parent].priority<priority){break}this._swap(index,parent);index=parent}};PriorityQueue.prototype._swap=function(i,j){var arr=this._arr;var keyIndices=this._keyIndices;var origArrI=arr[i];var origArrJ=arr[j];arr[i]=origArrJ;arr[j]=origArrI;keyIndices[origArrJ.key]=i;keyIndices[origArrI.key]=j}},{}],16:[function(require,module,exports){"use strict";module.exports=Graph;var DEFAULT_EDGE_NAME="\0";var GRAPH_NODE="\0";var EDGE_KEY_DELIM=""; | ||
* Decreases the priority for **key** to **priority**. If the new priority is | ||
* greater than the previous priority, this function will throw an Error. | ||
* | ||
* @param {Object} key the key for which to raise priority | ||
* @param {Number} priority the new priority for the key | ||
*/decrease(key,priority){var index=this.#keyIndices[key];if(priority>this.#arr[index].priority){throw new Error("New priority is greater than current priority. "+"Key: "+key+" Old: "+this.#arr[index].priority+" New: "+priority)}this.#arr[index].priority=priority;this.#decrease(index)}#heapify(i){var arr=this.#arr;var l=2*i;var r=l+1;var largest=i;if(l<arr.length){largest=arr[l].priority<arr[largest].priority?l:largest;if(r<arr.length){largest=arr[r].priority<arr[largest].priority?r:largest}if(largest!==i){this.#swap(i,largest);this.#heapify(largest)}}}#decrease(index){var arr=this.#arr;var priority=arr[index].priority;var parent;while(index!==0){parent=index>>1;if(arr[parent].priority<priority){break}this.#swap(index,parent);index=parent}}#swap(i,j){var arr=this.#arr;var keyIndices=this.#keyIndices;var origArrI=arr[i];var origArrJ=arr[j];arr[i]=origArrJ;arr[j]=origArrI;keyIndices[origArrJ.key]=i;keyIndices[origArrI.key]=j}}module.exports=PriorityQueue},{}],16:[function(require,module,exports){"use strict";var DEFAULT_EDGE_NAME="\0";var GRAPH_NODE="\0";var EDGE_KEY_DELIM=""; | ||
// Implementation notes: | ||
@@ -99,190 +99,190 @@ // | ||
// we're going to get to a performant hashtable in JavaScript. | ||
function Graph(opts){this._isDirected=true;this._isMultigraph=false;this._isCompound=false;if(opts){this._isDirected=opts.hasOwnProperty("directed")?opts.directed:true;this._isMultigraph=opts.hasOwnProperty("multigraph")?opts.multigraph:false;this._isCompound=opts.hasOwnProperty("compound")?opts.compound:false} | ||
class Graph{#isDirected=true;#isMultigraph=false;#isCompound=false; | ||
// Label for the graph itself | ||
this._label=undefined; | ||
#label; | ||
// Defaults to be set when creating a new node | ||
this._defaultNodeLabelFn=()=>undefined; | ||
#defaultNodeLabelFn=()=>undefined; | ||
// Defaults to be set when creating a new edge | ||
this._defaultEdgeLabelFn=()=>undefined; | ||
#defaultEdgeLabelFn=()=>undefined; | ||
// v -> label | ||
this._nodes={};if(this._isCompound){ | ||
// v -> parent | ||
this._parent={}; | ||
// v -> children | ||
this._children={};this._children[GRAPH_NODE]={}} | ||
#nodes={}; | ||
// v -> edgeObj | ||
this._in={}; | ||
#in={}; | ||
// u -> v -> Number | ||
this._preds={}; | ||
#preds={}; | ||
// v -> edgeObj | ||
this._out={}; | ||
#out={}; | ||
// v -> w -> Number | ||
this._sucs={}; | ||
#sucs={}; | ||
// e -> edgeObj | ||
this._edgeObjs={}; | ||
#edgeObjs={}; | ||
// e -> label | ||
this._edgeLabels={}} | ||
/* Number of nodes in the graph. Should only be changed by the implementation. */Graph.prototype._nodeCount=0; | ||
/* Number of edges in the graph. Should only be changed by the implementation. */Graph.prototype._edgeCount=0; | ||
#edgeLabels={}; | ||
/* Number of nodes in the graph. Should only be changed by the implementation. */#nodeCount=0; | ||
/* Number of edges in the graph. Should only be changed by the implementation. */#edgeCount=0;#parent;#children;constructor(opts){if(opts){this.#isDirected=opts.hasOwnProperty("directed")?opts.directed:true;this.#isMultigraph=opts.hasOwnProperty("multigraph")?opts.multigraph:false;this.#isCompound=opts.hasOwnProperty("compound")?opts.compound:false}if(this.#isCompound){ | ||
// v -> parent | ||
this.#parent={}; | ||
// v -> children | ||
this.#children={};this.#children[GRAPH_NODE]={}}} | ||
/* === Graph functions ========= */ | ||
/** | ||
* Whether graph was created with 'directed' flag set to true or not. | ||
*/Graph.prototype.isDirected=function(){return this._isDirected}; | ||
* Whether graph was created with 'directed' flag set to true or not. | ||
*/isDirected(){return this.#isDirected} | ||
/** | ||
* Whether graph was created with 'multigraph' flag set to true or not. | ||
*/Graph.prototype.isMultigraph=function(){return this._isMultigraph}; | ||
* Whether graph was created with 'multigraph' flag set to true or not. | ||
*/isMultigraph(){return this.#isMultigraph} | ||
/** | ||
* Whether graph was created with 'compound' flag set to true or not. | ||
*/Graph.prototype.isCompound=function(){return this._isCompound}; | ||
* Whether graph was created with 'compound' flag set to true or not. | ||
*/isCompound(){return this.#isCompound} | ||
/** | ||
* Sets the label of the graph. | ||
*/Graph.prototype.setGraph=function(label){this._label=label;return this}; | ||
* Sets the label of the graph. | ||
*/setGraph(label){this.#label=label;return this} | ||
/** | ||
* Gets the graph label. | ||
*/Graph.prototype.graph=function(){return this._label}; | ||
* Gets the graph label. | ||
*/graph(){return this.#label} | ||
/* === Node functions ========== */ | ||
/** | ||
* Sets the default node label. If newDefault is a function, it will be | ||
* invoked ach time when setting a label for a node. Otherwise, this label | ||
* will be assigned as default label in case if no label was specified while | ||
* setting a node. | ||
* Complexity: O(1). | ||
*/Graph.prototype.setDefaultNodeLabel=function(newDefault){this._defaultNodeLabelFn=newDefault;if(typeof newDefault!=="function"){this._defaultNodeLabelFn=()=>newDefault}return this}; | ||
* Sets the default node label. If newDefault is a function, it will be | ||
* invoked ach time when setting a label for a node. Otherwise, this label | ||
* will be assigned as default label in case if no label was specified while | ||
* setting a node. | ||
* Complexity: O(1). | ||
*/setDefaultNodeLabel(newDefault){this.#defaultNodeLabelFn=newDefault;if(typeof newDefault!=="function"){this.#defaultNodeLabelFn=()=>newDefault}return this} | ||
/** | ||
* Gets the number of nodes in the graph. | ||
* Complexity: O(1). | ||
*/Graph.prototype.nodeCount=function(){return this._nodeCount}; | ||
* Gets the number of nodes in the graph. | ||
* Complexity: O(1). | ||
*/nodeCount(){return this.#nodeCount} | ||
/** | ||
* Gets all nodes of the graph. Note, the in case of compound graph subnodes are | ||
* not included in list. | ||
* Complexity: O(1). | ||
*/Graph.prototype.nodes=function(){return Object.keys(this._nodes)}; | ||
* Gets all nodes of the graph. Note, the in case of compound graph subnodes are | ||
* not included in list. | ||
* Complexity: O(1). | ||
*/nodes(){return Object.keys(this.#nodes)} | ||
/** | ||
* Gets list of nodes without in-edges. | ||
* Complexity: O(|V|). | ||
*/Graph.prototype.sources=function(){var self=this;return this.nodes().filter(v=>Object.keys(self._in[v]).length===0)}; | ||
* Gets list of nodes without in-edges. | ||
* Complexity: O(|V|). | ||
*/sources(){var self=this;return this.nodes().filter(v=>Object.keys(self.#in[v]).length===0)} | ||
/** | ||
* Gets list of nodes without out-edges. | ||
* Complexity: O(|V|). | ||
*/Graph.prototype.sinks=function(){var self=this;return this.nodes().filter(v=>Object.keys(self._out[v]).length===0)}; | ||
* Gets list of nodes without out-edges. | ||
* Complexity: O(|V|). | ||
*/sinks(){var self=this;return this.nodes().filter(v=>Object.keys(self.#out[v]).length===0)} | ||
/** | ||
* Invokes setNode method for each node in names list. | ||
* Complexity: O(|names|). | ||
*/Graph.prototype.setNodes=function(vs,value){var args=arguments;var self=this;vs.forEach(function(v){if(args.length>1){self.setNode(v,value)}else{self.setNode(v)}});return this}; | ||
* Invokes setNode method for each node in names list. | ||
* Complexity: O(|names|). | ||
*/setNodes(vs,value){var args=arguments;var self=this;vs.forEach(function(v){if(args.length>1){self.setNode(v,value)}else{self.setNode(v)}});return this} | ||
/** | ||
* Creates or updates the value for the node v in the graph. If label is supplied | ||
* it is set as the value for the node. If label is not supplied and the node was | ||
* created by this call then the default node label will be assigned. | ||
* Complexity: O(1). | ||
*/Graph.prototype.setNode=function(v,value){if(this._nodes.hasOwnProperty(v)){if(arguments.length>1){this._nodes[v]=value}return this}this._nodes[v]=arguments.length>1?value:this._defaultNodeLabelFn(v);if(this._isCompound){this._parent[v]=GRAPH_NODE;this._children[v]={};this._children[GRAPH_NODE][v]=true}this._in[v]={};this._preds[v]={};this._out[v]={};this._sucs[v]={};++this._nodeCount;return this}; | ||
* Creates or updates the value for the node v in the graph. If label is supplied | ||
* it is set as the value for the node. If label is not supplied and the node was | ||
* created by this call then the default node label will be assigned. | ||
* Complexity: O(1). | ||
*/setNode(v,value){if(this.#nodes.hasOwnProperty(v)){if(arguments.length>1){this.#nodes[v]=value}return this}this.#nodes[v]=arguments.length>1?value:this.#defaultNodeLabelFn(v);if(this.#isCompound){this.#parent[v]=GRAPH_NODE;this.#children[v]={};this.#children[GRAPH_NODE][v]=true}this.#in[v]={};this.#preds[v]={};this.#out[v]={};this.#sucs[v]={};++this.#nodeCount;return this} | ||
/** | ||
* Gets the label of node with specified name. | ||
* Complexity: O(|V|). | ||
*/Graph.prototype.node=function(v){return this._nodes[v]}; | ||
* Gets the label of node with specified name. | ||
* Complexity: O(|V|). | ||
*/node(v){return this.#nodes[v]} | ||
/** | ||
* Detects whether graph has a node with specified name or not. | ||
*/Graph.prototype.hasNode=function(v){return this._nodes.hasOwnProperty(v)}; | ||
* Detects whether graph has a node with specified name or not. | ||
*/hasNode(v){return this.#nodes.hasOwnProperty(v)} | ||
/** | ||
* Remove the node with the name from the graph or do nothing if the node is not in | ||
* the graph. If the node was removed this function also removes any incident | ||
* edges. | ||
* Complexity: O(1). | ||
*/Graph.prototype.removeNode=function(v){var self=this;if(this._nodes.hasOwnProperty(v)){var removeEdge=e=>self.removeEdge(self._edgeObjs[e]);delete this._nodes[v];if(this._isCompound){this._removeFromParentsChildList(v);delete this._parent[v];this.children(v).forEach(function(child){self.setParent(child)});delete this._children[v]}Object.keys(this._in[v]).forEach(removeEdge);delete this._in[v];delete this._preds[v];Object.keys(this._out[v]).forEach(removeEdge);delete this._out[v];delete this._sucs[v];--this._nodeCount}return this}; | ||
* Remove the node with the name from the graph or do nothing if the node is not in | ||
* the graph. If the node was removed this function also removes any incident | ||
* edges. | ||
* Complexity: O(1). | ||
*/removeNode(v){var self=this;if(this.#nodes.hasOwnProperty(v)){var removeEdge=e=>self.removeEdge(self.#edgeObjs[e]);delete this.#nodes[v];if(this.#isCompound){this.#removeFromParentsChildList(v);delete this.#parent[v];this.children(v).forEach(function(child){self.setParent(child)});delete this.#children[v]}Object.keys(this.#in[v]).forEach(removeEdge);delete this.#in[v];delete this.#preds[v];Object.keys(this.#out[v]).forEach(removeEdge);delete this.#out[v];delete this.#sucs[v];--this.#nodeCount}return this} | ||
/** | ||
* Sets node p as a parent for node v if it is defined, or removes the | ||
* parent for v if p is undefined. Method throws an exception in case of | ||
* invoking it in context of noncompound graph. | ||
* Average-case complexity: O(1). | ||
*/Graph.prototype.setParent=function(v,parent){if(!this._isCompound){throw new Error("Cannot set parent in a non-compound graph")}if(parent===undefined){parent=GRAPH_NODE}else{ | ||
* Sets node p as a parent for node v if it is defined, or removes the | ||
* parent for v if p is undefined. Method throws an exception in case of | ||
* invoking it in context of noncompound graph. | ||
* Average-case complexity: O(1). | ||
*/setParent(v,parent){if(!this.#isCompound){throw new Error("Cannot set parent in a non-compound graph")}if(parent===undefined){parent=GRAPH_NODE}else{ | ||
// Coerce parent to string | ||
parent+="";for(var ancestor=parent;ancestor!==undefined;ancestor=this.parent(ancestor)){if(ancestor===v){throw new Error("Setting "+parent+" as parent of "+v+" would create a cycle")}}this.setNode(parent)}this.setNode(v);this._removeFromParentsChildList(v);this._parent[v]=parent;this._children[parent][v]=true;return this};Graph.prototype._removeFromParentsChildList=function(v){delete this._children[this._parent[v]][v]}; | ||
parent+="";for(var ancestor=parent;ancestor!==undefined;ancestor=this.parent(ancestor)){if(ancestor===v){throw new Error("Setting "+parent+" as parent of "+v+" would create a cycle")}}this.setNode(parent)}this.setNode(v);this.#removeFromParentsChildList(v);this.#parent[v]=parent;this.#children[parent][v]=true;return this}#removeFromParentsChildList(v){delete this.#children[this.#parent[v]][v]} | ||
/** | ||
* Gets parent node for node v. | ||
* Complexity: O(1). | ||
*/Graph.prototype.parent=function(v){if(this._isCompound){var parent=this._parent[v];if(parent!==GRAPH_NODE){return parent}}}; | ||
* Gets parent node for node v. | ||
* Complexity: O(1). | ||
*/parent(v){if(this.#isCompound){var parent=this.#parent[v];if(parent!==GRAPH_NODE){return parent}}} | ||
/** | ||
* Gets list of direct children of node v. | ||
* Complexity: O(1). | ||
*/Graph.prototype.children=function(v=GRAPH_NODE){if(this._isCompound){var children=this._children[v];if(children){return Object.keys(children)}}else if(v===GRAPH_NODE){return this.nodes()}else if(this.hasNode(v)){return[]}}; | ||
* Gets list of direct children of node v. | ||
* Complexity: O(1). | ||
*/children(v=GRAPH_NODE){if(this.#isCompound){var children=this.#children[v];if(children){return Object.keys(children)}}else if(v===GRAPH_NODE){return this.nodes()}else if(this.hasNode(v)){return[]}} | ||
/** | ||
* Return all nodes that are predecessors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/Graph.prototype.predecessors=function(v){var predsV=this._preds[v];if(predsV){return Object.keys(predsV)}}; | ||
* Return all nodes that are predecessors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/predecessors(v){var predsV=this.#preds[v];if(predsV){return Object.keys(predsV)}} | ||
/** | ||
* Return all nodes that are successors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/Graph.prototype.successors=function(v){var sucsV=this._sucs[v];if(sucsV){return Object.keys(sucsV)}}; | ||
* Return all nodes that are successors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/successors(v){var sucsV=this.#sucs[v];if(sucsV){return Object.keys(sucsV)}} | ||
/** | ||
* Return all nodes that are predecessors or successors of the specified node or undefined if | ||
* node v is not in the graph. | ||
* Complexity: O(|V|). | ||
*/Graph.prototype.neighbors=function(v){var preds=this.predecessors(v);if(preds){const union=new Set(preds);for(var succ of this.successors(v)){union.add(succ)}return Array.from(union.values())}};Graph.prototype.isLeaf=function(v){var neighbors;if(this.isDirected()){neighbors=this.successors(v)}else{neighbors=this.neighbors(v)}return neighbors.length===0}; | ||
* Return all nodes that are predecessors or successors of the specified node or undefined if | ||
* node v is not in the graph. | ||
* Complexity: O(|V|). | ||
*/neighbors(v){var preds=this.predecessors(v);if(preds){const union=new Set(preds);for(var succ of this.successors(v)){union.add(succ)}return Array.from(union.values())}}isLeaf(v){var neighbors;if(this.isDirected()){neighbors=this.successors(v)}else{neighbors=this.neighbors(v)}return neighbors.length===0} | ||
/** | ||
* Creates new graph with nodes filtered via filter. Edges incident to rejected node | ||
* are also removed. In case of compound graph, if parent is rejected by filter, | ||
* than all its children are rejected too. | ||
* Average-case complexity: O(|E|+|V|). | ||
*/Graph.prototype.filterNodes=function(filter){var copy=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});copy.setGraph(this.graph());var self=this;Object.entries(this._nodes).forEach(function([v,value]){if(filter(v)){copy.setNode(v,value)}});Object.values(this._edgeObjs).forEach(function(e){if(copy.hasNode(e.v)&©.hasNode(e.w)){copy.setEdge(e,self.edge(e))}});var parents={};function findParent(v){var parent=self.parent(v);if(parent===undefined||copy.hasNode(parent)){parents[v]=parent;return parent}else if(parent in parents){return parents[parent]}else{return findParent(parent)}}if(this._isCompound){copy.nodes().forEach(v=>copy.setParent(v,findParent(v)))}return copy}; | ||
* Creates new graph with nodes filtered via filter. Edges incident to rejected node | ||
* are also removed. In case of compound graph, if parent is rejected by filter, | ||
* than all its children are rejected too. | ||
* Average-case complexity: O(|E|+|V|). | ||
*/filterNodes(filter){var copy=new this.constructor({directed:this.#isDirected,multigraph:this.#isMultigraph,compound:this.#isCompound});copy.setGraph(this.graph());var self=this;Object.entries(this.#nodes).forEach(function([v,value]){if(filter(v)){copy.setNode(v,value)}});Object.values(this.#edgeObjs).forEach(function(e){if(copy.hasNode(e.v)&©.hasNode(e.w)){copy.setEdge(e,self.edge(e))}});var parents={};function findParent(v){var parent=self.parent(v);if(parent===undefined||copy.hasNode(parent)){parents[v]=parent;return parent}else if(parent in parents){return parents[parent]}else{return findParent(parent)}}if(this.#isCompound){copy.nodes().forEach(v=>copy.setParent(v,findParent(v)))}return copy} | ||
/* === Edge functions ========== */ | ||
/** | ||
* Sets the default edge label or factory function. This label will be | ||
* assigned as default label in case if no label was specified while setting | ||
* an edge or this function will be invoked each time when setting an edge | ||
* with no label specified and returned value * will be used as a label for edge. | ||
* Complexity: O(1). | ||
*/Graph.prototype.setDefaultEdgeLabel=function(newDefault){this._defaultEdgeLabelFn=newDefault;if(typeof newDefault!=="function"){this._defaultEdgeLabelFn=()=>newDefault}return this}; | ||
* Sets the default edge label or factory function. This label will be | ||
* assigned as default label in case if no label was specified while setting | ||
* an edge or this function will be invoked each time when setting an edge | ||
* with no label specified and returned value * will be used as a label for edge. | ||
* Complexity: O(1). | ||
*/setDefaultEdgeLabel(newDefault){this.#defaultEdgeLabelFn=newDefault;if(typeof newDefault!=="function"){this.#defaultEdgeLabelFn=()=>newDefault}return this} | ||
/** | ||
* Gets the number of edges in the graph. | ||
* Complexity: O(1). | ||
*/Graph.prototype.edgeCount=function(){return this._edgeCount}; | ||
* Gets the number of edges in the graph. | ||
* Complexity: O(1). | ||
*/edgeCount(){return this.#edgeCount} | ||
/** | ||
* Gets edges of the graph. In case of compound graph subgraphs are not considered. | ||
* Complexity: O(|E|). | ||
*/Graph.prototype.edges=function(){return Object.values(this._edgeObjs)}; | ||
* Gets edges of the graph. In case of compound graph subgraphs are not considered. | ||
* Complexity: O(|E|). | ||
*/edges(){return Object.values(this.#edgeObjs)} | ||
/** | ||
* Establish an edges path over the nodes in nodes list. If some edge is already | ||
* exists, it will update its label, otherwise it will create an edge between pair | ||
* of nodes with label provided or default label if no label provided. | ||
* Complexity: O(|nodes|). | ||
*/Graph.prototype.setPath=function(vs,value){var self=this;var args=arguments;vs.reduce(function(v,w){if(args.length>1){self.setEdge(v,w,value)}else{self.setEdge(v,w)}return w});return this}; | ||
* Establish an edges path over the nodes in nodes list. If some edge is already | ||
* exists, it will update its label, otherwise it will create an edge between pair | ||
* of nodes with label provided or default label if no label provided. | ||
* Complexity: O(|nodes|). | ||
*/setPath(vs,value){var self=this;var args=arguments;vs.reduce(function(v,w){if(args.length>1){self.setEdge(v,w,value)}else{self.setEdge(v,w)}return w});return this} | ||
/** | ||
* Creates or updates the label for the edge (v, w) with the optionally supplied | ||
* name. If label is supplied it is set as the value for the edge. If label is not | ||
* supplied and the edge was created by this call then the default edge label will | ||
* be assigned. The name parameter is only useful with multigraphs. | ||
*/Graph.prototype.setEdge=function(){var v,w,name,value;var valueSpecified=false;var arg0=arguments[0];if(typeof arg0==="object"&&arg0!==null&&"v"in arg0){v=arg0.v;w=arg0.w;name=arg0.name;if(arguments.length===2){value=arguments[1];valueSpecified=true}}else{v=arg0;w=arguments[1];name=arguments[3];if(arguments.length>2){value=arguments[2];valueSpecified=true}}v=""+v;w=""+w;if(name!==undefined){name=""+name}var e=edgeArgsToId(this._isDirected,v,w,name);if(this._edgeLabels.hasOwnProperty(e)){if(valueSpecified){this._edgeLabels[e]=value}return this}if(name!==undefined&&!this._isMultigraph){throw new Error("Cannot set a named edge when isMultigraph = false")} | ||
* Creates or updates the label for the edge (v, w) with the optionally supplied | ||
* name. If label is supplied it is set as the value for the edge. If label is not | ||
* supplied and the edge was created by this call then the default edge label will | ||
* be assigned. The name parameter is only useful with multigraphs. | ||
*/setEdge(){var v,w,name,value;var valueSpecified=false;var arg0=arguments[0];if(typeof arg0==="object"&&arg0!==null&&"v"in arg0){v=arg0.v;w=arg0.w;name=arg0.name;if(arguments.length===2){value=arguments[1];valueSpecified=true}}else{v=arg0;w=arguments[1];name=arguments[3];if(arguments.length>2){value=arguments[2];valueSpecified=true}}v=""+v;w=""+w;if(name!==undefined){name=""+name}var e=edgeArgsToId(this.#isDirected,v,w,name);if(this.#edgeLabels.hasOwnProperty(e)){if(valueSpecified){this.#edgeLabels[e]=value}return this}if(name!==undefined&&!this.#isMultigraph){throw new Error("Cannot set a named edge when isMultigraph = false")} | ||
// It didn't exist, so we need to create it. | ||
// First ensure the nodes exist. | ||
this.setNode(v);this.setNode(w);this._edgeLabels[e]=valueSpecified?value:this._defaultEdgeLabelFn(v,w,name);var edgeObj=edgeArgsToObj(this._isDirected,v,w,name); | ||
this.setNode(v);this.setNode(w);this.#edgeLabels[e]=valueSpecified?value:this.#defaultEdgeLabelFn(v,w,name);var edgeObj=edgeArgsToObj(this.#isDirected,v,w,name); | ||
// Ensure we add undirected edges in a consistent way. | ||
v=edgeObj.v;w=edgeObj.w;Object.freeze(edgeObj);this._edgeObjs[e]=edgeObj;incrementOrInitEntry(this._preds[w],v);incrementOrInitEntry(this._sucs[v],w);this._in[w][e]=edgeObj;this._out[v][e]=edgeObj;this._edgeCount++;return this}; | ||
v=edgeObj.v;w=edgeObj.w;Object.freeze(edgeObj);this.#edgeObjs[e]=edgeObj;incrementOrInitEntry(this.#preds[w],v);incrementOrInitEntry(this.#sucs[v],w);this.#in[w][e]=edgeObj;this.#out[v][e]=edgeObj;this.#edgeCount++;return this} | ||
/** | ||
* Gets the label for the specified edge. | ||
* Complexity: O(1). | ||
*/Graph.prototype.edge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);return this._edgeLabels[e]}; | ||
* Gets the label for the specified edge. | ||
* Complexity: O(1). | ||
*/edge(v,w,name){var e=arguments.length===1?edgeObjToId(this.#isDirected,arguments[0]):edgeArgsToId(this.#isDirected,v,w,name);return this.#edgeLabels[e]} | ||
/** | ||
* Detects whether the graph contains specified edge or not. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/Graph.prototype.hasEdge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);return this._edgeLabels.hasOwnProperty(e)}; | ||
* Detects whether the graph contains specified edge or not. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/hasEdge(v,w,name){var e=arguments.length===1?edgeObjToId(this.#isDirected,arguments[0]):edgeArgsToId(this.#isDirected,v,w,name);return this.#edgeLabels.hasOwnProperty(e)} | ||
/** | ||
* Removes the specified edge from the graph. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/Graph.prototype.removeEdge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);var edge=this._edgeObjs[e];if(edge){v=edge.v;w=edge.w;delete this._edgeLabels[e];delete this._edgeObjs[e];decrementOrRemoveEntry(this._preds[w],v);decrementOrRemoveEntry(this._sucs[v],w);delete this._in[w][e];delete this._out[v][e];this._edgeCount--}return this}; | ||
* Removes the specified edge from the graph. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/removeEdge(v,w,name){var e=arguments.length===1?edgeObjToId(this.#isDirected,arguments[0]):edgeArgsToId(this.#isDirected,v,w,name);var edge=this.#edgeObjs[e];if(edge){v=edge.v;w=edge.w;delete this.#edgeLabels[e];delete this.#edgeObjs[e];decrementOrRemoveEntry(this.#preds[w],v);decrementOrRemoveEntry(this.#sucs[v],w);delete this.#in[w][e];delete this.#out[v][e];this.#edgeCount--}return this} | ||
/** | ||
* Return all edges that point to the node v. Optionally filters those edges down to just those | ||
* coming from node u. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/Graph.prototype.inEdges=function(v,u){var inV=this._in[v];if(inV){var edges=Object.values(inV);if(!u){return edges}return edges.filter(edge=>edge.v===u)}}; | ||
* Return all edges that point to the node v. Optionally filters those edges down to just those | ||
* coming from node u. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/inEdges(v,u){var inV=this.#in[v];if(inV){var edges=Object.values(inV);if(!u){return edges}return edges.filter(edge=>edge.v===u)}} | ||
/** | ||
* Return all edges that are pointed at by node v. Optionally filters those edges down to just | ||
* those point to w. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/Graph.prototype.outEdges=function(v,w){var outV=this._out[v];if(outV){var edges=Object.values(outV);if(!w){return edges}return edges.filter(edge=>edge.w===w)}}; | ||
* Return all edges that are pointed at by node v. Optionally filters those edges down to just | ||
* those point to w. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/outEdges(v,w){var outV=this.#out[v];if(outV){var edges=Object.values(outV);if(!w){return edges}return edges.filter(edge=>edge.w===w)}} | ||
/** | ||
* Returns all edges to or from node v regardless of direction. Optionally filters those edges | ||
* down to just those between nodes v and w regardless of direction. | ||
* Complexity: O(|E|). | ||
*/Graph.prototype.nodeEdges=function(v,w){var inEdges=this.inEdges(v,w);if(inEdges){return inEdges.concat(this.outEdges(v,w))}};function incrementOrInitEntry(map,k){if(map[k]){map[k]++}else{map[k]=1}}function decrementOrRemoveEntry(map,k){if(!--map[k]){delete map[k]}}function edgeArgsToId(isDirected,v_,w_,name){var v=""+v_;var w=""+w_;if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}return v+EDGE_KEY_DELIM+w+EDGE_KEY_DELIM+(name===undefined?DEFAULT_EDGE_NAME:name)}function edgeArgsToObj(isDirected,v_,w_,name){var v=""+v_;var w=""+w_;if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}var edgeObj={v:v,w:w};if(name){edgeObj.name=name}return edgeObj}function edgeObjToId(isDirected,edgeObj){return edgeArgsToId(isDirected,edgeObj.v,edgeObj.w,edgeObj.name)}},{}],17:[function(require,module,exports){ | ||
* Returns all edges to or from node v regardless of direction. Optionally filters those edges | ||
* down to just those between nodes v and w regardless of direction. | ||
* Complexity: O(|E|). | ||
*/nodeEdges(v,w){var inEdges=this.inEdges(v,w);if(inEdges){return inEdges.concat(this.outEdges(v,w))}}}function incrementOrInitEntry(map,k){if(map[k]){map[k]++}else{map[k]=1}}function decrementOrRemoveEntry(map,k){if(!--map[k]){delete map[k]}}function edgeArgsToId(isDirected,v_,w_,name){var v=""+v_;var w=""+w_;if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}return v+EDGE_KEY_DELIM+w+EDGE_KEY_DELIM+(name===undefined?DEFAULT_EDGE_NAME:name)}function edgeArgsToObj(isDirected,v_,w_,name){var v=""+v_;var w=""+w_;if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}var edgeObj={v:v,w:w};if(name){edgeObj.name=name}return edgeObj}function edgeObjToId(isDirected,edgeObj){return edgeArgsToId(isDirected,edgeObj.v,edgeObj.w,edgeObj.name)}module.exports=Graph},{}],17:[function(require,module,exports){ | ||
// Includes only the "core" of graphlib | ||
@@ -303,2 +303,2 @@ module.exports={Graph:require("./graph"),version:require("./version")}},{"./graph":16,"./version":19}],18:[function(require,module,exports){var Graph=require("./graph");module.exports={write:write,read:read}; | ||
* // [ { v: 'a', w: 'b' } ] | ||
*/function read(json){var g=new Graph(json.options).setGraph(json.value);json.nodes.forEach(function(entry){g.setNode(entry.v,entry.value);if(entry.parent){g.setParent(entry.v,entry.parent)}});json.edges.forEach(function(entry){g.setEdge({v:entry.v,w:entry.w,name:entry.name},entry.value)});return g}},{"./graph":16}],19:[function(require,module,exports){module.exports="2.1.9"},{}]},{},[1])(1)}); | ||
*/function read(json){var g=new Graph(json.options).setGraph(json.value);json.nodes.forEach(function(entry){g.setNode(entry.v,entry.value);if(entry.parent){g.setParent(entry.v,entry.parent)}});json.edges.forEach(function(entry){g.setEdge({v:entry.v,w:entry.w,name:entry.name},entry.value)});return g}},{"./graph":16}],19:[function(require,module,exports){module.exports="2.1.11"},{}]},{},[1])(1)}); |
@@ -9,3 +9,3 @@ module.exports = dfs; | ||
* | ||
* Order must be one of "pre" or "post". | ||
* If the order is not "post", it will be treated as "pre". | ||
*/ | ||
@@ -17,7 +17,8 @@ function dfs(g, vs, order) { | ||
var navigation = (g.isDirected() ? g.successors : g.neighbors).bind(g); | ||
var navigation = g.isDirected() ? v => g.successors(v) : v => g.neighbors(v); | ||
var orderFunc = order === "post" ? postOrderDfs : preOrderDfs; | ||
var acc = []; | ||
var visited = {}; | ||
vs.forEach(function(v) { | ||
vs.forEach(v => { | ||
if (!g.hasNode(v)) { | ||
@@ -27,17 +28,43 @@ throw new Error("Graph does not have node: " + v); | ||
doDfs(g, v, order === "post", visited, navigation, acc); | ||
orderFunc(v, navigation, visited, acc); | ||
}); | ||
return acc; | ||
} | ||
function doDfs(g, v, postorder, visited, navigation, acc) { | ||
if (!visited.hasOwnProperty(v)) { | ||
visited[v] = true; | ||
function postOrderDfs(v, navigation, visited, acc) { | ||
var stack = [[v, false]]; | ||
while (stack.length > 0) { | ||
var curr = stack.pop(); | ||
if (curr[1]) { | ||
acc.push(curr[0]); | ||
} else { | ||
if (!visited.hasOwnProperty(curr[0])) { | ||
visited[curr[0]] = true; | ||
stack.push([curr[0], true]); | ||
forEachRight(navigation(curr[0]), w => stack.push([w, false])); | ||
} | ||
} | ||
} | ||
} | ||
if (!postorder) { acc.push(v); } | ||
navigation(v).forEach(function(w) { | ||
doDfs(g, w, postorder, visited, navigation, acc); | ||
}); | ||
if (postorder) { acc.push(v); } | ||
function preOrderDfs(v, navigation, visited, acc) { | ||
var stack = [v]; | ||
while (stack.length > 0) { | ||
var curr = stack.pop(); | ||
if (!visited.hasOwnProperty(curr)) { | ||
visited[curr] = true; | ||
acc.push(curr); | ||
forEachRight(navigation(curr), w => stack.push(w)); | ||
} | ||
} | ||
} | ||
function forEachRight(array, iteratee) { | ||
var length = array.length; | ||
while (length--) { | ||
iteratee(array[length], length, array); | ||
} | ||
return array; | ||
} |
@@ -1,4 +0,1 @@ | ||
module.exports = topsort; | ||
topsort.CycleException = CycleException; | ||
function topsort(g) { | ||
@@ -32,3 +29,9 @@ var visited = {}; | ||
function CycleException() {} | ||
CycleException.prototype = new Error(); // must be an instance of Error to pass testing | ||
class CycleException extends Error { | ||
constructor() { | ||
super(...arguments); | ||
} | ||
} | ||
module.exports = topsort; | ||
topsort.CycleException = CycleException; |
@@ -1,3 +0,1 @@ | ||
module.exports = PriorityQueue; | ||
/** | ||
@@ -10,142 +8,144 @@ * A min-priority queue data structure. This algorithm is derived from Cormen, | ||
*/ | ||
function PriorityQueue() { | ||
this._arr = []; | ||
this._keyIndices = {}; | ||
} | ||
class PriorityQueue { | ||
#arr = []; | ||
#keyIndices = {}; | ||
/** | ||
* Returns the number of elements in the queue. Takes `O(1)` time. | ||
*/ | ||
PriorityQueue.prototype.size = function() { | ||
return this._arr.length; | ||
}; | ||
/** | ||
* Returns the number of elements in the queue. Takes `O(1)` time. | ||
*/ | ||
size() { | ||
return this.#arr.length; | ||
} | ||
/** | ||
* Returns the keys that are in the queue. Takes `O(n)` time. | ||
*/ | ||
PriorityQueue.prototype.keys = function() { | ||
return this._arr.map(function(x) { return x.key; }); | ||
}; | ||
/** | ||
* Returns the keys that are in the queue. Takes `O(n)` time. | ||
*/ | ||
keys() { | ||
return this.#arr.map(function(x) { return x.key; }); | ||
} | ||
/** | ||
* Returns `true` if **key** is in the queue and `false` if not. | ||
*/ | ||
PriorityQueue.prototype.has = function(key) { | ||
return this._keyIndices.hasOwnProperty(key); | ||
}; | ||
/** | ||
* Returns `true` if **key** is in the queue and `false` if not. | ||
*/ | ||
has(key) { | ||
return this.#keyIndices.hasOwnProperty(key); | ||
} | ||
/** | ||
* Returns the priority for **key**. If **key** is not present in the queue | ||
* then this function returns `undefined`. Takes `O(1)` time. | ||
* | ||
* @param {Object} key | ||
*/ | ||
PriorityQueue.prototype.priority = function(key) { | ||
var index = this._keyIndices[key]; | ||
if (index !== undefined) { | ||
return this._arr[index].priority; | ||
/** | ||
* Returns the priority for **key**. If **key** is not present in the queue | ||
* then this function returns `undefined`. Takes `O(1)` time. | ||
* | ||
* @param {Object} key | ||
*/ | ||
priority(key) { | ||
var index = this.#keyIndices[key]; | ||
if (index !== undefined) { | ||
return this.#arr[index].priority; | ||
} | ||
} | ||
}; | ||
/** | ||
* Returns the key for the minimum element in this queue. If the queue is | ||
* empty this function throws an Error. Takes `O(1)` time. | ||
*/ | ||
PriorityQueue.prototype.min = function() { | ||
if (this.size() === 0) { | ||
throw new Error("Queue underflow"); | ||
/** | ||
* Returns the key for the minimum element in this queue. If the queue is | ||
* empty this function throws an Error. Takes `O(1)` time. | ||
*/ | ||
min() { | ||
if (this.size() === 0) { | ||
throw new Error("Queue underflow"); | ||
} | ||
return this.#arr[0].key; | ||
} | ||
return this._arr[0].key; | ||
}; | ||
/** | ||
* Inserts a new key into the priority queue. If the key already exists in | ||
* the queue this function returns `false`; otherwise it will return `true`. | ||
* Takes `O(n)` time. | ||
* | ||
* @param {Object} key the key to add | ||
* @param {Number} priority the initial priority for the key | ||
*/ | ||
PriorityQueue.prototype.add = function(key, priority) { | ||
var keyIndices = this._keyIndices; | ||
key = String(key); | ||
if (!keyIndices.hasOwnProperty(key)) { | ||
var arr = this._arr; | ||
var index = arr.length; | ||
keyIndices[key] = index; | ||
arr.push({key: key, priority: priority}); | ||
this._decrease(index); | ||
return true; | ||
/** | ||
* Inserts a new key into the priority queue. If the key already exists in | ||
* the queue this function returns `false`; otherwise it will return `true`. | ||
* Takes `O(n)` time. | ||
* | ||
* @param {Object} key the key to add | ||
* @param {Number} priority the initial priority for the key | ||
*/ | ||
add(key, priority) { | ||
var keyIndices = this.#keyIndices; | ||
key = String(key); | ||
if (!keyIndices.hasOwnProperty(key)) { | ||
var arr = this.#arr; | ||
var index = arr.length; | ||
keyIndices[key] = index; | ||
arr.push({key: key, priority: priority}); | ||
this.#decrease(index); | ||
return true; | ||
} | ||
return false; | ||
} | ||
return false; | ||
}; | ||
/** | ||
* Removes and returns the smallest key in the queue. Takes `O(log n)` time. | ||
*/ | ||
PriorityQueue.prototype.removeMin = function() { | ||
this._swap(0, this._arr.length - 1); | ||
var min = this._arr.pop(); | ||
delete this._keyIndices[min.key]; | ||
this._heapify(0); | ||
return min.key; | ||
}; | ||
/** | ||
* Removes and returns the smallest key in the queue. Takes `O(log n)` time. | ||
*/ | ||
removeMin() { | ||
this.#swap(0, this.#arr.length - 1); | ||
var min = this.#arr.pop(); | ||
delete this.#keyIndices[min.key]; | ||
this.#heapify(0); | ||
return min.key; | ||
} | ||
/** | ||
* Decreases the priority for **key** to **priority**. If the new priority is | ||
* greater than the previous priority, this function will throw an Error. | ||
* | ||
* @param {Object} key the key for which to raise priority | ||
* @param {Number} priority the new priority for the key | ||
*/ | ||
PriorityQueue.prototype.decrease = function(key, priority) { | ||
var index = this._keyIndices[key]; | ||
if (priority > this._arr[index].priority) { | ||
throw new Error("New priority is greater than current priority. " + | ||
"Key: " + key + " Old: " + this._arr[index].priority + " New: " + priority); | ||
/** | ||
* Decreases the priority for **key** to **priority**. If the new priority is | ||
* greater than the previous priority, this function will throw an Error. | ||
* | ||
* @param {Object} key the key for which to raise priority | ||
* @param {Number} priority the new priority for the key | ||
*/ | ||
decrease(key, priority) { | ||
var index = this.#keyIndices[key]; | ||
if (priority > this.#arr[index].priority) { | ||
throw new Error("New priority is greater than current priority. " + | ||
"Key: " + key + " Old: " + this.#arr[index].priority + " New: " + priority); | ||
} | ||
this.#arr[index].priority = priority; | ||
this.#decrease(index); | ||
} | ||
this._arr[index].priority = priority; | ||
this._decrease(index); | ||
}; | ||
PriorityQueue.prototype._heapify = function(i) { | ||
var arr = this._arr; | ||
var l = 2 * i; | ||
var r = l + 1; | ||
var largest = i; | ||
if (l < arr.length) { | ||
largest = arr[l].priority < arr[largest].priority ? l : largest; | ||
if (r < arr.length) { | ||
largest = arr[r].priority < arr[largest].priority ? r : largest; | ||
#heapify(i) { | ||
var arr = this.#arr; | ||
var l = 2 * i; | ||
var r = l + 1; | ||
var largest = i; | ||
if (l < arr.length) { | ||
largest = arr[l].priority < arr[largest].priority ? l : largest; | ||
if (r < arr.length) { | ||
largest = arr[r].priority < arr[largest].priority ? r : largest; | ||
} | ||
if (largest !== i) { | ||
this.#swap(i, largest); | ||
this.#heapify(largest); | ||
} | ||
} | ||
if (largest !== i) { | ||
this._swap(i, largest); | ||
this._heapify(largest); | ||
} | ||
} | ||
}; | ||
PriorityQueue.prototype._decrease = function(index) { | ||
var arr = this._arr; | ||
var priority = arr[index].priority; | ||
var parent; | ||
while (index !== 0) { | ||
parent = index >> 1; | ||
if (arr[parent].priority < priority) { | ||
break; | ||
#decrease(index) { | ||
var arr = this.#arr; | ||
var priority = arr[index].priority; | ||
var parent; | ||
while (index !== 0) { | ||
parent = index >> 1; | ||
if (arr[parent].priority < priority) { | ||
break; | ||
} | ||
this.#swap(index, parent); | ||
index = parent; | ||
} | ||
this._swap(index, parent); | ||
index = parent; | ||
} | ||
}; | ||
PriorityQueue.prototype._swap = function(i, j) { | ||
var arr = this._arr; | ||
var keyIndices = this._keyIndices; | ||
var origArrI = arr[i]; | ||
var origArrJ = arr[j]; | ||
arr[i] = origArrJ; | ||
arr[j] = origArrI; | ||
keyIndices[origArrJ.key] = i; | ||
keyIndices[origArrI.key] = j; | ||
}; | ||
#swap(i, j) { | ||
var arr = this.#arr; | ||
var keyIndices = this.#keyIndices; | ||
var origArrI = arr[i]; | ||
var origArrJ = arr[j]; | ||
arr[i] = origArrJ; | ||
arr[j] = origArrI; | ||
keyIndices[origArrJ.key] = i; | ||
keyIndices[origArrI.key] = j; | ||
} | ||
} | ||
module.exports = PriorityQueue; |
1019
lib/graph.js
"use strict"; | ||
module.exports = Graph; | ||
var DEFAULT_EDGE_NAME = "\x00"; | ||
@@ -19,622 +17,625 @@ var GRAPH_NODE = "\x00"; | ||
function Graph(opts) { | ||
this._isDirected = true; | ||
this._isMultigraph = false; | ||
this._isCompound = false; | ||
class Graph { | ||
#isDirected = true; | ||
#isMultigraph = false; | ||
#isCompound = false; | ||
if (opts) { | ||
this._isDirected = opts.hasOwnProperty("directed") ? opts.directed : true; | ||
this._isMultigraph = opts.hasOwnProperty("multigraph") ? opts.multigraph : false; | ||
this._isCompound = opts.hasOwnProperty("compound") ? opts.compound : false; | ||
} | ||
// Label for the graph itself | ||
this._label = undefined; | ||
#label; | ||
// Defaults to be set when creating a new node | ||
this._defaultNodeLabelFn = () => undefined; | ||
#defaultNodeLabelFn = () => undefined; | ||
// Defaults to be set when creating a new edge | ||
this._defaultEdgeLabelFn = () => undefined; | ||
#defaultEdgeLabelFn = () => undefined; | ||
// v -> label | ||
this._nodes = {}; | ||
#nodes = {}; | ||
if (this._isCompound) { | ||
// v -> parent | ||
this._parent = {}; | ||
// v -> children | ||
this._children = {}; | ||
this._children[GRAPH_NODE] = {}; | ||
} | ||
// v -> edgeObj | ||
this._in = {}; | ||
#in = {}; | ||
// u -> v -> Number | ||
this._preds = {}; | ||
#preds = {}; | ||
// v -> edgeObj | ||
this._out = {}; | ||
#out = {}; | ||
// v -> w -> Number | ||
this._sucs = {}; | ||
#sucs = {}; | ||
// e -> edgeObj | ||
this._edgeObjs = {}; | ||
#edgeObjs = {}; | ||
// e -> label | ||
this._edgeLabels = {}; | ||
} | ||
#edgeLabels = {}; | ||
/* Number of nodes in the graph. Should only be changed by the implementation. */ | ||
Graph.prototype._nodeCount = 0; | ||
/* Number of nodes in the graph. Should only be changed by the implementation. */ | ||
#nodeCount = 0; | ||
/* Number of edges in the graph. Should only be changed by the implementation. */ | ||
Graph.prototype._edgeCount = 0; | ||
/* Number of edges in the graph. Should only be changed by the implementation. */ | ||
#edgeCount = 0; | ||
#parent; | ||
/* === Graph functions ========= */ | ||
#children; | ||
/** | ||
* Whether graph was created with 'directed' flag set to true or not. | ||
*/ | ||
Graph.prototype.isDirected = function() { | ||
return this._isDirected; | ||
}; | ||
constructor(opts) { | ||
if (opts) { | ||
this.#isDirected = opts.hasOwnProperty("directed") ? opts.directed : true; | ||
this.#isMultigraph = opts.hasOwnProperty("multigraph") ? opts.multigraph : false; | ||
this.#isCompound = opts.hasOwnProperty("compound") ? opts.compound : false; | ||
} | ||
/** | ||
* Whether graph was created with 'multigraph' flag set to true or not. | ||
*/ | ||
Graph.prototype.isMultigraph = function() { | ||
return this._isMultigraph; | ||
}; | ||
if (this.#isCompound) { | ||
// v -> parent | ||
this.#parent = {}; | ||
/** | ||
* Whether graph was created with 'compound' flag set to true or not. | ||
*/ | ||
Graph.prototype.isCompound = function() { | ||
return this._isCompound; | ||
}; | ||
// v -> children | ||
this.#children = {}; | ||
this.#children[GRAPH_NODE] = {}; | ||
} | ||
} | ||
/** | ||
* Sets the label of the graph. | ||
*/ | ||
Graph.prototype.setGraph = function(label) { | ||
this._label = label; | ||
return this; | ||
}; | ||
/* === Graph functions ========= */ | ||
/** | ||
* Gets the graph label. | ||
*/ | ||
Graph.prototype.graph = function() { | ||
return this._label; | ||
}; | ||
/** | ||
* Whether graph was created with 'directed' flag set to true or not. | ||
*/ | ||
isDirected() { | ||
return this.#isDirected; | ||
} | ||
/** | ||
* Whether graph was created with 'multigraph' flag set to true or not. | ||
*/ | ||
isMultigraph() { | ||
return this.#isMultigraph; | ||
} | ||
/* === Node functions ========== */ | ||
/** | ||
* Whether graph was created with 'compound' flag set to true or not. | ||
*/ | ||
isCompound() { | ||
return this.#isCompound; | ||
} | ||
/** | ||
* Sets the default node label. If newDefault is a function, it will be | ||
* invoked ach time when setting a label for a node. Otherwise, this label | ||
* will be assigned as default label in case if no label was specified while | ||
* setting a node. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.setDefaultNodeLabel = function(newDefault) { | ||
this._defaultNodeLabelFn = newDefault; | ||
if (typeof newDefault !== 'function') { | ||
this._defaultNodeLabelFn = () => newDefault; | ||
/** | ||
* Sets the label of the graph. | ||
*/ | ||
setGraph(label) { | ||
this.#label = label; | ||
return this; | ||
} | ||
return this; | ||
}; | ||
/** | ||
* Gets the graph label. | ||
*/ | ||
graph() { | ||
return this.#label; | ||
} | ||
/** | ||
* Gets the number of nodes in the graph. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.nodeCount = function() { | ||
return this._nodeCount; | ||
}; | ||
/** | ||
* Gets all nodes of the graph. Note, the in case of compound graph subnodes are | ||
* not included in list. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.nodes = function() { | ||
return Object.keys(this._nodes); | ||
}; | ||
/* === Node functions ========== */ | ||
/** | ||
* Gets list of nodes without in-edges. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.sources = function() { | ||
var self = this; | ||
return this.nodes().filter(v => Object.keys(self._in[v]).length === 0); | ||
}; | ||
/** | ||
* Gets list of nodes without out-edges. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.sinks = function() { | ||
var self = this; | ||
return this.nodes().filter(v => Object.keys(self._out[v]).length === 0); | ||
}; | ||
/** | ||
* Invokes setNode method for each node in names list. | ||
* Complexity: O(|names|). | ||
*/ | ||
Graph.prototype.setNodes = function(vs, value) { | ||
var args = arguments; | ||
var self = this; | ||
vs.forEach(function(v) { | ||
if (args.length > 1) { | ||
self.setNode(v, value); | ||
} else { | ||
self.setNode(v); | ||
/** | ||
* Sets the default node label. If newDefault is a function, it will be | ||
* invoked ach time when setting a label for a node. Otherwise, this label | ||
* will be assigned as default label in case if no label was specified while | ||
* setting a node. | ||
* Complexity: O(1). | ||
*/ | ||
setDefaultNodeLabel(newDefault) { | ||
this.#defaultNodeLabelFn = newDefault; | ||
if (typeof newDefault !== 'function') { | ||
this.#defaultNodeLabelFn = () => newDefault; | ||
} | ||
}); | ||
return this; | ||
}; | ||
/** | ||
* Creates or updates the value for the node v in the graph. If label is supplied | ||
* it is set as the value for the node. If label is not supplied and the node was | ||
* created by this call then the default node label will be assigned. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.setNode = function(v, value) { | ||
if (this._nodes.hasOwnProperty(v)) { | ||
if (arguments.length > 1) { | ||
this._nodes[v] = value; | ||
} | ||
return this; | ||
} | ||
this._nodes[v] = arguments.length > 1 ? value : this._defaultNodeLabelFn(v); | ||
if (this._isCompound) { | ||
this._parent[v] = GRAPH_NODE; | ||
this._children[v] = {}; | ||
this._children[GRAPH_NODE][v] = true; | ||
/** | ||
* Gets the number of nodes in the graph. | ||
* Complexity: O(1). | ||
*/ | ||
nodeCount() { | ||
return this.#nodeCount; | ||
} | ||
this._in[v] = {}; | ||
this._preds[v] = {}; | ||
this._out[v] = {}; | ||
this._sucs[v] = {}; | ||
++this._nodeCount; | ||
return this; | ||
}; | ||
/** | ||
* Gets the label of node with specified name. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.node = function(v) { | ||
return this._nodes[v]; | ||
}; | ||
/** | ||
* Gets all nodes of the graph. Note, the in case of compound graph subnodes are | ||
* not included in list. | ||
* Complexity: O(1). | ||
*/ | ||
nodes() { | ||
return Object.keys(this.#nodes); | ||
} | ||
/** | ||
* Detects whether graph has a node with specified name or not. | ||
*/ | ||
Graph.prototype.hasNode = function(v) { | ||
return this._nodes.hasOwnProperty(v); | ||
}; | ||
/** | ||
* Gets list of nodes without in-edges. | ||
* Complexity: O(|V|). | ||
*/ | ||
sources() { | ||
var self = this; | ||
return this.nodes().filter(v => Object.keys(self.#in[v]).length === 0); | ||
} | ||
/** | ||
* Remove the node with the name from the graph or do nothing if the node is not in | ||
* the graph. If the node was removed this function also removes any incident | ||
* edges. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.removeNode = function(v) { | ||
var self = this; | ||
if (this._nodes.hasOwnProperty(v)) { | ||
var removeEdge = e => self.removeEdge(self._edgeObjs[e]); | ||
delete this._nodes[v]; | ||
if (this._isCompound) { | ||
this._removeFromParentsChildList(v); | ||
delete this._parent[v]; | ||
this.children(v).forEach(function(child) { | ||
self.setParent(child); | ||
}); | ||
delete this._children[v]; | ||
} | ||
Object.keys(this._in[v]).forEach(removeEdge); | ||
delete this._in[v]; | ||
delete this._preds[v]; | ||
Object.keys(this._out[v]).forEach(removeEdge); | ||
delete this._out[v]; | ||
delete this._sucs[v]; | ||
--this._nodeCount; | ||
/** | ||
* Gets list of nodes without out-edges. | ||
* Complexity: O(|V|). | ||
*/ | ||
sinks() { | ||
var self = this; | ||
return this.nodes().filter(v => Object.keys(self.#out[v]).length === 0); | ||
} | ||
return this; | ||
}; | ||
/** | ||
* Sets node p as a parent for node v if it is defined, or removes the | ||
* parent for v if p is undefined. Method throws an exception in case of | ||
* invoking it in context of noncompound graph. | ||
* Average-case complexity: O(1). | ||
*/ | ||
Graph.prototype.setParent = function(v, parent) { | ||
if (!this._isCompound) { | ||
throw new Error("Cannot set parent in a non-compound graph"); | ||
/** | ||
* Invokes setNode method for each node in names list. | ||
* Complexity: O(|names|). | ||
*/ | ||
setNodes(vs, value) { | ||
var args = arguments; | ||
var self = this; | ||
vs.forEach(function(v) { | ||
if (args.length > 1) { | ||
self.setNode(v, value); | ||
} else { | ||
self.setNode(v); | ||
} | ||
}); | ||
return this; | ||
} | ||
if (parent === undefined) { | ||
parent = GRAPH_NODE; | ||
} else { | ||
// Coerce parent to string | ||
parent += ""; | ||
for (var ancestor = parent; | ||
ancestor !== undefined; | ||
ancestor = this.parent(ancestor)) { | ||
if (ancestor === v) { | ||
throw new Error("Setting " + parent+ " as parent of " + v + | ||
" would create a cycle"); | ||
/** | ||
* Creates or updates the value for the node v in the graph. If label is supplied | ||
* it is set as the value for the node. If label is not supplied and the node was | ||
* created by this call then the default node label will be assigned. | ||
* Complexity: O(1). | ||
*/ | ||
setNode(v, value) { | ||
if (this.#nodes.hasOwnProperty(v)) { | ||
if (arguments.length > 1) { | ||
this.#nodes[v] = value; | ||
} | ||
return this; | ||
} | ||
this.setNode(parent); | ||
this.#nodes[v] = arguments.length > 1 ? value : this.#defaultNodeLabelFn(v); | ||
if (this.#isCompound) { | ||
this.#parent[v] = GRAPH_NODE; | ||
this.#children[v] = {}; | ||
this.#children[GRAPH_NODE][v] = true; | ||
} | ||
this.#in[v] = {}; | ||
this.#preds[v] = {}; | ||
this.#out[v] = {}; | ||
this.#sucs[v] = {}; | ||
++this.#nodeCount; | ||
return this; | ||
} | ||
this.setNode(v); | ||
this._removeFromParentsChildList(v); | ||
this._parent[v] = parent; | ||
this._children[parent][v] = true; | ||
return this; | ||
}; | ||
/** | ||
* Gets the label of node with specified name. | ||
* Complexity: O(|V|). | ||
*/ | ||
node(v) { | ||
return this.#nodes[v]; | ||
} | ||
Graph.prototype._removeFromParentsChildList = function(v) { | ||
delete this._children[this._parent[v]][v]; | ||
}; | ||
/** | ||
* Detects whether graph has a node with specified name or not. | ||
*/ | ||
hasNode(v) { | ||
return this.#nodes.hasOwnProperty(v); | ||
} | ||
/** | ||
* Gets parent node for node v. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.parent = function(v) { | ||
if (this._isCompound) { | ||
var parent = this._parent[v]; | ||
if (parent !== GRAPH_NODE) { | ||
return parent; | ||
/** | ||
* Remove the node with the name from the graph or do nothing if the node is not in | ||
* the graph. If the node was removed this function also removes any incident | ||
* edges. | ||
* Complexity: O(1). | ||
*/ | ||
removeNode(v) { | ||
var self = this; | ||
if (this.#nodes.hasOwnProperty(v)) { | ||
var removeEdge = e => self.removeEdge(self.#edgeObjs[e]); | ||
delete this.#nodes[v]; | ||
if (this.#isCompound) { | ||
this.#removeFromParentsChildList(v); | ||
delete this.#parent[v]; | ||
this.children(v).forEach(function(child) { | ||
self.setParent(child); | ||
}); | ||
delete this.#children[v]; | ||
} | ||
Object.keys(this.#in[v]).forEach(removeEdge); | ||
delete this.#in[v]; | ||
delete this.#preds[v]; | ||
Object.keys(this.#out[v]).forEach(removeEdge); | ||
delete this.#out[v]; | ||
delete this.#sucs[v]; | ||
--this.#nodeCount; | ||
} | ||
return this; | ||
} | ||
}; | ||
/** | ||
* Gets list of direct children of node v. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.children = function(v = GRAPH_NODE) { | ||
if (this._isCompound) { | ||
var children = this._children[v]; | ||
if (children) { | ||
return Object.keys(children); | ||
/** | ||
* Sets node p as a parent for node v if it is defined, or removes the | ||
* parent for v if p is undefined. Method throws an exception in case of | ||
* invoking it in context of noncompound graph. | ||
* Average-case complexity: O(1). | ||
*/ | ||
setParent(v, parent) { | ||
if (!this.#isCompound) { | ||
throw new Error("Cannot set parent in a non-compound graph"); | ||
} | ||
} else if (v === GRAPH_NODE) { | ||
return this.nodes(); | ||
} else if (this.hasNode(v)) { | ||
return []; | ||
if (parent === undefined) { | ||
parent = GRAPH_NODE; | ||
} else { | ||
// Coerce parent to string | ||
parent += ""; | ||
for (var ancestor = parent; ancestor !== undefined; ancestor = this.parent(ancestor)) { | ||
if (ancestor === v) { | ||
throw new Error("Setting " + parent+ " as parent of " + v + | ||
" would create a cycle"); | ||
} | ||
} | ||
this.setNode(parent); | ||
} | ||
this.setNode(v); | ||
this.#removeFromParentsChildList(v); | ||
this.#parent[v] = parent; | ||
this.#children[parent][v] = true; | ||
return this; | ||
} | ||
}; | ||
/** | ||
* Return all nodes that are predecessors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.predecessors = function(v) { | ||
var predsV = this._preds[v]; | ||
if (predsV) { | ||
return Object.keys(predsV); | ||
#removeFromParentsChildList(v) { | ||
delete this.#children[this.#parent[v]][v]; | ||
} | ||
}; | ||
/** | ||
* Return all nodes that are successors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.successors = function(v) { | ||
var sucsV = this._sucs[v]; | ||
if (sucsV) { | ||
return Object.keys(sucsV); | ||
/** | ||
* Gets parent node for node v. | ||
* Complexity: O(1). | ||
*/ | ||
parent(v) { | ||
if (this.#isCompound) { | ||
var parent = this.#parent[v]; | ||
if (parent !== GRAPH_NODE) { | ||
return parent; | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* Return all nodes that are predecessors or successors of the specified node or undefined if | ||
* node v is not in the graph. | ||
* Complexity: O(|V|). | ||
*/ | ||
Graph.prototype.neighbors = function(v) { | ||
var preds = this.predecessors(v); | ||
if (preds) { | ||
const union = new Set(preds); | ||
for (var succ of this.successors(v)) { | ||
union.add(succ); | ||
/** | ||
* Gets list of direct children of node v. | ||
* Complexity: O(1). | ||
*/ | ||
children(v = GRAPH_NODE) { | ||
if (this.#isCompound) { | ||
var children = this.#children[v]; | ||
if (children) { | ||
return Object.keys(children); | ||
} | ||
} else if (v === GRAPH_NODE) { | ||
return this.nodes(); | ||
} else if (this.hasNode(v)) { | ||
return []; | ||
} | ||
} | ||
return Array.from(union.values()); | ||
/** | ||
* Return all nodes that are predecessors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/ | ||
predecessors(v) { | ||
var predsV = this.#preds[v]; | ||
if (predsV) { | ||
return Object.keys(predsV); | ||
} | ||
} | ||
}; | ||
Graph.prototype.isLeaf = function (v) { | ||
var neighbors; | ||
if (this.isDirected()) { | ||
neighbors = this.successors(v); | ||
} else { | ||
neighbors = this.neighbors(v); | ||
/** | ||
* Return all nodes that are successors of the specified node or undefined if node v is not in | ||
* the graph. Behavior is undefined for undirected graphs - use neighbors instead. | ||
* Complexity: O(|V|). | ||
*/ | ||
successors(v) { | ||
var sucsV = this.#sucs[v]; | ||
if (sucsV) { | ||
return Object.keys(sucsV); | ||
} | ||
} | ||
return neighbors.length === 0; | ||
}; | ||
/** | ||
* Creates new graph with nodes filtered via filter. Edges incident to rejected node | ||
* are also removed. In case of compound graph, if parent is rejected by filter, | ||
* than all its children are rejected too. | ||
* Average-case complexity: O(|E|+|V|). | ||
*/ | ||
Graph.prototype.filterNodes = function(filter) { | ||
var copy = new this.constructor({ | ||
directed: this._isDirected, | ||
multigraph: this._isMultigraph, | ||
compound: this._isCompound | ||
}); | ||
/** | ||
* Return all nodes that are predecessors or successors of the specified node or undefined if | ||
* node v is not in the graph. | ||
* Complexity: O(|V|). | ||
*/ | ||
neighbors(v) { | ||
var preds = this.predecessors(v); | ||
if (preds) { | ||
const union = new Set(preds); | ||
for (var succ of this.successors(v)) { | ||
union.add(succ); | ||
} | ||
copy.setGraph(this.graph()); | ||
var self = this; | ||
Object.entries(this._nodes).forEach(function([v, value]) { | ||
if (filter(v)) { | ||
copy.setNode(v, value); | ||
return Array.from(union.values()); | ||
} | ||
}); | ||
} | ||
Object.values(this._edgeObjs).forEach(function(e) { | ||
if (copy.hasNode(e.v) && copy.hasNode(e.w)) { | ||
copy.setEdge(e, self.edge(e)); | ||
} | ||
}); | ||
var parents = {}; | ||
function findParent(v) { | ||
var parent = self.parent(v); | ||
if (parent === undefined || copy.hasNode(parent)) { | ||
parents[v] = parent; | ||
return parent; | ||
} else if (parent in parents) { | ||
return parents[parent]; | ||
isLeaf(v) { | ||
var neighbors; | ||
if (this.isDirected()) { | ||
neighbors = this.successors(v); | ||
} else { | ||
return findParent(parent); | ||
neighbors = this.neighbors(v); | ||
} | ||
return neighbors.length === 0; | ||
} | ||
if (this._isCompound) { | ||
copy.nodes().forEach(v => copy.setParent(v, findParent(v))); | ||
} | ||
/** | ||
* Creates new graph with nodes filtered via filter. Edges incident to rejected node | ||
* are also removed. In case of compound graph, if parent is rejected by filter, | ||
* than all its children are rejected too. | ||
* Average-case complexity: O(|E|+|V|). | ||
*/ | ||
filterNodes(filter) { | ||
var copy = new this.constructor({ | ||
directed: this.#isDirected, | ||
multigraph: this.#isMultigraph, | ||
compound: this.#isCompound | ||
}); | ||
return copy; | ||
}; | ||
copy.setGraph(this.graph()); | ||
/* === Edge functions ========== */ | ||
var self = this; | ||
Object.entries(this.#nodes).forEach(function([v, value]) { | ||
if (filter(v)) { | ||
copy.setNode(v, value); | ||
} | ||
}); | ||
/** | ||
* Sets the default edge label or factory function. This label will be | ||
* assigned as default label in case if no label was specified while setting | ||
* an edge or this function will be invoked each time when setting an edge | ||
* with no label specified and returned value * will be used as a label for edge. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.setDefaultEdgeLabel = function(newDefault) { | ||
this._defaultEdgeLabelFn = newDefault; | ||
if (typeof newDefault !== 'function') { | ||
this._defaultEdgeLabelFn = () => newDefault; | ||
} | ||
Object.values(this.#edgeObjs).forEach(function(e) { | ||
if (copy.hasNode(e.v) && copy.hasNode(e.w)) { | ||
copy.setEdge(e, self.edge(e)); | ||
} | ||
}); | ||
return this; | ||
}; | ||
var parents = {}; | ||
function findParent(v) { | ||
var parent = self.parent(v); | ||
if (parent === undefined || copy.hasNode(parent)) { | ||
parents[v] = parent; | ||
return parent; | ||
} else if (parent in parents) { | ||
return parents[parent]; | ||
} else { | ||
return findParent(parent); | ||
} | ||
} | ||
/** | ||
* Gets the number of edges in the graph. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.edgeCount = function() { | ||
return this._edgeCount; | ||
}; | ||
if (this.#isCompound) { | ||
copy.nodes().forEach(v => copy.setParent(v, findParent(v))); | ||
} | ||
/** | ||
* Gets edges of the graph. In case of compound graph subgraphs are not considered. | ||
* Complexity: O(|E|). | ||
*/ | ||
Graph.prototype.edges = function() { | ||
return Object.values(this._edgeObjs); | ||
}; | ||
return copy; | ||
} | ||
/** | ||
* Establish an edges path over the nodes in nodes list. If some edge is already | ||
* exists, it will update its label, otherwise it will create an edge between pair | ||
* of nodes with label provided or default label if no label provided. | ||
* Complexity: O(|nodes|). | ||
*/ | ||
Graph.prototype.setPath = function(vs, value) { | ||
var self = this; | ||
var args = arguments; | ||
vs.reduce(function(v, w) { | ||
if (args.length > 1) { | ||
self.setEdge(v, w, value); | ||
} else { | ||
self.setEdge(v, w); | ||
/* === Edge functions ========== */ | ||
/** | ||
* Sets the default edge label or factory function. This label will be | ||
* assigned as default label in case if no label was specified while setting | ||
* an edge or this function will be invoked each time when setting an edge | ||
* with no label specified and returned value * will be used as a label for edge. | ||
* Complexity: O(1). | ||
*/ | ||
setDefaultEdgeLabel(newDefault) { | ||
this.#defaultEdgeLabelFn = newDefault; | ||
if (typeof newDefault !== 'function') { | ||
this.#defaultEdgeLabelFn = () => newDefault; | ||
} | ||
return w; | ||
}); | ||
return this; | ||
}; | ||
/** | ||
* Creates or updates the label for the edge (v, w) with the optionally supplied | ||
* name. If label is supplied it is set as the value for the edge. If label is not | ||
* supplied and the edge was created by this call then the default edge label will | ||
* be assigned. The name parameter is only useful with multigraphs. | ||
*/ | ||
Graph.prototype.setEdge = function() { | ||
var v, w, name, value; | ||
var valueSpecified = false; | ||
var arg0 = arguments[0]; | ||
return this; | ||
} | ||
if (typeof arg0 === "object" && arg0 !== null && "v" in arg0) { | ||
v = arg0.v; | ||
w = arg0.w; | ||
name = arg0.name; | ||
if (arguments.length === 2) { | ||
value = arguments[1]; | ||
valueSpecified = true; | ||
} | ||
} else { | ||
v = arg0; | ||
w = arguments[1]; | ||
name = arguments[3]; | ||
if (arguments.length > 2) { | ||
value = arguments[2]; | ||
valueSpecified = true; | ||
} | ||
/** | ||
* Gets the number of edges in the graph. | ||
* Complexity: O(1). | ||
*/ | ||
edgeCount() { | ||
return this.#edgeCount; | ||
} | ||
v = "" + v; | ||
w = "" + w; | ||
if (name !== undefined) { | ||
name = "" + name; | ||
/** | ||
* Gets edges of the graph. In case of compound graph subgraphs are not considered. | ||
* Complexity: O(|E|). | ||
*/ | ||
edges() { | ||
return Object.values(this.#edgeObjs); | ||
} | ||
var e = edgeArgsToId(this._isDirected, v, w, name); | ||
if (this._edgeLabels.hasOwnProperty(e)) { | ||
if (valueSpecified) { | ||
this._edgeLabels[e] = value; | ||
} | ||
/** | ||
* Establish an edges path over the nodes in nodes list. If some edge is already | ||
* exists, it will update its label, otherwise it will create an edge between pair | ||
* of nodes with label provided or default label if no label provided. | ||
* Complexity: O(|nodes|). | ||
*/ | ||
setPath(vs, value) { | ||
var self = this; | ||
var args = arguments; | ||
vs.reduce(function(v, w) { | ||
if (args.length > 1) { | ||
self.setEdge(v, w, value); | ||
} else { | ||
self.setEdge(v, w); | ||
} | ||
return w; | ||
}); | ||
return this; | ||
} | ||
if (name !== undefined && !this._isMultigraph) { | ||
throw new Error("Cannot set a named edge when isMultigraph = false"); | ||
} | ||
/** | ||
* Creates or updates the label for the edge (v, w) with the optionally supplied | ||
* name. If label is supplied it is set as the value for the edge. If label is not | ||
* supplied and the edge was created by this call then the default edge label will | ||
* be assigned. The name parameter is only useful with multigraphs. | ||
*/ | ||
setEdge() { | ||
var v, w, name, value; | ||
var valueSpecified = false; | ||
var arg0 = arguments[0]; | ||
// It didn't exist, so we need to create it. | ||
// First ensure the nodes exist. | ||
this.setNode(v); | ||
this.setNode(w); | ||
if (typeof arg0 === "object" && arg0 !== null && "v" in arg0) { | ||
v = arg0.v; | ||
w = arg0.w; | ||
name = arg0.name; | ||
if (arguments.length === 2) { | ||
value = arguments[1]; | ||
valueSpecified = true; | ||
} | ||
} else { | ||
v = arg0; | ||
w = arguments[1]; | ||
name = arguments[3]; | ||
if (arguments.length > 2) { | ||
value = arguments[2]; | ||
valueSpecified = true; | ||
} | ||
} | ||
this._edgeLabels[e] = valueSpecified ? value : this._defaultEdgeLabelFn(v, w, name); | ||
v = "" + v; | ||
w = "" + w; | ||
if (name !== undefined) { | ||
name = "" + name; | ||
} | ||
var edgeObj = edgeArgsToObj(this._isDirected, v, w, name); | ||
// Ensure we add undirected edges in a consistent way. | ||
v = edgeObj.v; | ||
w = edgeObj.w; | ||
var e = edgeArgsToId(this.#isDirected, v, w, name); | ||
if (this.#edgeLabels.hasOwnProperty(e)) { | ||
if (valueSpecified) { | ||
this.#edgeLabels[e] = value; | ||
} | ||
return this; | ||
} | ||
Object.freeze(edgeObj); | ||
this._edgeObjs[e] = edgeObj; | ||
incrementOrInitEntry(this._preds[w], v); | ||
incrementOrInitEntry(this._sucs[v], w); | ||
this._in[w][e] = edgeObj; | ||
this._out[v][e] = edgeObj; | ||
this._edgeCount++; | ||
return this; | ||
}; | ||
if (name !== undefined && !this.#isMultigraph) { | ||
throw new Error("Cannot set a named edge when isMultigraph = false"); | ||
} | ||
/** | ||
* Gets the label for the specified edge. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.edge = function(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this._isDirected, arguments[0]) | ||
: edgeArgsToId(this._isDirected, v, w, name)); | ||
return this._edgeLabels[e]; | ||
}; | ||
// It didn't exist, so we need to create it. | ||
// First ensure the nodes exist. | ||
this.setNode(v); | ||
this.setNode(w); | ||
/** | ||
* Detects whether the graph contains specified edge or not. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.hasEdge = function(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this._isDirected, arguments[0]) | ||
: edgeArgsToId(this._isDirected, v, w, name)); | ||
return this._edgeLabels.hasOwnProperty(e); | ||
}; | ||
this.#edgeLabels[e] = valueSpecified ? value : this.#defaultEdgeLabelFn(v, w, name); | ||
/** | ||
* Removes the specified edge from the graph. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/ | ||
Graph.prototype.removeEdge = function(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this._isDirected, arguments[0]) | ||
: edgeArgsToId(this._isDirected, v, w, name)); | ||
var edge = this._edgeObjs[e]; | ||
if (edge) { | ||
v = edge.v; | ||
w = edge.w; | ||
delete this._edgeLabels[e]; | ||
delete this._edgeObjs[e]; | ||
decrementOrRemoveEntry(this._preds[w], v); | ||
decrementOrRemoveEntry(this._sucs[v], w); | ||
delete this._in[w][e]; | ||
delete this._out[v][e]; | ||
this._edgeCount--; | ||
var edgeObj = edgeArgsToObj(this.#isDirected, v, w, name); | ||
// Ensure we add undirected edges in a consistent way. | ||
v = edgeObj.v; | ||
w = edgeObj.w; | ||
Object.freeze(edgeObj); | ||
this.#edgeObjs[e] = edgeObj; | ||
incrementOrInitEntry(this.#preds[w], v); | ||
incrementOrInitEntry(this.#sucs[v], w); | ||
this.#in[w][e] = edgeObj; | ||
this.#out[v][e] = edgeObj; | ||
this.#edgeCount++; | ||
return this; | ||
} | ||
return this; | ||
}; | ||
/** | ||
* Return all edges that point to the node v. Optionally filters those edges down to just those | ||
* coming from node u. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/ | ||
Graph.prototype.inEdges = function(v, u) { | ||
var inV = this._in[v]; | ||
if (inV) { | ||
var edges = Object.values(inV); | ||
if (!u) { | ||
return edges; | ||
/** | ||
* Gets the label for the specified edge. | ||
* Complexity: O(1). | ||
*/ | ||
edge(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this.#isDirected, arguments[0]) | ||
: edgeArgsToId(this.#isDirected, v, w, name)); | ||
return this.#edgeLabels[e]; | ||
} | ||
/** | ||
* Detects whether the graph contains specified edge or not. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/ | ||
hasEdge(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this.#isDirected, arguments[0]) | ||
: edgeArgsToId(this.#isDirected, v, w, name)); | ||
return this.#edgeLabels.hasOwnProperty(e); | ||
} | ||
/** | ||
* Removes the specified edge from the graph. No subgraphs are considered. | ||
* Complexity: O(1). | ||
*/ | ||
removeEdge(v, w, name) { | ||
var e = (arguments.length === 1 | ||
? edgeObjToId(this.#isDirected, arguments[0]) | ||
: edgeArgsToId(this.#isDirected, v, w, name)); | ||
var edge = this.#edgeObjs[e]; | ||
if (edge) { | ||
v = edge.v; | ||
w = edge.w; | ||
delete this.#edgeLabels[e]; | ||
delete this.#edgeObjs[e]; | ||
decrementOrRemoveEntry(this.#preds[w], v); | ||
decrementOrRemoveEntry(this.#sucs[v], w); | ||
delete this.#in[w][e]; | ||
delete this.#out[v][e]; | ||
this.#edgeCount--; | ||
} | ||
return edges.filter(edge => edge.v === u); | ||
return this; | ||
} | ||
}; | ||
/** | ||
* Return all edges that are pointed at by node v. Optionally filters those edges down to just | ||
* those point to w. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/ | ||
Graph.prototype.outEdges = function(v, w) { | ||
var outV = this._out[v]; | ||
if (outV) { | ||
var edges = Object.values(outV); | ||
if (!w) { | ||
return edges; | ||
/** | ||
* Return all edges that point to the node v. Optionally filters those edges down to just those | ||
* coming from node u. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/ | ||
inEdges(v, u) { | ||
var inV = this.#in[v]; | ||
if (inV) { | ||
var edges = Object.values(inV); | ||
if (!u) { | ||
return edges; | ||
} | ||
return edges.filter(edge => edge.v === u); | ||
} | ||
return edges.filter(edge => edge.w === w); | ||
} | ||
}; | ||
/** | ||
* Returns all edges to or from node v regardless of direction. Optionally filters those edges | ||
* down to just those between nodes v and w regardless of direction. | ||
* Complexity: O(|E|). | ||
*/ | ||
Graph.prototype.nodeEdges = function(v, w) { | ||
var inEdges = this.inEdges(v, w); | ||
if (inEdges) { | ||
return inEdges.concat(this.outEdges(v, w)); | ||
/** | ||
* Return all edges that are pointed at by node v. Optionally filters those edges down to just | ||
* those point to w. Behavior is undefined for undirected graphs - use nodeEdges instead. | ||
* Complexity: O(|E|). | ||
*/ | ||
outEdges(v, w) { | ||
var outV = this.#out[v]; | ||
if (outV) { | ||
var edges = Object.values(outV); | ||
if (!w) { | ||
return edges; | ||
} | ||
return edges.filter(edge => edge.w === w); | ||
} | ||
} | ||
}; | ||
/** | ||
* Returns all edges to or from node v regardless of direction. Optionally filters those edges | ||
* down to just those between nodes v and w regardless of direction. | ||
* Complexity: O(|E|). | ||
*/ | ||
nodeEdges(v, w) { | ||
var inEdges = this.inEdges(v, w); | ||
if (inEdges) { | ||
return inEdges.concat(this.outEdges(v, w)); | ||
} | ||
} | ||
} | ||
function incrementOrInitEntry(map, k) { | ||
@@ -682,1 +683,3 @@ if (map[k]) { | ||
} | ||
module.exports = Graph; |
@@ -1,1 +0,1 @@ | ||
module.exports = '2.1.9'; | ||
module.exports = '2.1.11'; |
{ | ||
"name": "@dagrejs/graphlib", | ||
"version": "2.1.10", | ||
"version": "2.1.11", | ||
"description": "A directed and undirected multi-graph library", | ||
@@ -20,2 +20,3 @@ "author": "Chris Pettitt <cpettitt@gmail.com>", | ||
}, | ||
"types": "index.d.ts", | ||
"keywords": [ | ||
@@ -25,3 +26,2 @@ "graph", | ||
], | ||
"dependencies": {}, | ||
"devDependencies": { | ||
@@ -32,3 +32,2 @@ "benchmark": "2.1.4", | ||
"eslint": "8.35.0", | ||
"istanbul": "^0.4.5", | ||
"jshint": "2.13.5", | ||
@@ -43,2 +42,3 @@ "jshint-stylish": "2.2.1", | ||
"mocha": "10.1.0", | ||
"nyc": "^15.1.0", | ||
"requirejs": "2.3.6", | ||
@@ -45,0 +45,0 @@ "seedrandom": "3.0.5", |
@@ -11,2 +11,4 @@ # Graphlib | ||
There are 2 versions on NPM, but only [the one in the DagreJs org](https://www.npmjs.com/package/@dagrejs/graphlib) is receiving updates right now. | ||
# License | ||
@@ -13,0 +15,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
4269
20
0
165263