@fluidframework/merge-tree 0.51.0 → 0.52.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/dist/collections.d.ts +41 -92
- package/dist/collections.d.ts.map +1 -1
- package/dist/collections.js +4 -334
- package/dist/collections.js.map +1 -1
- package/dist/index.d.ts +10 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -12
- package/dist/index.js.map +1 -1
- package/dist/localReference.d.ts +2 -2
- package/dist/localReference.d.ts.map +1 -1
- package/dist/localReference.js +0 -1
- package/dist/localReference.js.map +1 -1
- package/dist/snapshotV1.d.ts +1 -1
- package/dist/snapshotV1.d.ts.map +1 -1
- package/dist/snapshotV1.js.map +1 -1
- package/dist/snapshotlegacy.d.ts +7 -28
- package/dist/snapshotlegacy.d.ts.map +1 -1
- package/dist/snapshotlegacy.js.map +1 -1
- package/lib/collections.d.ts +41 -92
- package/lib/collections.d.ts.map +1 -1
- package/lib/collections.js +5 -326
- package/lib/collections.js.map +1 -1
- package/lib/index.d.ts +10 -11
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +10 -11
- package/lib/index.js.map +1 -1
- package/lib/localReference.d.ts +2 -2
- package/lib/localReference.d.ts.map +1 -1
- package/lib/localReference.js +0 -1
- package/lib/localReference.js.map +1 -1
- package/lib/snapshotV1.d.ts +1 -1
- package/lib/snapshotV1.d.ts.map +1 -1
- package/lib/snapshotV1.js.map +1 -1
- package/lib/snapshotlegacy.d.ts +7 -28
- package/lib/snapshotlegacy.d.ts.map +1 -1
- package/lib/snapshotlegacy.js.map +1 -1
- package/package.json +9 -8
- package/src/collections.ts +109 -470
- package/src/index.ts +10 -11
- package/src/localReference.ts +3 -4
- package/src/snapshotV1.ts +1 -1
- package/src/snapshotlegacy.ts +9 -22
package/src/collections.ts
CHANGED
|
@@ -3,42 +3,39 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
/* eslint-disable @typescript-eslint/consistent-type-assertions, eqeqeq
|
|
6
|
+
/* eslint-disable @typescript-eslint/consistent-type-assertions, eqeqeq */
|
|
7
7
|
/* eslint-disable no-bitwise */
|
|
8
8
|
|
|
9
9
|
/* Remove once strictNullCheck is enabled */
|
|
10
10
|
|
|
11
|
-
import { Trace } from "@fluidframework/common-utils";
|
|
12
11
|
import {
|
|
13
12
|
ConflictAction,
|
|
14
13
|
IIntegerRange,
|
|
15
14
|
KeyComparer,
|
|
16
|
-
Property,
|
|
17
15
|
PropertyAction,
|
|
18
16
|
SortedDictionary,
|
|
19
17
|
} from "./base";
|
|
20
|
-
import { internedSpaces } from "./mergeTree";
|
|
21
18
|
|
|
22
19
|
export class Stack<T> {
|
|
23
|
-
items: T[] = [];
|
|
24
|
-
push(val: T) {
|
|
20
|
+
public items: T[] = [];
|
|
21
|
+
public push(val: T) {
|
|
25
22
|
this.items.push(val);
|
|
26
23
|
}
|
|
27
24
|
|
|
28
|
-
empty() {
|
|
25
|
+
public empty() {
|
|
29
26
|
return this.items.length === 0;
|
|
30
27
|
}
|
|
31
28
|
|
|
32
|
-
top(): T | undefined {
|
|
29
|
+
public top(): T | undefined {
|
|
33
30
|
return this.items[this.items.length - 1];
|
|
34
31
|
}
|
|
35
32
|
|
|
36
|
-
pop(): T | undefined {
|
|
33
|
+
public pop(): T | undefined {
|
|
37
34
|
return this.items.pop();
|
|
38
35
|
}
|
|
39
36
|
}
|
|
40
37
|
|
|
41
|
-
|
|
38
|
+
function ListRemoveEntry<U>(entry: List<U>): List<U> | undefined {
|
|
42
39
|
if (entry === undefined) {
|
|
43
40
|
return undefined;
|
|
44
41
|
}
|
|
@@ -52,7 +49,7 @@ export function ListRemoveEntry<U>(entry: List<U>): List<U> | undefined {
|
|
|
52
49
|
return (entry);
|
|
53
50
|
}
|
|
54
51
|
|
|
55
|
-
|
|
52
|
+
function ListMakeEntry<U>(data: U): List<U> {
|
|
56
53
|
return new List<U>(false, data);
|
|
57
54
|
}
|
|
58
55
|
|
|
@@ -61,22 +58,22 @@ export function ListMakeHead<U>(): List<U> {
|
|
|
61
58
|
}
|
|
62
59
|
|
|
63
60
|
export class List<T> {
|
|
64
|
-
next: List<T>;
|
|
65
|
-
prev: List<T>;
|
|
61
|
+
public next: List<T>;
|
|
62
|
+
public prev: List<T>;
|
|
66
63
|
|
|
67
|
-
constructor(public isHead: boolean,
|
|
64
|
+
constructor(public isHead: boolean, private data: T | undefined) {
|
|
68
65
|
this.prev = this;
|
|
69
66
|
this.next = this;
|
|
70
67
|
}
|
|
71
68
|
|
|
72
|
-
clear(): void {
|
|
69
|
+
public clear(): void {
|
|
73
70
|
if (this.isHead) {
|
|
74
71
|
this.prev = this;
|
|
75
72
|
this.next = this;
|
|
76
73
|
}
|
|
77
74
|
}
|
|
78
75
|
|
|
79
|
-
add(data: T): List<T> {
|
|
76
|
+
private add(data: T): List<T> {
|
|
80
77
|
const entry = ListMakeEntry(data);
|
|
81
78
|
this.prev.next = entry;
|
|
82
79
|
entry.next = this;
|
|
@@ -85,7 +82,7 @@ export class List<T> {
|
|
|
85
82
|
return (entry);
|
|
86
83
|
}
|
|
87
84
|
|
|
88
|
-
dequeue(): T | undefined {
|
|
85
|
+
public dequeue(): T | undefined {
|
|
89
86
|
if (!this.empty()) {
|
|
90
87
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
91
88
|
const removedEntry = ListRemoveEntry(this.next)!;
|
|
@@ -93,18 +90,18 @@ export class List<T> {
|
|
|
93
90
|
}
|
|
94
91
|
}
|
|
95
92
|
|
|
96
|
-
enqueue(data: T): List<T> {
|
|
93
|
+
public enqueue(data: T): List<T> {
|
|
97
94
|
return this.add(data);
|
|
98
95
|
}
|
|
99
96
|
|
|
100
|
-
walk(fn: (data: T, l: List<T>) => void): void {
|
|
97
|
+
public walk(fn: (data: T, l: List<T>) => void): void {
|
|
101
98
|
for (let entry = this.next; !(entry.isHead); entry = entry.next) {
|
|
102
99
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
103
100
|
fn(entry.data!, entry);
|
|
104
101
|
}
|
|
105
102
|
}
|
|
106
103
|
|
|
107
|
-
some(fn: (data: T, l: List<T>) => boolean, rev?: boolean): T[] {
|
|
104
|
+
public some(fn: (data: T, l: List<T>) => boolean, rev?: boolean): T[] {
|
|
108
105
|
const rtn: T[] = [];
|
|
109
106
|
const start = rev ? this.prev : this.next;
|
|
110
107
|
for (let entry = start; !(entry.isHead); entry = rev ? entry.prev : entry.next) {
|
|
@@ -122,7 +119,7 @@ export class List<T> {
|
|
|
122
119
|
return rtn;
|
|
123
120
|
}
|
|
124
121
|
|
|
125
|
-
count(): number {
|
|
122
|
+
public count(): number {
|
|
126
123
|
let entry: List<T>;
|
|
127
124
|
let i: number;
|
|
128
125
|
|
|
@@ -133,31 +130,23 @@ export class List<T> {
|
|
|
133
130
|
return (i);
|
|
134
131
|
}
|
|
135
132
|
|
|
136
|
-
first(): T | undefined {
|
|
133
|
+
public first(): T | undefined {
|
|
137
134
|
if (!this.empty()) {
|
|
138
135
|
return (this.next.data);
|
|
139
136
|
}
|
|
140
137
|
}
|
|
141
138
|
|
|
142
|
-
last(): T | undefined {
|
|
139
|
+
public last(): T | undefined {
|
|
143
140
|
if (!this.empty()) {
|
|
144
141
|
return (this.prev.data);
|
|
145
142
|
}
|
|
146
143
|
}
|
|
147
144
|
|
|
148
|
-
empty(): boolean {
|
|
145
|
+
public empty(): boolean {
|
|
149
146
|
return (this.next === this);
|
|
150
147
|
}
|
|
151
148
|
|
|
152
|
-
|
|
153
|
-
entry.isHead = false;
|
|
154
|
-
entry.next = this.next;
|
|
155
|
-
entry.prev = this;
|
|
156
|
-
this.next = entry;
|
|
157
|
-
entry.next.prev = entry;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
push(data: T): void {
|
|
149
|
+
public push(data: T): void {
|
|
161
150
|
const entry = ListMakeEntry(data);
|
|
162
151
|
entry.data = data;
|
|
163
152
|
entry.isHead = false;
|
|
@@ -166,46 +155,6 @@ export class List<T> {
|
|
|
166
155
|
this.next = entry;
|
|
167
156
|
entry.next.prev = entry;
|
|
168
157
|
}
|
|
169
|
-
|
|
170
|
-
popEntry(head: List<T>): List<T> | undefined {
|
|
171
|
-
if (this.next.isHead) {
|
|
172
|
-
return undefined;
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
return ListRemoveEntry(this.next);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
insertEntry(entry: List<T>): List<T> {
|
|
180
|
-
entry.isHead = false;
|
|
181
|
-
this.prev.next = entry;
|
|
182
|
-
entry.next = this;
|
|
183
|
-
entry.prev = this.prev;
|
|
184
|
-
this.prev = entry;
|
|
185
|
-
return entry;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
insertAfter(data: T): List<T> {
|
|
189
|
-
const entry: List<T> = ListMakeEntry(data);
|
|
190
|
-
entry.next = this.next;
|
|
191
|
-
entry.prev = this;
|
|
192
|
-
this.next = entry;
|
|
193
|
-
entry.next.prev = entry;
|
|
194
|
-
return (entry);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
insertBefore(data: T): List<T> {
|
|
198
|
-
const entry = ListMakeEntry(data);
|
|
199
|
-
return this.insertEntryBefore(entry);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
insertEntryBefore(entry: List<T>): List<T> {
|
|
203
|
-
this.prev.next = entry;
|
|
204
|
-
entry.next = this;
|
|
205
|
-
entry.prev = this.prev;
|
|
206
|
-
this.prev = entry;
|
|
207
|
-
return (entry);
|
|
208
|
-
}
|
|
209
158
|
}
|
|
210
159
|
|
|
211
160
|
export interface Comparer<T> {
|
|
@@ -213,14 +162,9 @@ export interface Comparer<T> {
|
|
|
213
162
|
min: T;
|
|
214
163
|
}
|
|
215
164
|
|
|
216
|
-
export const numberComparer: Comparer<number> = {
|
|
217
|
-
min: Number.MIN_VALUE,
|
|
218
|
-
compare: (a, b) => a - b,
|
|
219
|
-
};
|
|
220
|
-
|
|
221
165
|
export class Heap<T> {
|
|
222
|
-
L: T[];
|
|
223
|
-
count() {
|
|
166
|
+
private L: T[];
|
|
167
|
+
public count() {
|
|
224
168
|
return this.L.length - 1;
|
|
225
169
|
}
|
|
226
170
|
constructor(a: T[], public comp: Comparer<T>) {
|
|
@@ -229,11 +173,11 @@ export class Heap<T> {
|
|
|
229
173
|
this.add(a[i]);
|
|
230
174
|
}
|
|
231
175
|
}
|
|
232
|
-
peek() {
|
|
176
|
+
public peek() {
|
|
233
177
|
return this.L[1];
|
|
234
178
|
}
|
|
235
179
|
|
|
236
|
-
get() {
|
|
180
|
+
public get() {
|
|
237
181
|
const x = this.L[1];
|
|
238
182
|
this.L[1] = this.L[this.count()];
|
|
239
183
|
this.L.pop();
|
|
@@ -241,7 +185,7 @@ export class Heap<T> {
|
|
|
241
185
|
return x;
|
|
242
186
|
}
|
|
243
187
|
|
|
244
|
-
add(x: T) {
|
|
188
|
+
public add(x: T) {
|
|
245
189
|
this.L.push(x);
|
|
246
190
|
this.fixup(this.count());
|
|
247
191
|
}
|
|
@@ -274,98 +218,6 @@ export class Heap<T> {
|
|
|
274
218
|
}
|
|
275
219
|
}
|
|
276
220
|
|
|
277
|
-
// For testing
|
|
278
|
-
export function LinearDictionary<TKey, TData>(compareKeys: KeyComparer<TKey>): SortedDictionary<TKey, TData> {
|
|
279
|
-
const props: Property<TKey, TData>[] = [];
|
|
280
|
-
const compareProps = (a: Property<TKey, TData>, b: Property<TKey, TData>) => compareKeys(a.key, b.key);
|
|
281
|
-
function diag() {
|
|
282
|
-
console.log(`size is ${props.length}`);
|
|
283
|
-
}
|
|
284
|
-
function mapRange<TAccum>(action: PropertyAction<TKey, TData>, accum?: TAccum, start?: TKey, end?: TKey) {
|
|
285
|
-
let _start = start;
|
|
286
|
-
let _end = end;
|
|
287
|
-
|
|
288
|
-
if (props.length !== 0) { return; }
|
|
289
|
-
|
|
290
|
-
if (_start === undefined) {
|
|
291
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
292
|
-
_start = min()!.key;
|
|
293
|
-
}
|
|
294
|
-
if (_end === undefined) {
|
|
295
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
296
|
-
_end = max()!.key;
|
|
297
|
-
}
|
|
298
|
-
for (let i = 0, len = props.length; i < len; i++) {
|
|
299
|
-
if (compareKeys(_start, props[i].key) <= 0) {
|
|
300
|
-
const ecmp = compareKeys(_end, props[i].key);
|
|
301
|
-
if (ecmp < 0) {
|
|
302
|
-
break;
|
|
303
|
-
}
|
|
304
|
-
if (!action(props[i], accum)) {
|
|
305
|
-
break;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
function map<TAccum>(action: PropertyAction<TKey, TData>, accum?: TAccum) {
|
|
312
|
-
mapRange(action, accum);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
function min() {
|
|
316
|
-
if (props.length > 0) {
|
|
317
|
-
return props[0];
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
function max() {
|
|
321
|
-
if (props.length > 0) {
|
|
322
|
-
return props[props.length - 1];
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
function get(key: TKey) {
|
|
327
|
-
for (let i = 0, len = props.length; i < len; i++) {
|
|
328
|
-
if (props[i].key == key) {
|
|
329
|
-
return props[i];
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function put(key: TKey, data: TData) {
|
|
335
|
-
if (key !== undefined) {
|
|
336
|
-
if (data === undefined) {
|
|
337
|
-
remove(key);
|
|
338
|
-
}
|
|
339
|
-
else {
|
|
340
|
-
props.push({ key, data });
|
|
341
|
-
props.sort(compareProps); // Go to insertion sort if too slow
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
function remove(key: TKey) {
|
|
346
|
-
if (key !== undefined) {
|
|
347
|
-
for (let i = 0, len = props.length; i < len; i++) {
|
|
348
|
-
if (props[i].key == key) {
|
|
349
|
-
props[i] = props[len - 1];
|
|
350
|
-
props.length--;
|
|
351
|
-
props.sort(compareProps);
|
|
352
|
-
break;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
return {
|
|
358
|
-
min: min,
|
|
359
|
-
max: max,
|
|
360
|
-
map: map,
|
|
361
|
-
mapRange: mapRange,
|
|
362
|
-
remove: remove,
|
|
363
|
-
get: get,
|
|
364
|
-
put: put,
|
|
365
|
-
diag: diag,
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
|
|
369
221
|
export const enum RBColor {
|
|
370
222
|
RED,
|
|
371
223
|
BLACK,
|
|
@@ -397,34 +249,36 @@ export interface RBNodeActions<TKey, TData> {
|
|
|
397
249
|
}
|
|
398
250
|
|
|
399
251
|
export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData> {
|
|
400
|
-
root: RBNode<TKey, TData> | undefined;
|
|
401
|
-
constructor(public compareKeys: KeyComparer<TKey>, public aug?: IRBAugmentation<TKey, TData>) {
|
|
252
|
+
private root: RBNode<TKey, TData> | undefined;
|
|
402
253
|
|
|
403
|
-
|
|
254
|
+
constructor(
|
|
255
|
+
private readonly compareKeys: KeyComparer<TKey>,
|
|
256
|
+
private readonly aug?: IRBAugmentation<TKey, TData>,
|
|
257
|
+
) { }
|
|
404
258
|
|
|
405
|
-
makeNode(key: TKey, data: TData, color: RBColor, size: number) {
|
|
259
|
+
private makeNode(key: TKey, data: TData, color: RBColor, size: number) {
|
|
406
260
|
return <RBNode<TKey, TData>>{ key, data, color, size };
|
|
407
261
|
}
|
|
408
262
|
|
|
409
|
-
isRed(node: RBNode<TKey, TData> | undefined) {
|
|
263
|
+
private isRed(node: RBNode<TKey, TData> | undefined) {
|
|
410
264
|
return !!node && (node.color == RBColor.RED);
|
|
411
265
|
}
|
|
412
266
|
|
|
413
|
-
nodeSize(node: RBNode<TKey, TData> | undefined) {
|
|
267
|
+
private nodeSize(node: RBNode<TKey, TData> | undefined) {
|
|
414
268
|
return node ? node.size : 0;
|
|
415
269
|
}
|
|
416
|
-
size() {
|
|
270
|
+
public size() {
|
|
417
271
|
return this.nodeSize(this.root);
|
|
418
272
|
}
|
|
419
|
-
isEmpty() {
|
|
273
|
+
public isEmpty() {
|
|
420
274
|
return !this.root;
|
|
421
275
|
}
|
|
422
|
-
get(key: TKey) {
|
|
276
|
+
public get(key: TKey) {
|
|
423
277
|
if (key !== undefined) {
|
|
424
278
|
return this.nodeGet(this.root, key);
|
|
425
279
|
}
|
|
426
280
|
}
|
|
427
|
-
nodeGet(node: RBNode<TKey, TData> | undefined, key: TKey) {
|
|
281
|
+
private nodeGet(node: RBNode<TKey, TData> | undefined, key: TKey) {
|
|
428
282
|
let _node = node;
|
|
429
283
|
while (_node) {
|
|
430
284
|
const cmp = this.compareKeys(key, _node.key);
|
|
@@ -439,11 +293,11 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
439
293
|
}
|
|
440
294
|
}
|
|
441
295
|
}
|
|
442
|
-
contains(key: TKey) {
|
|
296
|
+
private contains(key: TKey) {
|
|
443
297
|
return this.get(key);
|
|
444
298
|
}
|
|
445
299
|
|
|
446
|
-
gather(key: TKey, matcher: IRBMatcher<TKey, TData>) {
|
|
300
|
+
public gather(key: TKey, matcher: IRBMatcher<TKey, TData>) {
|
|
447
301
|
const results = [] as RBNode<TKey, TData>[];
|
|
448
302
|
if (key !== undefined) {
|
|
449
303
|
this.nodeGather(this.root, results, key, matcher);
|
|
@@ -451,7 +305,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
451
305
|
return results;
|
|
452
306
|
}
|
|
453
307
|
|
|
454
|
-
nodeGather(
|
|
308
|
+
private nodeGather(
|
|
455
309
|
node: RBNode<TKey, TData> | undefined,
|
|
456
310
|
results: RBNode<TKey, TData>[],
|
|
457
311
|
key: TKey,
|
|
@@ -469,7 +323,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
469
323
|
}
|
|
470
324
|
}
|
|
471
325
|
|
|
472
|
-
walkExactMatchesForward(
|
|
326
|
+
public walkExactMatchesForward(
|
|
473
327
|
compareFn: (node: RBNode<TKey, TData>) => number,
|
|
474
328
|
actionFn: (node: RBNode<TKey, TData>) => void,
|
|
475
329
|
continueLeftFn: (number: number) => boolean,
|
|
@@ -477,7 +331,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
477
331
|
this.nodeWalkExactMatchesForward(this.root, compareFn, actionFn, continueLeftFn, continueRightFn);
|
|
478
332
|
}
|
|
479
333
|
|
|
480
|
-
nodeWalkExactMatchesForward(
|
|
334
|
+
private nodeWalkExactMatchesForward(
|
|
481
335
|
node: RBNode<TKey, TData> | undefined,
|
|
482
336
|
compareFn: (node: RBNode<TKey, TData>) => number,
|
|
483
337
|
actionFn: (node: RBNode<TKey, TData>) => void,
|
|
@@ -498,7 +352,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
498
352
|
}
|
|
499
353
|
}
|
|
500
354
|
|
|
501
|
-
walkExactMatchesBackward(
|
|
355
|
+
public walkExactMatchesBackward(
|
|
502
356
|
compareFn: (node: RBNode<TKey, TData>) => number,
|
|
503
357
|
actionFn: (node: RBNode<TKey, TData>) => void,
|
|
504
358
|
continueLeftFn: (number: number) => boolean,
|
|
@@ -506,7 +360,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
506
360
|
this.nodeWalkExactMatchesBackward(this.root, compareFn, actionFn, continueLeftFn, continueRightFn);
|
|
507
361
|
}
|
|
508
362
|
|
|
509
|
-
nodeWalkExactMatchesBackward(
|
|
363
|
+
private nodeWalkExactMatchesBackward(
|
|
510
364
|
node: RBNode<TKey, TData> | undefined,
|
|
511
365
|
compareFn: (node: RBNode<TKey, TData>) => number,
|
|
512
366
|
actionFn: (node: RBNode<TKey, TData>) => void,
|
|
@@ -527,7 +381,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
527
381
|
}
|
|
528
382
|
}
|
|
529
383
|
|
|
530
|
-
put(key: TKey, data: TData, conflict?: ConflictAction<TKey, TData>) {
|
|
384
|
+
public put(key: TKey, data: TData, conflict?: ConflictAction<TKey, TData>) {
|
|
531
385
|
if (key !== undefined) {
|
|
532
386
|
if (data === undefined) {
|
|
533
387
|
this.remove(key);
|
|
@@ -539,7 +393,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
539
393
|
}
|
|
540
394
|
}
|
|
541
395
|
|
|
542
|
-
nodePut(
|
|
396
|
+
private nodePut(
|
|
543
397
|
node: RBNode<TKey, TData> | undefined,
|
|
544
398
|
key: TKey, data: TData,
|
|
545
399
|
conflict?: ConflictAction<TKey, TData>,
|
|
@@ -590,7 +444,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
590
444
|
}
|
|
591
445
|
}
|
|
592
446
|
|
|
593
|
-
updateLocal(node: RBNode<TKey, TData>) {
|
|
447
|
+
private updateLocal(node: RBNode<TKey, TData>) {
|
|
594
448
|
if (this.aug) {
|
|
595
449
|
if (this.isRed(node.left)) {
|
|
596
450
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
@@ -604,20 +458,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
604
458
|
}
|
|
605
459
|
}
|
|
606
460
|
|
|
607
|
-
|
|
608
|
-
if (this.root) {
|
|
609
|
-
if ((!this.isRed(this.root.left)) && (!this.isRed(this.root.right))) {
|
|
610
|
-
this.root.color = RBColor.RED;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
this.root = this.nodeRemoveMin(this.root);
|
|
614
|
-
if (this.root) {
|
|
615
|
-
this.root.color = RBColor.BLACK;
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
// TODO: error on empty
|
|
619
|
-
}
|
|
620
|
-
nodeRemoveMin(node: RBNode<TKey, TData>) {
|
|
461
|
+
private nodeRemoveMin(node: RBNode<TKey, TData>) {
|
|
621
462
|
let _node = node;
|
|
622
463
|
if (_node.left) {
|
|
623
464
|
if ((!this.isRed(_node.left)) && (!this.isRed(_node.left.left))) {
|
|
@@ -630,42 +471,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
630
471
|
}
|
|
631
472
|
}
|
|
632
473
|
|
|
633
|
-
|
|
634
|
-
if (this.root) {
|
|
635
|
-
if ((!this.isRed(this.root.left)) && (!this.isRed(this.root.right))) {
|
|
636
|
-
this.root.color = RBColor.RED;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
this.root = this.nodeRemoveMax(this.root);
|
|
640
|
-
if (this.root) {
|
|
641
|
-
this.root.color = RBColor.BLACK;
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
// TODO: error on empty
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
nodeRemoveMax(node: RBNode<TKey, TData>) {
|
|
648
|
-
let _node = node;
|
|
649
|
-
|
|
650
|
-
if (this.isRed(_node.left)) {
|
|
651
|
-
_node = this.rotateRight(_node);
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
if (!_node.right) {
|
|
655
|
-
return undefined;
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
if ((!this.isRed(_node.right)) && (!this.isRed(_node.right.left))) {
|
|
659
|
-
_node = this.moveRedRight(_node);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
663
|
-
_node.right = this.nodeRemoveMax(_node.right!);
|
|
664
|
-
|
|
665
|
-
return this.balance(_node);
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
remove(key: TKey) {
|
|
474
|
+
public remove(key: TKey) {
|
|
669
475
|
if (key !== undefined) {
|
|
670
476
|
if (!this.contains(key)) {
|
|
671
477
|
return;
|
|
@@ -676,7 +482,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
676
482
|
// TODO: error on undefined key
|
|
677
483
|
}
|
|
678
484
|
|
|
679
|
-
removeExisting(key: TKey) {
|
|
485
|
+
public removeExisting(key: TKey) {
|
|
680
486
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
681
487
|
if ((!this.isRed(this.root!.left)) && (!this.isRed(this.root!.right))) {
|
|
682
488
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
@@ -687,7 +493,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
687
493
|
this.root = this.nodeRemove(this.root!, key);
|
|
688
494
|
}
|
|
689
495
|
|
|
690
|
-
nodeRemove(node: RBNode<TKey, TData>, key: TKey) {
|
|
496
|
+
private nodeRemove(node: RBNode<TKey, TData>, key: TKey) {
|
|
691
497
|
let _node = node;
|
|
692
498
|
if (this.compareKeys(key, _node.key) < 0) {
|
|
693
499
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
@@ -723,10 +529,10 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
723
529
|
}
|
|
724
530
|
return this.balance(_node);
|
|
725
531
|
}
|
|
726
|
-
height() {
|
|
532
|
+
private height() {
|
|
727
533
|
return this.nodeHeight(this.root);
|
|
728
534
|
}
|
|
729
|
-
nodeHeight(node: RBNode<TKey, TData> | undefined): number {
|
|
535
|
+
private nodeHeight(node: RBNode<TKey, TData> | undefined): number {
|
|
730
536
|
if (node === undefined) {
|
|
731
537
|
return -1;
|
|
732
538
|
}
|
|
@@ -735,13 +541,13 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
735
541
|
}
|
|
736
542
|
}
|
|
737
543
|
|
|
738
|
-
floor(key: TKey) {
|
|
544
|
+
public floor(key: TKey) {
|
|
739
545
|
if (!this.isEmpty()) {
|
|
740
546
|
return this.nodeFloor(this.root, key);
|
|
741
547
|
}
|
|
742
548
|
}
|
|
743
549
|
|
|
744
|
-
nodeFloor(node: RBNode<TKey, TData> | undefined, key: TKey): RBNode<TKey, TData> | undefined {
|
|
550
|
+
private nodeFloor(node: RBNode<TKey, TData> | undefined, key: TKey): RBNode<TKey, TData> | undefined {
|
|
745
551
|
if (node) {
|
|
746
552
|
const cmp = this.compareKeys(key, node.key);
|
|
747
553
|
if (cmp == 0) {
|
|
@@ -762,13 +568,13 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
762
568
|
}
|
|
763
569
|
}
|
|
764
570
|
|
|
765
|
-
ceil(key: TKey) {
|
|
571
|
+
public ceil(key: TKey) {
|
|
766
572
|
if (!this.isEmpty()) {
|
|
767
573
|
return this.nodeCeil(this.root, key);
|
|
768
574
|
}
|
|
769
575
|
}
|
|
770
576
|
|
|
771
|
-
nodeCeil(node: RBNode<TKey, TData> | undefined, key: TKey): RBNode<TKey, TData> | undefined {
|
|
577
|
+
private nodeCeil(node: RBNode<TKey, TData> | undefined, key: TKey): RBNode<TKey, TData> | undefined {
|
|
772
578
|
if (node) {
|
|
773
579
|
const cmp = this.compareKeys(key, node.key);
|
|
774
580
|
if (cmp == 0) {
|
|
@@ -789,13 +595,13 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
789
595
|
}
|
|
790
596
|
}
|
|
791
597
|
|
|
792
|
-
min() {
|
|
598
|
+
public min() {
|
|
793
599
|
if (this.root) {
|
|
794
600
|
return this.nodeMin(this.root);
|
|
795
601
|
}
|
|
796
602
|
}
|
|
797
603
|
|
|
798
|
-
nodeMin(node: RBNode<TKey, TData>): RBNode<TKey, TData> {
|
|
604
|
+
private nodeMin(node: RBNode<TKey, TData>): RBNode<TKey, TData> {
|
|
799
605
|
if (!node.left) {
|
|
800
606
|
return node;
|
|
801
607
|
}
|
|
@@ -804,13 +610,13 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
804
610
|
}
|
|
805
611
|
}
|
|
806
612
|
|
|
807
|
-
max() {
|
|
613
|
+
public max() {
|
|
808
614
|
if (this.root) {
|
|
809
615
|
return this.nodeMax(this.root);
|
|
810
616
|
}
|
|
811
617
|
}
|
|
812
618
|
|
|
813
|
-
nodeMax(node: RBNode<TKey, TData>): RBNode<TKey, TData> {
|
|
619
|
+
private nodeMax(node: RBNode<TKey, TData>): RBNode<TKey, TData> {
|
|
814
620
|
if (!node.right) {
|
|
815
621
|
return node;
|
|
816
622
|
}
|
|
@@ -819,7 +625,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
819
625
|
}
|
|
820
626
|
}
|
|
821
627
|
|
|
822
|
-
rotateRight(node: RBNode<TKey, TData>) {
|
|
628
|
+
private rotateRight(node: RBNode<TKey, TData>) {
|
|
823
629
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
824
630
|
const leftChild = node.left!;
|
|
825
631
|
node.left = leftChild.right;
|
|
@@ -835,7 +641,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
835
641
|
return leftChild;
|
|
836
642
|
}
|
|
837
643
|
|
|
838
|
-
rotateLeft(node: RBNode<TKey, TData>) {
|
|
644
|
+
private rotateLeft(node: RBNode<TKey, TData>) {
|
|
839
645
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
840
646
|
const rightChild = node.right!;
|
|
841
647
|
node.right = rightChild.left;
|
|
@@ -851,11 +657,11 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
851
657
|
return rightChild;
|
|
852
658
|
}
|
|
853
659
|
|
|
854
|
-
oppositeColor(c: RBColor) {
|
|
660
|
+
private oppositeColor(c: RBColor) {
|
|
855
661
|
return (c == RBColor.BLACK) ? RBColor.RED : RBColor.BLACK;
|
|
856
662
|
}
|
|
857
663
|
|
|
858
|
-
flipColors(node: RBNode<TKey, TData>) {
|
|
664
|
+
private flipColors(node: RBNode<TKey, TData>) {
|
|
859
665
|
node.color = this.oppositeColor(node.color);
|
|
860
666
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
861
667
|
node.left!.color = this.oppositeColor(node.left!.color);
|
|
@@ -863,7 +669,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
863
669
|
node.right!.color = this.oppositeColor(node.right!.color);
|
|
864
670
|
}
|
|
865
671
|
|
|
866
|
-
moveRedLeft(node: RBNode<TKey, TData>) {
|
|
672
|
+
private moveRedLeft(node: RBNode<TKey, TData>) {
|
|
867
673
|
let _node = node;
|
|
868
674
|
this.flipColors(_node);
|
|
869
675
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
@@ -876,7 +682,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
876
682
|
return _node;
|
|
877
683
|
}
|
|
878
684
|
|
|
879
|
-
moveRedRight(node: RBNode<TKey, TData>) {
|
|
685
|
+
private moveRedRight(node: RBNode<TKey, TData>) {
|
|
880
686
|
let _node = node;
|
|
881
687
|
this.flipColors(_node);
|
|
882
688
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
@@ -887,7 +693,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
887
693
|
return _node;
|
|
888
694
|
}
|
|
889
695
|
|
|
890
|
-
balance(input: RBNode<TKey, TData>) {
|
|
696
|
+
private balance(input: RBNode<TKey, TData>) {
|
|
891
697
|
let node: RBNode<TKey, TData> | undefined = input;
|
|
892
698
|
if (this.isRed(node.right)) {
|
|
893
699
|
node = this.rotateLeft(node);
|
|
@@ -906,16 +712,16 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
906
712
|
return node;
|
|
907
713
|
}
|
|
908
714
|
|
|
909
|
-
mapRange<TAccum>(action: PropertyAction<TKey, TData>, accum?: TAccum, start?: TKey, end?: TKey) {
|
|
715
|
+
public mapRange<TAccum>(action: PropertyAction<TKey, TData>, accum?: TAccum, start?: TKey, end?: TKey) {
|
|
910
716
|
this.nodeMap(this.root, action, start, end);
|
|
911
717
|
}
|
|
912
718
|
|
|
913
|
-
map<TAccum>(action: PropertyAction<TKey, TData>, accum?: TAccum) {
|
|
719
|
+
public map<TAccum>(action: PropertyAction<TKey, TData>, accum?: TAccum) {
|
|
914
720
|
// TODO: optimize to avoid comparisons
|
|
915
721
|
this.nodeMap(this.root, action, accum);
|
|
916
722
|
}
|
|
917
723
|
|
|
918
|
-
keys() {
|
|
724
|
+
public keys() {
|
|
919
725
|
const keyList = <TKey[]>[];
|
|
920
726
|
const actions = <RBNodeActions<TKey, TData>>{
|
|
921
727
|
showStructure: true,
|
|
@@ -933,15 +739,15 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
933
739
|
* false, traversal is halted.
|
|
934
740
|
* @param action - action to apply to each node
|
|
935
741
|
*/
|
|
936
|
-
walk(actions: RBNodeActions<TKey, TData>) {
|
|
742
|
+
public walk(actions: RBNodeActions<TKey, TData>) {
|
|
937
743
|
this.nodeWalk(this.root, actions);
|
|
938
744
|
}
|
|
939
745
|
|
|
940
|
-
walkBackward(actions: RBNodeActions<TKey, TData>) {
|
|
746
|
+
public walkBackward(actions: RBNodeActions<TKey, TData>) {
|
|
941
747
|
this.nodeWalkBackward(this.root, actions);
|
|
942
748
|
}
|
|
943
749
|
|
|
944
|
-
nodeWalk(node: RBNode<TKey, TData> | undefined, actions: RBNodeActions<TKey, TData>): boolean {
|
|
750
|
+
private nodeWalk(node: RBNode<TKey, TData> | undefined, actions: RBNodeActions<TKey, TData>): boolean {
|
|
945
751
|
let go = true;
|
|
946
752
|
if (node) {
|
|
947
753
|
if (actions.pre) {
|
|
@@ -969,7 +775,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
969
775
|
return go;
|
|
970
776
|
}
|
|
971
777
|
|
|
972
|
-
nodeWalkBackward(node: RBNode<TKey, TData> | undefined, actions: RBNodeActions<TKey, TData>): boolean {
|
|
778
|
+
private nodeWalkBackward(node: RBNode<TKey, TData> | undefined, actions: RBNodeActions<TKey, TData>): boolean {
|
|
973
779
|
let go = true;
|
|
974
780
|
if (node) {
|
|
975
781
|
if (actions.pre) {
|
|
@@ -997,7 +803,7 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
997
803
|
return go;
|
|
998
804
|
}
|
|
999
805
|
|
|
1000
|
-
nodeMap<TAccum>(
|
|
806
|
+
private nodeMap<TAccum>(
|
|
1001
807
|
node: RBNode<TKey, TData> | undefined,
|
|
1002
808
|
action: PropertyAction<TKey, TData>,
|
|
1003
809
|
accum?: TAccum,
|
|
@@ -1030,130 +836,17 @@ export class RedBlackTree<TKey, TData> implements SortedDictionary<TKey, TData>
|
|
|
1030
836
|
}
|
|
1031
837
|
return go;
|
|
1032
838
|
}
|
|
1033
|
-
diag() {
|
|
839
|
+
public diag() {
|
|
1034
840
|
console.log(`Height is ${this.height()}`);
|
|
1035
841
|
}
|
|
1036
842
|
}
|
|
1037
843
|
|
|
1038
|
-
export interface AugIntegerRangeNode {
|
|
1039
|
-
minmax: IIntegerRange;
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
844
|
export interface AugmentedIntervalNode {
|
|
1043
845
|
minmax: IInterval;
|
|
1044
846
|
}
|
|
1045
|
-
/**
|
|
1046
|
-
* Union of two ranges; assumes for both ranges start \<= end.
|
|
1047
|
-
* @param a - A range
|
|
1048
|
-
* @param b - A range
|
|
1049
|
-
*/
|
|
1050
|
-
export function integerRangeUnion(a: IIntegerRange, b: IIntegerRange) {
|
|
1051
|
-
return <IIntegerRange>{
|
|
1052
|
-
start: Math.min(a.start, b.start),
|
|
1053
|
-
end: Math.max(a.end, b.end),
|
|
1054
|
-
};
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
export function integerRangeOverlaps(a: IIntegerRange, b: IIntegerRange) {
|
|
1058
|
-
return (a.start < b.end) && (a.end > b.start);
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
export function integerRangeComparer(a: IIntegerRange, b: IIntegerRange) {
|
|
1062
|
-
if (a.start === b.start) {
|
|
1063
|
-
return a.end - b.end;
|
|
1064
|
-
} else {
|
|
1065
|
-
return a.start - b.start;
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
export const integerRangeCopy = (r: IIntegerRange) => <IIntegerRange>{ start: r.start, end: r.end };
|
|
1070
847
|
|
|
1071
848
|
export const integerRangeToString = (range: IIntegerRange) => `[${range.start},${range.end})`;
|
|
1072
849
|
|
|
1073
|
-
export type IntegerRangeNode = RBNode<IIntegerRange, AugIntegerRangeNode>;
|
|
1074
|
-
|
|
1075
|
-
// TODO: handle duplicate keys
|
|
1076
|
-
|
|
1077
|
-
export class IntegerRangeTree implements IRBAugmentation<IIntegerRange, AugIntegerRangeNode>,
|
|
1078
|
-
IRBMatcher<IIntegerRange, AugIntegerRangeNode> {
|
|
1079
|
-
ranges = new RedBlackTree<IIntegerRange, AugIntegerRangeNode>(integerRangeComparer, this);
|
|
1080
|
-
diag = false;
|
|
1081
|
-
|
|
1082
|
-
remove(r: IIntegerRange) {
|
|
1083
|
-
this.ranges.remove(r);
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
put(r: IIntegerRange) {
|
|
1087
|
-
this.ranges.put(r, { minmax: integerRangeCopy(r) });
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
toString() {
|
|
1091
|
-
return this.nodeToString(this.ranges.root);
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
nodeToString(node: IntegerRangeNode | undefined) {
|
|
1095
|
-
let buf = "";
|
|
1096
|
-
let indentAmt = 0;
|
|
1097
|
-
const actions = {
|
|
1098
|
-
pre: (n: IntegerRangeNode) => {
|
|
1099
|
-
let red = "";
|
|
1100
|
-
if (n.color === RBColor.RED) {
|
|
1101
|
-
red = "R ";
|
|
1102
|
-
}
|
|
1103
|
-
buf += internedSpaces(indentAmt);
|
|
1104
|
-
buf += `${red}key: ${integerRangeToString(n.key)} minmax: ${integerRangeToString(n.data.minmax)}\n`;
|
|
1105
|
-
indentAmt += 2;
|
|
1106
|
-
return true;
|
|
1107
|
-
},
|
|
1108
|
-
post: (n: IntegerRangeNode) => {
|
|
1109
|
-
indentAmt -= 2;
|
|
1110
|
-
return true;
|
|
1111
|
-
},
|
|
1112
|
-
showStructure: true,
|
|
1113
|
-
};
|
|
1114
|
-
this.ranges.nodeWalk(node, actions);
|
|
1115
|
-
return buf;
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
matchPos(pos: number) {
|
|
1119
|
-
return this.match({ start: pos, end: pos + 1 });
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
match(r: IIntegerRange) {
|
|
1123
|
-
return this.ranges.gather(r, this);
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
matchNode(node: IntegerRangeNode | undefined, key: IIntegerRange) {
|
|
1127
|
-
return !!node && integerRangeOverlaps(node.key, key);
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
continueSubtree(node: IntegerRangeNode | undefined, key: IIntegerRange) {
|
|
1131
|
-
const cont = !!node && integerRangeOverlaps(node.data.minmax, key);
|
|
1132
|
-
if (this.diag && (!cont)) {
|
|
1133
|
-
if (node) {
|
|
1134
|
-
console.log(`skipping subtree of size ${node.size} key ${integerRangeToString(key)}`);
|
|
1135
|
-
console.log(this.nodeToString(node));
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
return cont;
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
update(node: IntegerRangeNode) {
|
|
1142
|
-
if (node.left && node.right) {
|
|
1143
|
-
node.data.minmax = integerRangeUnion(node.key,
|
|
1144
|
-
integerRangeUnion(node.left.data.minmax, node.right.data.minmax));
|
|
1145
|
-
} else {
|
|
1146
|
-
if (node.left) {
|
|
1147
|
-
node.data.minmax = integerRangeUnion(node.key, node.left.data.minmax);
|
|
1148
|
-
} else if (node.right) {
|
|
1149
|
-
node.data.minmax = integerRangeUnion(node.key, node.right.data.minmax);
|
|
1150
|
-
} else {
|
|
1151
|
-
node.data.minmax = integerRangeCopy(node.key);
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
850
|
export interface IInterval {
|
|
1158
851
|
clone(): IInterval;
|
|
1159
852
|
compare(b: IInterval): number;
|
|
@@ -1164,31 +857,23 @@ export interface IInterval {
|
|
|
1164
857
|
union(b: IInterval): IInterval;
|
|
1165
858
|
}
|
|
1166
859
|
|
|
1167
|
-
|
|
860
|
+
const intervalComparer = (a: IInterval, b: IInterval) => a.compare(b);
|
|
1168
861
|
export type IntervalNode<T extends IInterval> = RBNode<T, AugmentedIntervalNode>;
|
|
1169
862
|
export type IntervalConflictResolver<TInterval> = (a: TInterval, b: TInterval) => TInterval;
|
|
1170
863
|
|
|
1171
864
|
export class IntervalTree<T extends IInterval> implements IRBAugmentation<T, AugmentedIntervalNode>,
|
|
1172
865
|
IRBMatcher<T, AugmentedIntervalNode> {
|
|
1173
|
-
intervals = new RedBlackTree<T, AugmentedIntervalNode>(intervalComparer, this);
|
|
1174
|
-
diag = false;
|
|
1175
|
-
timePut = false;
|
|
1176
|
-
putTime = 0;
|
|
1177
|
-
putCount = 0;
|
|
1178
|
-
|
|
1179
|
-
printTiming() {
|
|
1180
|
-
console.log(`put total = ${this.putTime} avg=${(this.putTime / this.putCount).toFixed(2)}`);
|
|
1181
|
-
}
|
|
866
|
+
public intervals = new RedBlackTree<T, AugmentedIntervalNode>(intervalComparer, this);
|
|
1182
867
|
|
|
1183
|
-
remove(x: T) {
|
|
868
|
+
public remove(x: T) {
|
|
1184
869
|
this.intervals.remove(x);
|
|
1185
870
|
}
|
|
1186
871
|
|
|
1187
|
-
removeExisting(x: T) {
|
|
872
|
+
public removeExisting(x: T) {
|
|
1188
873
|
this.intervals.removeExisting(x);
|
|
1189
874
|
}
|
|
1190
875
|
|
|
1191
|
-
put(x: T, conflict?: IntervalConflictResolver<T>) {
|
|
876
|
+
public put(x: T, conflict?: IntervalConflictResolver<T>) {
|
|
1192
877
|
let rbConflict: ConflictAction<T, AugmentedIntervalNode> | undefined;
|
|
1193
878
|
if (conflict) {
|
|
1194
879
|
rbConflict = (key: T, currentKey: T) => {
|
|
@@ -1198,17 +883,10 @@ export class IntervalTree<T extends IInterval> implements IRBAugmentation<T, Aug
|
|
|
1198
883
|
};
|
|
1199
884
|
};
|
|
1200
885
|
}
|
|
1201
|
-
|
|
1202
|
-
const trace = Trace.start();
|
|
1203
|
-
this.intervals.put(x, { minmax: x.clone() }, rbConflict);
|
|
1204
|
-
this.putTime += trace.trace().duration * 1000;
|
|
1205
|
-
this.putCount++;
|
|
1206
|
-
} else {
|
|
1207
|
-
this.intervals.put(x, { minmax: x.clone() }, rbConflict);
|
|
1208
|
-
}
|
|
886
|
+
this.intervals.put(x, { minmax: x.clone() }, rbConflict);
|
|
1209
887
|
}
|
|
1210
888
|
|
|
1211
|
-
map(fn: (x: T) => void) {
|
|
889
|
+
public map(fn: (x: T) => void) {
|
|
1212
890
|
const actions = <RBNodeActions<T, AugmentedIntervalNode>>{
|
|
1213
891
|
infix: (node) => {
|
|
1214
892
|
fn(node.key);
|
|
@@ -1219,7 +897,7 @@ export class IntervalTree<T extends IInterval> implements IRBAugmentation<T, Aug
|
|
|
1219
897
|
this.intervals.walk(actions);
|
|
1220
898
|
}
|
|
1221
899
|
|
|
1222
|
-
mapUntil(fn: (X: T) => boolean) {
|
|
900
|
+
public mapUntil(fn: (X: T) => boolean) {
|
|
1223
901
|
const actions = <RBNodeActions<T, AugmentedIntervalNode>>{
|
|
1224
902
|
infix: (node) => {
|
|
1225
903
|
return fn(node.key);
|
|
@@ -1229,7 +907,7 @@ export class IntervalTree<T extends IInterval> implements IRBAugmentation<T, Aug
|
|
|
1229
907
|
this.intervals.walk(actions);
|
|
1230
908
|
}
|
|
1231
909
|
|
|
1232
|
-
mapBackward(fn: (x: T) => void) {
|
|
910
|
+
public mapBackward(fn: (x: T) => void) {
|
|
1233
911
|
const actions = <RBNodeActions<T, AugmentedIntervalNode>>{
|
|
1234
912
|
infix: (node) => {
|
|
1235
913
|
fn(node.key);
|
|
@@ -1240,27 +918,20 @@ export class IntervalTree<T extends IInterval> implements IRBAugmentation<T, Aug
|
|
|
1240
918
|
this.intervals.walkBackward(actions);
|
|
1241
919
|
}
|
|
1242
920
|
|
|
1243
|
-
|
|
1244
|
-
match(x: T) {
|
|
921
|
+
// TODO: toString()
|
|
922
|
+
public match(x: T) {
|
|
1245
923
|
return this.intervals.gather(x, this);
|
|
1246
924
|
}
|
|
1247
925
|
|
|
1248
|
-
matchNode(node: IntervalNode<T> | undefined, key: T) {
|
|
926
|
+
public matchNode(node: IntervalNode<T> | undefined, key: T) {
|
|
1249
927
|
return !!node && node.key.overlaps(key);
|
|
1250
928
|
}
|
|
1251
929
|
|
|
1252
|
-
continueSubtree(node: IntervalNode<T> | undefined, key: T) {
|
|
1253
|
-
|
|
1254
|
-
if (this.diag && (!cont)) {
|
|
1255
|
-
if (node) {
|
|
1256
|
-
console.log(`skipping subtree of size ${node.size} key ${key.toString()}`);
|
|
1257
|
-
// console.log(this.nodeToString(node));
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
return cont;
|
|
930
|
+
public continueSubtree(node: IntervalNode<T> | undefined, key: T) {
|
|
931
|
+
return !!node && node.data.minmax.overlaps(key);
|
|
1261
932
|
}
|
|
1262
933
|
|
|
1263
|
-
update(node: IntervalNode<T>) {
|
|
934
|
+
public update(node: IntervalNode<T>) {
|
|
1264
935
|
if (node.left && node.right) {
|
|
1265
936
|
node.data.minmax = node.key.union(
|
|
1266
937
|
node.left.data.minmax.union(node.right.data.minmax));
|
|
@@ -1289,7 +960,7 @@ export interface TSTNode<T> {
|
|
|
1289
960
|
val?: T;
|
|
1290
961
|
}
|
|
1291
962
|
|
|
1292
|
-
|
|
963
|
+
interface TSTPrefix {
|
|
1293
964
|
text: string;
|
|
1294
965
|
}
|
|
1295
966
|
|
|
@@ -1303,19 +974,15 @@ export class TST<T> {
|
|
|
1303
974
|
private n = 0;
|
|
1304
975
|
private root: TSTNode<T> | undefined;
|
|
1305
976
|
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
}
|
|
1309
|
-
|
|
1310
|
-
size() {
|
|
977
|
+
public size() {
|
|
1311
978
|
return this.n;
|
|
1312
979
|
}
|
|
1313
980
|
|
|
1314
|
-
contains(key: string) {
|
|
981
|
+
private contains(key: string) {
|
|
1315
982
|
return this.get(key);
|
|
1316
983
|
}
|
|
1317
984
|
|
|
1318
|
-
get(key: string) {
|
|
985
|
+
public get(key: string) {
|
|
1319
986
|
const x = this.nodeGet(this.root, key, 0);
|
|
1320
987
|
if (x === undefined) {
|
|
1321
988
|
return undefined;
|
|
@@ -1323,7 +990,7 @@ export class TST<T> {
|
|
|
1323
990
|
return x.val;
|
|
1324
991
|
}
|
|
1325
992
|
|
|
1326
|
-
nodeGet(x: TSTNode<T> | undefined, key: string, d: number): TSTNode<T> | undefined {
|
|
993
|
+
private nodeGet(x: TSTNode<T> | undefined, key: string, d: number): TSTNode<T> | undefined {
|
|
1327
994
|
if (x === undefined) {
|
|
1328
995
|
return undefined;
|
|
1329
996
|
}
|
|
@@ -1340,7 +1007,7 @@ export class TST<T> {
|
|
|
1340
1007
|
else { return x; }
|
|
1341
1008
|
}
|
|
1342
1009
|
|
|
1343
|
-
put(key: string, val: T) {
|
|
1010
|
+
public put(key: string, val: T) {
|
|
1344
1011
|
if (!this.contains(key)) {
|
|
1345
1012
|
this.n++;
|
|
1346
1013
|
}
|
|
@@ -1348,7 +1015,7 @@ export class TST<T> {
|
|
|
1348
1015
|
// console.log(`put ${key}`);
|
|
1349
1016
|
}
|
|
1350
1017
|
|
|
1351
|
-
nodePut(x: TSTNode<T> | undefined, key: string, val: T, d: number) {
|
|
1018
|
+
private nodePut(x: TSTNode<T> | undefined, key: string, val: T, d: number) {
|
|
1352
1019
|
let _x = x;
|
|
1353
1020
|
const c = key.charAt(d);
|
|
1354
1021
|
if (_x === undefined) {
|
|
@@ -1369,14 +1036,14 @@ export class TST<T> {
|
|
|
1369
1036
|
return _x;
|
|
1370
1037
|
}
|
|
1371
1038
|
|
|
1372
|
-
neighbors(text: string, distance = 2) {
|
|
1039
|
+
public neighbors(text: string, distance = 2) {
|
|
1373
1040
|
let q = <ProxString<T>[]>[];
|
|
1374
1041
|
this.nodeProximity(this.root, { text: "" }, 0, text, distance, q);
|
|
1375
1042
|
q = q.filter((value) => (value.text.length > 0));
|
|
1376
1043
|
return q;
|
|
1377
1044
|
}
|
|
1378
1045
|
|
|
1379
|
-
keysWithPrefix(text: string) {
|
|
1046
|
+
public keysWithPrefix(text: string) {
|
|
1380
1047
|
const q = <string[]>[];
|
|
1381
1048
|
const x = this.nodeGet(this.root, text, 0);
|
|
1382
1049
|
if (x === undefined) {
|
|
@@ -1389,7 +1056,7 @@ export class TST<T> {
|
|
|
1389
1056
|
return q;
|
|
1390
1057
|
}
|
|
1391
1058
|
|
|
1392
|
-
collect(x: TSTNode<T> | undefined, prefix: TSTPrefix, q: string[]) {
|
|
1059
|
+
private collect(x: TSTNode<T> | undefined, prefix: TSTPrefix, q: string[]) {
|
|
1393
1060
|
if (x === undefined) {
|
|
1394
1061
|
return;
|
|
1395
1062
|
}
|
|
@@ -1401,7 +1068,7 @@ export class TST<T> {
|
|
|
1401
1068
|
this.collect(x.right, prefix, q);
|
|
1402
1069
|
}
|
|
1403
1070
|
|
|
1404
|
-
mapNode(x: TSTNode<T> | undefined, prefix: TSTPrefix, fn: (key: string, val: T) => void) {
|
|
1071
|
+
private mapNode(x: TSTNode<T> | undefined, prefix: TSTPrefix, fn: (key: string, val: T) => void) {
|
|
1405
1072
|
if (x === undefined) {
|
|
1406
1073
|
return;
|
|
1407
1074
|
}
|
|
@@ -1414,11 +1081,11 @@ export class TST<T> {
|
|
|
1414
1081
|
this.mapNode(x.right, prefix, fn);
|
|
1415
1082
|
}
|
|
1416
1083
|
|
|
1417
|
-
map(fn: (key: string, val: T) => void) {
|
|
1084
|
+
public map(fn: (key: string, val: T) => void) {
|
|
1418
1085
|
this.mapNode(this.root, { text: "" }, fn);
|
|
1419
1086
|
}
|
|
1420
1087
|
|
|
1421
|
-
pairsWithPrefix(text: string) {
|
|
1088
|
+
public pairsWithPrefix(text: string) {
|
|
1422
1089
|
const q = <TSTResult<T>[]>[];
|
|
1423
1090
|
const x = this.nodeGet(this.root, text, 0);
|
|
1424
1091
|
if (x === undefined) {
|
|
@@ -1431,7 +1098,7 @@ export class TST<T> {
|
|
|
1431
1098
|
return q;
|
|
1432
1099
|
}
|
|
1433
1100
|
|
|
1434
|
-
collectPairs(x: TSTNode<T> | undefined, prefix: TSTPrefix, q: TSTResult<T>[]) {
|
|
1101
|
+
private collectPairs(x: TSTNode<T> | undefined, prefix: TSTPrefix, q: TSTResult<T>[]) {
|
|
1435
1102
|
if (x === undefined) {
|
|
1436
1103
|
return;
|
|
1437
1104
|
}
|
|
@@ -1443,29 +1110,7 @@ export class TST<T> {
|
|
|
1443
1110
|
this.collectPairs(x.right, prefix, q);
|
|
1444
1111
|
}
|
|
1445
1112
|
|
|
1446
|
-
|
|
1447
|
-
if (x === undefined) {
|
|
1448
|
-
return;
|
|
1449
|
-
}
|
|
1450
|
-
const c = pattern.charAt(d);
|
|
1451
|
-
if ((c === ".") || (c < x.c)) {
|
|
1452
|
-
this.patternCollect(x.left, prefix, d, pattern, q);
|
|
1453
|
-
}
|
|
1454
|
-
else if ((c === ".") || (c === x.c)) {
|
|
1455
|
-
if ((d === (pattern.length - 1)) && (x.val !== undefined)) {
|
|
1456
|
-
q.push(prefix.text + x.c);
|
|
1457
|
-
}
|
|
1458
|
-
else if (d < (pattern.length - 1)) {
|
|
1459
|
-
this.patternCollect(x.mid, { text: prefix.text + x.c },
|
|
1460
|
-
d + 1, pattern, q);
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
if ((c === ".") || (c > x.c)) {
|
|
1464
|
-
this.patternCollect(x.right, prefix, d, pattern, q);
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
nodeProximity(
|
|
1113
|
+
private nodeProximity(
|
|
1469
1114
|
x: TSTNode<T> | undefined,
|
|
1470
1115
|
prefix: TSTPrefix,
|
|
1471
1116
|
d: number,
|
|
@@ -1500,10 +1145,4 @@ export class TST<T> {
|
|
|
1500
1145
|
this.nodeProximity(x.right, prefix, d, pattern, distance, q);
|
|
1501
1146
|
}
|
|
1502
1147
|
}
|
|
1503
|
-
|
|
1504
|
-
match(pattern: string) {
|
|
1505
|
-
const q = <string[]>[];
|
|
1506
|
-
this.patternCollect(this.root, { text: "" }, 0, pattern, q);
|
|
1507
|
-
return q;
|
|
1508
|
-
}
|
|
1509
1148
|
}
|