UNPKG

graph-builder

Version:

A graph builder library for modeling abstract graph structures.

136 lines 4.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const EndpointPair_1 = require("./EndpointPair"); /* * Copyright (C) 2016 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 */ /** * A class to facilitate the set returned by {@link Graph.edges}. */ class EndpointPairIterator { constructor(graph) { this.graph = graph; this.successorIterator = { next: () => ({ done: true, value: undefined }), }; this.nodeIterator = graph.nodes()[Symbol.iterator](); } static of(graph) { return graph.isDirected() ? new Directed(graph) : new Undirected(graph); } get node() { if (!this.nextNode || !this.nextNode.value) { throw new Error('No node'); } return this.nextNode.value; } /** * Called after {@link successorIterator} is exhausted. Advances {@link node} to the next node * and updates {@link successorIterator} to iterate through the successors of {@link node}. */ advance() { this.nextNode = this.nodeIterator.next(); if (this.nextNode.done) { return false; } this.successorIterator = this.graph.successors(this.nextNode.value)[Symbol.iterator](); return true; } } exports.EndpointPairIterator = EndpointPairIterator; /** * If the graph is directed, each ordered [source, target] pair will be visited once if there is * an edge connecting them. */ class Directed extends EndpointPairIterator { next() { while (true) { const successNext = this.successorIterator.next(); if (!successNext.done) { return { done: false, value: EndpointPair_1.EndpointPair.ordered(this.node, successNext.value), }; } if (!this.advance()) { return { done: true, value: undefined, }; } } } } /** * If the graph is undirected, each unordered [node, otherNode] pair (except self-loops) will be * visited twice if there is an edge connecting them. To avoid returning duplicate {@link * EndpointPair}s, we keep track of the nodes that we have visited. When processing endpoint * pairs, we skip if the "other node" is in the visited set, as shown below: * * <pre> * Nodes = {N1, N2, N3, N4} * N2 __ * / \ | | * N1----N3 N4__| * * Visited Nodes = {} * EndpointPair [N1, N2] - return * EndpointPair [N1, N3] - return * Visited Nodes = {N1} * EndpointPair [N2, N1] - skip * EndpointPair [N2, N3] - return * Visited Nodes = {N1, N2} * EndpointPair [N3, N1] - skip * EndpointPair [N3, N2] - skip * Visited Nodes = {N1, N2, N3} * EndpointPair [N4, N4] - return * Visited Nodes = {N1, N2, N3, N4} * </pre> */ class Undirected extends EndpointPairIterator { constructor(graph) { super(graph); this.visitedNodes = new Set(); } next() { while (true) { let successNext = this.successorIterator.next(); while (!successNext.done) { const otherNode = successNext.value; if (!this.visitedNodes.has(otherNode)) { return { value: EndpointPair_1.EndpointPair.unordered(this.node, otherNode), done: false, }; } successNext = this.successorIterator.next(); } // Add to visited set *after* processing neighbors so we still include self-loops. if (this.nextNode !== undefined) { this.visitedNodes.add(this.node); } if (!this.advance()) { this.visitedNodes = new Set(); return { done: true, value: undefined, }; } } } } //# sourceMappingURL=EndpointPairIterator.js.map