@fluidframework/merge-tree 3.0.2 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +30 -34
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +6 -7
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -7
- package/dist/index.js.map +1 -1
- package/dist/mergeTreeNodes.d.ts +0 -8
- package/dist/mergeTreeNodes.d.ts.map +1 -1
- package/dist/mergeTreeNodes.js +1 -11
- package/dist/mergeTreeNodes.js.map +1 -1
- package/dist/referencePositions.d.ts +0 -10
- package/dist/referencePositions.d.ts.map +1 -1
- package/dist/referencePositions.js +0 -16
- package/dist/referencePositions.js.map +1 -1
- package/dist/test/beastTest.spec.d.ts +0 -3
- package/dist/test/beastTest.spec.d.ts.map +1 -1
- package/dist/test/beastTest.spec.js +0 -201
- package/dist/test/beastTest.spec.js.map +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +2 -5
- package/dist/test/index.js.map +1 -1
- package/dist/test/testServer.d.ts +1 -2
- package/dist/test/testServer.d.ts.map +1 -1
- package/dist/test/testServer.js +3 -6
- package/dist/test/testServer.js.map +1 -1
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +7 -8
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +1 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -2
- package/lib/index.js.map +1 -1
- package/lib/mergeTreeNodes.d.ts +0 -8
- package/lib/mergeTreeNodes.d.ts.map +1 -1
- package/lib/mergeTreeNodes.js +0 -8
- package/lib/mergeTreeNodes.js.map +1 -1
- package/lib/referencePositions.d.ts +0 -10
- package/lib/referencePositions.d.ts.map +1 -1
- package/lib/referencePositions.js +0 -14
- package/lib/referencePositions.js.map +1 -1
- package/lib/test/beastTest.spec.d.ts +0 -3
- package/lib/test/beastTest.spec.d.ts.map +1 -1
- package/lib/test/beastTest.spec.js +1 -199
- package/lib/test/beastTest.spec.js.map +1 -1
- package/lib/test/index.d.ts +1 -1
- package/lib/test/index.d.ts.map +1 -1
- package/lib/test/index.js +1 -1
- package/lib/test/index.js.map +1 -1
- package/lib/test/testServer.d.ts +1 -2
- package/lib/test/testServer.d.ts.map +1 -1
- package/lib/test/testServer.js +3 -6
- package/lib/test/testServer.js.map +1 -1
- package/package.json +16 -16
- package/src/client.ts +6 -8
- package/src/index.ts +0 -17
- package/src/mergeTreeNodes.ts +0 -10
- package/src/referencePositions.ts +0 -16
- package/dist/collections/index.d.ts +0 -6
- package/dist/collections/index.d.ts.map +0 -1
- package/dist/collections/index.js +0 -11
- package/dist/collections/index.js.map +0 -1
- package/dist/collections/rbTree.d.ts +0 -160
- package/dist/collections/rbTree.d.ts.map +0 -1
- package/dist/collections/rbTree.js +0 -496
- package/dist/collections/rbTree.js.map +0 -1
- package/lib/collections/index.d.ts +0 -6
- package/lib/collections/index.d.ts.map +0 -1
- package/lib/collections/index.js +0 -6
- package/lib/collections/index.js.map +0 -1
- package/lib/collections/rbTree.d.ts +0 -160
- package/lib/collections/rbTree.d.ts.map +0 -1
- package/lib/collections/rbTree.js +0 -492
- package/lib/collections/rbTree.js.map +0 -1
- package/src/collections/index.ts +0 -20
- package/src/collections/rbTree.ts +0 -721
|
@@ -1,721 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
-
* Licensed under the MIT License.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @internal
|
|
8
|
-
*/
|
|
9
|
-
export const RBColor = {
|
|
10
|
-
RED: 0,
|
|
11
|
-
BLACK: 1,
|
|
12
|
-
} as const;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @internal
|
|
16
|
-
*/
|
|
17
|
-
export type RBColor = (typeof RBColor)[keyof typeof RBColor];
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @internal
|
|
21
|
-
*/
|
|
22
|
-
export interface RBNode<TKey, TData> {
|
|
23
|
-
key: TKey;
|
|
24
|
-
data: TData;
|
|
25
|
-
left: RBNode<TKey, TData> | undefined;
|
|
26
|
-
right: RBNode<TKey, TData> | undefined;
|
|
27
|
-
color: RBColor;
|
|
28
|
-
size: number;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* @internal
|
|
32
|
-
*/
|
|
33
|
-
export interface IRBAugmentation<TKey, TData> {
|
|
34
|
-
update(node: RBNode<TKey, TData>): void;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* @internal
|
|
38
|
-
*/
|
|
39
|
-
export interface IRBMatcher<TKey, TData> {
|
|
40
|
-
continueSubtree(node: RBNode<TKey, TData> | undefined, key: TKey): boolean;
|
|
41
|
-
matchNode(node: RBNode<TKey, TData> | undefined, key: TKey): boolean;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* @internal
|
|
46
|
-
*/
|
|
47
|
-
export interface RBNodeActions<TKey, TData> {
|
|
48
|
-
infix?(node: RBNode<TKey, TData>): boolean;
|
|
49
|
-
pre?(node: RBNode<TKey, TData>): boolean;
|
|
50
|
-
post?(node: RBNode<TKey, TData>): boolean;
|
|
51
|
-
showStructure?: boolean;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* @internal
|
|
56
|
-
*/
|
|
57
|
-
export interface KeyComparer<TKey> {
|
|
58
|
-
// eslint-disable-next-line @typescript-eslint/prefer-function-type
|
|
59
|
-
(a: TKey, b: TKey): number;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* @internal
|
|
64
|
-
*/
|
|
65
|
-
export interface Property<TKey, TData> {
|
|
66
|
-
key: TKey;
|
|
67
|
-
data: TData;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* @internal
|
|
72
|
-
*/
|
|
73
|
-
export interface PropertyAction<TKey, TData> {
|
|
74
|
-
// eslint-disable-next-line @typescript-eslint/prefer-function-type
|
|
75
|
-
<TAccum>(p: Property<TKey, TData>, accum?: TAccum): boolean;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* @internal
|
|
80
|
-
*/
|
|
81
|
-
export interface QProperty<TKey, TData> {
|
|
82
|
-
key?: TKey;
|
|
83
|
-
data?: TData;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* @internal
|
|
88
|
-
*/
|
|
89
|
-
export type ConflictAction<TKey, TData> = (
|
|
90
|
-
key: TKey,
|
|
91
|
-
currentKey: TKey,
|
|
92
|
-
data: TData,
|
|
93
|
-
currentData: TData,
|
|
94
|
-
) => QProperty<TKey, TData>;
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* @internal
|
|
98
|
-
*/
|
|
99
|
-
export interface SortedDictionary<TKey, TData> extends Dictionary<TKey, TData> {
|
|
100
|
-
max(): Property<TKey, TData> | undefined;
|
|
101
|
-
min(): Property<TKey, TData> | undefined;
|
|
102
|
-
mapRange<TAccum>(
|
|
103
|
-
action: PropertyAction<TKey, TData>,
|
|
104
|
-
accum?: TAccum,
|
|
105
|
-
start?: TKey,
|
|
106
|
-
end?: TKey,
|
|
107
|
-
): void;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* @internal
|
|
112
|
-
*/
|
|
113
|
-
export interface Dictionary<TKey, TData> {
|
|
114
|
-
get(key: TKey): Property<TKey, TData> | undefined;
|
|
115
|
-
put(key: TKey, data: TData, conflict?: ConflictAction<TKey, TData>): void;
|
|
116
|
-
remove(key: TKey): void;
|
|
117
|
-
map<TAccum>(action: PropertyAction<TKey, TData>, accum?: TAccum): void;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* @internal
|
|
122
|
-
*/
|
|
123
|
-
export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData> {
|
|
124
|
-
private root: RBNode<TKey, TData> | undefined;
|
|
125
|
-
|
|
126
|
-
constructor(
|
|
127
|
-
private readonly compareKeys: KeyComparer<TKey>,
|
|
128
|
-
private readonly aug?: IRBAugmentation<TKey, TData>,
|
|
129
|
-
) {}
|
|
130
|
-
|
|
131
|
-
private makeNode(key: TKey, data: TData, color: RBColor, size: number): RBNode<TKey, TData> {
|
|
132
|
-
return { key, data, color, size } as unknown as RBNode<TKey, TData>;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
private isRed(node: RBNode<TKey, TData> | undefined): boolean {
|
|
136
|
-
return !!node && node.color === RBColor.RED;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
private nodeSize(node: RBNode<TKey, TData> | undefined): number {
|
|
140
|
-
return node ? node.size : 0;
|
|
141
|
-
}
|
|
142
|
-
public size(): number {
|
|
143
|
-
return this.nodeSize(this.root);
|
|
144
|
-
}
|
|
145
|
-
public isEmpty(): boolean {
|
|
146
|
-
return !this.root;
|
|
147
|
-
}
|
|
148
|
-
public get(key: TKey): RBNode<TKey, TData> | undefined {
|
|
149
|
-
if (key !== undefined) {
|
|
150
|
-
return this.nodeGet(this.root, key);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
private nodeGet(
|
|
154
|
-
node: RBNode<TKey, TData> | undefined,
|
|
155
|
-
key: TKey,
|
|
156
|
-
): RBNode<TKey, TData> | undefined {
|
|
157
|
-
let _node = node;
|
|
158
|
-
while (_node) {
|
|
159
|
-
const cmp = this.compareKeys(key, _node.key);
|
|
160
|
-
if (cmp < 0) {
|
|
161
|
-
_node = _node.left;
|
|
162
|
-
} else if (cmp > 0) {
|
|
163
|
-
_node = _node.right;
|
|
164
|
-
} else {
|
|
165
|
-
return _node;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
private contains(key: TKey): RBNode<TKey, TData> | undefined {
|
|
170
|
-
return this.get(key);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
public gather(key: TKey, matcher: IRBMatcher<TKey, TData>): RBNode<TKey, TData>[] {
|
|
174
|
-
const results = [] as RBNode<TKey, TData>[];
|
|
175
|
-
if (key !== undefined) {
|
|
176
|
-
this.nodeGather(this.root, results, key, matcher);
|
|
177
|
-
}
|
|
178
|
-
return results;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
private nodeGather(
|
|
182
|
-
node: RBNode<TKey, TData> | undefined,
|
|
183
|
-
results: RBNode<TKey, TData>[],
|
|
184
|
-
key: TKey,
|
|
185
|
-
matcher: IRBMatcher<TKey, TData>,
|
|
186
|
-
): void {
|
|
187
|
-
if (node) {
|
|
188
|
-
if (matcher.continueSubtree(node.left, key)) {
|
|
189
|
-
this.nodeGather(node.left, results, key, matcher);
|
|
190
|
-
}
|
|
191
|
-
if (matcher.matchNode(node, key)) {
|
|
192
|
-
results.push(node);
|
|
193
|
-
}
|
|
194
|
-
if (matcher.continueSubtree(node.right, key)) {
|
|
195
|
-
this.nodeGather(node.right, results, key, matcher);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
public walkExactMatchesForward(
|
|
201
|
-
compareFn: (node: RBNode<TKey, TData>) => number,
|
|
202
|
-
actionFn: (node: RBNode<TKey, TData>) => void,
|
|
203
|
-
continueLeftFn: (number: number) => boolean,
|
|
204
|
-
continueRightFn: (number: number) => boolean,
|
|
205
|
-
): void {
|
|
206
|
-
this.nodeWalkExactMatchesForward(
|
|
207
|
-
this.root,
|
|
208
|
-
compareFn,
|
|
209
|
-
actionFn,
|
|
210
|
-
continueLeftFn,
|
|
211
|
-
continueRightFn,
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
private nodeWalkExactMatchesForward(
|
|
216
|
-
node: RBNode<TKey, TData> | undefined,
|
|
217
|
-
compareFn: (node: RBNode<TKey, TData>) => number,
|
|
218
|
-
actionFn: (node: RBNode<TKey, TData>) => void,
|
|
219
|
-
continueLeftFn: (number: number) => boolean,
|
|
220
|
-
continueRightFn: (number: number) => boolean,
|
|
221
|
-
): void {
|
|
222
|
-
if (!node) {
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
const result: number = compareFn(node);
|
|
226
|
-
if (continueLeftFn(result)) {
|
|
227
|
-
this.nodeWalkExactMatchesForward(
|
|
228
|
-
node.left,
|
|
229
|
-
compareFn,
|
|
230
|
-
actionFn,
|
|
231
|
-
continueLeftFn,
|
|
232
|
-
continueRightFn,
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
if (result === 0) {
|
|
236
|
-
actionFn(node);
|
|
237
|
-
}
|
|
238
|
-
if (continueRightFn(result)) {
|
|
239
|
-
this.nodeWalkExactMatchesForward(
|
|
240
|
-
node.right,
|
|
241
|
-
compareFn,
|
|
242
|
-
actionFn,
|
|
243
|
-
continueLeftFn,
|
|
244
|
-
continueRightFn,
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
public walkExactMatchesBackward(
|
|
250
|
-
compareFn: (node: RBNode<TKey, TData>) => number,
|
|
251
|
-
actionFn: (node: RBNode<TKey, TData>) => void,
|
|
252
|
-
continueLeftFn: (number: number) => boolean,
|
|
253
|
-
continueRightFn: (number: number) => boolean,
|
|
254
|
-
): void {
|
|
255
|
-
this.nodeWalkExactMatchesBackward(
|
|
256
|
-
this.root,
|
|
257
|
-
compareFn,
|
|
258
|
-
actionFn,
|
|
259
|
-
continueLeftFn,
|
|
260
|
-
continueRightFn,
|
|
261
|
-
);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
private nodeWalkExactMatchesBackward(
|
|
265
|
-
node: RBNode<TKey, TData> | undefined,
|
|
266
|
-
compareFn: (node: RBNode<TKey, TData>) => number,
|
|
267
|
-
actionFn: (node: RBNode<TKey, TData>) => void,
|
|
268
|
-
continueLeftFn: (cmp: number) => boolean,
|
|
269
|
-
continueRightFn: (cmp: number) => boolean,
|
|
270
|
-
): void {
|
|
271
|
-
if (!node) {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
const result: number = compareFn(node);
|
|
275
|
-
if (continueRightFn(result)) {
|
|
276
|
-
this.nodeWalkExactMatchesBackward(
|
|
277
|
-
node.right,
|
|
278
|
-
compareFn,
|
|
279
|
-
actionFn,
|
|
280
|
-
continueLeftFn,
|
|
281
|
-
continueRightFn,
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
if (result === 0) {
|
|
285
|
-
actionFn(node);
|
|
286
|
-
}
|
|
287
|
-
if (continueLeftFn(result)) {
|
|
288
|
-
this.nodeWalkExactMatchesBackward(
|
|
289
|
-
node.left,
|
|
290
|
-
compareFn,
|
|
291
|
-
actionFn,
|
|
292
|
-
continueLeftFn,
|
|
293
|
-
continueRightFn,
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
public put(key: TKey, data: TData, conflict?: ConflictAction<TKey, TData>): void {
|
|
299
|
-
if (key !== undefined) {
|
|
300
|
-
if (data === undefined) {
|
|
301
|
-
this.remove(key);
|
|
302
|
-
} else {
|
|
303
|
-
this.root = this.nodePut(this.root, key, data, conflict);
|
|
304
|
-
this.root.color = RBColor.BLACK;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
private nodePut(
|
|
310
|
-
node: RBNode<TKey, TData> | undefined,
|
|
311
|
-
key: TKey,
|
|
312
|
-
data: TData,
|
|
313
|
-
conflict?: ConflictAction<TKey, TData>,
|
|
314
|
-
): RBNode<TKey, TData> {
|
|
315
|
-
let _node = node;
|
|
316
|
-
if (_node) {
|
|
317
|
-
const cmp = this.compareKeys(key, _node.key);
|
|
318
|
-
if (cmp < 0) {
|
|
319
|
-
_node.left = this.nodePut(_node.left, key, data, conflict);
|
|
320
|
-
} else if (cmp > 0) {
|
|
321
|
-
_node.right = this.nodePut(_node.right, key, data, conflict);
|
|
322
|
-
} else {
|
|
323
|
-
if (conflict) {
|
|
324
|
-
const kd = conflict(key, _node.key, data, _node.data);
|
|
325
|
-
if (kd.key) {
|
|
326
|
-
_node.key = kd.key;
|
|
327
|
-
}
|
|
328
|
-
_node.data = kd.data ?? data;
|
|
329
|
-
} else {
|
|
330
|
-
_node.data = data;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
if (this.isRed(_node.right) && !this.isRed(_node.left)) {
|
|
334
|
-
_node = this.rotateLeft(_node);
|
|
335
|
-
}
|
|
336
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
337
|
-
if (this.isRed(_node.left) && this.isRed(_node.left!.left)) {
|
|
338
|
-
_node = this.rotateRight(_node);
|
|
339
|
-
}
|
|
340
|
-
if (this.isRed(_node.left) && this.isRed(_node.right)) {
|
|
341
|
-
this.flipColors(_node);
|
|
342
|
-
}
|
|
343
|
-
_node.size = this.nodeSize(_node.left) + this.nodeSize(_node.right) + 1;
|
|
344
|
-
if (this.aug) {
|
|
345
|
-
this.updateLocal(_node);
|
|
346
|
-
}
|
|
347
|
-
return _node;
|
|
348
|
-
} else {
|
|
349
|
-
return this.makeNode(key, data, RBColor.RED, 1);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
private updateLocal(node: RBNode<TKey, TData>): void {
|
|
354
|
-
if (this.aug) {
|
|
355
|
-
if (this.isRed(node.left)) {
|
|
356
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
357
|
-
this.aug.update(node.left!);
|
|
358
|
-
}
|
|
359
|
-
if (this.isRed(node.right)) {
|
|
360
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
361
|
-
this.aug.update(node.right!);
|
|
362
|
-
}
|
|
363
|
-
this.aug.update(node);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
private nodeRemoveMin(node: RBNode<TKey, TData>): RBNode<TKey, TData> | undefined {
|
|
368
|
-
let _node = node;
|
|
369
|
-
if (_node.left) {
|
|
370
|
-
if (!this.isRed(_node.left) && !this.isRed(_node.left.left)) {
|
|
371
|
-
_node = this.moveRedLeft(_node);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
375
|
-
_node.left = this.nodeRemoveMin(_node.left!);
|
|
376
|
-
return this.balance(_node);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
public remove(key: TKey): void {
|
|
381
|
-
if (key !== undefined) {
|
|
382
|
-
if (!this.contains(key)) {
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
this.removeExisting(key);
|
|
387
|
-
}
|
|
388
|
-
// TODO: error on undefined key
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
public removeExisting(key: TKey): void {
|
|
392
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
393
|
-
if (!this.isRed(this.root!.left) && !this.isRed(this.root!.right)) {
|
|
394
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
395
|
-
this.root!.color = RBColor.RED;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
399
|
-
this.root = this.nodeRemove(this.root!, key);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
private nodeRemove(node: RBNode<TKey, TData>, key: TKey): RBNode<TKey, TData> | undefined {
|
|
403
|
-
let _node = node;
|
|
404
|
-
if (this.compareKeys(key, _node.key) < 0) {
|
|
405
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
406
|
-
if (!this.isRed(_node.left) && !this.isRed(_node.left!.left)) {
|
|
407
|
-
_node = this.moveRedLeft(_node);
|
|
408
|
-
}
|
|
409
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
410
|
-
_node.left = this.nodeRemove(_node.left!, key);
|
|
411
|
-
} else {
|
|
412
|
-
if (this.isRed(_node.left)) {
|
|
413
|
-
_node = this.rotateRight(_node);
|
|
414
|
-
}
|
|
415
|
-
if (this.compareKeys(key, _node.key) === 0 && !_node.right) {
|
|
416
|
-
return undefined;
|
|
417
|
-
}
|
|
418
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
419
|
-
if (!this.isRed(_node.right) && !this.isRed(_node.right!.left)) {
|
|
420
|
-
_node = this.moveRedRight(_node);
|
|
421
|
-
}
|
|
422
|
-
if (this.compareKeys(key, _node.key) === 0) {
|
|
423
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
424
|
-
const subtreeMin = this.nodeMin(_node.right!);
|
|
425
|
-
_node.key = subtreeMin.key;
|
|
426
|
-
_node.data = subtreeMin.data;
|
|
427
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
428
|
-
_node.right = this.nodeRemoveMin(_node.right!);
|
|
429
|
-
} else {
|
|
430
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
431
|
-
_node.right = this.nodeRemove(_node.right!, key);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
return this.balance(_node);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* Finds the largest node that is less than or equal to a given key.
|
|
439
|
-
*/
|
|
440
|
-
public floor(key: TKey): RBNode<TKey, TData> | undefined {
|
|
441
|
-
if (!this.isEmpty()) {
|
|
442
|
-
return this.nodeFloor(this.root, key);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
private nodeFloor(
|
|
447
|
-
node: RBNode<TKey, TData> | undefined,
|
|
448
|
-
key: TKey,
|
|
449
|
-
): RBNode<TKey, TData> | undefined {
|
|
450
|
-
if (node) {
|
|
451
|
-
const cmp = this.compareKeys(key, node.key);
|
|
452
|
-
if (cmp === 0) {
|
|
453
|
-
return node;
|
|
454
|
-
} else if (cmp < 0) {
|
|
455
|
-
return this.nodeFloor(node.left, key);
|
|
456
|
-
} else {
|
|
457
|
-
const rightFloor = this.nodeFloor(node.right, key);
|
|
458
|
-
return rightFloor ?? node;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* Finds the smallest node that is greater than or equal to a given key.
|
|
465
|
-
*/
|
|
466
|
-
public ceil(key: TKey): RBNode<TKey, TData> | undefined {
|
|
467
|
-
if (!this.isEmpty()) {
|
|
468
|
-
return this.nodeCeil(this.root, key);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
private nodeCeil(
|
|
473
|
-
node: RBNode<TKey, TData> | undefined,
|
|
474
|
-
key: TKey,
|
|
475
|
-
): RBNode<TKey, TData> | undefined {
|
|
476
|
-
if (node) {
|
|
477
|
-
const cmp = this.compareKeys(key, node.key);
|
|
478
|
-
if (cmp === 0) {
|
|
479
|
-
return node;
|
|
480
|
-
} else if (cmp > 0) {
|
|
481
|
-
return this.nodeCeil(node.right, key);
|
|
482
|
-
} else {
|
|
483
|
-
const leftCeil = this.nodeCeil(node.left, key);
|
|
484
|
-
return leftCeil ?? node;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
public min(): RBNode<TKey, TData> | undefined {
|
|
490
|
-
if (this.root) {
|
|
491
|
-
return this.nodeMin(this.root);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
private nodeMin(node: RBNode<TKey, TData>): RBNode<TKey, TData> {
|
|
496
|
-
return node.left ? this.nodeMin(node.left) : node;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
public max(): RBNode<TKey, TData> | undefined {
|
|
500
|
-
if (this.root) {
|
|
501
|
-
return this.nodeMax(this.root);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
private nodeMax(node: RBNode<TKey, TData>): RBNode<TKey, TData> {
|
|
506
|
-
return node.right ? this.nodeMax(node.right) : node;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
private rotateRight(node: RBNode<TKey, TData>): RBNode<TKey, TData> {
|
|
510
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
511
|
-
const leftChild = node.left!;
|
|
512
|
-
node.left = leftChild.right;
|
|
513
|
-
leftChild.right = node;
|
|
514
|
-
leftChild.color = leftChild.right.color;
|
|
515
|
-
leftChild.right.color = RBColor.RED;
|
|
516
|
-
leftChild.size = node.size;
|
|
517
|
-
node.size = this.nodeSize(node.left) + this.nodeSize(node.right) + 1;
|
|
518
|
-
if (this.aug) {
|
|
519
|
-
this.updateLocal(node);
|
|
520
|
-
this.updateLocal(leftChild);
|
|
521
|
-
}
|
|
522
|
-
return leftChild;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
private rotateLeft(node: RBNode<TKey, TData>): RBNode<TKey, TData> {
|
|
526
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
527
|
-
const rightChild = node.right!;
|
|
528
|
-
node.right = rightChild.left;
|
|
529
|
-
rightChild.left = node;
|
|
530
|
-
rightChild.color = rightChild.left.color;
|
|
531
|
-
rightChild.left.color = RBColor.RED;
|
|
532
|
-
rightChild.size = node.size;
|
|
533
|
-
node.size = this.nodeSize(node.left) + this.nodeSize(node.right) + 1;
|
|
534
|
-
if (this.aug) {
|
|
535
|
-
this.updateLocal(node);
|
|
536
|
-
this.updateLocal(rightChild);
|
|
537
|
-
}
|
|
538
|
-
return rightChild;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
private oppositeColor(c: RBColor): RBColor {
|
|
542
|
-
return c === RBColor.BLACK ? RBColor.RED : RBColor.BLACK;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
private flipColors(node: RBNode<TKey, TData>): void {
|
|
546
|
-
node.color = this.oppositeColor(node.color);
|
|
547
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
548
|
-
node.left!.color = this.oppositeColor(node.left!.color);
|
|
549
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
550
|
-
node.right!.color = this.oppositeColor(node.right!.color);
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
private moveRedLeft(node: RBNode<TKey, TData>): RBNode<TKey, TData> {
|
|
554
|
-
let _node = node;
|
|
555
|
-
this.flipColors(_node);
|
|
556
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
557
|
-
if (this.isRed(_node.right!.left)) {
|
|
558
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
559
|
-
_node.right = this.rotateRight(_node.right!);
|
|
560
|
-
_node = this.rotateLeft(_node);
|
|
561
|
-
this.flipColors(_node);
|
|
562
|
-
}
|
|
563
|
-
return _node;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
private moveRedRight(node: RBNode<TKey, TData>): RBNode<TKey, TData> {
|
|
567
|
-
let _node = node;
|
|
568
|
-
this.flipColors(_node);
|
|
569
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
570
|
-
if (this.isRed(_node.left!.left)) {
|
|
571
|
-
_node = this.rotateRight(_node);
|
|
572
|
-
this.flipColors(_node);
|
|
573
|
-
}
|
|
574
|
-
return _node;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
private balance(input: RBNode<TKey, TData>): RBNode<TKey, TData> {
|
|
578
|
-
let node: RBNode<TKey, TData> | undefined = input;
|
|
579
|
-
if (this.isRed(node.right)) {
|
|
580
|
-
node = this.rotateLeft(node);
|
|
581
|
-
}
|
|
582
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
583
|
-
if (this.isRed(node.left) && this.isRed(node.left!.left)) {
|
|
584
|
-
node = this.rotateRight(node);
|
|
585
|
-
}
|
|
586
|
-
if (this.isRed(node.left) && this.isRed(node.right)) {
|
|
587
|
-
this.flipColors(node);
|
|
588
|
-
}
|
|
589
|
-
node.size = this.nodeSize(node.left) + this.nodeSize(node.right) + 1;
|
|
590
|
-
if (this.aug) {
|
|
591
|
-
this.aug.update(node);
|
|
592
|
-
}
|
|
593
|
-
return node;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
public mapRange<TAccum>(
|
|
597
|
-
action: PropertyAction<TKey, TData>,
|
|
598
|
-
accum?: TAccum,
|
|
599
|
-
start?: TKey,
|
|
600
|
-
end?: TKey,
|
|
601
|
-
): void {
|
|
602
|
-
this.nodeMap(this.root, action, accum, start, end);
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
public map<TAccum>(action: PropertyAction<TKey, TData>, accum?: TAccum): void {
|
|
606
|
-
// TODO: optimize to avoid comparisons
|
|
607
|
-
this.nodeMap(this.root, action, accum);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
public keys(): TKey[] {
|
|
611
|
-
const keyList: TKey[] = [];
|
|
612
|
-
const actions: RBNodeActions<TKey, TData> = {
|
|
613
|
-
showStructure: true,
|
|
614
|
-
infix: (node) => {
|
|
615
|
-
keyList.push(node.key);
|
|
616
|
-
return true;
|
|
617
|
-
},
|
|
618
|
-
};
|
|
619
|
-
this.walk(actions);
|
|
620
|
-
return keyList;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
/**
|
|
624
|
-
* Depth-first traversal with custom action; if action returns
|
|
625
|
-
* false, traversal is halted.
|
|
626
|
-
* @param action - action to apply to each node
|
|
627
|
-
*/
|
|
628
|
-
public walk(actions: RBNodeActions<TKey, TData>): void {
|
|
629
|
-
this.nodeWalk(this.root, actions);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
public walkBackward(actions: RBNodeActions<TKey, TData>): void {
|
|
633
|
-
this.nodeWalkBackward(this.root, actions);
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
private nodeWalk(
|
|
637
|
-
node: RBNode<TKey, TData> | undefined,
|
|
638
|
-
actions: RBNodeActions<TKey, TData>,
|
|
639
|
-
): boolean {
|
|
640
|
-
let go = true;
|
|
641
|
-
if (node) {
|
|
642
|
-
if (actions.pre && (!!actions.showStructure || node.color === RBColor.BLACK)) {
|
|
643
|
-
go = actions.pre(node);
|
|
644
|
-
}
|
|
645
|
-
if (node.left) {
|
|
646
|
-
go = this.nodeWalk(node.left, actions);
|
|
647
|
-
}
|
|
648
|
-
if (go && actions.infix && (!!actions.showStructure || node.color === RBColor.BLACK)) {
|
|
649
|
-
go = actions.infix(node);
|
|
650
|
-
}
|
|
651
|
-
if (go) {
|
|
652
|
-
go = this.nodeWalk(node.right, actions);
|
|
653
|
-
}
|
|
654
|
-
if (go && actions.post && (!!actions.showStructure || node.color === RBColor.BLACK)) {
|
|
655
|
-
go = actions.post(node);
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
return go;
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
private nodeWalkBackward(
|
|
662
|
-
node: RBNode<TKey, TData> | undefined,
|
|
663
|
-
actions: RBNodeActions<TKey, TData>,
|
|
664
|
-
): boolean {
|
|
665
|
-
let go = true;
|
|
666
|
-
if (node) {
|
|
667
|
-
if (actions.pre && (!!actions.showStructure || node.color === RBColor.BLACK)) {
|
|
668
|
-
go = actions.pre(node);
|
|
669
|
-
}
|
|
670
|
-
if (node.right) {
|
|
671
|
-
go = this.nodeWalkBackward(node.right, actions);
|
|
672
|
-
}
|
|
673
|
-
if (go && actions.infix && (!!actions.showStructure || node.color === RBColor.BLACK)) {
|
|
674
|
-
go = actions.infix(node);
|
|
675
|
-
}
|
|
676
|
-
if (go) {
|
|
677
|
-
go = this.nodeWalkBackward(node.left, actions);
|
|
678
|
-
}
|
|
679
|
-
if (go && actions.post && (!!actions.showStructure || node.color === RBColor.BLACK)) {
|
|
680
|
-
go = actions.post(node);
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
return go;
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
private nodeMap<TAccum>(
|
|
687
|
-
node: RBNode<TKey, TData> | undefined,
|
|
688
|
-
action: PropertyAction<TKey, TData>,
|
|
689
|
-
accum?: TAccum,
|
|
690
|
-
start?: TKey,
|
|
691
|
-
end?: TKey,
|
|
692
|
-
): boolean {
|
|
693
|
-
let _start = start;
|
|
694
|
-
let _end = end;
|
|
695
|
-
if (!node) {
|
|
696
|
-
return true;
|
|
697
|
-
}
|
|
698
|
-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- using ??= could change behavior if value is falsy
|
|
699
|
-
if (_start === undefined) {
|
|
700
|
-
_start = this.nodeMin(node).key;
|
|
701
|
-
}
|
|
702
|
-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- using ??= could change behavior if value is falsy
|
|
703
|
-
if (_end === undefined) {
|
|
704
|
-
_end = this.nodeMax(node).key;
|
|
705
|
-
}
|
|
706
|
-
const cmpStart = this.compareKeys(_start, node.key);
|
|
707
|
-
const cmpEnd = this.compareKeys(_end, node.key);
|
|
708
|
-
let go = true;
|
|
709
|
-
if (cmpStart < 0) {
|
|
710
|
-
go = this.nodeMap(node.left, action, accum, _start, _end);
|
|
711
|
-
}
|
|
712
|
-
if (go && cmpStart <= 0 && cmpEnd >= 0) {
|
|
713
|
-
// REVIEW: test for black node here
|
|
714
|
-
go = action(node, accum);
|
|
715
|
-
}
|
|
716
|
-
if (go && cmpEnd > 0) {
|
|
717
|
-
go = this.nodeMap(node.right, action, accum, _start, _end);
|
|
718
|
-
}
|
|
719
|
-
return go;
|
|
720
|
-
}
|
|
721
|
-
}
|