UNPKG

graph-builder

Version:

A graph builder library for modeling abstract graph structures.

415 lines 14.2 kB
"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