@dagrejs/graphlib
Version:
A directed and undirected multi-graph library
1,292 lines (1,270 loc) • 37.7 kB
JavaScript
"use strict";
var graphlib = (() => {
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// index.ts
var index_exports = {};
__export(index_exports, {
Graph: () => Graph,
alg: () => alg_exports,
json: () => json_exports,
version: () => version
});
// lib/graph.ts
var DEFAULT_EDGE_NAME = "\0";
var GRAPH_NODE = "\0";
var EDGE_KEY_DELIM = "";
var Graph = class {
constructor(opts) {
this._isDirected = true;
this._isMultigraph = false;
this._isCompound = false;
// v -> label
this._nodes = {};
// v -> edgeObj
this._in = {};
// u -> v -> Number
this._preds = {};
// v -> edgeObj
this._out = {};
// v -> w -> Number
this._sucs = {};
// e -> edgeObj
this._edgeObjs = {};
// e -> label
this._edgeLabels = {};
/* Number of nodes in the graph. Should only be changed by the implementation. */
this._nodeCount = 0;
/* Number of edges in the graph. Should only be changed by the implementation. */
this._edgeCount = 0;
// Defaults to be set when creating a new node
this._defaultNodeLabelFn = () => void 0;
// Defaults to be set when creating a new edge
this._defaultEdgeLabelFn = () => void 0;
if (opts) {
this._isDirected = "directed" in opts ? opts.directed : true;
this._isMultigraph = "multigraph" in opts ? opts.multigraph : false;
this._isCompound = "compound" in opts ? opts.compound : false;
}
if (this._isCompound) {
this._parent = {};
this._children = {};
this._children[GRAPH_NODE] = {};
}
}
/**
* Whether graph was created with 'directed' flag set to true or not.
*
* @returns whether the graph edges have an orientation.
*/
isDirected() {
return this._isDirected;
}
/**
* Whether graph was created with 'multigraph' flag set to true or not.
*
* @returns whether the pair of nodes of the graph can have multiple edges.
*/
isMultigraph() {
return this._isMultigraph;
}
/* === Graph functions ========= */
/**
* Whether graph was created with 'compound' flag set to true or not.
*
* @returns whether a node of the graph can have subnodes.
*/
isCompound() {
return this._isCompound;
}
/**
* Sets the label of the graph.
*
* @param label - label value.
* @returns the graph, allowing this to be chained with other functions.
*/
setGraph(label) {
this._label = label;
return this;
}
/**
* Gets the graph label.
*
* @returns currently assigned label for the graph or undefined if no label assigned.
*/
graph() {
return this._label;
}
/**
* Sets the default node label. This label will be assigned as default label
* in case if no label was specified while setting a node.
* Complexity: O(1).
*
* @param labelOrFn - default node label or label factory function.
* @returns the graph, allowing this to be chained with other functions.
*/
setDefaultNodeLabel(labelOrFn) {
if (typeof labelOrFn !== "function") {
this._defaultNodeLabelFn = () => labelOrFn;
} else {
this._defaultNodeLabelFn = labelOrFn;
}
return this;
}
/**
* Gets the number of nodes in the graph.
* Complexity: O(1).
*
* @returns nodes count.
*/
nodeCount() {
return this._nodeCount;
}
/* === Node functions ========== */
/**
* Gets all nodes of the graph. Note, the in case of compound graph subnodes are
* not included in list.
* Complexity: O(1).
*
* @returns list of graph nodes.
*/
nodes() {
return Object.keys(this._nodes);
}
/**
* Gets list of nodes without in-edges.
* Complexity: O(|V|).
*
* @returns the graph source nodes.
*/
sources() {
return this.nodes().filter((v) => Object.keys(this._in[v]).length === 0);
}
/**
* Gets list of nodes without out-edges.
* Complexity: O(|V|).
*
* @returns the graph sink nodes.
*/
sinks() {
return this.nodes().filter((v) => Object.keys(this._out[v]).length === 0);
}
/**
* Invokes setNode method for each node in names list.
* Complexity: O(|names|).
*
* @param names - list of nodes names to be set.
* @param label - value to set for each node in list.
* @returns the graph, allowing this to be chained with other functions.
*/
setNodes(names, label) {
names.forEach((v) => {
if (label !== void 0) {
this.setNode(v, label);
} else {
this.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).
*
* @param name - node name.
* @param label - value to set for node.
* @returns the graph, allowing this to be chained with other functions.
*/
setNode(name, label) {
if (name in this._nodes) {
if (arguments.length > 1) {
this._nodes[name] = label;
}
return this;
}
this._nodes[name] = arguments.length > 1 ? label : this._defaultNodeLabelFn(name);
if (this._isCompound) {
this._parent[name] = GRAPH_NODE;
this._children[name] = {};
this._children[GRAPH_NODE][name] = true;
}
this._in[name] = {};
this._preds[name] = {};
this._out[name] = {};
this._sucs[name] = {};
++this._nodeCount;
return this;
}
/**
* Gets the label of node with specified name.
* Complexity: O(|V|).
*
* @param name - node name.
* @returns label value of the node.
*/
node(name) {
return this._nodes[name];
}
/**
* Detects whether graph has a node with specified name or not.
*
* @param name - name of the node.
* @returns true if graph has node with specified name, false - otherwise.
*/
hasNode(name) {
return name in this._nodes;
}
/**
* 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).
*
* @param name - name of the node.
* @returns the graph, allowing this to be chained with other functions.
*/
removeNode(name) {
if (name in this._nodes) {
const removeEdge = (e) => this.removeEdge(this._edgeObjs[e]);
delete this._nodes[name];
if (this._isCompound) {
this._removeFromParentsChildList(name);
delete this._parent[name];
this.children(name).forEach((child) => {
this.setParent(child);
});
delete this._children[name];
}
Object.keys(this._in[name]).forEach(removeEdge);
delete this._in[name];
delete this._preds[name];
Object.keys(this._out[name]).forEach(removeEdge);
delete this._out[name];
delete this._sucs[name];
--this._nodeCount;
}
return this;
}
/**
* Sets node 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).
*
* @param v - node to be child for p.
* @param parent - node to be parent for v.
* @returns the graph, allowing this to be chained with other functions.
*/
setParent(v, parent) {
if (!this._isCompound) {
throw new Error("Cannot set parent in a non-compound graph");
}
if (parent === void 0) {
parent = GRAPH_NODE;
} else {
parent += "";
for (let ancestor = parent; ancestor !== void 0; 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;
}
/**
* Gets parent node for node v.
* Complexity: O(1).
*
* @param v - node to get parent of.
* @returns parent node name or void if v has no parent.
*/
parent(v) {
if (this._isCompound) {
const parent = this._parent[v];
if (parent !== GRAPH_NODE) {
return parent;
}
}
}
/**
* Gets list of direct children of node v.
* Complexity: O(1).
*
* @param v - node to get children of.
* @returns children nodes names list.
*/
children(v = GRAPH_NODE) {
if (this._isCompound) {
const 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 [];
}
/**
* 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|).
*
* @param v - node identifier.
* @returns node identifiers list or undefined if v is not in the graph.
*/
predecessors(v) {
const 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|).
*
* @param v - node identifier.
* @returns node identifiers list or undefined if v is not in the graph.
*/
successors(v) {
const 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|).
*
* @param v - node identifier.
* @returns node identifiers list or undefined if v is not in the graph.
*/
neighbors(v) {
const preds = this.predecessors(v);
if (preds) {
const union = new Set(preds);
for (const succ of this.successors(v)) {
union.add(succ);
}
return Array.from(union.values());
}
}
isLeaf(v) {
let 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|).
*
* @param filter - filtration function detecting whether the node should stay or not.
* @returns new graph made from current and nodes filtered.
*/
filterNodes(filter) {
const copy = new this.constructor({
directed: this._isDirected,
multigraph: this._isMultigraph,
compound: this._isCompound
});
copy.setGraph(this.graph());
Object.entries(this._nodes).forEach(([v, value]) => {
if (filter(v)) {
copy.setNode(v, value);
}
});
Object.values(this._edgeObjs).forEach((e) => {
if (copy.hasNode(e.v) && copy.hasNode(e.w)) {
copy.setEdge(e, this.edge(e));
}
});
const parents = {};
const findParent = (v) => {
const parent = this.parent(v);
if (!parent || copy.hasNode(parent)) {
parents[v] = parent != null ? parent : void 0;
return parent != null ? parent : void 0;
} 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;
}
/**
* Sets the default edge label. This label will be assigned as default label
* in case if no label was specified while setting an edge.
* Complexity: O(1).
*
* @param labelOrFn - default edge label or label factory function.
* @returns the graph, allowing this to be chained with other functions.
*/
setDefaultEdgeLabel(labelOrFn) {
if (typeof labelOrFn !== "function") {
this._defaultEdgeLabelFn = () => labelOrFn;
} else {
this._defaultEdgeLabelFn = labelOrFn;
}
return this;
}
/**
* Gets the number of edges in the graph.
* Complexity: O(1).
*
* @returns edges count.
*/
edgeCount() {
return this._edgeCount;
}
/**
* Gets edges of the graph. In case of compound graph subgraphs are not considered.
* Complexity: O(|E|).
*
* @returns graph edges list.
*/
edges() {
return Object.values(this._edgeObjs);
}
/* === Edge functions ========== */
/**
* 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|).
*
* @param nodes - list of nodes to be connected in series.
* @param label - value to set for each edge between pairs of nodes.
* @returns the graph, allowing this to be chained with other functions.
*/
setPath(nodes, label) {
nodes.reduce((v, w) => {
if (label !== void 0) {
this.setEdge(v, w, label);
} else {
this.setEdge(v, w);
}
return w;
});
return this;
}
setEdge(v, w, value, name) {
let vStr;
let wStr;
let nameStr;
let edgeValue;
let valueSpecified = false;
if (typeof v === "object" && v !== null && "v" in v) {
vStr = v.v;
wStr = v.w;
nameStr = v.name;
if (arguments.length === 2) {
edgeValue = w;
valueSpecified = true;
}
} else {
vStr = v;
wStr = w;
nameStr = name;
if (arguments.length > 2) {
edgeValue = value;
valueSpecified = true;
}
}
vStr = "" + vStr;
wStr = "" + wStr;
if (nameStr !== void 0) {
nameStr = "" + nameStr;
}
const e = edgeArgsToId(this._isDirected, vStr, wStr, nameStr);
if (e in this._edgeLabels) {
if (valueSpecified) {
this._edgeLabels[e] = edgeValue;
}
return this;
}
if (nameStr !== void 0 && !this._isMultigraph) {
throw new Error("Cannot set a named edge when isMultigraph = false");
}
this.setNode(vStr);
this.setNode(wStr);
this._edgeLabels[e] = valueSpecified ? edgeValue : this._defaultEdgeLabelFn(vStr, wStr, nameStr);
const edgeObj = edgeArgsToObj(this._isDirected, vStr, wStr, nameStr);
vStr = edgeObj.v;
wStr = edgeObj.w;
Object.freeze(edgeObj);
this._edgeObjs[e] = edgeObj;
incrementOrInitEntry(this._preds[wStr], vStr);
incrementOrInitEntry(this._sucs[vStr], wStr);
this._in[wStr][e] = edgeObj;
this._out[vStr][e] = edgeObj;
this._edgeCount++;
return this;
}
edge(v, w, name) {
const e = arguments.length === 1 ? edgeObjToId(this._isDirected, v) : edgeArgsToId(this._isDirected, v, w, name);
return this._edgeLabels[e];
}
edgeAsObj(v, w, name) {
const edgeLabel = arguments.length === 1 ? this.edge(v) : this.edge(v, w, name);
if (typeof edgeLabel !== "object") {
return { label: edgeLabel };
}
return edgeLabel;
}
hasEdge(v, w, name) {
const e = arguments.length === 1 ? edgeObjToId(this._isDirected, v) : edgeArgsToId(this._isDirected, v, w, name);
return e in this._edgeLabels;
}
removeEdge(v, w, name) {
const e = arguments.length === 1 ? edgeObjToId(this._isDirected, v) : edgeArgsToId(this._isDirected, v, w, name);
const edge = this._edgeObjs[e];
if (edge) {
const vStr = edge.v;
const wStr = edge.w;
delete this._edgeLabels[e];
delete this._edgeObjs[e];
decrementOrRemoveEntry(this._preds[wStr], vStr);
decrementOrRemoveEntry(this._sucs[vStr], wStr);
delete this._in[wStr][e];
delete this._out[vStr][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 void for undirected graphs - use nodeEdges instead.
* Complexity: O(|E|).
*
* @param v - edge sink node.
* @param w - edge source node.
* @returns edges descriptors list if v is in the graph, or void otherwise.
*/
inEdges(v, w) {
if (this.isDirected()) {
return this.filterEdges(this._in[v], v, w);
}
return this.nodeEdges(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 void for undirected graphs - use nodeEdges instead.
* Complexity: O(|E|).
*
* @param v - edge source node.
* @param w - edge sink node.
* @returns edges descriptors list if v is in the graph, or void otherwise.
*/
outEdges(v, w) {
if (this.isDirected()) {
return this.filterEdges(this._out[v], v, w);
}
return this.nodeEdges(v, 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|).
*
* @param v - edge adjacent node.
* @param w - edge adjacent node.
* @returns edges descriptors list if v is in the graph, or void otherwise.
*/
nodeEdges(v, w) {
if (v in this._nodes) {
return this.filterEdges({ ...this._in[v], ...this._out[v] }, v, w);
}
}
_removeFromParentsChildList(v) {
delete this._children[this._parent[v]][v];
}
filterEdges(setV, localEdge, remoteEdge) {
if (!setV) {
return;
}
const edges = Object.values(setV);
if (!remoteEdge) {
return edges;
}
return edges.filter((edge) => {
return edge.v === localEdge && edge.w === remoteEdge || edge.v === remoteEdge && edge.w === localEdge;
});
}
};
function incrementOrInitEntry(map, k) {
if (map[k]) {
map[k]++;
} else {
map[k] = 1;
}
}
function decrementOrRemoveEntry(map, k) {
if (map[k] !== void 0 && !--map[k]) {
delete map[k];
}
}
function edgeArgsToId(isDirected, v_, w_, name) {
let v = "" + v_;
let w = "" + w_;
if (!isDirected && v > w) {
const tmp = v;
v = w;
w = tmp;
}
return v + EDGE_KEY_DELIM + w + EDGE_KEY_DELIM + (name === void 0 ? DEFAULT_EDGE_NAME : name);
}
function edgeArgsToObj(isDirected, v_, w_, name) {
let v = "" + v_;
let w = "" + w_;
if (!isDirected && v > w) {
const tmp = v;
v = w;
w = tmp;
}
const edgeObj = { v, w };
if (name) {
edgeObj.name = name;
}
return edgeObj;
}
function edgeObjToId(isDirected, edgeObj) {
return edgeArgsToId(isDirected, edgeObj.v, edgeObj.w, edgeObj.name);
}
// lib/version.ts
var version = "4.0.1";
// lib/json.ts
var json_exports = {};
__export(json_exports, {
read: () => read,
write: () => write
});
function write(graph) {
const json = {
options: {
directed: graph.isDirected(),
multigraph: graph.isMultigraph(),
compound: graph.isCompound()
},
nodes: writeNodes(graph),
edges: writeEdges(graph)
};
const graphLabel = graph.graph();
if (graphLabel !== void 0) {
json.value = structuredClone(graphLabel);
}
return json;
}
function writeNodes(g) {
return g.nodes().map((v) => {
const nodeValue = g.node(v);
const parent = g.parent(v);
const node = { v };
if (nodeValue !== void 0) {
node.value = nodeValue;
}
if (parent !== void 0) {
node.parent = parent;
}
return node;
});
}
function writeEdges(g) {
return g.edges().map((e) => {
const edgeValue = g.edge(e);
const edge = { v: e.v, w: e.w };
if (e.name !== void 0) {
edge.name = e.name;
}
if (edgeValue !== void 0) {
edge.value = edgeValue;
}
return edge;
});
}
function read(json) {
const g = new Graph(json.options);
if (json.value !== void 0) {
g.setGraph(json.value);
}
json.nodes.forEach((entry) => {
g.setNode(entry.v, entry.value);
if (entry.parent) {
g.setParent(entry.v, entry.parent);
}
});
json.edges.forEach((entry) => {
g.setEdge({ v: entry.v, w: entry.w, name: entry.name }, entry.value);
});
return g;
}
// lib/alg/index.ts
var alg_exports = {};
__export(alg_exports, {
CycleException: () => CycleException,
bellmanFord: () => bellmanFord,
components: () => components,
dijkstra: () => dijkstra,
dijkstraAll: () => dijkstraAll,
findCycles: () => findCycles,
floydWarshall: () => floydWarshall,
isAcyclic: () => isAcyclic,
postorder: () => postorder,
preorder: () => preorder,
prim: () => prim,
shortestPaths: () => shortestPaths,
tarjan: () => tarjan,
topsort: () => topsort
});
// lib/alg/bellman-ford.ts
var DEFAULT_WEIGHT_FUNC = () => 1;
function bellmanFord(g, source, weightFn, edgeFn) {
return runBellmanFord(
g,
String(source),
weightFn || DEFAULT_WEIGHT_FUNC,
edgeFn || function(v) {
return g.outEdges(v);
}
);
}
function runBellmanFord(g, source, weightFn, edgeFn) {
const results = {};
let didADistanceUpgrade;
let iterations = 0;
const nodes = g.nodes();
const relaxEdge = function(edge) {
const edgeWeight = weightFn(edge);
if (results[edge.v].distance + edgeWeight < results[edge.w].distance) {
results[edge.w] = {
distance: results[edge.v].distance + edgeWeight,
predecessor: edge.v
};
didADistanceUpgrade = true;
}
};
const relaxAllEdges = function() {
nodes.forEach(function(vertex) {
edgeFn(vertex).forEach(function(edge) {
const inVertex = edge.v === vertex ? edge.v : edge.w;
const outVertex = inVertex === edge.v ? edge.w : edge.v;
relaxEdge({ v: inVertex, w: outVertex });
});
});
};
nodes.forEach(function(v) {
const distance = v === source ? 0 : Number.POSITIVE_INFINITY;
results[v] = { distance, predecessor: "" };
});
const numberOfNodes = nodes.length;
for (let i = 1; i < numberOfNodes; i++) {
didADistanceUpgrade = false;
iterations++;
relaxAllEdges();
if (!didADistanceUpgrade) {
break;
}
}
if (iterations === numberOfNodes - 1) {
didADistanceUpgrade = false;
relaxAllEdges();
if (didADistanceUpgrade) {
throw new Error("The graph contains a negative weight cycle");
}
}
return results;
}
// lib/alg/components.ts
function components(graph) {
const visited = {};
const cmpts = [];
let cmpt;
function dfs2(v) {
if (v in visited) return;
visited[v] = true;
cmpt.push(v);
graph.successors(v).forEach(dfs2);
graph.predecessors(v).forEach(dfs2);
}
graph.nodes().forEach(function(v) {
cmpt = [];
dfs2(v);
if (cmpt.length) {
cmpts.push(cmpt);
}
});
return cmpts;
}
// lib/data/priority-queue.ts
var PriorityQueue = class {
constructor() {
this._arr = [];
this._keyIndices = {};
}
/**
* 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.
*/
keys() {
return this._arr.map((x) => x.key);
}
/**
* Returns `true` if **key** is in the queue and `false` if not.
*/
has(key) {
return key in this._keyIndices;
}
/**
* Returns the priority for **key**. If **key** is not present in the queue
* then this function returns `undefined`. Takes `O(1)` time.
*/
priority(key) {
const index = this._keyIndices[key];
if (index !== void 0) {
return this._arr[index].priority;
}
return void 0;
}
/**
* 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.
*/
add(key, priority) {
const keyIndices = this._keyIndices;
const keyStr = String(key);
if (!(keyStr in keyIndices)) {
const arr = this._arr;
const index = arr.length;
keyIndices[keyStr] = index;
arr.push({ key: keyStr, priority });
this._decrease(index);
return true;
}
return false;
}
/**
* Removes and returns the smallest key in the queue. Takes `O(log n)` time.
*/
removeMin() {
this._swap(0, this._arr.length - 1);
const 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.
*/
decrease(key, priority) {
const index = this._keyIndices[key];
if (index === void 0) {
throw new Error(`Key not found: ${key}`);
}
const currentPriority = this._arr[index].priority;
if (priority > currentPriority) {
throw new Error(
`New priority is greater than current priority. Key: ${key} Old: ${currentPriority} New: ${priority}`
);
}
this._arr[index].priority = priority;
this._decrease(index);
}
_heapify(i) {
const arr = this._arr;
const l = 2 * i;
const r = l + 1;
let 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) {
const arr = this._arr;
const priority = arr[index].priority;
let parent;
while (index !== 0) {
parent = index >> 1;
if (arr[parent].priority < priority) {
break;
}
this._swap(index, parent);
index = parent;
}
}
_swap(i, j) {
const arr = this._arr;
const keyIndices = this._keyIndices;
const origArrI = arr[i];
const origArrJ = arr[j];
arr[i] = origArrJ;
arr[j] = origArrI;
keyIndices[origArrJ.key] = i;
keyIndices[origArrI.key] = j;
}
};
// lib/alg/dijkstra.ts
var DEFAULT_WEIGHT_FUNC2 = () => 1;
function dijkstra(graph, source, weightFn, edgeFn) {
const defaultEdgeFn = function(v) {
return graph.outEdges(v);
};
return runDijkstra(
graph,
String(source),
weightFn || DEFAULT_WEIGHT_FUNC2,
edgeFn || defaultEdgeFn
);
}
function runDijkstra(graph, source, weightFn, edgeFn) {
const results = {};
const pq = new PriorityQueue();
let v, vEntry;
const updateNeighbors = function(edge) {
const w = edge.v !== v ? edge.v : edge.w;
const wEntry = results[w];
const weight = weightFn(edge);
const 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);
}
};
graph.nodes().forEach(function(v2) {
const distance = v2 === source ? 0 : Number.POSITIVE_INFINITY;
results[v2] = { distance, predecessor: "" };
pq.add(v2, distance);
});
while (pq.size() > 0) {
v = pq.removeMin();
vEntry = results[v];
if (vEntry.distance === Number.POSITIVE_INFINITY) {
break;
}
edgeFn(v).forEach(updateNeighbors);
}
return results;
}
// lib/alg/dijkstra-all.ts
function dijkstraAll(graph, weightFn, edgeFn) {
return graph.nodes().reduce(function(acc, v) {
acc[v] = dijkstra(graph, v, weightFn, edgeFn);
return acc;
}, {});
}
// lib/alg/tarjan.ts
function tarjan(graph) {
let index = 0;
const stack = [];
const visited = {};
const results = [];
function dfs2(v) {
const entry = visited[v] = {
onStack: true,
lowlink: index,
index: index++
};
stack.push(v);
graph.successors(v).forEach(function(w) {
if (!(w in visited)) {
dfs2(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) {
const cmpt = [];
let w;
do {
w = stack.pop();
visited[w].onStack = false;
cmpt.push(w);
} while (v !== w);
results.push(cmpt);
}
}
graph.nodes().forEach(function(v) {
if (!(v in visited)) {
dfs2(v);
}
});
return results;
}
// lib/alg/find-cycles.ts
function findCycles(graph) {
return tarjan(graph).filter(function(cmpt) {
return cmpt.length > 1 || cmpt.length === 1 && graph.hasEdge(cmpt[0], cmpt[0]);
});
}
// lib/alg/floyd-warshall.ts
var DEFAULT_WEIGHT_FUNC3 = () => 1;
function floydWarshall(graph, weightFn, edgeFn) {
return runFloydWarshall(
graph,
weightFn || DEFAULT_WEIGHT_FUNC3,
edgeFn || function(v) {
return graph.outEdges(v);
}
);
}
function runFloydWarshall(graph, weightFn, edgeFn) {
const results = {};
const nodes = graph.nodes();
nodes.forEach(function(v) {
results[v] = {};
results[v][v] = { distance: 0, predecessor: "" };
nodes.forEach(function(w) {
if (v !== w) {
results[v][w] = { distance: Number.POSITIVE_INFINITY, predecessor: "" };
}
});
edgeFn(v).forEach(function(edge) {
const w = edge.v === v ? edge.w : edge.v;
const d = weightFn(edge);
results[v][w] = { distance: d, predecessor: v };
});
});
nodes.forEach(function(k) {
const rowK = results[k];
nodes.forEach(function(i) {
const rowI = results[i];
nodes.forEach(function(j) {
const ik = rowI[k];
const kj = rowK[j];
const ij = rowI[j];
const altDistance = ik.distance + kj.distance;
if (altDistance < ij.distance) {
ij.distance = altDistance;
ij.predecessor = kj.predecessor;
}
});
});
});
return results;
}
// lib/alg/topsort.ts
var CycleException = class extends Error {
constructor(...args) {
super(...args);
}
};
function topsort(graph) {
const visited = {};
const stack = {};
const results = [];
function visit(node) {
if (node in stack) {
throw new CycleException();
}
if (!(node in visited)) {
stack[node] = true;
visited[node] = true;
graph.predecessors(node).forEach(visit);
delete stack[node];
results.push(node);
}
}
graph.sinks().forEach(visit);
if (Object.keys(visited).length !== graph.nodeCount()) {
throw new CycleException();
}
return results;
}
// lib/alg/is-acyclic.ts
function isAcyclic(graph) {
try {
topsort(graph);
} catch (e) {
if (e instanceof CycleException) {
return false;
}
throw e;
}
return true;
}
// lib/alg/reduce.ts
function reduce(g, vs, order, fn, acc) {
if (!Array.isArray(vs)) {
vs = [vs];
}
const navigation = ((v) => {
var _a;
return (_a = g.isDirected() ? g.successors(v) : g.neighbors(v)) != null ? _a : [];
});
const visited = {};
vs.forEach(function(v) {
if (!g.hasNode(v)) {
throw new Error("Graph does not have node: " + v);
}
acc = doReduce(g, v, order === "post", visited, navigation, fn, acc);
});
return acc;
}
function doReduce(g, v, postorder2, visited, navigation, fn, acc) {
if (!(v in visited)) {
visited[v] = true;
if (!postorder2) {
acc = fn(acc, v);
}
navigation(v).forEach(function(w) {
acc = doReduce(g, w, postorder2, visited, navigation, fn, acc);
});
if (postorder2) {
acc = fn(acc, v);
}
}
return acc;
}
// lib/alg/dfs.ts
function dfs(g, vs, order) {
return reduce(g, vs, order, function(acc, v) {
acc.push(v);
return acc;
}, []);
}
// lib/alg/postorder.ts
function postorder(graph, vs) {
return dfs(graph, vs, "post");
}
// lib/alg/preorder.ts
function preorder(graph, vs) {
return dfs(graph, vs, "pre");
}
// lib/alg/prim.ts
function prim(graph, weightFn) {
const result = new Graph();
const parents = {};
const pq = new PriorityQueue();
let v;
function updateNeighbors(edge) {
const w = edge.v === v ? edge.w : edge.v;
const pri = pq.priority(w);
if (pri !== void 0) {
const edgeWeight = weightFn(edge);
if (edgeWeight < pri) {
parents[w] = v;
pq.decrease(w, edgeWeight);
}
}
}
if (graph.nodeCount() === 0) {
return result;
}
graph.nodes().forEach(function(v2) {
pq.add(v2, Number.POSITIVE_INFINITY);
result.setNode(v2);
});
pq.decrease(graph.nodes()[0], 0);
let init = false;
while (pq.size() > 0) {
v = pq.removeMin();
if (v in parents) {
result.setEdge(v, parents[v]);
} else if (init) {
throw new Error("Input graph is not connected: " + graph);
} else {
init = true;
}
graph.nodeEdges(v).forEach(updateNeighbors);
}
return result;
}
// lib/alg/shortest-paths.ts
function shortestPaths(g, source, weightFn, edgeFn) {
return runShortestPaths(
g,
source,
weightFn,
edgeFn != null ? edgeFn : ((v) => {
const edges = g.outEdges(v);
return edges != null ? edges : [];
})
);
}
function runShortestPaths(g, source, weightFn, edgeFn) {
if (weightFn === void 0) {
return dijkstra(g, source, weightFn, edgeFn);
}
let negativeEdgeExists = false;
const nodes = g.nodes();
for (let i = 0; i < nodes.length; i++) {
const adjList = edgeFn(nodes[i]);
for (let j = 0; j < adjList.length; j++) {
const edge = adjList[j];
const inVertex = edge.v === nodes[i] ? edge.v : edge.w;
const outVertex = inVertex === edge.v ? edge.w : edge.v;
if (weightFn({ v: inVertex, w: outVertex }) < 0) {
negativeEdgeExists = true;
}
}
if (negativeEdgeExists) {
return bellmanFord(g, source, weightFn, edgeFn);
}
}
return dijkstra(g, source, weightFn, edgeFn);
}
return __toCommonJS(index_exports);
})();
//# sourceMappingURL=graphlib.js.map