-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathpartially-ordered-set.js
144 lines (144 loc) · 4.37 KB
/
partially-ordered-set.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
"use strict";
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.PartiallyOrderedSet = exports.CircularDependencyFoundException = exports.DependencyNotFoundException = void 0;
const exception_1 = require("../exception");
class DependencyNotFoundException extends exception_1.BaseException {
constructor() {
super('One of the dependencies is not part of the set.');
}
}
exports.DependencyNotFoundException = DependencyNotFoundException;
class CircularDependencyFoundException extends exception_1.BaseException {
constructor() {
super('Circular dependencies found.');
}
}
exports.CircularDependencyFoundException = CircularDependencyFoundException;
class PartiallyOrderedSet {
_items = new Map();
_checkCircularDependencies(item, deps) {
if (deps.has(item)) {
throw new CircularDependencyFoundException();
}
deps.forEach((dep) => this._checkCircularDependencies(item, this._items.get(dep) || new Set()));
}
clear() {
this._items.clear();
}
has(item) {
return this._items.has(item);
}
get size() {
return this._items.size;
}
forEach(callbackfn, thisArg) {
for (const x of this) {
callbackfn.call(thisArg, x, x, this);
}
}
/**
* Returns an iterable of [v,v] pairs for every value `v` in the set.
*/
*entries() {
for (const item of this) {
yield [item, item];
}
}
/**
* Despite its name, returns an iterable of the values in the set,
*/
keys() {
return this.values();
}
/**
* Returns an iterable of values in the set.
*/
values() {
return this[Symbol.iterator]();
}
add(item, deps = new Set()) {
if (Array.isArray(deps)) {
deps = new Set(deps);
}
// Verify item is not already in the set.
if (this._items.has(item)) {
const itemDeps = this._items.get(item) || new Set();
// If the dependency list is equal, just return, otherwise remove and keep going.
let equal = true;
for (const dep of deps) {
if (!itemDeps.has(dep)) {
equal = false;
break;
}
}
if (equal) {
for (const dep of itemDeps) {
if (!deps.has(dep)) {
equal = false;
break;
}
}
}
if (equal) {
return this;
}
else {
this._items.delete(item);
}
}
// Verify all dependencies are part of the Set.
for (const dep of deps) {
if (!this._items.has(dep)) {
throw new DependencyNotFoundException();
}
}
// Verify there's no dependency cycle.
this._checkCircularDependencies(item, deps);
this._items.set(item, new Set(deps));
return this;
}
delete(item) {
if (!this._items.has(item)) {
return false;
}
// Remove it from all dependencies if force == true.
this._items.forEach((value) => value.delete(item));
return this._items.delete(item);
}
*[Symbol.iterator]() {
const copy = new Map(this._items);
for (const [key, value] of copy.entries()) {
copy.set(key, new Set(value));
}
while (copy.size > 0) {
const run = [];
// Take the first item without dependencies.
for (const [item, deps] of copy.entries()) {
if (deps.size == 0) {
run.push(item);
}
}
for (const item of run) {
copy.forEach((s) => s.delete(item));
copy.delete(item);
yield item;
}
if (run.length == 0) {
// uh oh...
throw new CircularDependencyFoundException();
}
}
return undefined;
}
get [Symbol.toStringTag]() {
return 'Set';
}
}
exports.PartiallyOrderedSet = PartiallyOrderedSet;