graph-builder
Version:
A graph builder library for modeling abstract graph structures.
415 lines • 14.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const AbstractBaseGraph_1 = require("./AbstractBaseGraph");
const ImmutableSet_1 = require("../collect/ImmutableSet");
/*
* Copyright (C) 2017 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Modifications (C) 2019 Ben Sorohan
*/
var Order;
(function (Order) {
Order[Order["PREORDER"] = 0] = "PREORDER";
Order[Order["POSTORDER"] = 1] = "POSTORDER";
})(Order || (Order = {}));
const isSuccessorsAccessor = (g) => (typeof g.successors === 'function');
/**
* Create a traverser for traversing a graph or tree.
*
* @remarks
*
* There are two entry points for creating a `Traverser`: {@link
* Traversers.forTree} and {@link Traversers.forGraph}. You should choose one
* based on your answers to the following question:
*
* Is there only one path to any node that's reachable from any start node? (If so, the
* graph to be traversed is a tree or forest even if it is a subgraph of a graph which is
* neither.)
*
* If your answer is:
*
* - (1) "no" use {@link Traversers.forGraph}.
*
* - (1) "yes" use {@link Traversers.forTree}.
*
* @public
*/
class Traversers {
static forGraph(graph) {
const successorsFunction = isSuccessorsAccessor(graph) ? (n) => (graph.successors(n)) : graph;
return new GraphTraverser(successorsFunction);
}
static forTree(tree) {
if (tree instanceof AbstractBaseGraph_1.AbstractBaseGraph) {
if (!tree.isDirected()) {
throw new Error("Undirected graphs can never be trees.");
}
}
// if (tree instanceof Network) {
// checkArgument(((Network<?, ?>) tree).isDirected(), "Undirected networks can never be trees.");
// }
const successorsFunction = isSuccessorsAccessor(tree) ? (n) => (tree.successors(n)) : tree;
return new TreeTraverser(successorsFunction);
}
}
exports.Traversers = Traversers;
var Iterables;
(function (Iterables) {
Iterables.isEmpty = (iterable) => {
return iterable[Symbol.iterator]().next().done;
};
Iterables.addAll = (queue, iterable) => {
};
})(Iterables || (Iterables = {}));
class GraphTraverser {
constructor(graphSuccessors) {
this.graphSuccessors = graphSuccessors;
}
breadthFirst(...startNodes) {
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet_1.ImmutableSet.empty();
}
for (const startNode of startNodes) {
this.checkThatNodeIsInGraph(startNode);
}
return {
[Symbol.iterator]: () => new GraphIterator.BreadthFirstIterator(this.graphSuccessors, startNodes),
};
}
depthFirstPreOrder(...startNodes) {
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet_1.ImmutableSet.empty();
}
for (const startNode of startNodes) {
this.checkThatNodeIsInGraph(startNode);
}
return {
[Symbol.iterator]: () => new GraphIterator.DepthFirstIterator(this.graphSuccessors, startNodes, Order.PREORDER),
};
}
depthFirstPostOrder(...startNodes) {
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet_1.ImmutableSet.empty();
}
for (const startNode of startNodes) {
this.checkThatNodeIsInGraph(startNode);
}
return {
[Symbol.iterator]: () => new GraphIterator.DepthFirstIterator(this.graphSuccessors, startNodes, Order.POSTORDER),
};
}
checkThatNodeIsInGraph(startNode) {
// successors() throws an IllegalArgumentException for nodes that are not an element of the
// graph.
this.graphSuccessors(startNode);
}
}
class TreeTraverser {
constructor(treeSuccessors) {
this.treeSuccessors = treeSuccessors;
}
breadthFirst(...startNodes) {
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet_1.ImmutableSet.empty();
}
for (const startNode of startNodes) {
this.checkThatNodeIsInTree(startNode);
}
return {
[Symbol.iterator]: () => new TreeIterator.BreadthFirstIterator(this.treeSuccessors, startNodes),
};
}
depthFirstPreOrder(...startNodes) {
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet_1.ImmutableSet.empty();
}
for (const node of startNodes) {
this.checkThatNodeIsInTree(node);
}
return {
[Symbol.iterator]: () => new TreeIterator.DepthFirstPreOrderIterator(this.treeSuccessors, startNodes),
};
}
depthFirstPostOrder(...startNodes) {
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet_1.ImmutableSet.empty();
}
for (const startNode of startNodes) {
this.checkThatNodeIsInTree(startNode);
}
return {
[Symbol.iterator]: () => new TreeIterator.DepthFirstPostOrderIterator(this.treeSuccessors, startNodes),
};
}
checkThatNodeIsInTree(startNode) {
// successors() throws an IllegalArgumentException for nodes that are not an element of the
// graph.
this.treeSuccessors(startNode);
}
}
class ArrayDeque {
constructor() {
this.queue = []; // head at the beginning, tail at the end
}
[Symbol.iterator]() {
return this.queue[Symbol.iterator]();
}
isEmpty() {
return this.queue.length === 0;
}
// add to head
add(item) {
this.queue.push(item);
return true;
}
addLast(item) {
return this.add(item);
}
// remove from head
pop() {
if (this.isEmpty()) {
throw new Error('Queue is empty');
}
return this.queue.shift();
}
remove() {
return this.pop();
}
// add to tail
push(item) {
this.queue.unshift(item);
return true;
}
// get from head (don't remove)
getFirst() {
if (this.isEmpty()) {
throw new Error('Queue is empty');
}
return this.queue[0];
}
// get from tail (don't remove)
getLast() {
if (this.isEmpty()) {
throw new Error('Queue is empty');
}
return this.queue[this.queue.length - 1];
}
// remove from tail
removeLast() {
if (this.isEmpty()) {
throw new Error('Queue is empty');
}
return this.queue.pop();
}
// add all to head
addAll(i) {
let modified = false;
for (const item of i) {
this.add(item);
modified = true;
}
return modified;
}
}
var GraphIterator;
(function (GraphIterator) {
class BreadthFirstIterator {
constructor(graphSuccessors, roots) {
this.graphSuccessors = graphSuccessors;
this.queue = new ArrayDeque();
this.visited = new Set();
for (const root of roots) {
// add all roots to the queue, skipping duplicates
const has = this.visited.has(root);
this.visited.add(root);
if (!has) {
this.queue.add(root);
}
}
}
next() {
if (this.queue.isEmpty()) {
return {
done: true,
value: undefined,
};
}
const current = this.queue.remove();
for (const neighbor of this.graphSuccessors(current)) {
const has = this.visited.has(neighbor);
this.visited.add(neighbor);
if (!has) {
this.queue.add(neighbor);
}
}
return {
done: false,
value: current,
};
}
}
GraphIterator.BreadthFirstIterator = BreadthFirstIterator;
/** A simple tuple of a node and a partially iterated Iterator of its successors. */
class NodeAndSuccessors {
constructor(node, successors) {
this.node = node;
this.successorIterator = successors[Symbol.iterator]();
}
}
class DepthFirstIterator {
constructor(graphSuccessors, roots, order) {
this.graphSuccessors = graphSuccessors;
this.order = order;
this.stack = new ArrayDeque();
this.visited = new Set();
this.stack.push(new NodeAndSuccessors(undefined, roots));
this.order = order;
}
next() {
while (true) {
if (this.stack.isEmpty()) {
return {
done: true,
value: undefined,
};
}
const nodeAndSuccessors = this.stack.getFirst();
const firstVisit = (nodeAndSuccessors.node === undefined || !this.visited.has(nodeAndSuccessors.node));
if (nodeAndSuccessors.node !== undefined) {
this.visited.add(nodeAndSuccessors.node);
}
const successor = nodeAndSuccessors.successorIterator.next();
const lastVisit = successor.done;
const produceNode = (firstVisit && this.order == Order.PREORDER) || (lastVisit && this.order == Order.POSTORDER);
if (lastVisit) {
this.stack.pop();
}
else {
// we need to push a neighbor, but only if we haven't already seen it
if (!this.visited.has(successor.value)) {
this.stack.push(this.withSuccessors(successor.value));
}
}
if (produceNode && nodeAndSuccessors.node !== undefined) {
return {
done: false,
value: nodeAndSuccessors.node,
};
}
}
}
withSuccessors(node) {
return new NodeAndSuccessors(node, this.graphSuccessors(node));
}
}
GraphIterator.DepthFirstIterator = DepthFirstIterator;
})(GraphIterator || (GraphIterator = {}));
var TreeIterator;
(function (TreeIterator) {
const withChildren = (treeSuccessors, node) => {
return new NodeAndChildren(node, treeSuccessors(node));
};
/** A simple tuple of a node and a partially iterated Iterator of its children. */
class NodeAndChildren {
constructor(node, children) {
this.node = node;
this.node = node;
this.childIterator = children[Symbol.iterator]();
}
}
class BreadthFirstIterator {
constructor(treeSuccessors, roots) {
this.treeSuccessors = treeSuccessors;
this.queue = new ArrayDeque();
for (const root of roots) {
this.queue.add(root);
}
}
next() {
const hasNext = !this.queue.isEmpty();
if (!hasNext) {
return {
done: true,
value: undefined,
};
}
const current = this.queue.remove();
this.queue.addAll(this.treeSuccessors(current));
return {
done: false,
value: current,
};
}
}
TreeIterator.BreadthFirstIterator = BreadthFirstIterator;
class DepthFirstPreOrderIterator {
constructor(treeSuccessors, roots) {
this.treeSuccessors = treeSuccessors;
this.stack = new ArrayDeque();
this.stack.addLast(roots[Symbol.iterator]());
}
next() {
if (this.stack.isEmpty()) {
return {
done: true,
value: undefined,
};
}
const iterator = this.stack.getLast(); // throws NoSuchElementException if empty
const result = iterator.next();
if (result.done) {
this.stack.removeLast();
return this.next();
}
const iterable = this.treeSuccessors(result.value);
if (!Iterables.isEmpty(iterable)) {
this.stack.addLast(iterable[Symbol.iterator]());
}
return result;
}
}
TreeIterator.DepthFirstPreOrderIterator = DepthFirstPreOrderIterator;
class DepthFirstPostOrderIterator {
constructor(tree, roots) {
this.tree = tree;
this.stack = new ArrayDeque();
this.stack.addLast(new NodeAndChildren(undefined, roots));
}
next() {
while (!this.stack.isEmpty()) {
const top = this.stack.getLast();
const child = top.childIterator.next();
if (!child.done) {
this.stack.addLast(withChildren(this.tree, child.value));
}
else {
this.stack.removeLast();
if (top.node !== undefined) {
return {
done: false,
value: top.node,
};
}
}
}
return {
done: true,
value: undefined,
};
}
}
TreeIterator.DepthFirstPostOrderIterator = DepthFirstPostOrderIterator;
})(TreeIterator || (TreeIterator = {}));
//# sourceMappingURL=Traverser.js.map