graph-builder
Version:
A graph builder library for modeling abstract graph structures.
227 lines • 8.74 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const ImmutableSet_1 = require("../collect/ImmutableSet");
const ImmutableMap_1 = require("../collect/ImmutableMap");
// @todo: assertion lib
const checkNonNegative = (n) => {
if (n < 0)
throw new Error('Negative number');
};
const checkPositive = (n) => {
if (n <= 0)
throw new Error('Non positive number');
};
const checkState = (assert) => {
if (!assert) {
throw new Error('Invalid state');
}
};
/*
* 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
*/
const PRED = Symbol('PRED');
/**
* An implementation of {@link GraphConnections} for directed graphs.
*/
class DirectedGraphConnections {
// Every value in adjacentNodeValues this map must either be an instance of PredAndSucc with a successorValue of
// type V, PRED (representing predecessor), or an instance of type V (representing successor).
constructor(adjacentNodeValues, predecessorCount, successorCount) {
this.adjacentNodeValues = adjacentNodeValues;
this.predecessorCount = predecessorCount;
this.successorCount = successorCount;
checkState(predecessorCount <= adjacentNodeValues.size &&
successorCount <= adjacentNodeValues.size);
}
static of() {
return new DirectedGraphConnections(new Map(), 0, 0);
}
static ofImmutable(predecessors, successorValues) {
const adjacentNodeValues = new Map(successorValues);
for (const predecessor of predecessors) {
const value = successorValues.get(predecessor); // or should be from adjacentNodeValues?
adjacentNodeValues.set(predecessor, PRED);
if (value !== undefined) {
adjacentNodeValues.set(predecessor, new PredAndSucc(value));
}
}
return new DirectedGraphConnections(ImmutableMap_1.ImmutableMap.copyOf(adjacentNodeValues), predecessors.size, successorValues.size);
}
adjacentNodes() {
return ImmutableSet_1.ImmutableSet.fromIterable(this.adjacentNodeValues.keys());
}
predecessors() {
const that = this;
return ImmutableSet_1.ImmutableSet.fromSetOperations({
[Symbol.iterator]: () => {
const entries = that.adjacentNodeValues.entries();
const next = () => {
let result = entries.next();
while (!result.done) {
const [key, value] = result.value;
if (DirectedGraphConnections.isPredecessor(value)) {
return {
value: key,
done: false,
};
}
result = entries.next();
}
return {
value: undefined,
done: true,
};
};
const iterableIterator = {
[Symbol.iterator]: () => iterableIterator,
next,
};
return iterableIterator;
},
size: () => that.predecessorCount,
has: (n) => {
const value = that.adjacentNodeValues.get(n);
return value !== undefined && DirectedGraphConnections.isPredecessor(value);
},
});
}
successors() {
const that = this;
return ImmutableSet_1.ImmutableSet.fromSetOperations({
[Symbol.iterator]: () => {
const entries = that.adjacentNodeValues.entries();
const next = () => {
let result = entries.next();
while (!result.done) {
const [key, value] = result.value;
if (DirectedGraphConnections.isSuccessor(value)) {
return {
value: key,
done: false,
};
}
result = entries.next();
}
return {
value: undefined,
done: true,
};
};
const iterableIterator = {
[Symbol.iterator]: () => iterableIterator,
next,
};
return iterableIterator;
},
size: () => that.successorCount,
has: (n) => {
const value = that.adjacentNodeValues.get(n);
return value !== undefined && DirectedGraphConnections.isSuccessor(value);
},
});
}
value(node) {
const value = this.adjacentNodeValues.get(node);
if (value == PRED) {
return undefined;
}
if (value instanceof PredAndSucc) {
return value.successorValue;
}
return value;
}
removePredecessor(node) {
const previousValue = this.adjacentNodeValues.get(node);
if (previousValue == PRED) {
this.adjacentNodeValues.delete(node);
checkNonNegative(--this.predecessorCount);
}
else if (previousValue instanceof PredAndSucc) {
this.adjacentNodeValues.set(node, previousValue.successorValue);
checkNonNegative(--this.predecessorCount);
}
}
removeSuccessor(node) {
const previousValue = this.adjacentNodeValues.get(node);
if (previousValue === undefined || previousValue == PRED) {
return undefined;
}
else if (previousValue instanceof PredAndSucc) {
this.adjacentNodeValues.set(node, PRED);
checkNonNegative(--this.successorCount);
return previousValue.successorValue;
}
else { // successor
this.adjacentNodeValues.delete(node);
checkNonNegative(--this.successorCount);
return previousValue;
}
}
addPredecessor(node, unused) {
const previousValue = this.adjacentNodeValues.get(node);
this.adjacentNodeValues.set(node, PRED);
if (previousValue === undefined) {
checkPositive(++this.predecessorCount);
}
else if (previousValue instanceof PredAndSucc) {
// Restore previous PredAndSucc.
this.adjacentNodeValues.set(node, previousValue);
}
else if (previousValue !== PRED) { // successor
// Do NOT use method parameter value 'unused'. In directed graphs, successors store the value.
this.adjacentNodeValues.set(node, new PredAndSucc(previousValue));
checkPositive(++this.predecessorCount);
}
}
addSuccessor(node, value) {
const previousValue = this.adjacentNodeValues.get(node);
this.adjacentNodeValues.set(node, value);
if (previousValue === undefined) {
checkPositive(++this.successorCount);
return undefined;
}
else if (previousValue instanceof PredAndSucc) {
this.adjacentNodeValues.set(node, new PredAndSucc(value));
return previousValue.successorValue;
}
else if (previousValue === PRED) {
this.adjacentNodeValues.set(node, new PredAndSucc(value));
checkPositive(++this.successorCount);
return undefined;
}
else { // successor
return previousValue;
}
}
static isPredecessor(value) {
return (value == PRED) || (value instanceof PredAndSucc);
}
static isSuccessor(value) {
return (value != PRED) && (value !== undefined);
}
}
exports.DirectedGraphConnections = DirectedGraphConnections;
/**
* A wrapper class to indicate a node is both a predecessor and successor while still providing
* the successor value.
*/
class PredAndSucc {
constructor(successorValue) {
this.successorValue = successorValue;
}
}
//# sourceMappingURL=DirectedGraphConnections.js.map