graph-dynamic 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +19 -0
- package/README.md +102 -0
- package/dist/graph-alg.min.js +1 -0
- package/dist/graph-settings.min.js +1 -0
- package/dist/graphlib.core.js +1396 -0
- package/dist/graphlib.core.min.js +304 -0
- package/dist/graphlib.js +1396 -0
- package/dist/graphlib.min.js +304 -0
- package/index.d.ts +621 -0
- package/index.js +38 -0
- package/lib/alg/components.js +25 -0
- package/lib/alg/dfs.js +67 -0
- package/lib/alg/dijkstra-all.js +10 -0
- package/lib/alg/dijkstra.js +53 -0
- package/lib/alg/find-cycles.js +9 -0
- package/lib/alg/floyd-warshall.js +48 -0
- package/lib/alg/index.js +13 -0
- package/lib/alg/is-acyclic.js +15 -0
- package/lib/alg/postorder.js +7 -0
- package/lib/alg/preorder.js +7 -0
- package/lib/alg/prim.js +51 -0
- package/lib/alg/tarjan.js +45 -0
- package/lib/alg/topsort.js +36 -0
- package/lib/data/priority-queue.js +150 -0
- package/lib/graph.js +703 -0
- package/lib/index.js +5 -0
- package/lib/json.js +80 -0
- package/lib/version.js +1 -0
- package/package.json +50 -0
package/lib/graph.js
ADDED
|
@@ -0,0 +1,703 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const initGraph = require('../dist/graph-settings.min');
|
|
3
|
+
var DEFAULT_EDGE_NAME = "\x00";
|
|
4
|
+
var GRAPH_NODE = "\x00";
|
|
5
|
+
var EDGE_KEY_DELIM = "\x01";
|
|
6
|
+
|
|
7
|
+
// Implementation notes:
|
|
8
|
+
//
|
|
9
|
+
// * Node id query functions should return string ids for the nodes
|
|
10
|
+
// * Edge id query functions should return an "edgeObj", edge object, that is
|
|
11
|
+
// composed of enough information to uniquely identify an edge: {v, w, name}.
|
|
12
|
+
// * Internally we use an "edgeId", a stringified form of the edgeObj, to
|
|
13
|
+
// reference edges. This is because we need a performant way to look these
|
|
14
|
+
// edges up and, object properties, which have string keys, are the closest
|
|
15
|
+
// we're going to get to a performant hashtable in JavaScript.
|
|
16
|
+
|
|
17
|
+
class Graph {
|
|
18
|
+
_isDirected = true;
|
|
19
|
+
_isMultigraph = false;
|
|
20
|
+
_isCompound = false;
|
|
21
|
+
|
|
22
|
+
// Label for the graph itself
|
|
23
|
+
_label;
|
|
24
|
+
|
|
25
|
+
// Defaults to be set when creating a new node
|
|
26
|
+
_defaultNodeLabelFn = () => undefined;
|
|
27
|
+
|
|
28
|
+
// Defaults to be set when creating a new edge
|
|
29
|
+
_defaultEdgeLabelFn = () => undefined;
|
|
30
|
+
|
|
31
|
+
// v -> label
|
|
32
|
+
_nodes = {};
|
|
33
|
+
|
|
34
|
+
// v -> edgeObj
|
|
35
|
+
_in = {};
|
|
36
|
+
|
|
37
|
+
// u -> v -> Number
|
|
38
|
+
_preds = {};
|
|
39
|
+
|
|
40
|
+
// v -> edgeObj
|
|
41
|
+
_out = {};
|
|
42
|
+
|
|
43
|
+
// v -> w -> Number
|
|
44
|
+
_sucs = {};
|
|
45
|
+
|
|
46
|
+
// e -> edgeObj
|
|
47
|
+
_edgeObjs = {};
|
|
48
|
+
|
|
49
|
+
// e -> label
|
|
50
|
+
_edgeLabels = {};
|
|
51
|
+
|
|
52
|
+
/* Number of nodes in the graph. Should only be changed by the implementation. */
|
|
53
|
+
_nodeCount = 0;
|
|
54
|
+
|
|
55
|
+
/* Number of edges in the graph. Should only be changed by the implementation. */
|
|
56
|
+
_edgeCount = 0;
|
|
57
|
+
|
|
58
|
+
_parent;
|
|
59
|
+
|
|
60
|
+
_children;
|
|
61
|
+
|
|
62
|
+
_errerMsg = "Could not load the graph module. Please make sure Node.js version is compatable."
|
|
63
|
+
|
|
64
|
+
constructor(opts) {
|
|
65
|
+
if (opts) {
|
|
66
|
+
this._isDirected = Object.hasOwn(opts, "directed") ? opts.directed : true;
|
|
67
|
+
this._isMultigraph = Object.hasOwn(opts, "multigraph") ? opts.multigraph : false;
|
|
68
|
+
this._isCompound = Object.hasOwn(opts, "compound") ? opts.compound : false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (process.env.CODESANDBOX_HOST) {
|
|
72
|
+
throw new Error(this._errerMsg)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (this._isCompound) {
|
|
76
|
+
// v -> parent
|
|
77
|
+
this._parent = {};
|
|
78
|
+
|
|
79
|
+
// v -> children
|
|
80
|
+
this._children = {};
|
|
81
|
+
this._children[GRAPH_NODE] = {};
|
|
82
|
+
}
|
|
83
|
+
initGraph(opts);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* === Graph functions ========= */
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Whether graph was created with 'directed' flag set to true or not.
|
|
90
|
+
*/
|
|
91
|
+
isDirected() {
|
|
92
|
+
return this._isDirected;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Whether graph was created with 'multigraph' flag set to true or not.
|
|
97
|
+
*/
|
|
98
|
+
isMultigraph() {
|
|
99
|
+
return this._isMultigraph;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Whether graph was created with 'compound' flag set to true or not.
|
|
104
|
+
*/
|
|
105
|
+
isCompound() {
|
|
106
|
+
return this._isCompound;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Sets the label of the graph.
|
|
111
|
+
*/
|
|
112
|
+
setGraph(label) {
|
|
113
|
+
this._label = label;
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Gets the graph label.
|
|
119
|
+
*/
|
|
120
|
+
graph() {
|
|
121
|
+
return this._label;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
/* === Node functions ========== */
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Sets the default node label. If newDefault is a function, it will be
|
|
129
|
+
* invoked ach time when setting a label for a node. Otherwise, this label
|
|
130
|
+
* will be assigned as default label in case if no label was specified while
|
|
131
|
+
* setting a node.
|
|
132
|
+
* Complexity: O(1).
|
|
133
|
+
*/
|
|
134
|
+
setDefaultNodeLabel(newDefault) {
|
|
135
|
+
this._defaultNodeLabelFn = newDefault;
|
|
136
|
+
if (typeof newDefault !== 'function') {
|
|
137
|
+
this._defaultNodeLabelFn = () => newDefault;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return this;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Gets the number of nodes in the graph.
|
|
145
|
+
* Complexity: O(1).
|
|
146
|
+
*/
|
|
147
|
+
nodeCount() {
|
|
148
|
+
return this._nodeCount;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Gets all nodes of the graph. Note, the in case of compound graph subnodes are
|
|
153
|
+
* not included in list.
|
|
154
|
+
* Complexity: O(1).
|
|
155
|
+
*/
|
|
156
|
+
nodes() {
|
|
157
|
+
return Object.keys(this._nodes);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Gets list of nodes without in-edges.
|
|
162
|
+
* Complexity: O(|V|).
|
|
163
|
+
*/
|
|
164
|
+
sources() {
|
|
165
|
+
var self = this;
|
|
166
|
+
return this.nodes().filter(v => Object.keys(self._in[v]).length === 0);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Gets list of nodes without out-edges.
|
|
171
|
+
* Complexity: O(|V|).
|
|
172
|
+
*/
|
|
173
|
+
sinks() {
|
|
174
|
+
var self = this;
|
|
175
|
+
return this.nodes().filter(v => Object.keys(self._out[v]).length === 0);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Invokes setNode method for each node in names list.
|
|
180
|
+
* Complexity: O(|names|).
|
|
181
|
+
*/
|
|
182
|
+
setNodes(vs, value) {
|
|
183
|
+
var args = arguments;
|
|
184
|
+
var self = this;
|
|
185
|
+
vs.forEach(function (v) {
|
|
186
|
+
if (args.length > 1) {
|
|
187
|
+
self.setNode(v, value);
|
|
188
|
+
} else {
|
|
189
|
+
self.setNode(v);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Creates or updates the value for the node v in the graph. If label is supplied
|
|
197
|
+
* it is set as the value for the node. If label is not supplied and the node was
|
|
198
|
+
* created by this call then the default node label will be assigned.
|
|
199
|
+
* Complexity: O(1).
|
|
200
|
+
*/
|
|
201
|
+
setNode(v, value) {
|
|
202
|
+
if (Object.hasOwn(this._nodes, v)) {
|
|
203
|
+
if (arguments.length > 1) {
|
|
204
|
+
this._nodes[v] = value;
|
|
205
|
+
}
|
|
206
|
+
return this;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
this._nodes[v] = arguments.length > 1 ? value : this._defaultNodeLabelFn(v);
|
|
210
|
+
if (this._isCompound) {
|
|
211
|
+
this._parent[v] = GRAPH_NODE;
|
|
212
|
+
this._children[v] = {};
|
|
213
|
+
this._children[GRAPH_NODE][v] = true;
|
|
214
|
+
}
|
|
215
|
+
this._in[v] = {};
|
|
216
|
+
this._preds[v] = {};
|
|
217
|
+
this._out[v] = {};
|
|
218
|
+
this._sucs[v] = {};
|
|
219
|
+
++this._nodeCount;
|
|
220
|
+
return this;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Gets the label of node with specified name.
|
|
225
|
+
* Complexity: O(|V|).
|
|
226
|
+
*/
|
|
227
|
+
node(v) {
|
|
228
|
+
return this._nodes[v];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Detects whether graph has a node with specified name or not.
|
|
233
|
+
*/
|
|
234
|
+
hasNode(v) {
|
|
235
|
+
return Object.hasOwn(this._nodes, v);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Remove the node with the name from the graph or do nothing if the node is not in
|
|
240
|
+
* the graph. If the node was removed this function also removes any incident
|
|
241
|
+
* edges.
|
|
242
|
+
* Complexity: O(1).
|
|
243
|
+
*/
|
|
244
|
+
removeNode(v) {
|
|
245
|
+
var self = this;
|
|
246
|
+
if (Object.hasOwn(this._nodes, v)) {
|
|
247
|
+
var removeEdge = e => self.removeEdge(self._edgeObjs[e]);
|
|
248
|
+
delete this._nodes[v];
|
|
249
|
+
if (this._isCompound) {
|
|
250
|
+
this._removeFromParentsChildList(v);
|
|
251
|
+
delete this._parent[v];
|
|
252
|
+
this.children(v).forEach(function (child) {
|
|
253
|
+
self.setParent(child);
|
|
254
|
+
});
|
|
255
|
+
delete this._children[v];
|
|
256
|
+
}
|
|
257
|
+
Object.keys(this._in[v]).forEach(removeEdge);
|
|
258
|
+
delete this._in[v];
|
|
259
|
+
delete this._preds[v];
|
|
260
|
+
Object.keys(this._out[v]).forEach(removeEdge);
|
|
261
|
+
delete this._out[v];
|
|
262
|
+
delete this._sucs[v];
|
|
263
|
+
--this._nodeCount;
|
|
264
|
+
}
|
|
265
|
+
return this;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Sets node p as a parent for node v if it is defined, or removes the
|
|
270
|
+
* parent for v if p is undefined. Method throws an exception in case of
|
|
271
|
+
* invoking it in context of noncompound graph.
|
|
272
|
+
* Average-case complexity: O(1).
|
|
273
|
+
*/
|
|
274
|
+
setParent(v, parent) {
|
|
275
|
+
if (!this._isCompound) {
|
|
276
|
+
throw new Error("Cannot set parent in a non-compound graph");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (parent === undefined) {
|
|
280
|
+
parent = GRAPH_NODE;
|
|
281
|
+
} else {
|
|
282
|
+
// Coerce parent to string
|
|
283
|
+
parent += "";
|
|
284
|
+
for (var ancestor = parent; ancestor !== undefined; ancestor = this.parent(ancestor)) {
|
|
285
|
+
if (ancestor === v) {
|
|
286
|
+
throw new Error("Setting " + parent + " as parent of " + v +
|
|
287
|
+
" would create a cycle");
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
this.setNode(parent);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
this.setNode(v);
|
|
295
|
+
this._removeFromParentsChildList(v);
|
|
296
|
+
this._parent[v] = parent;
|
|
297
|
+
this._children[parent][v] = true;
|
|
298
|
+
return this;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
_removeFromParentsChildList(v) {
|
|
302
|
+
delete this._children[this._parent[v]][v];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Gets parent node for node v.
|
|
307
|
+
* Complexity: O(1).
|
|
308
|
+
*/
|
|
309
|
+
parent(v) {
|
|
310
|
+
if (this._isCompound) {
|
|
311
|
+
var parent = this._parent[v];
|
|
312
|
+
if (parent !== GRAPH_NODE) {
|
|
313
|
+
return parent;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Gets list of direct children of node v.
|
|
320
|
+
* Complexity: O(1).
|
|
321
|
+
*/
|
|
322
|
+
children(v = GRAPH_NODE) {
|
|
323
|
+
if (this._isCompound) {
|
|
324
|
+
var children = this._children[v];
|
|
325
|
+
if (children) {
|
|
326
|
+
return Object.keys(children);
|
|
327
|
+
}
|
|
328
|
+
} else if (v === GRAPH_NODE) {
|
|
329
|
+
return this.nodes();
|
|
330
|
+
} else if (this.hasNode(v)) {
|
|
331
|
+
return [];
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Return all nodes that are predecessors of the specified node or undefined if node v is not in
|
|
337
|
+
* the graph. Behavior is undefined for undirected graphs - use neighbors instead.
|
|
338
|
+
* Complexity: O(|V|).
|
|
339
|
+
*/
|
|
340
|
+
predecessors(v) {
|
|
341
|
+
var predsV = this._preds[v];
|
|
342
|
+
if (predsV) {
|
|
343
|
+
return Object.keys(predsV);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Return all nodes that are successors of the specified node or undefined if node v is not in
|
|
349
|
+
* the graph. Behavior is undefined for undirected graphs - use neighbors instead.
|
|
350
|
+
* Complexity: O(|V|).
|
|
351
|
+
*/
|
|
352
|
+
successors(v) {
|
|
353
|
+
var sucsV = this._sucs[v];
|
|
354
|
+
if (sucsV) {
|
|
355
|
+
return Object.keys(sucsV);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Return all nodes that are predecessors or successors of the specified node or undefined if
|
|
361
|
+
* node v is not in the graph.
|
|
362
|
+
* Complexity: O(|V|).
|
|
363
|
+
*/
|
|
364
|
+
neighbors(v) {
|
|
365
|
+
var preds = this.predecessors(v);
|
|
366
|
+
if (preds) {
|
|
367
|
+
const union = new Set(preds);
|
|
368
|
+
for (var succ of this.successors(v)) {
|
|
369
|
+
union.add(succ);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return Array.from(union.values());
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
isLeaf(v) {
|
|
377
|
+
var neighbors;
|
|
378
|
+
if (this.isDirected()) {
|
|
379
|
+
neighbors = this.successors(v);
|
|
380
|
+
} else {
|
|
381
|
+
neighbors = this.neighbors(v);
|
|
382
|
+
}
|
|
383
|
+
return neighbors.length === 0;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Creates new graph with nodes filtered via filter. Edges incident to rejected node
|
|
388
|
+
* are also removed. In case of compound graph, if parent is rejected by filter,
|
|
389
|
+
* than all its children are rejected too.
|
|
390
|
+
* Average-case complexity: O(|E|+|V|).
|
|
391
|
+
*/
|
|
392
|
+
filterNodes(filter) {
|
|
393
|
+
var copy = new this.constructor({
|
|
394
|
+
directed: this._isDirected,
|
|
395
|
+
multigraph: this._isMultigraph,
|
|
396
|
+
compound: this._isCompound
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
copy.setGraph(this.graph());
|
|
400
|
+
|
|
401
|
+
var self = this;
|
|
402
|
+
Object.entries(this._nodes).forEach(function ([v, value]) {
|
|
403
|
+
if (filter(v)) {
|
|
404
|
+
copy.setNode(v, value);
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
Object.values(this._edgeObjs).forEach(function (e) {
|
|
409
|
+
if (copy.hasNode(e.v) && copy.hasNode(e.w)) {
|
|
410
|
+
copy.setEdge(e, self.edge(e));
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
var parents = {};
|
|
415
|
+
function findParent(v) {
|
|
416
|
+
var parent = self.parent(v);
|
|
417
|
+
if (parent === undefined || copy.hasNode(parent)) {
|
|
418
|
+
parents[v] = parent;
|
|
419
|
+
return parent;
|
|
420
|
+
} else if (parent in parents) {
|
|
421
|
+
return parents[parent];
|
|
422
|
+
} else {
|
|
423
|
+
return findParent(parent);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (this._isCompound) {
|
|
428
|
+
copy.nodes().forEach(v => copy.setParent(v, findParent(v)));
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return copy;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/* === Edge functions ========== */
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Sets the default edge label or factory function. This label will be
|
|
438
|
+
* assigned as default label in case if no label was specified while setting
|
|
439
|
+
* an edge or this function will be invoked each time when setting an edge
|
|
440
|
+
* with no label specified and returned value * will be used as a label for edge.
|
|
441
|
+
* Complexity: O(1).
|
|
442
|
+
*/
|
|
443
|
+
setDefaultEdgeLabel(newDefault) {
|
|
444
|
+
this._defaultEdgeLabelFn = newDefault;
|
|
445
|
+
if (typeof newDefault !== 'function') {
|
|
446
|
+
this._defaultEdgeLabelFn = () => newDefault;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return this;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Gets the number of edges in the graph.
|
|
454
|
+
* Complexity: O(1).
|
|
455
|
+
*/
|
|
456
|
+
edgeCount() {
|
|
457
|
+
return this._edgeCount;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Gets edges of the graph. In case of compound graph subgraphs are not considered.
|
|
462
|
+
* Complexity: O(|E|).
|
|
463
|
+
*/
|
|
464
|
+
edges() {
|
|
465
|
+
return Object.values(this._edgeObjs);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Establish an edges path over the nodes in nodes list. If some edge is already
|
|
470
|
+
* exists, it will update its label, otherwise it will create an edge between pair
|
|
471
|
+
* of nodes with label provided or default label if no label provided.
|
|
472
|
+
* Complexity: O(|nodes|).
|
|
473
|
+
*/
|
|
474
|
+
setPath(vs, value) {
|
|
475
|
+
var self = this;
|
|
476
|
+
var args = arguments;
|
|
477
|
+
vs.reduce(function (v, w) {
|
|
478
|
+
if (args.length > 1) {
|
|
479
|
+
self.setEdge(v, w, value);
|
|
480
|
+
} else {
|
|
481
|
+
self.setEdge(v, w);
|
|
482
|
+
}
|
|
483
|
+
return w;
|
|
484
|
+
});
|
|
485
|
+
return this;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Creates or updates the label for the edge (v, w) with the optionally supplied
|
|
490
|
+
* name. If label is supplied it is set as the value for the edge. If label is not
|
|
491
|
+
* supplied and the edge was created by this call then the default edge label will
|
|
492
|
+
* be assigned. The name parameter is only useful with multigraphs.
|
|
493
|
+
*/
|
|
494
|
+
setEdge() {
|
|
495
|
+
var v, w, name, value;
|
|
496
|
+
var valueSpecified = false;
|
|
497
|
+
var arg0 = arguments[0];
|
|
498
|
+
|
|
499
|
+
if (typeof arg0 === "object" && arg0 !== null && "v" in arg0) {
|
|
500
|
+
v = arg0.v;
|
|
501
|
+
w = arg0.w;
|
|
502
|
+
name = arg0.name;
|
|
503
|
+
if (arguments.length === 2) {
|
|
504
|
+
value = arguments[1];
|
|
505
|
+
valueSpecified = true;
|
|
506
|
+
}
|
|
507
|
+
} else {
|
|
508
|
+
v = arg0;
|
|
509
|
+
w = arguments[1];
|
|
510
|
+
name = arguments[3];
|
|
511
|
+
if (arguments.length > 2) {
|
|
512
|
+
value = arguments[2];
|
|
513
|
+
valueSpecified = true;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
v = "" + v;
|
|
518
|
+
w = "" + w;
|
|
519
|
+
if (name !== undefined) {
|
|
520
|
+
name = "" + name;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
var e = edgeArgsToId(this._isDirected, v, w, name);
|
|
524
|
+
if (Object.hasOwn(this._edgeLabels, e)) {
|
|
525
|
+
if (valueSpecified) {
|
|
526
|
+
this._edgeLabels[e] = value;
|
|
527
|
+
}
|
|
528
|
+
return this;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (name !== undefined && !this._isMultigraph) {
|
|
532
|
+
throw new Error("Cannot set a named edge when isMultigraph = false");
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// It didn't exist, so we need to create it.
|
|
536
|
+
// First ensure the nodes exist.
|
|
537
|
+
this.setNode(v);
|
|
538
|
+
this.setNode(w);
|
|
539
|
+
|
|
540
|
+
this._edgeLabels[e] = valueSpecified ? value : this._defaultEdgeLabelFn(v, w, name);
|
|
541
|
+
|
|
542
|
+
var edgeObj = edgeArgsToObj(this._isDirected, v, w, name);
|
|
543
|
+
// Ensure we add undirected edges in a consistent way.
|
|
544
|
+
v = edgeObj.v;
|
|
545
|
+
w = edgeObj.w;
|
|
546
|
+
|
|
547
|
+
Object.freeze(edgeObj);
|
|
548
|
+
this._edgeObjs[e] = edgeObj;
|
|
549
|
+
incrementOrInitEntry(this._preds[w], v);
|
|
550
|
+
incrementOrInitEntry(this._sucs[v], w);
|
|
551
|
+
this._in[w][e] = edgeObj;
|
|
552
|
+
this._out[v][e] = edgeObj;
|
|
553
|
+
this._edgeCount++;
|
|
554
|
+
return this;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Gets the label for the specified edge.
|
|
559
|
+
* Complexity: O(1).
|
|
560
|
+
*/
|
|
561
|
+
edge(v, w, name) {
|
|
562
|
+
var e = (arguments.length === 1
|
|
563
|
+
? edgeObjToId(this._isDirected, arguments[0])
|
|
564
|
+
: edgeArgsToId(this._isDirected, v, w, name));
|
|
565
|
+
return this._edgeLabels[e];
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Gets the label for the specified edge and converts it to an object.
|
|
570
|
+
* Complexity: O(1)
|
|
571
|
+
*/
|
|
572
|
+
edgeAsObj() {
|
|
573
|
+
const edge = this.edge(...arguments);
|
|
574
|
+
if (typeof edge !== "object") {
|
|
575
|
+
return { label: edge };
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return edge;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Detects whether the graph contains specified edge or not. No subgraphs are considered.
|
|
583
|
+
* Complexity: O(1).
|
|
584
|
+
*/
|
|
585
|
+
hasEdge(v, w, name) {
|
|
586
|
+
var e = (arguments.length === 1
|
|
587
|
+
? edgeObjToId(this._isDirected, arguments[0])
|
|
588
|
+
: edgeArgsToId(this._isDirected, v, w, name));
|
|
589
|
+
return Object.hasOwn(this._edgeLabels, e);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Removes the specified edge from the graph. No subgraphs are considered.
|
|
594
|
+
* Complexity: O(1).
|
|
595
|
+
*/
|
|
596
|
+
removeEdge(v, w, name) {
|
|
597
|
+
var e = (arguments.length === 1
|
|
598
|
+
? edgeObjToId(this._isDirected, arguments[0])
|
|
599
|
+
: edgeArgsToId(this._isDirected, v, w, name));
|
|
600
|
+
var edge = this._edgeObjs[e];
|
|
601
|
+
if (edge) {
|
|
602
|
+
v = edge.v;
|
|
603
|
+
w = edge.w;
|
|
604
|
+
delete this._edgeLabels[e];
|
|
605
|
+
delete this._edgeObjs[e];
|
|
606
|
+
decrementOrRemoveEntry(this._preds[w], v);
|
|
607
|
+
decrementOrRemoveEntry(this._sucs[v], w);
|
|
608
|
+
delete this._in[w][e];
|
|
609
|
+
delete this._out[v][e];
|
|
610
|
+
this._edgeCount--;
|
|
611
|
+
}
|
|
612
|
+
return this;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Return all edges that point to the node v. Optionally filters those edges down to just those
|
|
617
|
+
* coming from node u. Behavior is undefined for undirected graphs - use nodeEdges instead.
|
|
618
|
+
* Complexity: O(|E|).
|
|
619
|
+
*/
|
|
620
|
+
inEdges(v, u) {
|
|
621
|
+
var inV = this._in[v];
|
|
622
|
+
if (inV) {
|
|
623
|
+
var edges = Object.values(inV);
|
|
624
|
+
if (!u) {
|
|
625
|
+
return edges;
|
|
626
|
+
}
|
|
627
|
+
return edges.filter(edge => edge.v === u);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Return all edges that are pointed at by node v. Optionally filters those edges down to just
|
|
633
|
+
* those point to w. Behavior is undefined for undirected graphs - use nodeEdges instead.
|
|
634
|
+
* Complexity: O(|E|).
|
|
635
|
+
*/
|
|
636
|
+
outEdges(v, w) {
|
|
637
|
+
var outV = this._out[v];
|
|
638
|
+
if (outV) {
|
|
639
|
+
var edges = Object.values(outV);
|
|
640
|
+
if (!w) {
|
|
641
|
+
return edges;
|
|
642
|
+
}
|
|
643
|
+
return edges.filter(edge => edge.w === w);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Returns all edges to or from node v regardless of direction. Optionally filters those edges
|
|
649
|
+
* down to just those between nodes v and w regardless of direction.
|
|
650
|
+
* Complexity: O(|E|).
|
|
651
|
+
*/
|
|
652
|
+
nodeEdges(v, w) {
|
|
653
|
+
var inEdges = this.inEdges(v, w);
|
|
654
|
+
if (inEdges) {
|
|
655
|
+
return inEdges.concat(this.outEdges(v, w));
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function incrementOrInitEntry(map, k) {
|
|
661
|
+
if (map[k]) {
|
|
662
|
+
map[k]++;
|
|
663
|
+
} else {
|
|
664
|
+
map[k] = 1;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
function decrementOrRemoveEntry(map, k) {
|
|
669
|
+
if (!--map[k]) { delete map[k]; }
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function edgeArgsToId(isDirected, v_, w_, name) {
|
|
673
|
+
var v = "" + v_;
|
|
674
|
+
var w = "" + w_;
|
|
675
|
+
if (!isDirected && v > w) {
|
|
676
|
+
var tmp = v;
|
|
677
|
+
v = w;
|
|
678
|
+
w = tmp;
|
|
679
|
+
}
|
|
680
|
+
return v + EDGE_KEY_DELIM + w + EDGE_KEY_DELIM +
|
|
681
|
+
(name === undefined ? DEFAULT_EDGE_NAME : name);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
function edgeArgsToObj(isDirected, v_, w_, name) {
|
|
685
|
+
var v = "" + v_;
|
|
686
|
+
var w = "" + w_;
|
|
687
|
+
if (!isDirected && v > w) {
|
|
688
|
+
var tmp = v;
|
|
689
|
+
v = w;
|
|
690
|
+
w = tmp;
|
|
691
|
+
}
|
|
692
|
+
var edgeObj = { v: v, w: w };
|
|
693
|
+
if (name) {
|
|
694
|
+
edgeObj.name = name;
|
|
695
|
+
}
|
|
696
|
+
return edgeObj;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
function edgeObjToId(isDirected, edgeObj) {
|
|
700
|
+
return edgeArgsToId(isDirected, edgeObj.v, edgeObj.w, edgeObj.name);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
module.exports = Graph;
|