hyperformula-dc
Version:
HyperFormula is a JavaScript engine for efficient processing of spreadsheet-like data and formulas
455 lines (384 loc) • 15.1 kB
JavaScript
require("core-js/modules/es.array.slice.js");
require("core-js/modules/es.function.name.js");
require("core-js/modules/es.symbol.js");
require("core-js/modules/es.symbol.description.js");
require("core-js/modules/es.symbol.iterator.js");
exports.__esModule = true;
exports.Graph = void 0;
require("core-js/modules/es.array.iterator.js");
require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.set.js");
require("core-js/modules/es.string.iterator.js");
require("core-js/modules/web.dom-collections.iterator.js");
require("core-js/modules/es.map.js");
require("core-js/modules/web.dom-collections.for-each.js");
require("core-js/modules/es.array.from.js");
require("core-js/modules/es.array.reverse.js");
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
/**
* @license
* Copyright (c) 2021 Handsoncode. All rights reserved.
*/
var NodeVisitStatus;
(function (NodeVisitStatus) {
NodeVisitStatus[NodeVisitStatus["ON_STACK"] = 0] = "ON_STACK";
NodeVisitStatus[NodeVisitStatus["PROCESSED"] = 1] = "PROCESSED";
NodeVisitStatus[NodeVisitStatus["POPPED"] = 2] = "POPPED";
})(NodeVisitStatus || (NodeVisitStatus = {}));
/**
* Provides graph directed structure
*
* Invariants:
* - this.edges(node) exists if and only if node is in the graph
* - this.specialNodes* are always subset of this.nodes
* - this.edges(node) is subset of this.nodes (i.e. it does not contain nodes not present in graph) -- this invariant DOES NOT HOLD right now
*/
var Graph = /*#__PURE__*/function () {
function Graph(dependencyQuery) {
_classCallCheck(this, Graph);
this.dependencyQuery = dependencyQuery;
/** Set with nodes in graph. */
this.nodes = new Set();
this.specialNodes = new Set();
this.specialNodesStructuralChanges = new Set();
this.specialNodesRecentlyChanged = new Set();
this.infiniteRanges = new Set();
/** Nodes adjacency mapping. */
this.edges = new Map();
}
/**
* Adds node to a graph
*
* @param node - a node to be added
*/
_createClass(Graph, [{
key: "addNode",
value: function addNode(node) {
this.nodes.add(node);
if (!this.edges.has(node)) {
this.edges.set(node, new Set());
}
}
/**
* Adds edge between nodes.
*
* The nodes had to be added to the graph before, or the error will be raised
*
* @param fromNode - node from which edge is outcoming
* @param toNode - node to which edge is incoming
*/
}, {
key: "addEdge",
value: function addEdge(fromNode, toNode) {
if (!this.nodes.has(fromNode)) {
throw new Error("Unknown node ".concat(fromNode));
}
if (!this.nodes.has(toNode)) {
throw new Error("Unknown node ".concat(toNode));
} // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.edges.get(fromNode).add(toNode);
}
}, {
key: "removeEdge",
value: function removeEdge(fromNode, toNode) {
if (this.existsEdge(fromNode, toNode)) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.edges.get(fromNode).delete(toNode);
} else {
throw new Error('Edge does not exist');
}
}
}, {
key: "softRemoveEdge",
value: function softRemoveEdge(fromNode, toNode) {
var _a;
(_a = this.edges.get(fromNode)) === null || _a === void 0 ? void 0 : _a.delete(toNode);
}
}, {
key: "removeIncomingEdges",
value: function removeIncomingEdges(toNode) {
this.edges.forEach(function (nodeEdges) {
nodeEdges.delete(toNode);
});
}
/**
* Returns nodes adjacent to given node
*
* @param node - node to which adjacent nodes we want to retrieve
*/
}, {
key: "adjacentNodes",
value: function adjacentNodes(node) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.edges.get(node);
}
}, {
key: "adjacentNodesCount",
value: function adjacentNodesCount(node) {
return this.adjacentNodes(node).size;
}
/**
* Checks whether a node is present in graph
*
* @param node - node to check
*/
}, {
key: "hasNode",
value: function hasNode(node) {
return this.nodes.has(node);
}
/**
* Returns number of nodes in graph
*/
}, {
key: "nodesCount",
value: function nodesCount() {
return this.nodes.size;
}
/**
* Returns number of edges in graph
*/
}, {
key: "edgesCount",
value: function edgesCount() {
var result = 0;
this.edges.forEach(function (edgesForNode) {
return result += edgesForNode.size;
});
return result;
}
}, {
key: "removeNode",
value: function removeNode(node) {
var _iterator = _createForOfIteratorHelper(this.adjacentNodes(node).values()),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var adjacentNode = _step.value;
this.markNodeAsSpecialRecentlyChanged(adjacentNode);
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
this.edges.delete(node);
this.nodes.delete(node);
this.specialNodes.delete(node);
this.specialNodesRecentlyChanged.delete(node);
this.specialNodesStructuralChanges.delete(node);
this.infiniteRanges.delete(node);
return this.removeDependencies(node);
}
}, {
key: "markNodeAsSpecial",
value: function markNodeAsSpecial(node) {
this.specialNodes.add(node);
}
}, {
key: "markNodeAsSpecialRecentlyChanged",
value: function markNodeAsSpecialRecentlyChanged(node) {
if (this.nodes.has(node)) {
this.specialNodesRecentlyChanged.add(node);
}
}
}, {
key: "markNodeAsChangingWithStructure",
value: function markNodeAsChangingWithStructure(node) {
this.specialNodesStructuralChanges.add(node);
}
}, {
key: "clearSpecialNodesRecentlyChanged",
value: function clearSpecialNodesRecentlyChanged() {
this.specialNodesRecentlyChanged.clear();
}
}, {
key: "markNodeAsInfiniteRange",
value: function markNodeAsInfiniteRange(node) {
this.infiniteRanges.add(node);
}
/**
* Checks whether exists edge between nodes
*
* @param fromNode - node from which edge is outcoming
* @param toNode - node to which edge is incoming
*/
}, {
key: "existsEdge",
value: function existsEdge(fromNode, toNode) {
var _a, _b;
return (_b = (_a = this.edges.get(fromNode)) === null || _a === void 0 ? void 0 : _a.has(toNode)) !== null && _b !== void 0 ? _b : false;
}
/*
* return a topological sort order, but separates vertices that exist in some cycle
*/
}, {
key: "topSortWithScc",
value: function topSortWithScc() {
return this.getTopSortedWithSccSubgraphFrom(Array.from(this.nodes), function () {
return true;
}, function () {});
}
/**
*
* an iterative implementation of Tarjan's algorithm for finding strongly connected compontents
* returns vertices in order of topological sort, but vertices that are on cycles are kept separate
*
* @param modifiedNodes - seed for computation. During engine init run, all of the vertices of grap. In recomputation run, changed vertices.
* @param operatingFunction - recomputes value of a node, and returns whether a change occured
* @param onCycle - action to be performed when node is on cycle
*/
}, {
key: "getTopSortedWithSccSubgraphFrom",
value: function getTopSortedWithSccSubgraphFrom(modifiedNodes, operatingFunction, onCycle) {
var _this = this;
var entranceTime = new Map();
var low = new Map();
var parent = new Map();
var inSCC = new Set(); // node status life cycle:
// undefined -> ON_STACK -> PROCESSED -> POPPED
var nodeStatus = new Map();
var order = [];
var time = 0;
var sccNonSingletons = new Set();
modifiedNodes.reverse();
modifiedNodes.forEach(function (v) {
if (nodeStatus.get(v) !== undefined) {
return;
}
var DFSstack = [v];
var SCCstack = [];
nodeStatus.set(v, NodeVisitStatus.ON_STACK);
var _loop = function _loop() {
var u = DFSstack[DFSstack.length - 1]; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
switch (nodeStatus.get(u)) {
case NodeVisitStatus.ON_STACK:
{
entranceTime.set(u, time);
low.set(u, time);
SCCstack.push(u);
time++;
_this.adjacentNodes(u).forEach(function (t) {
if (entranceTime.get(t) === undefined) {
DFSstack.push(t);
parent.set(t, u);
nodeStatus.set(t, NodeVisitStatus.ON_STACK);
}
});
nodeStatus.set(u, NodeVisitStatus.PROCESSED);
break;
}
case NodeVisitStatus.PROCESSED:
{
// leaving this DFS subtree
var uLow; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
uLow = entranceTime.get(u);
_this.adjacentNodes(u).forEach(function (t) {
if (!inSCC.has(t)) {
if (parent.get(t) === u) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
uLow = Math.min(uLow, low.get(t));
} else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
uLow = Math.min(uLow, entranceTime.get(t));
}
}
});
low.set(u, uLow);
if (uLow === entranceTime.get(u)) {
var currentSCC = [];
do {
currentSCC.push(SCCstack[SCCstack.length - 1]);
SCCstack.pop();
} while (currentSCC[currentSCC.length - 1] !== u);
currentSCC.forEach(function (t) {
inSCC.add(t);
});
order.push.apply(order, currentSCC);
if (currentSCC.length > 1) {
currentSCC.forEach(function (t) {
sccNonSingletons.add(t);
});
}
}
DFSstack.pop();
nodeStatus.set(u, NodeVisitStatus.POPPED);
break;
}
case NodeVisitStatus.POPPED:
{
// it's a 'shadow' copy, we already processed this vertex and can ignore it
DFSstack.pop();
break;
}
}
};
while (DFSstack.length > 0) {
_loop();
}
});
var shouldBeUpdatedMapping = new Set(modifiedNodes);
var sorted = [];
var cycled = [];
order.reverse();
order.forEach(function (t) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if (sccNonSingletons.has(t) || _this.adjacentNodes(t).has(t)) {
cycled.push(t);
onCycle(t);
_this.adjacentNodes(t).forEach(function (s) {
return shouldBeUpdatedMapping.add(s);
});
} else {
sorted.push(t);
if (shouldBeUpdatedMapping.has(t) && operatingFunction(t)) {
_this.adjacentNodes(t).forEach(function (s) {
return shouldBeUpdatedMapping.add(s);
});
}
}
});
return {
sorted: sorted,
cycled: cycled
};
}
}, {
key: "getDependencies",
value: function getDependencies(vertex) {
var result = [];
this.edges.forEach(function (adjacentNodes, sourceNode) {
if (adjacentNodes.has(vertex)) {
result.push(sourceNode);
}
});
return result;
}
}, {
key: "removeDependencies",
value: function removeDependencies(node) {
var dependencies = this.dependencyQuery(node);
var _iterator2 = _createForOfIteratorHelper(dependencies),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var dependency = _step2.value;
this.softRemoveEdge(dependency, node);
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
return dependencies;
}
}]);
return Graph;
}();
exports.Graph = Graph;
;