data-structure-typed 2.0.0 → 2.0.1

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/dist/cjs/data-structures/graph/abstract-graph.js +14 -14
  3. package/dist/cjs/data-structures/graph/abstract-graph.js.map +1 -1
  4. package/dist/cjs/data-structures/hash/hash-map.d.ts +46 -0
  5. package/dist/cjs/data-structures/hash/hash-map.js +46 -0
  6. package/dist/cjs/data-structures/hash/hash-map.js.map +1 -1
  7. package/dist/cjs/data-structures/linked-list/singly-linked-list.d.ts +66 -0
  8. package/dist/cjs/data-structures/linked-list/singly-linked-list.js +66 -0
  9. package/dist/cjs/data-structures/linked-list/singly-linked-list.js.map +1 -1
  10. package/dist/cjs/data-structures/queue/queue.d.ts +47 -0
  11. package/dist/cjs/data-structures/queue/queue.js +47 -0
  12. package/dist/cjs/data-structures/queue/queue.js.map +1 -1
  13. package/dist/cjs/data-structures/stack/stack.d.ts +121 -0
  14. package/dist/cjs/data-structures/stack/stack.js +121 -0
  15. package/dist/cjs/data-structures/stack/stack.js.map +1 -1
  16. package/dist/esm/data-structures/graph/abstract-graph.js +14 -14
  17. package/dist/esm/data-structures/graph/abstract-graph.js.map +1 -1
  18. package/dist/esm/data-structures/hash/hash-map.d.ts +46 -0
  19. package/dist/esm/data-structures/hash/hash-map.js +46 -0
  20. package/dist/esm/data-structures/hash/hash-map.js.map +1 -1
  21. package/dist/esm/data-structures/linked-list/singly-linked-list.d.ts +66 -0
  22. package/dist/esm/data-structures/linked-list/singly-linked-list.js +66 -0
  23. package/dist/esm/data-structures/linked-list/singly-linked-list.js.map +1 -1
  24. package/dist/esm/data-structures/queue/queue.d.ts +47 -0
  25. package/dist/esm/data-structures/queue/queue.js +47 -0
  26. package/dist/esm/data-structures/queue/queue.js.map +1 -1
  27. package/dist/esm/data-structures/stack/stack.d.ts +121 -0
  28. package/dist/esm/data-structures/stack/stack.js +121 -0
  29. package/dist/esm/data-structures/stack/stack.js.map +1 -1
  30. package/dist/individuals/binary-tree/avl-tree-counter.mjs +4701 -0
  31. package/dist/individuals/binary-tree/avl-tree-multi-map.mjs +4514 -0
  32. package/dist/individuals/binary-tree/avl-tree.mjs +4321 -0
  33. package/dist/individuals/binary-tree/binary-tree.mjs +3097 -0
  34. package/dist/individuals/binary-tree/bst.mjs +3858 -0
  35. package/dist/individuals/binary-tree/red-black-tree.mjs +4391 -0
  36. package/dist/individuals/binary-tree/tree-counter.mjs +4806 -0
  37. package/dist/individuals/binary-tree/tree-multi-map.mjs +4582 -0
  38. package/dist/individuals/graph/directed-graph.mjs +2910 -0
  39. package/dist/individuals/graph/undirected-graph.mjs +2745 -0
  40. package/dist/individuals/hash/hash-map.mjs +1040 -0
  41. package/dist/individuals/heap/heap.mjs +909 -0
  42. package/dist/individuals/heap/max-heap.mjs +671 -0
  43. package/dist/individuals/heap/min-heap.mjs +659 -0
  44. package/dist/individuals/linked-list/doubly-linked-list.mjs +1495 -0
  45. package/dist/individuals/linked-list/singly-linked-list.mjs +1479 -0
  46. package/dist/individuals/priority-queue/max-priority-queue.mjs +768 -0
  47. package/dist/individuals/priority-queue/min-priority-queue.mjs +757 -0
  48. package/dist/individuals/priority-queue/priority-queue.mjs +670 -0
  49. package/dist/individuals/queue/deque.mjs +1262 -0
  50. package/dist/individuals/queue/queue.mjs +1865 -0
  51. package/dist/individuals/stack/stack.mjs +415 -0
  52. package/dist/individuals/trie/trie.mjs +687 -0
  53. package/dist/umd/data-structure-typed.js +14 -14
  54. package/dist/umd/data-structure-typed.min.js +2 -2
  55. package/dist/umd/data-structure-typed.min.js.map +1 -1
  56. package/package.json +2 -2
  57. package/src/data-structures/graph/abstract-graph.ts +14 -14
  58. package/src/data-structures/hash/hash-map.ts +46 -0
  59. package/src/data-structures/linked-list/singly-linked-list.ts +66 -0
  60. package/src/data-structures/queue/queue.ts +47 -0
  61. package/src/data-structures/stack/stack.ts +121 -0
  62. package/test/unit/data-structures/graph/directed-graph.test.ts +37 -37
  63. package/test/unit/data-structures/graph/undirected-graph.test.ts +2 -2
  64. package/test/unit/data-structures/hash/hash-map.test.ts +135 -0
  65. package/test/unit/data-structures/linked-list/singly-linked-list.test.ts +72 -1
  66. package/test/unit/data-structures/queue/queue.test.ts +214 -0
  67. package/test/unit/data-structures/stack/stack.test.ts +165 -0
@@ -0,0 +1,3097 @@
1
+ // src/utils/utils.ts
2
+ var THUNK_SYMBOL = Symbol("thunk");
3
+ var isThunk = (fnOrValue) => {
4
+ return typeof fnOrValue === "function" && fnOrValue.__THUNK__ === THUNK_SYMBOL;
5
+ };
6
+ var toThunk = (fn) => {
7
+ const thunk = () => fn();
8
+ thunk.__THUNK__ = THUNK_SYMBOL;
9
+ return thunk;
10
+ };
11
+ var trampoline = (fn) => {
12
+ const cont = (...args) => toThunk(() => fn(...args));
13
+ return Object.assign(
14
+ (...args) => {
15
+ let result = fn(...args);
16
+ while (isThunk(result) && typeof result === "function") {
17
+ result = result();
18
+ }
19
+ return result;
20
+ },
21
+ { cont }
22
+ );
23
+ };
24
+ function isPrimitiveComparable(value) {
25
+ const valueType = typeof value;
26
+ if (valueType === "number") return true;
27
+ return valueType === "bigint" || valueType === "string" || valueType === "boolean";
28
+ }
29
+ function tryObjectToPrimitive(obj) {
30
+ if (typeof obj.valueOf === "function") {
31
+ const valueOfResult = obj.valueOf();
32
+ if (valueOfResult !== obj) {
33
+ if (isPrimitiveComparable(valueOfResult)) return valueOfResult;
34
+ if (typeof valueOfResult === "object" && valueOfResult !== null) return tryObjectToPrimitive(valueOfResult);
35
+ }
36
+ }
37
+ if (typeof obj.toString === "function") {
38
+ const stringResult = obj.toString();
39
+ if (stringResult !== "[object Object]") return stringResult;
40
+ }
41
+ return null;
42
+ }
43
+ function isComparable(value, isForceObjectComparable = false) {
44
+ if (value === null || value === void 0) return false;
45
+ if (isPrimitiveComparable(value)) return true;
46
+ if (typeof value !== "object") return false;
47
+ if (value instanceof Date) return true;
48
+ if (isForceObjectComparable) return true;
49
+ const comparableValue = tryObjectToPrimitive(value);
50
+ if (comparableValue === null || comparableValue === void 0) return false;
51
+ return isPrimitiveComparable(comparableValue);
52
+ }
53
+
54
+ // src/data-structures/base/iterable-element-base.ts
55
+ var IterableElementBase = class {
56
+ /**
57
+ * The protected constructor initializes the options for the IterableElementBase class, including the
58
+ * toElementFn function.
59
+ * @param [options] - An optional object that contains the following properties:
60
+ */
61
+ constructor(options) {
62
+ if (options) {
63
+ const { toElementFn } = options;
64
+ if (typeof toElementFn === "function") this._toElementFn = toElementFn;
65
+ else if (toElementFn) throw new TypeError("toElementFn must be a function type");
66
+ }
67
+ }
68
+ _toElementFn;
69
+ get toElementFn() {
70
+ return this._toElementFn;
71
+ }
72
+ /**
73
+ * Time Complexity: O(n)
74
+ * Space Complexity: O(1)
75
+ *
76
+ * The function is an implementation of the Symbol.iterator method that returns an IterableIterator.
77
+ * @param {any[]} args - The `args` parameter in the code snippet represents a rest parameter. It
78
+ * allows the function to accept any number of arguments as an array. In this case, the `args`
79
+ * parameter is used to pass any number of arguments to the `_getIterator` method.
80
+ */
81
+ *[Symbol.iterator](...args) {
82
+ yield* this._getIterator(...args);
83
+ }
84
+ /**
85
+ * Time Complexity: O(n)
86
+ * Space Complexity: O(n)
87
+ *
88
+ * The function returns an iterator that yields all the values in the object.
89
+ */
90
+ *values() {
91
+ for (const item of this) {
92
+ yield item;
93
+ }
94
+ }
95
+ /**
96
+ * Time Complexity: O(n)
97
+ * Space Complexity: O(1)
98
+ *
99
+ * The `every` function checks if every element in the array satisfies a given predicate.
100
+ * @param predicate - The `predicate` parameter is a callback function that takes three arguments:
101
+ * the current element being processed, its index, and the array it belongs to. It should return a
102
+ * boolean value indicating whether the element satisfies a certain condition or not.
103
+ * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that specifies the value
104
+ * to be used as `this` when executing the `predicate` function. If `thisArg` is provided, it will be
105
+ * passed as the `this` value to the `predicate` function. If `thisArg` is
106
+ * @returns The `every` method is returning a boolean value. It returns `true` if every element in
107
+ * the array satisfies the provided predicate function, and `false` otherwise.
108
+ */
109
+ every(predicate, thisArg) {
110
+ let index = 0;
111
+ for (const item of this) {
112
+ if (!predicate.call(thisArg, item, index++, this)) {
113
+ return false;
114
+ }
115
+ }
116
+ return true;
117
+ }
118
+ /**
119
+ * Time Complexity: O(n)
120
+ * Space Complexity: O(1)
121
+ *
122
+ * The "some" function checks if at least one element in a collection satisfies a given predicate.
123
+ * @param predicate - The `predicate` parameter is a callback function that takes three arguments:
124
+ * `value`, `index`, and `array`. It should return a boolean value indicating whether the current
125
+ * element satisfies the condition.
126
+ * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that specifies the value
127
+ * to be used as the `this` value when executing the `predicate` function. If `thisArg` is provided,
128
+ * it will be passed as the `this` value to the `predicate` function. If `thisArg
129
+ * @returns a boolean value. It returns true if the predicate function returns true for any element
130
+ * in the collection, and false otherwise.
131
+ */
132
+ some(predicate, thisArg) {
133
+ let index = 0;
134
+ for (const item of this) {
135
+ if (predicate.call(thisArg, item, index++, this)) {
136
+ return true;
137
+ }
138
+ }
139
+ return false;
140
+ }
141
+ /**
142
+ * Time Complexity: O(n)
143
+ * Space Complexity: O(1)
144
+ *
145
+ * The `forEach` function iterates over each element in an array-like object and calls a callback
146
+ * function for each element.
147
+ * @param callbackfn - The callbackfn parameter is a function that will be called for each element in
148
+ * the array. It takes three arguments: the current element being processed, the index of the current
149
+ * element, and the array that forEach was called upon.
150
+ * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that specifies the value
151
+ * to be used as `this` when executing the `callbackfn` function. If `thisArg` is provided, it will
152
+ * be passed as the `this` value to the `callbackfn` function. If `thisArg
153
+ */
154
+ forEach(callbackfn, thisArg) {
155
+ let index = 0;
156
+ for (const item of this) {
157
+ callbackfn.call(thisArg, item, index++, this);
158
+ }
159
+ }
160
+ /**
161
+ * Time Complexity: O(n)
162
+ * Space Complexity: O(1)
163
+ *
164
+ * The `find` function iterates over the elements of an array-like object and returns the first
165
+ * element that satisfies the provided callback function.
166
+ * @param predicate - The predicate parameter is a function that will be called for each element in
167
+ * the array. It takes three arguments: the current element being processed, the index of the current
168
+ * element, and the array itself. The function should return a boolean value indicating whether the
169
+ * current element matches the desired condition.
170
+ * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that specifies the value
171
+ * to be used as `this` when executing the `callbackfn` function. If `thisArg` is provided, it will
172
+ * be passed as the `this` value to the `callbackfn` function. If `thisArg
173
+ * @returns The `find` method returns the first element in the array that satisfies the provided
174
+ * callback function. If no element satisfies the callback function, `undefined` is returned.
175
+ */
176
+ find(predicate, thisArg) {
177
+ let index = 0;
178
+ for (const item of this) {
179
+ if (predicate.call(thisArg, item, index++, this)) return item;
180
+ }
181
+ return;
182
+ }
183
+ /**
184
+ * Time Complexity: O(n)
185
+ * Space Complexity: O(1)
186
+ *
187
+ * The function checks if a given element exists in a collection.
188
+ * @param {E} element - The parameter "element" is of type E, which means it can be any type. It
189
+ * represents the element that we want to check for existence in the collection.
190
+ * @returns a boolean value. It returns true if the element is found in the collection, and false
191
+ * otherwise.
192
+ */
193
+ has(element) {
194
+ for (const ele of this) {
195
+ if (ele === element) return true;
196
+ }
197
+ return false;
198
+ }
199
+ /**
200
+ * Time Complexity: O(n)
201
+ * Space Complexity: O(1)
202
+ *
203
+ * The `reduce` function iterates over the elements of an array-like object and applies a callback
204
+ * function to reduce them into a single value.
205
+ * @param callbackfn - The callbackfn parameter is a function that will be called for each element in
206
+ * the array. It takes four arguments:
207
+ * @param {U} initialValue - The initialValue parameter is the initial value of the accumulator. It
208
+ * is the value that the accumulator starts with before the reduction operation begins.
209
+ * @returns The `reduce` method is returning the final value of the accumulator after iterating over
210
+ * all the elements in the array and applying the callback function to each element.
211
+ */
212
+ reduce(callbackfn, initialValue) {
213
+ let accumulator = initialValue ?? 0;
214
+ let index = 0;
215
+ for (const item of this) {
216
+ accumulator = callbackfn(accumulator, item, index++, this);
217
+ }
218
+ return accumulator;
219
+ }
220
+ /**
221
+ * Time Complexity: O(n)
222
+ * Space Complexity: O(n)
223
+ *
224
+ * The `toArray` function converts a linked list into an array.
225
+ * @returns The `toArray()` method is returning an array of type `E[]`.
226
+ */
227
+ toArray() {
228
+ return [...this];
229
+ }
230
+ /**
231
+ * Time Complexity: O(n)
232
+ * Space Complexity: O(n)
233
+ *
234
+ * The print function logs the elements of an array to the console.
235
+ */
236
+ toVisual() {
237
+ return [...this];
238
+ }
239
+ /**
240
+ * Time Complexity: O(n)
241
+ * Space Complexity: O(n)
242
+ *
243
+ * The print function logs the elements of an array to the console.
244
+ */
245
+ print() {
246
+ console.log(this.toVisual());
247
+ }
248
+ };
249
+
250
+ // src/data-structures/base/linear-base.ts
251
+ var LinearBase = class _LinearBase extends IterableElementBase {
252
+ /**
253
+ * The constructor initializes the LinearBase class with optional options, setting the maximum length
254
+ * if provided.
255
+ * @param [options] - The `options` parameter is an optional object that can be passed to the
256
+ * constructor. It is of type `LinearBaseOptions<E, R>`. The constructor checks if the `options`
257
+ * object is provided and then extracts the `maxLen` property from it. If `maxLen` is a
258
+ */
259
+ constructor(options) {
260
+ super(options);
261
+ if (options) {
262
+ const { maxLen } = options;
263
+ if (typeof maxLen === "number" && maxLen > 0 && maxLen % 1 === 0) this._maxLen = maxLen;
264
+ }
265
+ }
266
+ _maxLen = -1;
267
+ get maxLen() {
268
+ return this._maxLen;
269
+ }
270
+ /**
271
+ * Time Complexity: O(n)
272
+ * Space Complexity: O(1)
273
+ *
274
+ * The function indexOf searches for a specified element starting from a given index in an array-like
275
+ * object and returns the index of the first occurrence, or -1 if not found.
276
+ * @param {E} searchElement - The `searchElement` parameter in the `indexOf` function represents the
277
+ * element that you want to find within the array. The function will search for this element starting
278
+ * from the `fromIndex` (if provided) up to the end of the array. If the `searchElement` is found
279
+ * within the
280
+ * @param {number} [fromIndex=0] - The `fromIndex` parameter in the `indexOf` function represents the
281
+ * index at which to start searching for the `searchElement` within the array. If provided, the
282
+ * search will begin at this index and continue to the end of the array. If `fromIndex` is not
283
+ * specified, the default
284
+ * @returns The `indexOf` method is returning the index of the `searchElement` if it is found in the
285
+ * array starting from the `fromIndex`. If the `searchElement` is not found, it returns -1.
286
+ */
287
+ indexOf(searchElement, fromIndex = 0) {
288
+ if (this.length === 0) return -1;
289
+ if (fromIndex < 0) fromIndex = this.length + fromIndex;
290
+ if (fromIndex < 0) fromIndex = 0;
291
+ for (let i = fromIndex; i < this.length; i++) {
292
+ const element = this.at(i);
293
+ if (element === searchElement) return i;
294
+ }
295
+ return -1;
296
+ }
297
+ /**
298
+ * Time Complexity: O(n)
299
+ * Space Complexity: O(1)
300
+ *
301
+ * The function `lastIndexOf` in TypeScript returns the index of the last occurrence of a specified
302
+ * element in an array.
303
+ * @param {E} searchElement - The `searchElement` parameter is the element that you want to find the
304
+ * last index of within the array. The `lastIndexOf` method will search the array starting from the
305
+ * `fromIndex` (or the end of the array if not specified) and return the index of the last occurrence
306
+ * of the
307
+ * @param {number} fromIndex - The `fromIndex` parameter in the `lastIndexOf` method specifies the
308
+ * index at which to start searching for the `searchElement` in the array. By default, it starts
309
+ * searching from the last element of the array (`this.length - 1`). If a specific `fromIndex` is
310
+ * provided
311
+ * @returns The last index of the `searchElement` in the array is being returned. If the
312
+ * `searchElement` is not found in the array, -1 is returned.
313
+ */
314
+ lastIndexOf(searchElement, fromIndex = this.length - 1) {
315
+ if (this.length === 0) return -1;
316
+ if (fromIndex >= this.length) fromIndex = this.length - 1;
317
+ if (fromIndex < 0) fromIndex = this.length + fromIndex;
318
+ for (let i = fromIndex; i >= 0; i--) {
319
+ const element = this.at(i);
320
+ if (element === searchElement) return i;
321
+ }
322
+ return -1;
323
+ }
324
+ /**
325
+ * Time Complexity: O(n)
326
+ * Space Complexity: O(1)
327
+ *
328
+ * The `findIndex` function iterates over an array and returns the index of the first element that
329
+ * satisfies the provided predicate function.
330
+ * @param predicate - The `predicate` parameter in the `findIndex` function is a callback function
331
+ * that takes three arguments: `item`, `index`, and the array `this`. It should return a boolean
332
+ * value indicating whether the current element satisfies the condition being checked for.
333
+ * @param {any} [thisArg] - The `thisArg` parameter in the `findIndex` function is an optional
334
+ * parameter that specifies the value to use as `this` when executing the `predicate` function. If
335
+ * provided, the `predicate` function will be called with `thisArg` as its `this` value. If `
336
+ * @returns The `findIndex` method is returning the index of the first element in the array that
337
+ * satisfies the provided predicate function. If no such element is found, it returns -1.
338
+ */
339
+ findIndex(predicate, thisArg) {
340
+ for (let i = 0; i < this.length; i++) {
341
+ const item = this.at(i);
342
+ if (item !== void 0 && predicate.call(thisArg, item, i, this)) return i;
343
+ }
344
+ return -1;
345
+ }
346
+ /**
347
+ * Time Complexity: O(n + m)
348
+ * Space Complexity: O(n + m)
349
+ *
350
+ * The `concat` function in TypeScript concatenates multiple items into a new list, handling both
351
+ * individual elements and instances of `LinearBase`.
352
+ * @param {(E | this)[]} items - The `concat` method takes in an array of items, where
353
+ * each item can be either of type `E` or an instance of `LinearBase<E, R>`.
354
+ * @returns The `concat` method is returning a new instance of the class that it belongs to, with the
355
+ * items passed as arguments concatenated to it.
356
+ */
357
+ concat(...items) {
358
+ const newList = this.clone();
359
+ for (const item of items) {
360
+ if (item instanceof _LinearBase) {
361
+ newList.pushMany(item);
362
+ } else {
363
+ newList.push(item);
364
+ }
365
+ }
366
+ return newList;
367
+ }
368
+ /**
369
+ * Time Complexity: O(n log n)
370
+ * Space Complexity: O(n)
371
+ *
372
+ * The `sort` function in TypeScript sorts the elements of a collection using a specified comparison
373
+ * function.
374
+ * @param [compareFn] - The `compareFn` parameter is a function that defines the sort order. It takes
375
+ * two elements `a` and `b` as input and returns a number indicating their relative order. If the
376
+ * returned value is negative, `a` comes before `b`. If the returned value is positive, `
377
+ * @returns The `sort` method is returning the instance of the object on which it is called (this),
378
+ * after sorting the elements based on the provided comparison function (compareFn).
379
+ */
380
+ sort(compareFn) {
381
+ const arr = this.toArray();
382
+ arr.sort(compareFn);
383
+ this.clear();
384
+ for (const item of arr) this.push(item);
385
+ return this;
386
+ }
387
+ /**
388
+ * Time Complexity: O(n + m)
389
+ * Space Complexity: O(m)
390
+ *
391
+ * The `splice` function in TypeScript removes elements from an array and optionally inserts new
392
+ * elements at the specified index.
393
+ * @param {number} start - The `start` parameter in the `splice` method indicates the index at which
394
+ * to start modifying the array. If `start` is a negative number, it will count from the end of the
395
+ * array.
396
+ * @param {number} [deleteCount=0] - The `deleteCount` parameter in the `splice` method specifies the
397
+ * number of elements to remove from the array starting at the specified `start` index. If
398
+ * `deleteCount` is not provided or is 0, no elements are removed, and only new elements are inserted
399
+ * at the `start`
400
+ * @param {E[]} items - The `items` parameter in the `splice` method represents the elements that
401
+ * will be inserted into the array at the specified `start` index. These elements can be of any type
402
+ * and you can pass multiple elements separated by commas. The `splice` method will insert these
403
+ * items into the array at the
404
+ * @returns The `splice` method returns a list of elements that were removed from the original list
405
+ * during the operation.
406
+ */
407
+ splice(start, deleteCount = 0, ...items) {
408
+ const removedList = this._createInstance();
409
+ start = start < 0 ? this.length + start : start;
410
+ start = Math.max(0, Math.min(start, this.length));
411
+ deleteCount = Math.max(0, Math.min(deleteCount, this.length - start));
412
+ for (let i = 0; i < deleteCount; i++) {
413
+ const removed = this.deleteAt(start);
414
+ if (removed !== void 0) {
415
+ removedList.push(removed);
416
+ }
417
+ }
418
+ for (let i = 0; i < items.length; i++) {
419
+ this.addAt(start + i, items[i]);
420
+ }
421
+ return removedList;
422
+ }
423
+ /**
424
+ * Time Complexity: O(n)
425
+ * Space Complexity: O(1)
426
+ *
427
+ * The `join` function in TypeScript returns a string by joining the elements of an array with a
428
+ * specified separator.
429
+ * @param {string} [separator=,] - The `separator` parameter is a string that specifies the character
430
+ * or characters that will be used to separate each element when joining them into a single string.
431
+ * By default, the separator is set to a comma (`,`), but you can provide a different separator if
432
+ * needed.
433
+ * @returns The `join` method is being returned, which takes an optional `separator` parameter
434
+ * (defaulting to a comma) and returns a string created by joining all elements of the array after
435
+ * converting it to an array.
436
+ */
437
+ join(separator = ",") {
438
+ return this.toArray().join(separator);
439
+ }
440
+ /**
441
+ * Time Complexity: O(n)
442
+ * Space Complexity: O(n)
443
+ *
444
+ * The function `toReversedArray` takes an array and returns a new array with its elements in reverse
445
+ * order.
446
+ * @returns The `toReversedArray()` function returns an array of elements of type `E` in reverse
447
+ * order.
448
+ */
449
+ toReversedArray() {
450
+ const array = [];
451
+ for (let i = this.length - 1; i >= 0; i--) {
452
+ array.push(this.at(i));
453
+ }
454
+ return array;
455
+ }
456
+ /**
457
+ * Time Complexity: O(n)
458
+ * Space Complexity: O(1)
459
+ *
460
+ * The `reduceRight` function in TypeScript iterates over an array from right to left and applies a
461
+ * callback function to each element, accumulating a single result.
462
+ * @param callbackfn - The `callbackfn` parameter in the `reduceRight` method is a function that will
463
+ * be called on each element in the array from right to left. It takes four arguments:
464
+ * @param {U} [initialValue] - The `initialValue` parameter in the `reduceRight` method is an
465
+ * optional parameter that specifies the initial value of the accumulator. If provided, the
466
+ * `accumulator` will start with this initial value before iterating over the elements of the array.
467
+ * If `initialValue` is not provided, the accumulator will
468
+ * @returns The `reduceRight` method is returning the final accumulated value after applying the
469
+ * callback function to each element in the array from right to left.
470
+ */
471
+ reduceRight(callbackfn, initialValue) {
472
+ let accumulator = initialValue ?? 0;
473
+ for (let i = this.length - 1; i >= 0; i--) {
474
+ accumulator = callbackfn(accumulator, this.at(i), i, this);
475
+ }
476
+ return accumulator;
477
+ }
478
+ /**
479
+ * Time Complexity: O(m)
480
+ * Space Complexity: O(m)
481
+ *
482
+ * The `slice` function in TypeScript creates a new instance by extracting a portion of elements from
483
+ * the original instance based on the specified start and end indices.
484
+ * @param {number} [start=0] - The `start` parameter in the `slice` method represents the index at
485
+ * which to begin extracting elements from an array-like object. If no `start` parameter is provided,
486
+ * the default value is 0, meaning the extraction will start from the beginning of the array.
487
+ * @param {number} end - The `end` parameter in the `slice` method represents the index at which to
488
+ * end the slicing. By default, if no `end` parameter is provided, it will slice until the end of the
489
+ * array (i.e., `this.length`).
490
+ * @returns The `slice` method is returning a new instance of the object with elements sliced from
491
+ * the specified start index (default is 0) to the specified end index (default is the length of the
492
+ * object).
493
+ */
494
+ slice(start = 0, end = this.length) {
495
+ start = start < 0 ? this.length + start : start;
496
+ end = end < 0 ? this.length + end : end;
497
+ const newList = this._createInstance();
498
+ for (let i = start; i < end; i++) {
499
+ newList.push(this.at(i));
500
+ }
501
+ return newList;
502
+ }
503
+ /**
504
+ * Time Complexity: O(n)
505
+ * Space Complexity: O(1)
506
+ *
507
+ * The `fill` function in TypeScript fills a specified range in an array-like object with a given
508
+ * value.
509
+ * @param {E} value - The `value` parameter in the `fill` method represents the element that will be
510
+ * used to fill the specified range in the array.
511
+ * @param [start=0] - The `start` parameter specifies the index at which to start filling the array
512
+ * with the specified value. If not provided, it defaults to 0, indicating the beginning of the
513
+ * array.
514
+ * @param end - The `end` parameter in the `fill` function represents the index at which the filling
515
+ * of values should stop. It specifies the end of the range within the array where the `value` should
516
+ * be filled.
517
+ * @returns The `fill` method is returning the modified object (`this`) after filling the specified
518
+ * range with the provided value.
519
+ */
520
+ fill(value, start = 0, end = this.length) {
521
+ start = start < 0 ? this.length + start : start;
522
+ end = end < 0 ? this.length + end : end;
523
+ if (start < 0) start = 0;
524
+ if (end > this.length) end = this.length;
525
+ if (start >= end) return this;
526
+ for (let i = start; i < end; i++) {
527
+ this.setAt(i, value);
528
+ }
529
+ return this;
530
+ }
531
+ };
532
+
533
+ // src/data-structures/queue/queue.ts
534
+ var Queue = class _Queue extends LinearBase {
535
+ constructor(elements = [], options) {
536
+ super(options);
537
+ if (options) {
538
+ const { autoCompactRatio = 0.5 } = options;
539
+ this._autoCompactRatio = autoCompactRatio;
540
+ }
541
+ this.pushMany(elements);
542
+ }
543
+ _elements = [];
544
+ get elements() {
545
+ return this._elements;
546
+ }
547
+ _offset = 0;
548
+ get offset() {
549
+ return this._offset;
550
+ }
551
+ get length() {
552
+ return this.elements.length - this.offset;
553
+ }
554
+ _autoCompactRatio = 0.5;
555
+ get autoCompactRatio() {
556
+ return this._autoCompactRatio;
557
+ }
558
+ set autoCompactRatio(v) {
559
+ this._autoCompactRatio = v;
560
+ }
561
+ /**
562
+ * Time Complexity: O(1)
563
+ * Space Complexity: O(1)
564
+ *
565
+ * The `first` function returns the first element of the array `_elements` if it exists, otherwise it returns `undefined`.
566
+ * @returns The `get first()` method returns the first element of the data structure, represented by the `_elements` array at
567
+ * the `_offset` index. If the data structure is empty (length is 0), it returns `undefined`.
568
+ */
569
+ get first() {
570
+ return this.length > 0 ? this.elements[this.offset] : void 0;
571
+ }
572
+ /**
573
+ * Time Complexity: O(1)
574
+ * Space Complexity: O(1)
575
+ *
576
+ * The `last` function returns the last element in an array-like data structure, or undefined if the structure is empty.
577
+ * @returns The method `get last()` returns the last element of the `_elements` array if the array is not empty. If the
578
+ * array is empty, it returns `undefined`.
579
+ */
580
+ get last() {
581
+ return this.length > 0 ? this.elements[this.elements.length - 1] : void 0;
582
+ }
583
+ /**
584
+ * Time Complexity: O(n)
585
+ * Space Complexity: O(n)
586
+ *
587
+ * The function "fromArray" creates a new Queue object from an array of elements.Creates a queue from an existing array.
588
+ * @public
589
+ * @param {E[]} elements - The "elements" parameter is an array of elements of type E.
590
+ * @returns The method is returning a new instance of the Queue class, initialized with the elements from the input
591
+ * array.
592
+ */
593
+ static fromArray(elements) {
594
+ return new _Queue(elements);
595
+ }
596
+ /**
597
+ * Time Complexity: O(1)
598
+ * Space Complexity: O(1)
599
+ *
600
+ * The push function adds an element to the end of the queue and returns true. Adds an element at the back of the queue.
601
+ * @param {E} element - The `element` parameter represents the element that you want to add to the queue.
602
+ * @returns Always returns true, indicating the element was successfully added.
603
+ */
604
+ push(element) {
605
+ this.elements.push(element);
606
+ if (this._maxLen > 0 && this.length > this._maxLen) this.shift();
607
+ return true;
608
+ }
609
+ /**
610
+ * Time Complexity: O(k)
611
+ * Space Complexity: O(k)
612
+ *
613
+ * The `pushMany` function iterates over elements and pushes them into an array after applying a
614
+ * transformation function if provided.
615
+ * @param {Iterable<E> | Iterable<R>} elements - The `elements` parameter in the `pushMany` function
616
+ * is an iterable containing elements of type `E` or `R`.
617
+ * @returns The `pushMany` function is returning an array of boolean values indicating whether each
618
+ * element was successfully pushed into the data structure.
619
+ */
620
+ pushMany(elements) {
621
+ const ans = [];
622
+ for (const el of elements) {
623
+ if (this.toElementFn) ans.push(this.push(this.toElementFn(el)));
624
+ else ans.push(this.push(el));
625
+ }
626
+ return ans;
627
+ }
628
+ /**
629
+ * Time Complexity: O(1)
630
+ * Space Complexity: O(1)
631
+ *
632
+ * The `shift` function removes and returns the first element in the queue, and adjusts the internal data structure if
633
+ * necessary to optimize performance.
634
+ * @returns The function `shift()` returns either the first element in the queue or `undefined` if the queue is empty.
635
+ */
636
+ shift() {
637
+ if (this.length === 0) return void 0;
638
+ const first = this.first;
639
+ this._offset += 1;
640
+ if (this.offset / this.elements.length > this.autoCompactRatio) this.compact();
641
+ return first;
642
+ }
643
+ /**
644
+ * Time Complexity: O(n)
645
+ * Space Complexity: O(1)
646
+ *
647
+ * The delete function removes an element from the list.
648
+ * @param {E} element - Specify the element to be deleted
649
+ * @return A boolean value indicating whether the element was successfully deleted or not
650
+ */
651
+ delete(element) {
652
+ const index = this.elements.indexOf(element);
653
+ return !!this.deleteAt(index);
654
+ }
655
+ /**
656
+ * Time Complexity: O(n)
657
+ * Space Complexity: O(1)
658
+ *
659
+ * The deleteAt function deletes the element at a given index.
660
+ * @param {number} index - Determine the index of the element to be deleted
661
+ * @return A boolean value
662
+ */
663
+ deleteAt(index) {
664
+ const deleted = this.elements[index];
665
+ this.elements.splice(index, 1);
666
+ return deleted;
667
+ }
668
+ /**
669
+ * Time Complexity: O(1)
670
+ * Space Complexity: O(1)
671
+ *
672
+ * The `at` function returns the element at a specified index adjusted by an offset, or `undefined`
673
+ * if the index is out of bounds.
674
+ * @param {number} index - The `index` parameter represents the position of the element you want to
675
+ * retrieve from the data structure.
676
+ * @returns The `at` method is returning the element at the specified index adjusted by the offset
677
+ * `_offset`.
678
+ */
679
+ at(index) {
680
+ return this.elements[index + this._offset];
681
+ }
682
+ /**
683
+ * Time Complexity: O(n)
684
+ * Space Complexity: O(1)
685
+ *
686
+ * The `reverse` function in TypeScript reverses the elements of an array starting from a specified
687
+ * offset.
688
+ * @returns The `reverse()` method is returning the modified object itself (`this`) after reversing
689
+ * the elements in the array and resetting the offset to 0.
690
+ */
691
+ reverse() {
692
+ this._elements = this.elements.slice(this.offset).reverse();
693
+ this._offset = 0;
694
+ return this;
695
+ }
696
+ /**
697
+ * Time Complexity: O(n)
698
+ * Space Complexity: O(1)
699
+ *
700
+ * The function `addAt` inserts a new element at a specified index in an array, returning true if
701
+ * successful and false if the index is out of bounds.
702
+ * @param {number} index - The `index` parameter represents the position at which the `newElement`
703
+ * should be added in the array.
704
+ * @param {E} newElement - The `newElement` parameter represents the element that you want to insert
705
+ * into the array at the specified index.
706
+ * @returns The `addAt` method returns a boolean value - `true` if the new element was successfully
707
+ * added at the specified index, and `false` if the index is out of bounds (less than 0 or greater
708
+ * than the length of the array).
709
+ */
710
+ addAt(index, newElement) {
711
+ if (index < 0 || index > this.length) return false;
712
+ this._elements.splice(this.offset + index, 0, newElement);
713
+ return true;
714
+ }
715
+ /**
716
+ * Time Complexity: O(1)
717
+ * Space Complexity: O(1)
718
+ *
719
+ * The function `setAt` updates an element at a specified index in an array-like data structure.
720
+ * @param {number} index - The `index` parameter is a number that represents the position in the
721
+ * array where the new element will be set.
722
+ * @param {E} newElement - The `newElement` parameter represents the new value that you want to set
723
+ * at the specified index in the array.
724
+ * @returns The `setAt` method returns a boolean value - `true` if the element was successfully set
725
+ * at the specified index, and `false` if the index is out of bounds (less than 0 or greater than the
726
+ * length of the array).
727
+ */
728
+ setAt(index, newElement) {
729
+ if (index < 0 || index > this.length) return false;
730
+ this._elements[this.offset + index] = newElement;
731
+ return true;
732
+ }
733
+ /**
734
+ * Time Complexity: O(1)
735
+ * Space Complexity: O(1)
736
+ *
737
+ * The function checks if a data structure is empty by comparing its length to zero.
738
+ * @returns {boolean} A boolean value indicating whether the length of the object is 0 or not.
739
+ */
740
+ isEmpty() {
741
+ return this.length === 0;
742
+ }
743
+ /**
744
+ * Time Complexity: O(1)
745
+ * Space Complexity: O(1)
746
+ *
747
+ * The clear function resets the elements array and offset to their initial values.
748
+ */
749
+ clear() {
750
+ this._elements = [];
751
+ this._offset = 0;
752
+ }
753
+ /**
754
+ * Time Complexity: O(n)
755
+ * Space Complexity: O(1)
756
+ *
757
+ * The `compact` function in TypeScript slices the elements array based on the offset and resets the
758
+ * offset to zero.
759
+ * @returns The `compact()` method is returning a boolean value of `true`.
760
+ */
761
+ compact() {
762
+ this._elements = this.elements.slice(this.offset);
763
+ this._offset = 0;
764
+ return true;
765
+ }
766
+ /**
767
+ * Time Complexity: O(n)
768
+ * Space Complexity: O(n)
769
+ *
770
+ * The function overrides the splice method to remove and insert elements in a queue-like data
771
+ * structure.
772
+ * @param {number} start - The `start` parameter in the `splice` method specifies the index at which
773
+ * to start changing the array. Items will be added or removed starting from this index.
774
+ * @param {number} [deleteCount=0] - The `deleteCount` parameter in the `splice` method specifies the
775
+ * number of elements to remove from the array starting at the specified `start` index. If
776
+ * `deleteCount` is not provided, it defaults to 0, meaning no elements will be removed but new
777
+ * elements can still be inserted at
778
+ * @param {E[]} items - The `items` parameter in the `splice` method represents the elements that
779
+ * will be added to the array at the specified `start` index. These elements will replace the
780
+ * existing elements starting from the `start` index for the `deleteCount` number of elements.
781
+ * @returns The `splice` method is returning the `removedQueue`, which is an instance of the same
782
+ * class as the original object.
783
+ */
784
+ splice(start, deleteCount = 0, ...items) {
785
+ const removedQueue = this._createInstance();
786
+ start = Math.max(0, Math.min(start, this.length));
787
+ deleteCount = Math.max(0, Math.min(deleteCount, this.length - start));
788
+ const globalStartIndex = this.offset + start;
789
+ const removedElements = this._elements.splice(globalStartIndex, deleteCount, ...items);
790
+ removedQueue.pushMany(removedElements);
791
+ this.compact();
792
+ return removedQueue;
793
+ }
794
+ /**
795
+ * Time Complexity: O(n)
796
+ * Space Complexity: O(n)
797
+ *
798
+ * The `clone()` function returns a new Queue object with the same elements as the original Queue.
799
+ * @returns The `clone()` method is returning a new instance of the `Queue` class.
800
+ */
801
+ clone() {
802
+ return new _Queue(this.elements.slice(this.offset), { toElementFn: this.toElementFn, maxLen: this._maxLen });
803
+ }
804
+ /**
805
+ * Time Complexity: O(n)
806
+ * Space Complexity: O(n)
807
+ *
808
+ * The `filter` function creates a new `Queue` object containing elements from the original `Queue`
809
+ * that satisfy a given predicate function.
810
+ * @param predicate - The `predicate` parameter is a callback function that takes three arguments:
811
+ * the current element being iterated over, the index of the current element, and the queue itself.
812
+ * It should return a boolean value indicating whether the element should be included in the filtered
813
+ * queue or not.
814
+ * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that specifies the value
815
+ * to be used as `this` when executing the `predicate` function. If `thisArg` is provided, it will be
816
+ * passed as the `this` value to the `predicate` function. If `thisArg` is
817
+ * @returns The `filter` method is returning a new `Queue` object that contains the elements that
818
+ * satisfy the given predicate function.
819
+ */
820
+ filter(predicate, thisArg) {
821
+ const newDeque = this._createInstance({
822
+ toElementFn: this._toElementFn,
823
+ autoCompactRatio: this._autoCompactRatio,
824
+ maxLen: this._maxLen
825
+ });
826
+ let index = 0;
827
+ for (const el of this) {
828
+ if (predicate.call(thisArg, el, index, this)) {
829
+ newDeque.push(el);
830
+ }
831
+ index++;
832
+ }
833
+ return newDeque;
834
+ }
835
+ /**
836
+ * Time Complexity: O(n)
837
+ * Space Complexity: O(n)
838
+ *
839
+ * The `map` function in TypeScript creates a new Queue by applying a callback function to each
840
+ * element in the original Queue.
841
+ * @param callback - The `callback` parameter is a function that will be applied to each element in
842
+ * the queue. It takes the current element, its index, and the queue itself as arguments, and returns
843
+ * a new element.
844
+ * @param [toElementFn] - The `toElementFn` parameter is an optional function that can be provided to
845
+ * convert a raw element of type `RM` to a new element of type `EM`. This function is used within the
846
+ * `map` method to transform each raw element before passing it to the `callback` function. If
847
+ * @param {any} [thisArg] - The `thisArg` parameter in the `map` function is used to specify the
848
+ * value of `this` when executing the `callback` function. It allows you to set the context (the
849
+ * value of `this`) within the callback function. If `thisArg` is provided, it will be
850
+ * @returns A new Queue object containing elements of type EM, which are the result of applying the
851
+ * callback function to each element in the original Queue object.
852
+ */
853
+ map(callback, toElementFn, thisArg) {
854
+ const newDeque = new _Queue([], {
855
+ toElementFn,
856
+ autoCompactRatio: this._autoCompactRatio,
857
+ maxLen: this._maxLen
858
+ });
859
+ let index = 0;
860
+ for (const el of this) {
861
+ newDeque.push(callback.call(thisArg, el, index, this));
862
+ index++;
863
+ }
864
+ return newDeque;
865
+ }
866
+ /**
867
+ * Time Complexity: O(n)
868
+ * Space Complexity: O(n)
869
+ *
870
+ * The function `_getIterator` returns an iterable iterator for the elements in the class.
871
+ */
872
+ *_getIterator() {
873
+ for (const item of this.elements.slice(this.offset)) {
874
+ yield item;
875
+ }
876
+ }
877
+ /**
878
+ * The function `_createInstance` returns a new instance of the `Queue` class with the specified
879
+ * options.
880
+ * @param [options] - The `options` parameter in the `_createInstance` method is of type
881
+ * `QueueOptions<E, R>`, which is used to configure the behavior of the queue being created. It
882
+ * allows you to specify settings or properties that can influence how the queue operates.
883
+ * @returns An instance of the `Queue` class with an empty array and the provided options is being
884
+ * returned.
885
+ */
886
+ _createInstance(options) {
887
+ return new _Queue([], options);
888
+ }
889
+ /**
890
+ * The function `_getReverseIterator` returns an iterator that iterates over elements in reverse
891
+ * order.
892
+ */
893
+ *_getReverseIterator() {
894
+ for (let i = this.length - 1; i >= 0; i--) {
895
+ const cur = this.at(i);
896
+ if (cur !== void 0) yield cur;
897
+ }
898
+ }
899
+ };
900
+
901
+ // src/data-structures/base/iterable-entry-base.ts
902
+ var IterableEntryBase = class {
903
+ /**
904
+ * Time Complexity: O(n)
905
+ * Space Complexity: O(1)
906
+ *
907
+ * The function is an implementation of the Symbol.iterator method that returns an iterable iterator.
908
+ * @param {any[]} args - The `args` parameter in the code snippet represents a rest parameter. It
909
+ * allows the function to accept any number of arguments as an array. In this case, the `args`
910
+ * parameter is used to pass any additional arguments to the `_getIterator` method.
911
+ */
912
+ *[Symbol.iterator](...args) {
913
+ yield* this._getIterator(...args);
914
+ }
915
+ /**
916
+ * Time Complexity: O(n)
917
+ * Space Complexity: O(n)
918
+ *
919
+ * The function returns an iterator that yields key-value pairs from the object, where the value can
920
+ * be undefined.
921
+ */
922
+ *entries() {
923
+ for (const item of this) {
924
+ yield item;
925
+ }
926
+ }
927
+ /**
928
+ * Time Complexity: O(n)
929
+ * Space Complexity: O(n)
930
+ *
931
+ * The function returns an iterator that yields the keys of a data structure.
932
+ */
933
+ *keys() {
934
+ for (const item of this) {
935
+ yield item[0];
936
+ }
937
+ }
938
+ /**
939
+ * Time Complexity: O(n)
940
+ * Space Complexity: O(n)
941
+ *
942
+ * The function returns an iterator that yields the values of a collection.
943
+ */
944
+ *values() {
945
+ for (const item of this) {
946
+ yield item[1];
947
+ }
948
+ }
949
+ /**
950
+ * Time Complexity: O(n)
951
+ * Space Complexity: O(1)
952
+ *
953
+ * The `every` function checks if every element in a collection satisfies a given condition.
954
+ * @param predicate - The `predicate` parameter is a callback function that takes three arguments:
955
+ * `value`, `key`, and `index`. It should return a boolean value indicating whether the condition is
956
+ * met for the current element in the iteration.
957
+ * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that specifies the value
958
+ * to be used as `this` when executing the `predicate` function. If `thisArg` is provided, it will be
959
+ * passed as the first argument to the `predicate` function. If `thisArg` is not provided
960
+ * @returns The `every` method is returning a boolean value. It returns `true` if every element in
961
+ * the collection satisfies the provided predicate function, and `false` otherwise.
962
+ */
963
+ every(predicate, thisArg) {
964
+ let index = 0;
965
+ for (const item of this) {
966
+ if (!predicate.call(thisArg, item[0], item[1], index++, this)) {
967
+ return false;
968
+ }
969
+ }
970
+ return true;
971
+ }
972
+ /**
973
+ * Time Complexity: O(n)
974
+ * Space Complexity: O(1)
975
+ *
976
+ * The "some" function iterates over a collection and returns true if at least one element satisfies
977
+ * a given predicate.
978
+ * @param predicate - The `predicate` parameter is a callback function that takes three arguments:
979
+ * `value`, `key`, and `index`. It should return a boolean value indicating whether the condition is
980
+ * met for the current element in the iteration.
981
+ * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that specifies the value
982
+ * to be used as the `this` value when executing the `predicate` function. If `thisArg` is provided,
983
+ * it will be passed as the first argument to the `predicate` function. If `thisArg` is
984
+ * @returns a boolean value. It returns true if the predicate function returns true for any pair in
985
+ * the collection, and false otherwise.
986
+ */
987
+ some(predicate, thisArg) {
988
+ let index = 0;
989
+ for (const item of this) {
990
+ if (predicate.call(thisArg, item[0], item[1], index++, this)) {
991
+ return true;
992
+ }
993
+ }
994
+ return false;
995
+ }
996
+ /**
997
+ * Time Complexity: O(n)
998
+ * Space Complexity: O(1)
999
+ *
1000
+ * The `forEach` function iterates over each key-value pair in a collection and executes a callback
1001
+ * function for each pair.
1002
+ * @param callbackfn - The callback function that will be called for each element in the collection.
1003
+ * It takes four parameters: the value of the current element, the key of the current element, the
1004
+ * index of the current element, and the collection itself.
1005
+ * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that allows you to
1006
+ * specify the value of `this` within the callback function. If `thisArg` is provided, it will be
1007
+ * used as the `this` value when calling the callback function. If `thisArg` is not provided, `
1008
+ */
1009
+ forEach(callbackfn, thisArg) {
1010
+ let index = 0;
1011
+ for (const item of this) {
1012
+ const [key, value] = item;
1013
+ callbackfn.call(thisArg, key, value, index++, this);
1014
+ }
1015
+ }
1016
+ /**
1017
+ * Time Complexity: O(n)
1018
+ * Space Complexity: O(1)
1019
+ *
1020
+ * The `find` function iterates over the entries of a collection and returns the first value for
1021
+ * which the callback function returns true.
1022
+ * @param callbackfn - The callback function that will be called for each entry in the collection. It
1023
+ * takes three arguments: the value of the entry, the key of the entry, and the index of the entry in
1024
+ * the collection. It should return a boolean value indicating whether the current entry matches the
1025
+ * desired condition.
1026
+ * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that specifies the value
1027
+ * to be used as `this` when executing the `callbackfn` function. If `thisArg` is provided, it will
1028
+ * be passed as the `this` value to the `callbackfn` function. If `thisArg
1029
+ * @returns The method `find` returns the value of the first element in the iterable that satisfies
1030
+ * the provided callback function. If no element satisfies the callback function, `undefined` is
1031
+ * returned.
1032
+ */
1033
+ find(callbackfn, thisArg) {
1034
+ let index = 0;
1035
+ for (const item of this) {
1036
+ const [key, value] = item;
1037
+ if (callbackfn.call(thisArg, key, value, index++, this)) return item;
1038
+ }
1039
+ return;
1040
+ }
1041
+ /**
1042
+ * Time Complexity: O(n)
1043
+ * Space Complexity: O(1)
1044
+ *
1045
+ * The function checks if a given key exists in a collection.
1046
+ * @param {K} key - The parameter "key" is of type K, which means it can be any type. It represents
1047
+ * the key that we want to check for existence in the data structure.
1048
+ * @returns a boolean value. It returns true if the key is found in the collection, and false
1049
+ * otherwise.
1050
+ */
1051
+ has(key) {
1052
+ for (const item of this) {
1053
+ const [itemKey] = item;
1054
+ if (itemKey === key) return true;
1055
+ }
1056
+ return false;
1057
+ }
1058
+ /**
1059
+ * Time Complexity: O(n)
1060
+ * Space Complexity: O(1)
1061
+ *
1062
+ * The function checks if a given value exists in a collection.
1063
+ * @param {V} value - The parameter "value" is the value that we want to check if it exists in the
1064
+ * collection.
1065
+ * @returns a boolean value, either true or false.
1066
+ */
1067
+ hasValue(value) {
1068
+ for (const [, elementValue] of this) {
1069
+ if (elementValue === value) return true;
1070
+ }
1071
+ return false;
1072
+ }
1073
+ /**
1074
+ * Time Complexity: O(n)
1075
+ * Space Complexity: O(1)
1076
+ *
1077
+ * The `get` function retrieves the value associated with a given key from a collection.
1078
+ * @param {K} key - K (the type of the key) - This parameter represents the key that is being
1079
+ * searched for in the collection.
1080
+ * @returns The `get` method returns the value associated with the specified key if it exists in the
1081
+ * collection, otherwise it returns `undefined`.
1082
+ */
1083
+ get(key) {
1084
+ for (const item of this) {
1085
+ const [itemKey, value] = item;
1086
+ if (itemKey === key) return value;
1087
+ }
1088
+ return;
1089
+ }
1090
+ /**
1091
+ * Time Complexity: O(n)
1092
+ * Space Complexity: O(1)
1093
+ *
1094
+ * The `reduce` function iterates over key-value pairs and applies a callback function to each pair,
1095
+ * accumulating a single value.
1096
+ * @param callbackfn - The callback function that will be called for each element in the collection.
1097
+ * It takes four arguments: the current accumulator value, the current value of the element, the key
1098
+ * of the element, and the index of the element in the collection. It should return the updated
1099
+ * accumulator value.
1100
+ * @param {U} initialValue - The `initialValue` parameter is the initial value of the accumulator. It
1101
+ * is the value that will be used as the first argument to the `callbackfn` function when reducing
1102
+ * the elements of the collection.
1103
+ * @returns The `reduce` method is returning the final value of the accumulator after iterating over
1104
+ * all the elements in the collection.
1105
+ */
1106
+ reduce(callbackfn, initialValue) {
1107
+ let accumulator = initialValue;
1108
+ let index = 0;
1109
+ for (const item of this) {
1110
+ const [key, value] = item;
1111
+ accumulator = callbackfn(accumulator, value, key, index++, this);
1112
+ }
1113
+ return accumulator;
1114
+ }
1115
+ /**
1116
+ * Time Complexity: O(n)
1117
+ * Space Complexity: O(n)
1118
+ *
1119
+ * The print function logs the elements of an array to the console.
1120
+ */
1121
+ toVisual() {
1122
+ return [...this];
1123
+ }
1124
+ /**
1125
+ * Time Complexity: O(n)
1126
+ * Space Complexity: O(n)
1127
+ *
1128
+ * The print function logs the elements of an array to the console.
1129
+ */
1130
+ print() {
1131
+ console.log(this.toVisual());
1132
+ }
1133
+ };
1134
+
1135
+ // src/common/index.ts
1136
+ var Range = class {
1137
+ constructor(low, high, includeLow = true, includeHigh = true) {
1138
+ this.low = low;
1139
+ this.high = high;
1140
+ this.includeLow = includeLow;
1141
+ this.includeHigh = includeHigh;
1142
+ if (!(isComparable(low) && isComparable(high))) throw new RangeError("low or high is not comparable");
1143
+ if (low > high) throw new RangeError("low must be less than or equal to high");
1144
+ }
1145
+ // Determine whether a key is within the range
1146
+ isInRange(key, comparator) {
1147
+ const lowCheck = this.includeLow ? comparator(key, this.low) >= 0 : comparator(key, this.low) > 0;
1148
+ const highCheck = this.includeHigh ? comparator(key, this.high) <= 0 : comparator(key, this.high) < 0;
1149
+ return lowCheck && highCheck;
1150
+ }
1151
+ };
1152
+
1153
+ // src/data-structures/binary-tree/binary-tree.ts
1154
+ var BinaryTreeNode = class {
1155
+ key;
1156
+ value;
1157
+ parent = void 0;
1158
+ /**
1159
+ * The constructor function initializes an object with a key and an optional value in TypeScript.
1160
+ * @param {K} key - The `key` parameter in the constructor function is used to store the key value
1161
+ * for the key-value pair.
1162
+ * @param {V} [value] - The `value` parameter in the constructor is optional, meaning it does not
1163
+ * have to be provided when creating an instance of the class. If a `value` is not provided, it will
1164
+ * default to `undefined`.
1165
+ */
1166
+ constructor(key, value) {
1167
+ this.key = key;
1168
+ this.value = value;
1169
+ }
1170
+ _left = void 0;
1171
+ get left() {
1172
+ return this._left;
1173
+ }
1174
+ set left(v) {
1175
+ if (v) {
1176
+ v.parent = this;
1177
+ }
1178
+ this._left = v;
1179
+ }
1180
+ _right = void 0;
1181
+ get right() {
1182
+ return this._right;
1183
+ }
1184
+ set right(v) {
1185
+ if (v) {
1186
+ v.parent = this;
1187
+ }
1188
+ this._right = v;
1189
+ }
1190
+ _height = 0;
1191
+ get height() {
1192
+ return this._height;
1193
+ }
1194
+ set height(value) {
1195
+ this._height = value;
1196
+ }
1197
+ _color = "BLACK";
1198
+ get color() {
1199
+ return this._color;
1200
+ }
1201
+ set color(value) {
1202
+ this._color = value;
1203
+ }
1204
+ _count = 1;
1205
+ get count() {
1206
+ return this._count;
1207
+ }
1208
+ set count(value) {
1209
+ this._count = value;
1210
+ }
1211
+ get familyPosition() {
1212
+ if (!this.parent) {
1213
+ return this.left || this.right ? "ROOT" : "ISOLATED";
1214
+ }
1215
+ if (this.parent.left === this) {
1216
+ return this.left || this.right ? "ROOT_LEFT" : "LEFT";
1217
+ } else if (this.parent.right === this) {
1218
+ return this.left || this.right ? "ROOT_RIGHT" : "RIGHT";
1219
+ }
1220
+ return "MAL_NODE";
1221
+ }
1222
+ };
1223
+ var BinaryTree = class _BinaryTree extends IterableEntryBase {
1224
+ iterationType = "ITERATIVE";
1225
+ /**
1226
+ * This TypeScript constructor function initializes a binary tree with optional options and adds
1227
+ * elements based on the provided input.
1228
+ * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the constructor is an
1229
+ * iterable that can contain either objects of type `K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined ` or `R`. It
1230
+ * is used to initialize the binary tree with keys, nodes, entries, or raw data.
1231
+ * @param [options] - The `options` parameter in the constructor is an optional object that can
1232
+ * contain the following properties:
1233
+ */
1234
+ constructor(keysNodesEntriesOrRaws = [], options) {
1235
+ super();
1236
+ if (options) {
1237
+ const { iterationType, toEntryFn, isMapMode, isDuplicate } = options;
1238
+ if (iterationType) this.iterationType = iterationType;
1239
+ if (isMapMode !== void 0) this._isMapMode = isMapMode;
1240
+ if (isDuplicate !== void 0) this._isDuplicate = isDuplicate;
1241
+ if (typeof toEntryFn === "function") this._toEntryFn = toEntryFn;
1242
+ else if (toEntryFn) throw TypeError("toEntryFn must be a function type");
1243
+ }
1244
+ if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws);
1245
+ }
1246
+ _isMapMode = true;
1247
+ get isMapMode() {
1248
+ return this._isMapMode;
1249
+ }
1250
+ _isDuplicate = false;
1251
+ get isDuplicate() {
1252
+ return this._isDuplicate;
1253
+ }
1254
+ _store = /* @__PURE__ */ new Map();
1255
+ get store() {
1256
+ return this._store;
1257
+ }
1258
+ _root;
1259
+ get root() {
1260
+ return this._root;
1261
+ }
1262
+ _size = 0;
1263
+ get size() {
1264
+ return this._size;
1265
+ }
1266
+ _NIL = new BinaryTreeNode(NaN);
1267
+ get NIL() {
1268
+ return this._NIL;
1269
+ }
1270
+ _toEntryFn;
1271
+ get toEntryFn() {
1272
+ return this._toEntryFn;
1273
+ }
1274
+ /**
1275
+ * Time Complexity: O(1)
1276
+ * Space Complexity: O(1)
1277
+ *
1278
+ * The function creates a new binary tree node with a specified key and optional value.
1279
+ * @param {K} key - The `key` parameter is the key of the node being created in the binary tree.
1280
+ * @param {V} [value] - The `value` parameter in the `createNode` function is optional, meaning it is
1281
+ * not required to be provided when calling the function. If a `value` is provided, it should be of
1282
+ * type `V`, which is the type of the value associated with the node.
1283
+ * @returns A new BinaryTreeNode instance with the provided key and value is being returned, casted
1284
+ * as BinaryTreeNode<K, V>.
1285
+ */
1286
+ createNode(key, value) {
1287
+ return new BinaryTreeNode(key, this._isMapMode ? void 0 : value);
1288
+ }
1289
+ /**
1290
+ * Time Complexity: O(1)
1291
+ * Space Complexity: O(1)
1292
+ *
1293
+ * The function creates a binary tree with the specified options.
1294
+ * @param [options] - The `options` parameter in the `createTree` function is an optional parameter
1295
+ * that allows you to provide partial configuration options for creating a binary tree. It is of type
1296
+ * `Partial<BinaryTreeOptions<K, V, R>>`, which means you can pass in an object containing a subset
1297
+ * of properties
1298
+ * @returns A new instance of a binary tree with the specified options is being returned.
1299
+ */
1300
+ createTree(options) {
1301
+ return new _BinaryTree([], {
1302
+ iterationType: this.iterationType,
1303
+ isMapMode: this._isMapMode,
1304
+ toEntryFn: this._toEntryFn,
1305
+ ...options
1306
+ });
1307
+ }
1308
+ /**
1309
+ * Time Complexity: O(n)
1310
+ * Space Complexity: O(log n)
1311
+ *
1312
+ * The function `ensureNode` in TypeScript checks if a given input is a node, entry, key, or raw
1313
+ * value and returns the corresponding node or null.
1314
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The `keyNodeOrEntry`
1315
+ * parameter in the `ensureNode` function can be of type `K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined ` or `R`. It
1316
+ * is used to determine whether the input is a key, node, entry, or raw data. The
1317
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `ensureNode` function
1318
+ * is used to specify the type of iteration to be performed. It has a default value of
1319
+ * `this.iterationType` if not explicitly provided.
1320
+ * @returns The `ensureNode` function returns either a node, `null`, or `undefined` based on the
1321
+ * conditions specified in the code snippet.
1322
+ */
1323
+ ensureNode(keyNodeOrEntry, iterationType = this.iterationType) {
1324
+ if (keyNodeOrEntry === null) return null;
1325
+ if (keyNodeOrEntry === void 0) return;
1326
+ if (keyNodeOrEntry === this._NIL) return;
1327
+ if (this.isNode(keyNodeOrEntry)) return keyNodeOrEntry;
1328
+ if (this.isEntry(keyNodeOrEntry)) {
1329
+ const key = keyNodeOrEntry[0];
1330
+ if (key === null) return null;
1331
+ if (key === void 0) return;
1332
+ return this.getNode(key, this._root, iterationType);
1333
+ }
1334
+ return this.getNode(keyNodeOrEntry, this._root, iterationType);
1335
+ }
1336
+ /**
1337
+ * Time Complexity: O(1)
1338
+ * Space Complexity: O(1)
1339
+ *
1340
+ * The function isNode checks if the input is an instance of BinaryTreeNode.
1341
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The parameter
1342
+ * `keyNodeOrEntry` can be either a key, a node, an entry, or raw data. The function is
1343
+ * checking if the input is an instance of a `BinaryTreeNode` and returning a boolean value
1344
+ * accordingly.
1345
+ * @returns The function `isNode` is checking if the input `keyNodeOrEntry` is an instance of
1346
+ * `BinaryTreeNode`. If it is, the function returns `true`, indicating that the input is a node. If
1347
+ * it is not an instance of `BinaryTreeNode`, the function returns `false`, indicating that the input
1348
+ * is not a node.
1349
+ */
1350
+ isNode(keyNodeOrEntry) {
1351
+ return keyNodeOrEntry instanceof BinaryTreeNode;
1352
+ }
1353
+ /**
1354
+ * Time Complexity: O(1)
1355
+ * Space Complexity: O(1)
1356
+ *
1357
+ * The function `isRaw` checks if the input parameter is of type `R` by verifying if it is an object.
1358
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined | R} keyNodeEntryOrRaw - K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined
1359
+ * @returns The function `isRaw` is checking if the `keyNodeEntryOrRaw` parameter is of type `R` by
1360
+ * checking if it is an object. If the parameter is an object, the function will return `true`,
1361
+ * indicating that it is of type `R`.
1362
+ */
1363
+ isRaw(keyNodeEntryOrRaw) {
1364
+ return this._toEntryFn !== void 0 && typeof keyNodeEntryOrRaw === "object";
1365
+ }
1366
+ /**
1367
+ * Time Complexity: O(1)
1368
+ * Space Complexity: O(1)
1369
+ *
1370
+ * The function `isRealNode` checks if a given input is a valid node in a binary tree.
1371
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The `keyNodeOrEntry`
1372
+ * parameter in the `isRealNode` function can be of type `K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined ` or `R`.
1373
+ * The function checks if the input parameter is a `BinaryTreeNode<K, V>` type by verifying if it is not equal
1374
+ * @returns The function `isRealNode` is checking if the input `keyNodeOrEntry` is a valid
1375
+ * node by comparing it to `this._NIL`, `null`, and `undefined`. If the input is not one of these
1376
+ * values, it then calls the `isNode` method to further determine if the input is a node. The
1377
+ * function will return a boolean value indicating whether the
1378
+ */
1379
+ isRealNode(keyNodeOrEntry) {
1380
+ if (keyNodeOrEntry === this._NIL || keyNodeOrEntry === null || keyNodeOrEntry === void 0) return false;
1381
+ return this.isNode(keyNodeOrEntry);
1382
+ }
1383
+ /**
1384
+ * Time Complexity: O(1)
1385
+ * Space Complexity: O(1)
1386
+ *
1387
+ * The function checks if a given input is a valid node or null.
1388
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The parameter
1389
+ * `keyNodeOrEntry` in the `isRealNodeOrNull` function can be of type `BTNRep<K,
1390
+ * V, BinaryTreeNode<K, V>>` or `R`. It is a union type that can either be a key, a node, an entry, or
1391
+ * @returns The function `isRealNodeOrNull` is returning a boolean value. It checks if the input
1392
+ * `keyNodeOrEntry` is either `null` or a real node, and returns `true` if it is a node or
1393
+ * `null`, and `false` otherwise.
1394
+ */
1395
+ isRealNodeOrNull(keyNodeOrEntry) {
1396
+ return keyNodeOrEntry === null || this.isRealNode(keyNodeOrEntry);
1397
+ }
1398
+ /**
1399
+ * Time Complexity: O(1)
1400
+ * Space Complexity: O(1)
1401
+ *
1402
+ * The function isNIL checks if a given key, node, entry, or raw value is equal to the _NIL value.
1403
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - BTNRep<K, V,
1404
+ * BinaryTreeNode<K, V>>
1405
+ * @returns The function is checking if the `keyNodeOrEntry` parameter is equal to the `_NIL`
1406
+ * property of the current object and returning a boolean value based on that comparison.
1407
+ */
1408
+ isNIL(keyNodeOrEntry) {
1409
+ return keyNodeOrEntry === this._NIL;
1410
+ }
1411
+ /**
1412
+ * Time Complexity: O(1)
1413
+ * Space Complexity: O(1)
1414
+ *
1415
+ * The function `isRange` checks if the input parameter is an instance of the `Range` class.
1416
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined | NodePredicate<BinaryTreeNode<K, V>> | Range<K>} keyNodeEntryOrPredicate
1417
+ * - The `keyNodeEntryOrPredicate` parameter in the `isRange` function can be
1418
+ * of type `K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined `, `NodePredicate<BinaryTreeNode<K, V>>`, or
1419
+ * `Range<K>`. The function checks if the `keyNodeEntry
1420
+ * @returns The `isRange` function is checking if the `keyNodeEntryOrPredicate` parameter is an
1421
+ * instance of the `Range` class. If it is an instance of `Range`, the function will return `true`,
1422
+ * indicating that the parameter is a `Range<K>`. If it is not an instance of `Range`, the function
1423
+ * will return `false`.
1424
+ */
1425
+ isRange(keyNodeEntryOrPredicate) {
1426
+ return keyNodeEntryOrPredicate instanceof Range;
1427
+ }
1428
+ /**
1429
+ * Time Complexity: O(1)
1430
+ * Space Complexity: O(1)
1431
+ *
1432
+ * The function determines whether a given key, node, entry, or raw data is a leaf node in a binary
1433
+ * tree.
1434
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The parameter
1435
+ * `keyNodeOrEntry` can be of type `K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined ` or `R`. It represents a
1436
+ * key, node, entry, or raw data in a binary tree structure. The function `isLeaf` checks whether the
1437
+ * provided
1438
+ * @returns The function `isLeaf` returns a boolean value indicating whether the input
1439
+ * `keyNodeOrEntry` is a leaf node in a binary tree.
1440
+ */
1441
+ isLeaf(keyNodeOrEntry) {
1442
+ keyNodeOrEntry = this.ensureNode(keyNodeOrEntry);
1443
+ if (keyNodeOrEntry === void 0) return false;
1444
+ if (keyNodeOrEntry === null) return true;
1445
+ return !this.isRealNode(keyNodeOrEntry.left) && !this.isRealNode(keyNodeOrEntry.right);
1446
+ }
1447
+ /**
1448
+ * Time Complexity: O(1)
1449
+ * Space Complexity: O(1)
1450
+ *
1451
+ * The function `isEntry` checks if the input is a BTNEntry object by verifying if it is an array
1452
+ * with a length of 2.
1453
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The `keyNodeOrEntry`
1454
+ * parameter in the `isEntry` function can be of type `K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined ` or type `R`.
1455
+ * The function checks if the provided `keyNodeOrEntry` is of type `BTN
1456
+ * @returns The `isEntry` function is checking if the `keyNodeOrEntry` parameter is an array
1457
+ * with a length of 2. If it is, then it returns `true`, indicating that the parameter is of type
1458
+ * `BTNEntry<K, V>`. If the condition is not met, it returns `false`.
1459
+ */
1460
+ isEntry(keyNodeOrEntry) {
1461
+ return Array.isArray(keyNodeOrEntry) && keyNodeOrEntry.length === 2;
1462
+ }
1463
+ /**
1464
+ * Time Complexity O(1)
1465
+ * Space Complexity O(1)
1466
+ *
1467
+ * The function `isValidKey` checks if a given key is comparable.
1468
+ * @param {any} key - The `key` parameter is of type `any`, which means it can be any data type in
1469
+ * TypeScript.
1470
+ * @returns The function `isValidKey` is checking if the `key` parameter is `null` or if it is comparable.
1471
+ * If the `key` is `null`, the function returns `true`. Otherwise, it returns the result of the
1472
+ * `isComparable` function, which is not provided in the code snippet.
1473
+ */
1474
+ isValidKey(key) {
1475
+ if (key === null) return true;
1476
+ return isComparable(key);
1477
+ }
1478
+ /**
1479
+ * Time Complexity O(n)
1480
+ * Space Complexity O(1)
1481
+ *
1482
+ * The `add` function in TypeScript adds a new node to a binary tree while handling duplicate keys
1483
+ * and finding the correct insertion position.
1484
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The `add` method you provided
1485
+ * seems to be for adding a new node to a binary tree structure. The `keyNodeOrEntry`
1486
+ * parameter in the method can accept different types of values:
1487
+ * @param {V} [value] - The `value` parameter in the `add` method represents the value associated
1488
+ * with the key that you want to add to the binary tree. When adding a key-value pair to the binary
1489
+ * tree, you provide the key and its corresponding value. The `add` method then creates a new node
1490
+ * with this
1491
+ * @returns The `add` method returns a boolean value. It returns `true` if the insertion of the new
1492
+ * node was successful, and `false` if the insertion position could not be found or if a duplicate
1493
+ * key was found and the node was replaced instead of inserted.
1494
+ */
1495
+ add(keyNodeOrEntry, value) {
1496
+ const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
1497
+ if (newNode === void 0) return false;
1498
+ if (!this._root) {
1499
+ this._setRoot(newNode);
1500
+ if (this._isMapMode) this._setValue(newNode?.key, newValue);
1501
+ this._size = 1;
1502
+ return true;
1503
+ }
1504
+ const queue = new Queue([this._root]);
1505
+ let potentialParent;
1506
+ while (queue.length > 0) {
1507
+ const cur = queue.shift();
1508
+ if (!cur) continue;
1509
+ if (!this._isDuplicate) {
1510
+ if (newNode !== null && cur.key === newNode.key) {
1511
+ this._replaceNode(cur, newNode);
1512
+ if (this._isMapMode) this._setValue(cur.key, newValue);
1513
+ return true;
1514
+ }
1515
+ }
1516
+ if (potentialParent === void 0 && (cur.left === void 0 || cur.right === void 0)) {
1517
+ potentialParent = cur;
1518
+ }
1519
+ if (cur.left !== null) {
1520
+ if (cur.left) queue.push(cur.left);
1521
+ }
1522
+ if (cur.right !== null) {
1523
+ if (cur.right) queue.push(cur.right);
1524
+ }
1525
+ }
1526
+ if (potentialParent) {
1527
+ if (potentialParent.left === void 0) {
1528
+ potentialParent.left = newNode;
1529
+ } else if (potentialParent.right === void 0) {
1530
+ potentialParent.right = newNode;
1531
+ }
1532
+ if (this._isMapMode) this._setValue(newNode?.key, newValue);
1533
+ this._size++;
1534
+ return true;
1535
+ }
1536
+ return false;
1537
+ }
1538
+ /**
1539
+ * Time Complexity: O(k * n)
1540
+ * Space Complexity: O(k)
1541
+ *
1542
+ * The `addMany` function takes in multiple keys or nodes or entries or raw values along with
1543
+ * optional values, and adds them to a data structure while returning an array indicating whether
1544
+ * each insertion was successful.
1545
+ * @param keysNodesEntriesOrRaws - `keysNodesEntriesOrRaws` is an iterable that can contain a
1546
+ * mix of keys, nodes, entries, or raw values. Each element in this iterable can be of type
1547
+ * `K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined ` or `R`.
1548
+ * @param [values] - The `values` parameter in the `addMany` function is an optional parameter that
1549
+ * accepts an iterable of values. These values correspond to the keys or nodes being added in the
1550
+ * `keysNodesEntriesOrRaws` parameter. If provided, the function will iterate over the values and
1551
+ * assign them
1552
+ * @returns The `addMany` method returns an array of boolean values indicating whether each key,
1553
+ * node, entry, or raw value was successfully added to the data structure. Each boolean value
1554
+ * corresponds to the success of adding the corresponding key or value in the input iterable.
1555
+ */
1556
+ addMany(keysNodesEntriesOrRaws, values) {
1557
+ const inserted = [];
1558
+ let valuesIterator;
1559
+ if (values) {
1560
+ valuesIterator = values[Symbol.iterator]();
1561
+ }
1562
+ for (let keyNodeEntryOrRaw of keysNodesEntriesOrRaws) {
1563
+ let value = void 0;
1564
+ if (valuesIterator) {
1565
+ const valueResult = valuesIterator.next();
1566
+ if (!valueResult.done) {
1567
+ value = valueResult.value;
1568
+ }
1569
+ }
1570
+ if (this.isRaw(keyNodeEntryOrRaw)) keyNodeEntryOrRaw = this._toEntryFn(keyNodeEntryOrRaw);
1571
+ inserted.push(this.add(keyNodeEntryOrRaw, value));
1572
+ }
1573
+ return inserted;
1574
+ }
1575
+ /**
1576
+ * Time Complexity: O(k * n)
1577
+ * Space Complexity: O(1)
1578
+ *
1579
+ * The `merge` function in TypeScript merges another binary tree into the current tree by adding all
1580
+ * elements from the other tree.
1581
+ * @param anotherTree - BinaryTree<K, V, R, MK, MV, MR>
1582
+ */
1583
+ merge(anotherTree) {
1584
+ this.addMany(anotherTree, []);
1585
+ }
1586
+ /**
1587
+ * Time Complexity: O(k * n)
1588
+ * Space Complexity: O(1)
1589
+ *
1590
+ * The `refill` function clears the existing data structure and then adds new key-value pairs based
1591
+ * on the provided input.
1592
+ * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the `refill`
1593
+ * method can accept an iterable containing a mix of `K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined ` objects or `R`
1594
+ * objects.
1595
+ * @param [values] - The `values` parameter in the `refill` method is an optional parameter that
1596
+ * accepts an iterable of values of type `V` or `undefined`.
1597
+ */
1598
+ refill(keysNodesEntriesOrRaws, values) {
1599
+ this.clear();
1600
+ this.addMany(keysNodesEntriesOrRaws, values);
1601
+ }
1602
+ /**
1603
+ * Time Complexity: O(n)
1604
+ * Space Complexity: O(1)
1605
+ *
1606
+ * The function `delete` in TypeScript implements the deletion of a node in a binary tree and returns
1607
+ * the deleted node along with information for tree balancing.
1608
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry
1609
+ * - The `delete` method you provided is used to delete a node from a binary tree based on the key,
1610
+ * node, entry or raw data. The method returns an array of
1611
+ * `BinaryTreeDeleteResult` objects containing information about the deleted node and whether
1612
+ * balancing is needed.
1613
+ * @returns The `delete` method returns an array of `BinaryTreeDeleteResult` objects. Each object in
1614
+ * the array contains information about the node that was deleted (`deleted`) and the node that may
1615
+ * need to be balanced (`needBalanced`).
1616
+ */
1617
+ delete(keyNodeOrEntry) {
1618
+ const deletedResult = [];
1619
+ if (!this._root) return deletedResult;
1620
+ const curr = this.getNode(keyNodeOrEntry);
1621
+ if (!curr) return deletedResult;
1622
+ const parent = curr?.parent;
1623
+ let needBalanced;
1624
+ let orgCurrent = curr;
1625
+ if (!curr.left && !curr.right && !parent) {
1626
+ this._setRoot(void 0);
1627
+ } else if (curr.left) {
1628
+ const leftSubTreeRightMost = this.getRightMost((node) => node, curr.left);
1629
+ if (leftSubTreeRightMost) {
1630
+ const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent;
1631
+ orgCurrent = this._swapProperties(curr, leftSubTreeRightMost);
1632
+ if (parentOfLeftSubTreeMax) {
1633
+ if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost)
1634
+ parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left;
1635
+ else parentOfLeftSubTreeMax.left = leftSubTreeRightMost.left;
1636
+ needBalanced = parentOfLeftSubTreeMax;
1637
+ }
1638
+ }
1639
+ } else if (parent) {
1640
+ const { familyPosition: fp } = curr;
1641
+ if (fp === "LEFT" || fp === "ROOT_LEFT") {
1642
+ parent.left = curr.right;
1643
+ } else if (fp === "RIGHT" || fp === "ROOT_RIGHT") {
1644
+ parent.right = curr.right;
1645
+ }
1646
+ needBalanced = parent;
1647
+ } else {
1648
+ this._setRoot(curr.right);
1649
+ curr.right = void 0;
1650
+ }
1651
+ this._size = this._size - 1;
1652
+ deletedResult.push({ deleted: orgCurrent, needBalanced });
1653
+ if (this._isMapMode && orgCurrent) this._store.delete(orgCurrent.key);
1654
+ return deletedResult;
1655
+ }
1656
+ /**
1657
+ * Time Complexity: O(n)
1658
+ * Space Complexity: O(k + log n)
1659
+ *
1660
+ * The `search` function in TypeScript performs a depth-first or breadth-first search on a tree
1661
+ * structure based on a given predicate or key, with options to return multiple results or just one.
1662
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined | NodePredicate<BinaryTreeNode<K, V>>} keyNodeEntryOrPredicate - The
1663
+ * `keyNodeEntryOrPredicate` parameter in the `search` function can accept three types of values:
1664
+ * @param [onlyOne=false] - The `onlyOne` parameter in the `search` function is a boolean flag that
1665
+ * determines whether the search should stop after finding the first matching node. If `onlyOne` is
1666
+ * set to `true`, the search will return as soon as a matching node is found. If `onlyOne` is
1667
+ * @param {C} callback - The `callback` parameter in the `search` function is a callback function
1668
+ * that will be called on each node that matches the search criteria. It is of type `C`, which
1669
+ * extends `NodeCallback<BinaryTreeNode<K, V> | null>`. The default value for `callback` is `this._DEFAULT_NODE_CALLBACK` if
1670
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter in the `search` function is
1671
+ * used to specify the node from which the search operation should begin. It represents the starting
1672
+ * point in the binary tree where the search will be performed. If no specific `startNode` is
1673
+ * provided, the search operation will start from the root
1674
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `search` function
1675
+ * specifies the type of iteration to be used when searching for nodes in a binary tree. It can have
1676
+ * two possible values:
1677
+ * @returns The `search` function returns an array of values that match the provided criteria based
1678
+ * on the search algorithm implemented within the function.
1679
+ */
1680
+ search(keyNodeEntryOrPredicate, onlyOne = false, callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType) {
1681
+ if (keyNodeEntryOrPredicate === void 0) return [];
1682
+ if (keyNodeEntryOrPredicate === null) return [];
1683
+ startNode = this.ensureNode(startNode);
1684
+ if (!startNode) return [];
1685
+ const predicate = this._ensurePredicate(keyNodeEntryOrPredicate);
1686
+ const ans = [];
1687
+ if (iterationType === "RECURSIVE") {
1688
+ const dfs = (cur) => {
1689
+ if (predicate(cur)) {
1690
+ ans.push(callback(cur));
1691
+ if (onlyOne) return;
1692
+ }
1693
+ if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return;
1694
+ if (this.isRealNode(cur.left)) dfs(cur.left);
1695
+ if (this.isRealNode(cur.right)) dfs(cur.right);
1696
+ };
1697
+ dfs(startNode);
1698
+ } else {
1699
+ const stack = [startNode];
1700
+ while (stack.length > 0) {
1701
+ const cur = stack.pop();
1702
+ if (this.isRealNode(cur)) {
1703
+ if (predicate(cur)) {
1704
+ ans.push(callback(cur));
1705
+ if (onlyOne) return ans;
1706
+ }
1707
+ if (this.isRealNode(cur.left)) stack.push(cur.left);
1708
+ if (this.isRealNode(cur.right)) stack.push(cur.right);
1709
+ }
1710
+ }
1711
+ }
1712
+ return ans;
1713
+ }
1714
+ /**
1715
+ * Time Complexity: O(n)
1716
+ * Space Complexity: O(k + log n)
1717
+ *
1718
+ * The function `getNodes` retrieves nodes from a binary tree based on a key, node, entry, raw data,
1719
+ * or predicate, with options for recursive or iterative traversal.
1720
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined | NodePredicate<BinaryTreeNode<K, V>>} keyNodeEntryOrPredicate
1721
+ * - The `getNodes` function you provided takes several parameters:
1722
+ * @param [onlyOne=false] - The `onlyOne` parameter in the `getNodes` function is a boolean flag that
1723
+ * determines whether to return only the first node that matches the criteria specified by the
1724
+ * `keyNodeEntryOrPredicate` parameter. If `onlyOne` is set to `true`, the function will
1725
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter in the
1726
+ * `getNodes` function is used to specify the starting point for traversing the binary tree. It
1727
+ * represents the root node of the binary tree or the node from which the traversal should begin. If
1728
+ * not provided, the default value is set to `this._root
1729
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `getNodes` function
1730
+ * determines the type of iteration to be performed when traversing the nodes of a binary tree. It
1731
+ * can have two possible values:
1732
+ * @returns The `getNodes` function returns an array of nodes that satisfy the provided condition
1733
+ * based on the input parameters and the iteration type specified.
1734
+ */
1735
+ getNodes(keyNodeEntryOrPredicate, onlyOne = false, startNode = this._root, iterationType = this.iterationType) {
1736
+ return this.search(keyNodeEntryOrPredicate, onlyOne, (node) => node, startNode, iterationType);
1737
+ }
1738
+ /**
1739
+ * Time Complexity: O(n)
1740
+ * Space Complexity: O(log n)
1741
+ *
1742
+ * The `getNode` function retrieves a node based on the provided key, node, entry, raw data, or
1743
+ * predicate.
1744
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined | NodePredicate<BinaryTreeNode<K, V>>} keyNodeEntryOrPredicate
1745
+ * - The `keyNodeEntryOrPredicate` parameter in the `getNode` function can accept a key,
1746
+ * node, entry, raw data, or a predicate function.
1747
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter in the
1748
+ * `getNode` function is used to specify the starting point for searching for a node in a binary
1749
+ * tree. If no specific starting point is provided, the default value is set to `this._root`, which
1750
+ * is typically the root node of the binary tree.
1751
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `getNode` method is
1752
+ * used to specify the type of iteration to be performed when searching for a node. It has a default
1753
+ * value of `this.iterationType`, which means it will use the iteration type defined in the current
1754
+ * context if no specific value is provided
1755
+ * @returns The `getNode` function is returning the first node that matches the specified criteria,
1756
+ * or `null` if no matching node is found.
1757
+ */
1758
+ getNode(keyNodeEntryOrPredicate, startNode = this._root, iterationType = this.iterationType) {
1759
+ return this.search(keyNodeEntryOrPredicate, true, (node) => node, startNode, iterationType)[0];
1760
+ }
1761
+ /**
1762
+ * Time Complexity: O(n)
1763
+ * Space Complexity: O(log n)
1764
+ *
1765
+ * This function overrides the `get` method to retrieve the value associated with a specified key,
1766
+ * node, entry, raw data, or predicate in a data structure.
1767
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined | NodePredicate<BinaryTreeNode<K, V>>} keyNodeEntryOrPredicate
1768
+ * - The `keyNodeEntryOrPredicate` parameter in the `get` method can accept one of the
1769
+ * following types:
1770
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter in the `get`
1771
+ * method is used to specify the starting point for searching for a key or node in the binary tree.
1772
+ * If no specific starting point is provided, the default starting point is the root of the binary
1773
+ * tree (`this._root`).
1774
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `get` method is used
1775
+ * to specify the type of iteration to be performed when searching for a key in the binary tree. It
1776
+ * is an optional parameter with a default value of `this.iterationType`, which means it will use the
1777
+ * iteration type defined in the
1778
+ * @returns The `get` method is returning the value associated with the specified key, node, entry,
1779
+ * raw data, or predicate in the binary tree map. If the specified key or node is found in the tree,
1780
+ * the method returns the corresponding value. If the key or node is not found, it returns
1781
+ * `undefined`.
1782
+ */
1783
+ get(keyNodeEntryOrPredicate, startNode = this._root, iterationType = this.iterationType) {
1784
+ if (this._isMapMode) {
1785
+ const key = this._extractKey(keyNodeEntryOrPredicate);
1786
+ if (key === null || key === void 0) return;
1787
+ return this._store.get(key);
1788
+ }
1789
+ return this.getNode(keyNodeEntryOrPredicate, startNode, iterationType)?.value;
1790
+ }
1791
+ /**
1792
+ * Time Complexity: O(n)
1793
+ * Space Complexity: O(log n)
1794
+ *
1795
+ * The `has` function in TypeScript checks if a specified key, node, entry, raw data, or predicate
1796
+ * exists in the data structure.
1797
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined | NodePredicate<BinaryTreeNode<K, V>>} keyNodeEntryOrPredicate
1798
+ * - The `keyNodeEntryOrPredicate` parameter in the `override has` method can accept one of
1799
+ * the following types:
1800
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter in the
1801
+ * `override` method is used to specify the starting point for the search operation within the data
1802
+ * structure. It defaults to `this._root` if not provided explicitly.
1803
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `override has` method
1804
+ * is used to specify the type of iteration to be performed. It has a default value of
1805
+ * `this.iterationType`, which means it will use the iteration type defined in the current context if
1806
+ * no value is provided when calling the method.
1807
+ * @returns The `override has` method is returning a boolean value. It checks if there are any nodes
1808
+ * that match the provided key, node, entry, raw data, or predicate in the tree structure. If there
1809
+ * are matching nodes, it returns `true`, indicating that the tree contains the specified element.
1810
+ * Otherwise, it returns `false`.
1811
+ */
1812
+ has(keyNodeEntryOrPredicate, startNode = this._root, iterationType = this.iterationType) {
1813
+ return this.search(keyNodeEntryOrPredicate, true, (node) => node, startNode, iterationType).length > 0;
1814
+ }
1815
+ /**
1816
+ * Time Complexity: O(1)
1817
+ * Space Complexity: O(1)
1818
+ *
1819
+ * The clear function removes nodes and values in map mode.
1820
+ */
1821
+ clear() {
1822
+ this._clearNodes();
1823
+ if (this._isMapMode) this._clearValues();
1824
+ }
1825
+ /**
1826
+ * Time Complexity: O(1)
1827
+ * Space Complexity: O(1)
1828
+ *
1829
+ * The `isEmpty` function in TypeScript checks if a data structure has no elements and returns a
1830
+ * boolean value.
1831
+ * @returns The `isEmpty()` method is returning a boolean value, specifically `true` if the `_size`
1832
+ * property is equal to 0, indicating that the data structure is empty, and `false` otherwise.
1833
+ */
1834
+ isEmpty() {
1835
+ return this._size === 0;
1836
+ }
1837
+ /**
1838
+ * Time Complexity: O(n)
1839
+ * Space Complexity: O(log n)
1840
+ *
1841
+ * The function checks if a binary tree is perfectly balanced by comparing its minimum height with
1842
+ * its height.
1843
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter is the starting
1844
+ * point for checking if the binary tree is perfectly balanced. It represents the root node of the
1845
+ * binary tree or a specific node from which the balance check should begin.
1846
+ * @returns The method `isPerfectlyBalanced` is returning a boolean value, which indicates whether
1847
+ * the tree starting from the `startNode` node is perfectly balanced or not. The return value is
1848
+ * determined by comparing the minimum height of the tree with the height of the tree. If the minimum
1849
+ * height plus 1 is greater than or equal to the height of the tree, then it is considered perfectly
1850
+ * balanced and
1851
+ */
1852
+ isPerfectlyBalanced(startNode = this._root) {
1853
+ return this.getMinHeight(startNode) + 1 >= this.getHeight(startNode);
1854
+ }
1855
+ /**
1856
+ * Time Complexity: O(n)
1857
+ * Space Complexity: O(log n)
1858
+ *
1859
+ * The function `isBST` in TypeScript checks if a binary search tree is valid using either recursive
1860
+ * or iterative methods.
1861
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter in the `isBST`
1862
+ * function represents the starting point for checking whether a binary search tree (BST) is valid.
1863
+ * It can be a node in the BST or a reference to the root of the BST. If no specific node is
1864
+ * provided, the function will default to
1865
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `isBST` function
1866
+ * determines whether the function should use a recursive approach or an iterative approach to check
1867
+ * if the binary search tree (BST) is valid.
1868
+ * @returns The `isBST` method is returning a boolean value, which indicates whether the binary
1869
+ * search tree (BST) represented by the given root node is a valid BST or not. The method checks if
1870
+ * the tree satisfies the BST property, where for every node, all nodes in its left subtree have keys
1871
+ * less than the node's key, and all nodes in its right subtree have keys greater than the node's
1872
+ */
1873
+ isBST(startNode = this._root, iterationType = this.iterationType) {
1874
+ startNode = this.ensureNode(startNode);
1875
+ if (!startNode) return true;
1876
+ if (iterationType === "RECURSIVE") {
1877
+ const dfs = (cur, min, max) => {
1878
+ if (!this.isRealNode(cur)) return true;
1879
+ const numKey = Number(cur.key);
1880
+ if (numKey <= min || numKey >= max) return false;
1881
+ return dfs(cur.left, min, numKey) && dfs(cur.right, numKey, max);
1882
+ };
1883
+ const isStandardBST = dfs(startNode, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
1884
+ const isInverseBST = dfs(startNode, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER);
1885
+ return isStandardBST || isInverseBST;
1886
+ } else {
1887
+ const checkBST = (checkMax = false) => {
1888
+ const stack = [];
1889
+ let prev = checkMax ? Number.MAX_SAFE_INTEGER : Number.MIN_SAFE_INTEGER;
1890
+ let curr = startNode;
1891
+ while (this.isRealNode(curr) || stack.length > 0) {
1892
+ while (this.isRealNode(curr)) {
1893
+ stack.push(curr);
1894
+ curr = curr.left;
1895
+ }
1896
+ curr = stack.pop();
1897
+ const numKey = Number(curr.key);
1898
+ if (!this.isRealNode(curr) || !checkMax && prev >= numKey || checkMax && prev <= numKey) return false;
1899
+ prev = numKey;
1900
+ curr = curr.right;
1901
+ }
1902
+ return true;
1903
+ };
1904
+ const isStandardBST = checkBST(false), isInverseBST = checkBST(true);
1905
+ return isStandardBST || isInverseBST;
1906
+ }
1907
+ }
1908
+ /**
1909
+ * Time Complexity: O(n)
1910
+ * Space Complexity: O(log n)
1911
+ *
1912
+ * The `getDepth` function calculates the depth between two nodes in a binary tree.
1913
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } dist - The `dist` parameter in the `getDepth`
1914
+ * function represents the node or entry in a binary tree map, or a reference to a node in the tree.
1915
+ * It is the target node for which you want to calculate the depth from the `startNode` node.
1916
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter in the
1917
+ * `getDepth` function represents the starting point from which you want to calculate the depth of a
1918
+ * given node or entry in a binary tree. If no specific starting point is provided, the default value
1919
+ * for `startNode` is set to the root of the binary
1920
+ * @returns The `getDepth` method returns the depth of a given node `dist` relative to the
1921
+ * `startNode` node in a binary tree. If the `dist` node is not found in the path to the `startNode`
1922
+ * node, it returns the depth of the `dist` node from the root of the tree.
1923
+ */
1924
+ getDepth(dist, startNode = this._root) {
1925
+ let distEnsured = this.ensureNode(dist);
1926
+ const beginRootEnsured = this.ensureNode(startNode);
1927
+ let depth = 0;
1928
+ while (distEnsured?.parent) {
1929
+ if (distEnsured === beginRootEnsured) {
1930
+ return depth;
1931
+ }
1932
+ depth++;
1933
+ distEnsured = distEnsured.parent;
1934
+ }
1935
+ return depth;
1936
+ }
1937
+ /**
1938
+ * Time Complexity: O(n)
1939
+ * Space Complexity: O(log n)
1940
+ *
1941
+ * The `getHeight` function calculates the maximum height of a binary tree using either a recursive
1942
+ * or iterative approach in TypeScript.
1943
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter is the starting
1944
+ * point from which the height of the binary tree will be calculated. It can be a node in the binary
1945
+ * tree or a reference to the root of the tree. If not provided, it defaults to the root of the
1946
+ * binary tree data structure.
1947
+ * @param {IterationType} iterationType - The `iterationType` parameter is used to determine the type
1948
+ * of iteration to be performed while calculating the height of the binary tree. It can have two
1949
+ * possible values:
1950
+ * @returns The `getHeight` method returns the height of the binary tree starting from the specified
1951
+ * root node. The height is calculated based on the maximum depth of the tree, considering either a
1952
+ * recursive approach or an iterative approach depending on the `iterationType` parameter.
1953
+ */
1954
+ getHeight(startNode = this._root, iterationType = this.iterationType) {
1955
+ startNode = this.ensureNode(startNode);
1956
+ if (!this.isRealNode(startNode)) return -1;
1957
+ if (iterationType === "RECURSIVE") {
1958
+ const _getMaxHeight = (cur) => {
1959
+ if (!this.isRealNode(cur)) return -1;
1960
+ const leftHeight = _getMaxHeight(cur.left);
1961
+ const rightHeight = _getMaxHeight(cur.right);
1962
+ return Math.max(leftHeight, rightHeight) + 1;
1963
+ };
1964
+ return _getMaxHeight(startNode);
1965
+ } else {
1966
+ const stack = [{ node: startNode, depth: 0 }];
1967
+ let maxHeight = 0;
1968
+ while (stack.length > 0) {
1969
+ const { node, depth } = stack.pop();
1970
+ if (this.isRealNode(node.left)) stack.push({ node: node.left, depth: depth + 1 });
1971
+ if (this.isRealNode(node.right)) stack.push({ node: node.right, depth: depth + 1 });
1972
+ maxHeight = Math.max(maxHeight, depth);
1973
+ }
1974
+ return maxHeight;
1975
+ }
1976
+ }
1977
+ /**
1978
+ * Time Complexity: O(n)
1979
+ * Space Complexity: O(log n)
1980
+ *
1981
+ * The `getMinHeight` function calculates the minimum height of a binary tree using either a
1982
+ * recursive or iterative approach in TypeScript.
1983
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter in the
1984
+ * `getMinHeight` function represents the starting node from which the minimum height of the binary
1985
+ * tree will be calculated. It is either a node in the binary tree or a reference to the root of the
1986
+ * tree. If not provided, the default value is the root
1987
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `getMinHeight` method
1988
+ * specifies the type of iteration to use when calculating the minimum height of a binary tree. It
1989
+ * can have two possible values:
1990
+ * @returns The `getMinHeight` method returns the minimum height of the binary tree starting from the
1991
+ * specified root node. The height is calculated based on the shortest path from the root node to a
1992
+ * leaf node in the tree. The method uses either a recursive approach or an iterative approach (using
1993
+ * a stack) based on the `iterationType` parameter.
1994
+ */
1995
+ getMinHeight(startNode = this._root, iterationType = this.iterationType) {
1996
+ startNode = this.ensureNode(startNode);
1997
+ if (!startNode) return -1;
1998
+ if (iterationType === "RECURSIVE") {
1999
+ const _getMinHeight = (cur) => {
2000
+ if (!this.isRealNode(cur)) return 0;
2001
+ if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return 0;
2002
+ const leftMinHeight = _getMinHeight(cur.left);
2003
+ const rightMinHeight = _getMinHeight(cur.right);
2004
+ return Math.min(leftMinHeight, rightMinHeight) + 1;
2005
+ };
2006
+ return _getMinHeight(startNode);
2007
+ } else {
2008
+ const stack = [];
2009
+ let node = startNode, last = null;
2010
+ const depths = /* @__PURE__ */ new Map();
2011
+ while (stack.length > 0 || node) {
2012
+ if (this.isRealNode(node)) {
2013
+ stack.push(node);
2014
+ node = node.left;
2015
+ } else {
2016
+ node = stack[stack.length - 1];
2017
+ if (!this.isRealNode(node.right) || last === node.right) {
2018
+ node = stack.pop();
2019
+ if (this.isRealNode(node)) {
2020
+ const leftMinHeight = this.isRealNode(node.left) ? depths.get(node.left) : -1;
2021
+ const rightMinHeight = this.isRealNode(node.right) ? depths.get(node.right) : -1;
2022
+ depths.set(node, 1 + Math.min(leftMinHeight, rightMinHeight));
2023
+ last = node;
2024
+ node = null;
2025
+ }
2026
+ } else node = node.right;
2027
+ }
2028
+ }
2029
+ return depths.get(startNode);
2030
+ }
2031
+ }
2032
+ /**
2033
+ * Time Complexity: O(log n)
2034
+ * Space Complexity: O(log n)
2035
+ *
2036
+ * The function `getPathToRoot` in TypeScript retrieves the path from a given node to the root of a
2037
+ * tree structure, applying a specified callback function along the way.
2038
+ * @param {C} callback - The `callback` parameter is a function that is used to process each node in
2039
+ * the path to the root. It is expected to be a function that takes a node as an argument and returns
2040
+ * a value based on that node. The return type of the callback function is determined by the generic
2041
+ * type `C
2042
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } beginNode - The `beginNode` parameter in the
2043
+ * `getPathToRoot` function can be either a key, a node, an entry, or any other value of type `R`.
2044
+ * @param [isReverse=true] - The `isReverse` parameter in the `getPathToRoot` function determines
2045
+ * whether the resulting path from the given `beginNode` to the root should be in reverse order or
2046
+ * not. If `isReverse` is set to `true`, the path will be reversed before being returned. If `is
2047
+ * @returns The function `getPathToRoot` returns an array of the return values of the callback
2048
+ * function `callback` applied to each node in the path from the `beginNode` to the root node. The
2049
+ * array is either in reverse order or in the original order based on the value of the `isReverse`
2050
+ * parameter.
2051
+ */
2052
+ getPathToRoot(beginNode, callback = this._DEFAULT_NODE_CALLBACK, isReverse = false) {
2053
+ const result = [];
2054
+ let beginNodeEnsured = this.ensureNode(beginNode);
2055
+ if (!beginNodeEnsured) return result;
2056
+ while (beginNodeEnsured.parent) {
2057
+ result.push(callback(beginNodeEnsured));
2058
+ beginNodeEnsured = beginNodeEnsured.parent;
2059
+ }
2060
+ result.push(callback(beginNodeEnsured));
2061
+ return isReverse ? result.reverse() : result;
2062
+ }
2063
+ /**
2064
+ * Time Complexity: O(log n)
2065
+ * Space Complexity: O(log n)
2066
+ *
2067
+ * The function `getLeftMost` retrieves the leftmost node in a binary tree using either recursive or
2068
+ * tail-recursive iteration.
2069
+ * @param {C} callback - The `callback` parameter is a function that will be called with the leftmost
2070
+ * node of a binary tree or with `undefined` if the tree is empty. It is provided with a default
2071
+ * value of `_DEFAULT_NODE_CALLBACK` if not specified.
2072
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter in the
2073
+ * `getLeftMost` function represents the starting point for finding the leftmost node in a binary
2074
+ * tree. It can be either a key, a node, or an entry in the binary tree structure. If no specific
2075
+ * starting point is provided, the function will default
2076
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `getLeftMost` function
2077
+ * specifies the type of iteration to be used when traversing the binary tree nodes. It can have two
2078
+ * possible values:
2079
+ * @returns The `getLeftMost` function returns the result of the callback function `C` applied to the
2080
+ * leftmost node in the binary tree starting from the `startNode` node. If the `startNode` node is
2081
+ * `NIL`, it returns the result of the callback function applied to `undefined`. If the `startNode`
2082
+ * node is not a real node, it returns the result of the callback
2083
+ */
2084
+ getLeftMost(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType) {
2085
+ if (this.isNIL(startNode)) return callback(void 0);
2086
+ startNode = this.ensureNode(startNode);
2087
+ if (!this.isRealNode(startNode)) return callback(startNode);
2088
+ if (iterationType === "RECURSIVE") {
2089
+ const dfs = (cur) => {
2090
+ if (!this.isRealNode(cur.left)) return cur;
2091
+ return dfs(cur.left);
2092
+ };
2093
+ return callback(dfs(startNode));
2094
+ } else {
2095
+ const dfs = trampoline((cur) => {
2096
+ if (!this.isRealNode(cur.left)) return cur;
2097
+ return dfs.cont(cur.left);
2098
+ });
2099
+ return callback(dfs(startNode));
2100
+ }
2101
+ }
2102
+ /**
2103
+ * Time Complexity: O(log n)
2104
+ * Space Complexity: O(log n)
2105
+ *
2106
+ * The function `getRightMost` retrieves the rightmost node in a binary tree using either recursive
2107
+ * or iterative traversal methods.
2108
+ * @param {C} callback - The `callback` parameter is a function that will be called with the result
2109
+ * of finding the rightmost node in a binary tree. It is of type `NodeCallback<OptNodeOrNull<BinaryTreeNode<K, V>>>`,
2110
+ * which means it is a callback function that can accept either an optional binary tree node or null
2111
+ * as
2112
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter in the
2113
+ * `getRightMost` function represents the starting point for finding the rightmost node in a binary
2114
+ * tree. It can be either a key, a node, or an entry in the binary tree structure. If no specific
2115
+ * starting point is provided, the function will default
2116
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `getRightMost`
2117
+ * function specifies the type of iteration to be used when traversing the binary tree nodes. It can
2118
+ * have two possible values:
2119
+ * @returns The `getRightMost` function returns the result of the callback function `C`, which is
2120
+ * passed as a parameter to the function. The callback function is called with the rightmost node in
2121
+ * the binary tree structure, determined based on the specified iteration type ('RECURSIVE' or
2122
+ * other).
2123
+ */
2124
+ getRightMost(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType) {
2125
+ if (this.isNIL(startNode)) return callback(void 0);
2126
+ startNode = this.ensureNode(startNode);
2127
+ if (!startNode) return callback(startNode);
2128
+ if (iterationType === "RECURSIVE") {
2129
+ const dfs = (cur) => {
2130
+ if (!this.isRealNode(cur.right)) return cur;
2131
+ return dfs(cur.right);
2132
+ };
2133
+ return callback(dfs(startNode));
2134
+ } else {
2135
+ const dfs = trampoline((cur) => {
2136
+ if (!this.isRealNode(cur.right)) return cur;
2137
+ return dfs.cont(cur.right);
2138
+ });
2139
+ return callback(dfs(startNode));
2140
+ }
2141
+ }
2142
+ /**
2143
+ * Time Complexity: O(log n)
2144
+ * Space Complexity: O(log n)
2145
+ *
2146
+ * The function `getPredecessor` in TypeScript returns the predecessor node of a given node in a
2147
+ * binary tree.
2148
+ * @param {BinaryTreeNode<K, V>} node - The `getPredecessor` function you provided seems to be attempting to find the
2149
+ * predecessor of a given node in a binary tree. However, there seems to be a logical issue in the
2150
+ * while loop condition that might cause an infinite loop.
2151
+ * @returns The `getPredecessor` function returns the predecessor node of the input `BinaryTreeNode<K, V>` parameter.
2152
+ * If the left child of the input node exists, it traverses to the rightmost node of the left subtree
2153
+ * to find the predecessor. If the left child does not exist, it returns the input node itself.
2154
+ */
2155
+ getPredecessor(node) {
2156
+ if (this.isRealNode(node.left)) {
2157
+ let predecessor = node.left;
2158
+ while (!this.isRealNode(predecessor) || this.isRealNode(predecessor.right) && predecessor.right !== node) {
2159
+ if (this.isRealNode(predecessor)) {
2160
+ predecessor = predecessor.right;
2161
+ }
2162
+ }
2163
+ return predecessor;
2164
+ } else {
2165
+ return node;
2166
+ }
2167
+ }
2168
+ /**
2169
+ * Time Complexity: O(log n)
2170
+ * Space Complexity: O(log n)
2171
+ *
2172
+ * The function `getSuccessor` in TypeScript returns the next node in an in-order traversal of a
2173
+ * binary tree.
2174
+ * @param {K | BinaryTreeNode<K, V> | null} [x] - The `getSuccessor` function takes a parameter `x`, which can be of
2175
+ * type `K`, `BinaryTreeNode<K, V>`, or `null`.
2176
+ * @returns The `getSuccessor` function returns the successor node of the input node `x`. If `x` has
2177
+ * a right child, the function returns the leftmost node in the right subtree of `x`. If `x` does not
2178
+ * have a right child, the function traverses up the parent nodes until it finds a node that is not
2179
+ * the right child of its parent, and returns that node
2180
+ */
2181
+ getSuccessor(x) {
2182
+ x = this.ensureNode(x);
2183
+ if (!this.isRealNode(x)) return void 0;
2184
+ if (this.isRealNode(x.right)) {
2185
+ return this.getLeftMost((node) => node, x.right);
2186
+ }
2187
+ let y = x.parent;
2188
+ while (this.isRealNode(y) && x === y.right) {
2189
+ x = y;
2190
+ y = y.parent;
2191
+ }
2192
+ return y;
2193
+ }
2194
+ /**
2195
+ * Time complexity: O(n)
2196
+ * Space complexity: O(n)
2197
+ *
2198
+ * The function performs a depth-first search on a binary tree structure based on the specified
2199
+ * parameters.
2200
+ * @param {C} callback - The `callback` parameter is a function that will be called for each node
2201
+ * visited during the depth-first search. It should accept a `BinaryTreeNode` as an argument and
2202
+ * return an optional node or null. The default value for this parameter is `_DEFAULT_NODE_CALLBACK`.
2203
+ * @param {DFSOrderPattern} [pattern=IN] - The `pattern` parameter in the `dfs` function specifies
2204
+ * the order in which the nodes are visited during a depth-first search traversal. The possible
2205
+ * values for the `pattern` parameter are:
2206
+ * @param {boolean} [onlyOne=false] - The `onlyOne` parameter in the `dfs` function is a boolean flag
2207
+ * that determines whether the depth-first search should stop after finding the first matching node
2208
+ * or continue searching for all matching nodes. If `onlyOne` is set to `true`, the search will stop
2209
+ * after finding the first matching node
2210
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined}
2211
+ * startNode - The `startNode` parameter in the `dfs` function can be one of the following types:
2212
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `dfs` function
2213
+ * specifies the type of iteration to be performed during the Depth-First Search traversal. It is
2214
+ * used to determine the order in which nodes are visited during the traversal. The possible values
2215
+ * for `iterationType` are typically defined as an enum or a
2216
+ * @param [includeNull=false] - The `includeNull` parameter in the `dfs` function determines whether
2217
+ * null nodes should be included in the depth-first search traversal. If `includeNull` is set to
2218
+ * `true`, null nodes will be included in the traversal process. If it is set to `false`, null nodes
2219
+ * will be skipped
2220
+ * @returns The `dfs` method is returning an array of the return type of the callback function `C`.
2221
+ */
2222
+ dfs(callback = this._DEFAULT_NODE_CALLBACK, pattern = "IN", onlyOne = false, startNode = this._root, iterationType = this.iterationType, includeNull = false) {
2223
+ startNode = this.ensureNode(startNode);
2224
+ if (!startNode) return [];
2225
+ return this._dfs(callback, pattern, onlyOne, startNode, iterationType, includeNull);
2226
+ }
2227
+ /**
2228
+ * Time complexity: O(n)
2229
+ * Space complexity: O(n)
2230
+ *
2231
+ * The `bfs` function performs a breadth-first search traversal on a binary tree or binary search
2232
+ * tree, executing a specified callback function on each node visited.
2233
+ * @param {C} callback - The `callback` parameter in the `bfs` function is a function that will be
2234
+ * called on each node visited during the breadth-first search traversal. It is a generic type `C`
2235
+ * that extends the `NodeCallback` type, which takes a parameter of type `BinaryTreeNode<K, V>` or `null`.
2236
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter in the `bfs`
2237
+ * function represents the starting point for the breadth-first search traversal in a binary tree. It
2238
+ * can be specified as a key, node, or entry in the binary tree structure. If not provided, the
2239
+ * default value is the root node of the binary
2240
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `bfs` function
2241
+ * determines the type of iteration to be performed on the binary tree nodes. It can have two
2242
+ * possible values:
2243
+ * @param [includeNull=false] - The `includeNull` parameter in the `bfs` function determines whether
2244
+ * to include `null` values in the breadth-first search traversal of a binary tree. If `includeNull`
2245
+ * is set to `true`, the traversal will include `null` values for nodes that do not have children
2246
+ * (left
2247
+ * @returns The `bfs` function returns an array of values that are the result of applying the
2248
+ * provided callback function to each node in the binary tree in a breadth-first search manner.
2249
+ */
2250
+ bfs(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType, includeNull = false) {
2251
+ startNode = this.ensureNode(startNode);
2252
+ if (!startNode) return [];
2253
+ const ans = [];
2254
+ if (iterationType === "RECURSIVE") {
2255
+ const queue = new Queue([
2256
+ startNode
2257
+ ]);
2258
+ const dfs = (level) => {
2259
+ if (queue.length === 0) return;
2260
+ const current = queue.shift();
2261
+ ans.push(callback(current));
2262
+ if (includeNull) {
2263
+ if (current && this.isRealNodeOrNull(current.left)) queue.push(current.left);
2264
+ if (current && this.isRealNodeOrNull(current.right)) queue.push(current.right);
2265
+ } else {
2266
+ if (this.isRealNode(current.left)) queue.push(current.left);
2267
+ if (this.isRealNode(current.right)) queue.push(current.right);
2268
+ }
2269
+ dfs(level + 1);
2270
+ };
2271
+ dfs(0);
2272
+ } else {
2273
+ const queue = new Queue([startNode]);
2274
+ while (queue.length > 0) {
2275
+ const levelSize = queue.length;
2276
+ for (let i = 0; i < levelSize; i++) {
2277
+ const current = queue.shift();
2278
+ ans.push(callback(current));
2279
+ if (includeNull) {
2280
+ if (current && this.isRealNodeOrNull(current.left)) queue.push(current.left);
2281
+ if (current && this.isRealNodeOrNull(current.right)) queue.push(current.right);
2282
+ } else {
2283
+ if (this.isRealNode(current.left)) queue.push(current.left);
2284
+ if (this.isRealNode(current.right)) queue.push(current.right);
2285
+ }
2286
+ }
2287
+ }
2288
+ }
2289
+ return ans;
2290
+ }
2291
+ /**
2292
+ * Time complexity: O(n)
2293
+ * Space complexity: O(n)
2294
+ *
2295
+ * The `leaves` function in TypeScript returns an array of values from leaf nodes in a binary tree
2296
+ * structure based on a specified callback and iteration type.
2297
+ * @param {C} callback - The `callback` parameter is a function that will be called on each leaf node
2298
+ * in the binary tree. It is optional and defaults to a default callback function if not provided.
2299
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter in the `leaves`
2300
+ * method is used to specify the starting point for finding and processing the leaves of a binary
2301
+ * tree. It can be provided as either a key, a node, or an entry in the binary tree structure. If not
2302
+ * explicitly provided, the default value
2303
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `leaves` method
2304
+ * specifies the type of iteration to be performed when collecting the leaves of a binary tree. It
2305
+ * can have two possible values:
2306
+ * @returns The `leaves` method returns an array of values that are the result of applying the
2307
+ * provided callback function to each leaf node in the binary tree.
2308
+ */
2309
+ leaves(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType) {
2310
+ startNode = this.ensureNode(startNode);
2311
+ const leaves = [];
2312
+ if (!this.isRealNode(startNode)) return [];
2313
+ if (iterationType === "RECURSIVE") {
2314
+ const dfs = (cur) => {
2315
+ if (this.isLeaf(cur)) {
2316
+ leaves.push(callback(cur));
2317
+ }
2318
+ if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return;
2319
+ if (this.isRealNode(cur.left)) dfs(cur.left);
2320
+ if (this.isRealNode(cur.right)) dfs(cur.right);
2321
+ };
2322
+ dfs(startNode);
2323
+ } else {
2324
+ const queue = new Queue([startNode]);
2325
+ while (queue.length > 0) {
2326
+ const cur = queue.shift();
2327
+ if (this.isRealNode(cur)) {
2328
+ if (this.isLeaf(cur)) {
2329
+ leaves.push(callback(cur));
2330
+ }
2331
+ if (this.isRealNode(cur.left)) queue.push(cur.left);
2332
+ if (this.isRealNode(cur.right)) queue.push(cur.right);
2333
+ }
2334
+ }
2335
+ }
2336
+ return leaves;
2337
+ }
2338
+ /**
2339
+ * Time complexity: O(n)
2340
+ * Space complexity: O(n)
2341
+ *
2342
+ * The `listLevels` function in TypeScript generates a list of nodes at each level of a binary tree,
2343
+ * using either recursive or iterative traversal based on the specified iteration type.
2344
+ * @param {C} callback - The `callback` parameter is a function that will be applied to each node in
2345
+ * the binary tree during the traversal. It is used to process each node and determine what
2346
+ * information to include in the output for each level of the tree.
2347
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter in the
2348
+ * `listLevels` function represents the starting point for traversing the binary tree. It can be
2349
+ * either a key, a node, or an entry in the binary tree. If not provided, the default value is the
2350
+ * root of the binary tree.
2351
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `listLevels` function
2352
+ * determines the type of iteration to be performed on the binary tree nodes. It can have two
2353
+ * possible values:
2354
+ * @param [includeNull=false] - The `includeNull` parameter in the `listLevels` method determines
2355
+ * whether or not to include null nodes in the traversal of the binary tree. If `includeNull` is set
2356
+ * to `true`, the traversal will include null nodes in the levels of the tree. If set to `false`,
2357
+ * null
2358
+ * @returns The `listLevels` method returns an array of arrays, where each inner array represents a
2359
+ * level in a binary tree. Each inner array contains the return value of the provided callback
2360
+ * function applied to the nodes at that level.
2361
+ */
2362
+ listLevels(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType, includeNull = false) {
2363
+ startNode = this.ensureNode(startNode);
2364
+ const levelsNodes = [];
2365
+ if (!startNode) return levelsNodes;
2366
+ if (iterationType === "RECURSIVE") {
2367
+ const _recursive = (node, level) => {
2368
+ if (!levelsNodes[level]) levelsNodes[level] = [];
2369
+ levelsNodes[level].push(callback(node));
2370
+ if (includeNull) {
2371
+ if (node && this.isRealNodeOrNull(node.left)) _recursive(node.left, level + 1);
2372
+ if (node && this.isRealNodeOrNull(node.right)) _recursive(node.right, level + 1);
2373
+ } else {
2374
+ if (node && node.left) _recursive(node.left, level + 1);
2375
+ if (node && node.right) _recursive(node.right, level + 1);
2376
+ }
2377
+ };
2378
+ _recursive(startNode, 0);
2379
+ } else {
2380
+ const stack = [[startNode, 0]];
2381
+ while (stack.length > 0) {
2382
+ const head = stack.pop();
2383
+ const [node, level] = head;
2384
+ if (!levelsNodes[level]) levelsNodes[level] = [];
2385
+ levelsNodes[level].push(callback(node));
2386
+ if (includeNull) {
2387
+ if (node && this.isRealNodeOrNull(node.right)) stack.push([node.right, level + 1]);
2388
+ if (node && this.isRealNodeOrNull(node.left)) stack.push([node.left, level + 1]);
2389
+ } else {
2390
+ if (node && node.right) stack.push([node.right, level + 1]);
2391
+ if (node && node.left) stack.push([node.left, level + 1]);
2392
+ }
2393
+ }
2394
+ }
2395
+ return levelsNodes;
2396
+ }
2397
+ /**
2398
+ * Time complexity: O(n)
2399
+ * Space complexity: O(n)
2400
+ *
2401
+ * The `morris` function in TypeScript performs a Depth-First Search traversal on a binary tree using
2402
+ * Morris Traversal algorithm with different order patterns.
2403
+ * @param {C} callback - The `callback` parameter in the `morris` function is a function that will be
2404
+ * called on each node in the binary tree during the traversal. It is of type `C`, which extends the
2405
+ * `NodeCallback<BinaryTreeNode<K, V> | null>` type. The default value for `callback` is `this._DEFAULT
2406
+ * @param {DFSOrderPattern} [pattern=IN] - The `pattern` parameter in the `morris` function specifies
2407
+ * the type of Depth-First Search (DFS) order pattern to traverse the binary tree. The possible
2408
+ * values for the `pattern` parameter are:
2409
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter in the `morris`
2410
+ * function is the starting point for the Morris traversal algorithm. It represents the root node of
2411
+ * the binary tree or the node from which the traversal should begin. It can be provided as either a
2412
+ * key, a node, an entry, or a reference
2413
+ * @returns The `morris` function is returning an array of values that are the result of applying the
2414
+ * provided callback function to each node in the binary tree in the specified order pattern (IN,
2415
+ * PRE, or POST).
2416
+ */
2417
+ morris(callback = this._DEFAULT_NODE_CALLBACK, pattern = "IN", startNode = this._root) {
2418
+ startNode = this.ensureNode(startNode);
2419
+ if (!startNode) return [];
2420
+ const ans = [];
2421
+ let cur = startNode;
2422
+ const _reverseEdge = (node) => {
2423
+ let pre = null;
2424
+ let next = null;
2425
+ while (node) {
2426
+ next = node.right;
2427
+ node.right = pre;
2428
+ pre = node;
2429
+ node = next;
2430
+ }
2431
+ return pre;
2432
+ };
2433
+ const _printEdge = (node) => {
2434
+ const tail = _reverseEdge(node);
2435
+ let cur2 = tail;
2436
+ while (cur2) {
2437
+ ans.push(callback(cur2));
2438
+ cur2 = cur2.right;
2439
+ }
2440
+ _reverseEdge(tail);
2441
+ };
2442
+ switch (pattern) {
2443
+ case "IN":
2444
+ while (cur) {
2445
+ if (cur.left) {
2446
+ const predecessor = this.getPredecessor(cur);
2447
+ if (!predecessor.right) {
2448
+ predecessor.right = cur;
2449
+ cur = cur.left;
2450
+ continue;
2451
+ } else {
2452
+ predecessor.right = null;
2453
+ }
2454
+ }
2455
+ ans.push(callback(cur));
2456
+ cur = cur.right;
2457
+ }
2458
+ break;
2459
+ case "PRE":
2460
+ while (cur) {
2461
+ if (cur.left) {
2462
+ const predecessor = this.getPredecessor(cur);
2463
+ if (!predecessor.right) {
2464
+ predecessor.right = cur;
2465
+ ans.push(callback(cur));
2466
+ cur = cur.left;
2467
+ continue;
2468
+ } else {
2469
+ predecessor.right = null;
2470
+ }
2471
+ } else {
2472
+ ans.push(callback(cur));
2473
+ }
2474
+ cur = cur.right;
2475
+ }
2476
+ break;
2477
+ case "POST":
2478
+ while (cur) {
2479
+ if (cur.left) {
2480
+ const predecessor = this.getPredecessor(cur);
2481
+ if (predecessor.right === null) {
2482
+ predecessor.right = cur;
2483
+ cur = cur.left;
2484
+ continue;
2485
+ } else {
2486
+ predecessor.right = null;
2487
+ _printEdge(cur.left);
2488
+ }
2489
+ }
2490
+ cur = cur.right;
2491
+ }
2492
+ _printEdge(startNode);
2493
+ break;
2494
+ }
2495
+ return ans;
2496
+ }
2497
+ /**
2498
+ * Time complexity: O(n)
2499
+ * Space complexity: O(n)
2500
+ *
2501
+ * The `clone` function creates a deep copy of a tree structure by traversing it using breadth-first
2502
+ * search.
2503
+ * @returns The `clone()` method is returning a cloned copy of the tree with the same structure and
2504
+ * values as the original tree. The method creates a new tree, iterates over the nodes of the
2505
+ * original tree using breadth-first search (bfs), and adds the nodes to the new tree. If a node in
2506
+ * the original tree is null, a null node is added to the cloned tree. If a node
2507
+ */
2508
+ clone() {
2509
+ const cloned = this.createTree();
2510
+ this._clone(cloned);
2511
+ return cloned;
2512
+ }
2513
+ /**
2514
+ * Time Complexity: O(n)
2515
+ * Space Complexity: O(n)
2516
+ *
2517
+ * The `filter` function iterates over key-value pairs in a tree data structure and creates a new
2518
+ * tree with elements that satisfy a given predicate.
2519
+ * @param predicate - The `predicate` parameter in the `filter` method is a function that will be
2520
+ * called with four arguments: the `value` of the current entry, the `key` of the current entry, the
2521
+ * `index` of the current entry in the iteration, and the reference to the tree itself (`
2522
+ * @param {any} [thisArg] - The `thisArg` parameter in the `filter` method allows you to specify the
2523
+ * value of `this` that should be used when executing the `predicate` function. This is useful when
2524
+ * the `predicate` function relies on the context of a specific object or value. By providing a
2525
+ * `thisArg
2526
+ * @returns The `filter` method is returning a new tree that contains entries that pass the provided
2527
+ * predicate function.
2528
+ */
2529
+ filter(predicate, thisArg) {
2530
+ const newTree = this.createTree();
2531
+ let index = 0;
2532
+ for (const [key, value] of this) {
2533
+ if (predicate.call(thisArg, key, value, index++, this)) {
2534
+ newTree.add([key, value]);
2535
+ }
2536
+ }
2537
+ return newTree;
2538
+ }
2539
+ /**
2540
+ * Time Complexity: O(n)
2541
+ * Space Complexity: O(n)
2542
+ *
2543
+ * The `map` function in TypeScript creates a new BinaryTree by applying a callback function to each
2544
+ * entry in the original BinaryTree.
2545
+ * @param callback - A function that will be called for each entry in the current binary tree. It
2546
+ * takes the key, value (which can be undefined), and an array containing the mapped key and value as
2547
+ * arguments.
2548
+ * @param [options] - The `options` parameter in the `map` method is of type `BinaryTreeOptions<MK,
2549
+ * MV, MR>`. It is an optional parameter that allows you to specify additional options for the binary
2550
+ * tree being created during the mapping process. These options could include things like custom
2551
+ * comparators, initial
2552
+ * @param {any} [thisArg] - The `thisArg` parameter in the `map` method is used to specify the value
2553
+ * of `this` when executing the `callback` function. It allows you to set the context (value of
2554
+ * `this`) within the callback function. If `thisArg` is provided, it will be passed
2555
+ * @returns The `map` function is returning a new `BinaryTree` instance filled with entries that are
2556
+ * the result of applying the provided `callback` function to each entry in the original tree.
2557
+ */
2558
+ map(callback, options, thisArg) {
2559
+ const newTree = new _BinaryTree([], options);
2560
+ let index = 0;
2561
+ for (const [key, value] of this) {
2562
+ newTree.add(callback.call(thisArg, key, value, index++, this));
2563
+ }
2564
+ return newTree;
2565
+ }
2566
+ /**
2567
+ * Time Complexity: O(n)
2568
+ * Space Complexity: O(n)
2569
+ *
2570
+ * The function `toVisual` in TypeScript overrides the visual representation of a binary tree with
2571
+ * customizable options for displaying undefined, null, and sentinel nodes.
2572
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter in the
2573
+ * `toVisual` method is used to specify the starting point for visualizing the binary tree structure.
2574
+ * It can be a node, key, entry, or the root of the tree. If no specific starting point is provided,
2575
+ * the default is set to the root
2576
+ * @param {BinaryTreePrintOptions} [options] - The `options` parameter in the `toVisual` method is an
2577
+ * object that contains the following properties:
2578
+ * @returns The `override toVisual` method returns a string that represents the visual display of the
2579
+ * binary tree based on the provided options for showing undefined, null, and Red-Black NIL nodes.
2580
+ * The method constructs the visual representation by calling the `_displayAux` method and appending
2581
+ * the lines to the output string. The final output string contains the visual representation of the
2582
+ * binary tree with the specified options.
2583
+ */
2584
+ toVisual(startNode = this._root, options) {
2585
+ const opts = { isShowUndefined: false, isShowNull: true, isShowRedBlackNIL: false, ...options };
2586
+ startNode = this.ensureNode(startNode);
2587
+ let output = "";
2588
+ if (!startNode) return output;
2589
+ if (opts.isShowUndefined) output += `U for undefined
2590
+ `;
2591
+ if (opts.isShowNull) output += `N for null
2592
+ `;
2593
+ if (opts.isShowRedBlackNIL) output += `S for Sentinel Node(NIL)
2594
+ `;
2595
+ const display = (root) => {
2596
+ const [lines] = this._displayAux(root, opts);
2597
+ let paragraph = "";
2598
+ for (const line of lines) {
2599
+ paragraph += line + "\n";
2600
+ }
2601
+ output += paragraph;
2602
+ };
2603
+ display(startNode);
2604
+ return output;
2605
+ }
2606
+ /**
2607
+ * Time Complexity: O(n)
2608
+ * Space Complexity: O(n)
2609
+ *
2610
+ * The function `print` in TypeScript overrides the default print behavior to log a visual
2611
+ * representation of the binary tree to the console.
2612
+ * @param {BinaryTreePrintOptions} [options] - The `options` parameter is used to specify the
2613
+ * printing options for the binary tree. It is an optional parameter that allows you to customize how
2614
+ * the binary tree is printed, such as choosing between different traversal orders or formatting
2615
+ * options.
2616
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } startNode - The `startNode` parameter in the
2617
+ * `override print` method is used to specify the starting point for printing the binary tree. It can
2618
+ * be either a key, a node, an entry, or the root of the tree. If no specific starting point is
2619
+ * provided, the default value is set to
2620
+ */
2621
+ print(options, startNode = this._root) {
2622
+ console.log(this.toVisual(startNode, options));
2623
+ }
2624
+ _clone(cloned) {
2625
+ this.bfs(
2626
+ (node) => {
2627
+ if (node === null) cloned.add(null);
2628
+ else {
2629
+ if (this._isMapMode) cloned.add([node.key, this._store.get(node.key)]);
2630
+ else cloned.add([node.key, node.value]);
2631
+ }
2632
+ },
2633
+ this._root,
2634
+ this.iterationType,
2635
+ true
2636
+ );
2637
+ if (this._isMapMode) cloned._store = this._store;
2638
+ }
2639
+ /**
2640
+ * Time Complexity: O(1)
2641
+ * Space Complexity: O(1)
2642
+ *
2643
+ * The function `keyValueNodeEntryRawToNodeAndValue` converts various input types into a node object
2644
+ * or returns null.
2645
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The
2646
+ * `keyValueNodeEntryRawToNodeAndValue` function takes in a parameter `keyNodeOrEntry`, which
2647
+ * can be of type `K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined ` or `R`. This parameter represents either a key, a
2648
+ * node, an entry
2649
+ * @param {V} [value] - The `value` parameter in the `keyValueNodeEntryRawToNodeAndValue` function is
2650
+ * an optional parameter of type `V`. It represents the value associated with the key in the node
2651
+ * being created. If a `value` is provided, it will be used when creating the node. If
2652
+ * @returns The `keyValueNodeEntryRawToNodeAndValue` function returns an optional node
2653
+ * (`BinaryTreeNode<K, V> | null | undefined`) based on the input parameters provided. The function checks the type of the
2654
+ * input parameter (`keyNodeOrEntry`) and processes it accordingly to return a node or null
2655
+ * value.
2656
+ */
2657
+ _keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value) {
2658
+ if (keyNodeOrEntry === void 0) return [void 0, void 0];
2659
+ if (keyNodeOrEntry === null) return [null, void 0];
2660
+ if (this.isNode(keyNodeOrEntry)) return [keyNodeOrEntry, value];
2661
+ if (this.isEntry(keyNodeOrEntry)) {
2662
+ const [key, entryValue] = keyNodeOrEntry;
2663
+ if (key === void 0) return [void 0, void 0];
2664
+ else if (key === null) return [null, void 0];
2665
+ const finalValue = value ?? entryValue;
2666
+ return [this.createNode(key, finalValue), finalValue];
2667
+ }
2668
+ return [this.createNode(keyNodeOrEntry, value), value];
2669
+ }
2670
+ /**
2671
+ * Time complexity: O(n)
2672
+ * Space complexity: O(n)
2673
+ *
2674
+ * The `_dfs` function performs a depth-first search traversal on a binary tree, with customizable
2675
+ * options for traversal order and node processing.
2676
+ * @param {C} callback - The `callback` parameter in the `_dfs` method is a function that will be
2677
+ * called on each node visited during the depth-first search traversal. It is a generic type `C` that
2678
+ * extends `NodeCallback<BinaryTreeNode<K, V> | null>`. The default value for `callback`
2679
+ * @param {DFSOrderPattern} [pattern=IN] - The `pattern` parameter in the `_dfs` method specifies the
2680
+ * order in which the nodes are visited during a depth-first search traversal. It can have one of the
2681
+ * following values:
2682
+ * @param {boolean} [onlyOne=false] - The `onlyOne` parameter in the `_dfs` method is a boolean flag
2683
+ * that determines whether the traversal should stop after processing a single node. If `onlyOne` is
2684
+ * set to `true`, the traversal will return as soon as a single node is processed. If it is set to
2685
+ * `false
2686
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined}
2687
+ * startNode - The `startNode` parameter in the `_dfs` method is used to specify the starting node
2688
+ * for the depth-first search traversal. It can be provided in different forms:
2689
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `_dfs` method
2690
+ * specifies whether the traversal should be done recursively or iteratively. It can have two
2691
+ * possible values:
2692
+ * @param [includeNull=false] - The `includeNull` parameter in the `_dfs` method determines whether
2693
+ * null nodes should be included in the traversal process. If `includeNull` is set to `true`, the
2694
+ * method will consider null nodes as valid nodes to visit or process. If `includeNull` is set to
2695
+ * `false`,
2696
+ * @param shouldVisitLeft - The `shouldVisitLeft` parameter in the `_dfs` method is a function that
2697
+ * determines whether the left child of a node should be visited during the Depth-First Search
2698
+ * traversal. By default, it checks if the node is not null or undefined before visiting the left
2699
+ * child. You can customize this behavior
2700
+ * @param shouldVisitRight - The `shouldVisitRight` parameter in the `_dfs` method is a function that
2701
+ * determines whether to visit the right child node of the current node during a depth-first search
2702
+ * traversal. The default implementation of this function checks if the node is not null or undefined
2703
+ * before deciding to visit it.
2704
+ * @param shouldVisitRoot - The `shouldVisitRoot` parameter in the `_dfs` method is a function that
2705
+ * determines whether a given node should be visited during the depth-first search traversal. The
2706
+ * function takes a node as an argument and returns a boolean value indicating whether the node
2707
+ * should be visited.
2708
+ * @param shouldProcessRoot - The `shouldProcessRoot` parameter in the `_dfs` method is a function
2709
+ * that determines whether the root node should be processed during the Depth-First Search traversal.
2710
+ * It takes a node (BinaryTreeNode<K, V> | null | undefined) as input and returns a boolean value. If
2711
+ * the function
2712
+ * @returns The `_dfs` method returns an array of the return type of the provided callback function
2713
+ * `C`.
2714
+ */
2715
+ _dfs(callback = this._DEFAULT_NODE_CALLBACK, pattern = "IN", onlyOne = false, startNode = this._root, iterationType = this.iterationType, includeNull = false, shouldVisitLeft = (node) => !!node, shouldVisitRight = (node) => !!node, shouldVisitRoot = (node) => {
2716
+ if (includeNull) return this.isRealNodeOrNull(node);
2717
+ return this.isRealNode(node);
2718
+ }, shouldProcessRoot = (node) => this.isRealNodeOrNull(node)) {
2719
+ startNode = this.ensureNode(startNode);
2720
+ if (!startNode) return [];
2721
+ const ans = [];
2722
+ if (iterationType === "RECURSIVE") {
2723
+ const dfs = (node) => {
2724
+ if (!shouldVisitRoot(node)) return;
2725
+ const visitLeft = () => {
2726
+ if (shouldVisitLeft(node) && node?.left !== void 0) dfs(node?.left);
2727
+ };
2728
+ const visitRight = () => {
2729
+ if (shouldVisitRight(node) && node?.right !== void 0) dfs(node?.right);
2730
+ };
2731
+ switch (pattern) {
2732
+ case "IN":
2733
+ visitLeft();
2734
+ if (shouldProcessRoot(node)) {
2735
+ ans.push(callback(node));
2736
+ if (onlyOne) return;
2737
+ }
2738
+ visitRight();
2739
+ break;
2740
+ case "PRE":
2741
+ if (shouldProcessRoot(node)) {
2742
+ ans.push(callback(node));
2743
+ if (onlyOne) return;
2744
+ }
2745
+ visitLeft();
2746
+ visitRight();
2747
+ break;
2748
+ case "POST":
2749
+ visitLeft();
2750
+ visitRight();
2751
+ if (shouldProcessRoot(node)) {
2752
+ ans.push(callback(node));
2753
+ if (onlyOne) return;
2754
+ }
2755
+ break;
2756
+ }
2757
+ };
2758
+ dfs(startNode);
2759
+ } else {
2760
+ const stack = [{ opt: 0 /* VISIT */, node: startNode }];
2761
+ const pushLeft = (cur) => {
2762
+ if (shouldVisitLeft(cur.node)) stack.push({ opt: 0 /* VISIT */, node: cur.node?.left });
2763
+ };
2764
+ const pushRight = (cur) => {
2765
+ if (shouldVisitRight(cur.node)) stack.push({ opt: 0 /* VISIT */, node: cur.node?.right });
2766
+ };
2767
+ const pushRoot = (cur) => {
2768
+ if (shouldVisitRoot(cur.node)) stack.push({ opt: 1 /* PROCESS */, node: cur.node });
2769
+ };
2770
+ while (stack.length > 0) {
2771
+ const cur = stack.pop();
2772
+ if (cur === void 0) continue;
2773
+ if (!shouldVisitRoot(cur.node)) continue;
2774
+ if (cur.opt === 1 /* PROCESS */) {
2775
+ if (shouldProcessRoot(cur.node) && cur.node !== void 0) {
2776
+ ans.push(callback(cur.node));
2777
+ if (onlyOne) return ans;
2778
+ }
2779
+ } else {
2780
+ switch (pattern) {
2781
+ case "IN":
2782
+ pushRight(cur);
2783
+ pushRoot(cur);
2784
+ pushLeft(cur);
2785
+ break;
2786
+ case "PRE":
2787
+ pushRight(cur);
2788
+ pushLeft(cur);
2789
+ pushRoot(cur);
2790
+ break;
2791
+ case "POST":
2792
+ pushRoot(cur);
2793
+ pushRight(cur);
2794
+ pushLeft(cur);
2795
+ break;
2796
+ }
2797
+ }
2798
+ }
2799
+ }
2800
+ return ans;
2801
+ }
2802
+ /**
2803
+ * Time Complexity: O(1)
2804
+ * Space Complexity: O(1)
2805
+ *
2806
+ * The function `_getIterator` returns an iterable iterator for a binary tree data structure, either
2807
+ * using an iterative approach or a recursive approach based on the specified iteration type.
2808
+ * @param node - The `node` parameter in the `_getIterator` method represents the current node being
2809
+ * processed during iteration. It is initially set to the root node of the data structure (or the
2810
+ * node passed as an argument), and then it is traversed through the data structure based on the
2811
+ * iteration type specified (`ITER
2812
+ * @returns The `_getIterator` method returns an IterableIterator containing key-value pairs of nodes
2813
+ * in a binary tree structure. The method uses an iterative approach to traverse the tree based on
2814
+ * the `iterationType` property. If the `iterationType` is set to 'ITERATIVE', the method uses a
2815
+ * stack to perform an in-order traversal of the tree. If the `iterationType` is not 'ITERATIVE
2816
+ */
2817
+ *_getIterator(node = this._root) {
2818
+ if (!node) return;
2819
+ if (this.iterationType === "ITERATIVE") {
2820
+ const stack = [];
2821
+ let current = node;
2822
+ while (current || stack.length > 0) {
2823
+ while (this.isRealNode(current)) {
2824
+ stack.push(current);
2825
+ current = current.left;
2826
+ }
2827
+ current = stack.pop();
2828
+ if (this.isRealNode(current)) {
2829
+ if (this._isMapMode) yield [current.key, this._store.get(current.key)];
2830
+ else yield [current.key, current.value];
2831
+ current = current.right;
2832
+ }
2833
+ }
2834
+ } else {
2835
+ if (node.left && this.isRealNode(node)) {
2836
+ yield* this[Symbol.iterator](node.left);
2837
+ }
2838
+ if (this._isMapMode) yield [node.key, this._store.get(node.key)];
2839
+ else yield [node.key, node.value];
2840
+ if (node.right && this.isRealNode(node)) {
2841
+ yield* this[Symbol.iterator](node.right);
2842
+ }
2843
+ }
2844
+ }
2845
+ /**
2846
+ * Time Complexity: O(n)
2847
+ * Space Complexity: O(n)
2848
+ *
2849
+ * The function `_displayAux` in TypeScript is responsible for generating the display layout of nodes
2850
+ * in a binary tree based on specified options.
2851
+ * @param node - The `node` parameter in the `_displayAux` function represents a node in a binary
2852
+ * tree. It can be either a valid node containing a key or a special type of node like null,
2853
+ * undefined, or a Red-Black tree NIL node. The function checks the type of the node and its
2854
+ * @param {BinaryTreePrintOptions} options - The `options` parameter in the `_displayAux` function
2855
+ * contains the following properties:
2856
+ * @returns The `_displayAux` function returns a `NodeDisplayLayout`, which is an array containing
2857
+ * information about how to display a node in a binary tree. The `NodeDisplayLayout` consists of four
2858
+ * elements:
2859
+ */
2860
+ _displayAux(node, options) {
2861
+ const { isShowNull, isShowUndefined, isShowRedBlackNIL } = options;
2862
+ const emptyDisplayLayout = [["\u2500"], 1, 0, 0];
2863
+ if (node === null && !isShowNull) {
2864
+ return emptyDisplayLayout;
2865
+ } else if (node === void 0 && !isShowUndefined) {
2866
+ return emptyDisplayLayout;
2867
+ } else if (this.isNIL(node) && !isShowRedBlackNIL) {
2868
+ return emptyDisplayLayout;
2869
+ } else if (node !== null && node !== void 0) {
2870
+ const key = node.key, line = this.isNIL(node) ? "S" : String(key), width = line.length;
2871
+ return _buildNodeDisplay(
2872
+ line,
2873
+ width,
2874
+ this._displayAux(node.left, options),
2875
+ this._displayAux(node.right, options)
2876
+ );
2877
+ } else {
2878
+ const line = node === void 0 ? "U" : "N", width = line.length;
2879
+ return _buildNodeDisplay(line, width, [[""], 1, 0, 0], [[""], 1, 0, 0]);
2880
+ }
2881
+ function _buildNodeDisplay(line, width, left, right) {
2882
+ const [leftLines, leftWidth, leftHeight, leftMiddle] = left;
2883
+ const [rightLines, rightWidth, rightHeight, rightMiddle] = right;
2884
+ const firstLine = " ".repeat(Math.max(0, leftMiddle + 1)) + "_".repeat(Math.max(0, leftWidth - leftMiddle - 1)) + line + "_".repeat(Math.max(0, rightMiddle)) + " ".repeat(Math.max(0, rightWidth - rightMiddle));
2885
+ const secondLine = (leftHeight > 0 ? " ".repeat(leftMiddle) + "/" + " ".repeat(leftWidth - leftMiddle - 1) : " ".repeat(leftWidth)) + " ".repeat(width) + (rightHeight > 0 ? " ".repeat(rightMiddle) + "\\" + " ".repeat(rightWidth - rightMiddle - 1) : " ".repeat(rightWidth));
2886
+ const mergedLines = [firstLine, secondLine];
2887
+ for (let i = 0; i < Math.max(leftHeight, rightHeight); i++) {
2888
+ const leftLine = i < leftHeight ? leftLines[i] : " ".repeat(leftWidth);
2889
+ const rightLine = i < rightHeight ? rightLines[i] : " ".repeat(rightWidth);
2890
+ mergedLines.push(leftLine + " ".repeat(width) + rightLine);
2891
+ }
2892
+ return [
2893
+ mergedLines,
2894
+ leftWidth + width + rightWidth,
2895
+ Math.max(leftHeight, rightHeight) + 2,
2896
+ leftWidth + Math.floor(width / 2)
2897
+ ];
2898
+ }
2899
+ }
2900
+ _DEFAULT_NODE_CALLBACK = (node) => node ? node.key : void 0;
2901
+ /**
2902
+ * Time Complexity: O(1)
2903
+ * Space Complexity: O(1)
2904
+ *
2905
+ * The _swapProperties function swaps key and value properties between two nodes in a binary tree.
2906
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } srcNode - The `srcNode` parameter in the
2907
+ * `_swapProperties` method can be either a BTNRep object containing key and value
2908
+ * properties, or it can be of type R.
2909
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } destNode - The `destNode` parameter in the
2910
+ * `_swapProperties` method represents the node or entry where the properties will be swapped with
2911
+ * the `srcNode`. It can be of type `K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined ` or `R`. The method ensures that
2912
+ * both `srcNode
2913
+ * @returns The `_swapProperties` method returns either the `destNode` with its key and value swapped
2914
+ * with the `srcNode`, or `undefined` if either `srcNode` or `destNode` is falsy.
2915
+ */
2916
+ _swapProperties(srcNode, destNode) {
2917
+ srcNode = this.ensureNode(srcNode);
2918
+ destNode = this.ensureNode(destNode);
2919
+ if (srcNode && destNode) {
2920
+ const { key, value } = destNode;
2921
+ const tempNode = this.createNode(key, value);
2922
+ if (tempNode) {
2923
+ destNode.key = srcNode.key;
2924
+ if (!this._isMapMode) destNode.value = srcNode.value;
2925
+ srcNode.key = tempNode.key;
2926
+ if (!this._isMapMode) srcNode.value = tempNode.value;
2927
+ }
2928
+ return destNode;
2929
+ }
2930
+ return void 0;
2931
+ }
2932
+ /**
2933
+ * Time Complexity: O(1)
2934
+ * Space Complexity: O(1)
2935
+ *
2936
+ * The _replaceNode function replaces an old node with a new node in a binary tree structure.
2937
+ * @param {BinaryTreeNode<K, V>} oldNode - The `oldNode` parameter represents the node that you want to replace in a
2938
+ * tree data structure.
2939
+ * @param {BinaryTreeNode<K, V>} newNode - The `newNode` parameter in the `_replaceNode` function represents the node
2940
+ * that will replace the `oldNode` in a tree data structure. This function is responsible for
2941
+ * updating the parent, left child, right child, and root (if necessary) references when replacing a
2942
+ * node in the tree.
2943
+ * @returns The method `_replaceNode` is returning the `newNode` that was passed as a parameter after
2944
+ * replacing the `oldNode` with it in the binary tree structure.
2945
+ */
2946
+ _replaceNode(oldNode, newNode) {
2947
+ if (oldNode.parent) {
2948
+ if (oldNode.parent.left === oldNode) {
2949
+ oldNode.parent.left = newNode;
2950
+ } else if (oldNode.parent.right === oldNode) {
2951
+ oldNode.parent.right = newNode;
2952
+ }
2953
+ }
2954
+ newNode.left = oldNode.left;
2955
+ newNode.right = oldNode.right;
2956
+ newNode.parent = oldNode.parent;
2957
+ if (this._root === oldNode) {
2958
+ this._setRoot(newNode);
2959
+ }
2960
+ return newNode;
2961
+ }
2962
+ /**
2963
+ * Time Complexity: O(1)
2964
+ * Space Complexity: O(1)
2965
+ *
2966
+ * The function _setRoot sets the root node of a data structure while updating the parent reference
2967
+ * of the previous root node.
2968
+ * @param v - The parameter `v` in the `_setRoot` method is of type `BinaryTreeNode<K, V> | null | undefined`, which means
2969
+ * it can either be an optional `BinaryTreeNode<K, V>` type or `null`.
2970
+ */
2971
+ _setRoot(v) {
2972
+ if (v) {
2973
+ v.parent = void 0;
2974
+ }
2975
+ this._root = v;
2976
+ }
2977
+ /**
2978
+ * Time Complexity: O(1)
2979
+ * Space Complexity: O(1)
2980
+ *
2981
+ * The function `_ensurePredicate` in TypeScript ensures that the input is converted into a valid
2982
+ * predicate function for a binary tree node.
2983
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined | NodePredicate<BinaryTreeNode<K, V>>} keyNodeEntryOrPredicate - The
2984
+ * `_ensurePredicate` method in the provided code snippet is responsible for ensuring that the input
2985
+ * parameter `keyNodeEntryOrPredicate` is transformed into a valid predicate function that can be
2986
+ * used for filtering nodes in a binary tree.
2987
+ * @returns A NodePredicate<BinaryTreeNode<K, V>> function is being returned.
2988
+ */
2989
+ _ensurePredicate(keyNodeEntryOrPredicate) {
2990
+ if (keyNodeEntryOrPredicate === null || keyNodeEntryOrPredicate === void 0)
2991
+ return (node) => node ? false : false;
2992
+ if (this._isPredicate(keyNodeEntryOrPredicate)) return keyNodeEntryOrPredicate;
2993
+ if (this.isRealNode(keyNodeEntryOrPredicate))
2994
+ return (node) => node === keyNodeEntryOrPredicate;
2995
+ if (this.isEntry(keyNodeEntryOrPredicate)) {
2996
+ const [key] = keyNodeEntryOrPredicate;
2997
+ return (node) => {
2998
+ if (!node) return false;
2999
+ return node.key === key;
3000
+ };
3001
+ }
3002
+ return (node) => {
3003
+ if (!node) return false;
3004
+ return node.key === keyNodeEntryOrPredicate;
3005
+ };
3006
+ }
3007
+ /**
3008
+ * Time Complexity: O(1)
3009
+ * Space Complexity: O(1)
3010
+ *
3011
+ * The function `_isPredicate` checks if a given parameter is a function.
3012
+ * @param {any} p - The parameter `p` is a variable of type `any`, which means it can hold any type
3013
+ * of value. In this context, the function `_isPredicate` is checking if `p` is a function that
3014
+ * satisfies the type `NodePredicate<BinaryTreeNode<K, V>>`.
3015
+ * @returns The function is checking if the input `p` is a function and returning a boolean value
3016
+ * based on that check. If `p` is a function, it will return `true`, indicating that `p` is a
3017
+ * predicate function for a binary tree node. If `p` is not a function, it will return `false`.
3018
+ */
3019
+ _isPredicate(p) {
3020
+ return typeof p === "function";
3021
+ }
3022
+ /**
3023
+ * Time Complexity: O(1)
3024
+ * Space Complexity: O(1)
3025
+ *
3026
+ * The function `_extractKey` in TypeScript returns the key from a given input, which can be a node,
3027
+ * entry, raw data, or null/undefined.
3028
+ * @param {K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The `_extractKey` method you provided is a
3029
+ * TypeScript method that takes in a parameter `keyNodeOrEntry` of type `K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined `,
3030
+ * where `BTNRep` is a generic type with keys `K`, `V`, and `BinaryTreeNode<K, V>`, and `
3031
+ * @returns The `_extractKey` method returns the key value extracted from the `keyNodeOrEntry`
3032
+ * parameter. The return value can be a key value of type `K`, `null`, or `undefined`, depending on
3033
+ * the conditions checked in the method.
3034
+ */
3035
+ _extractKey(keyNodeOrEntry) {
3036
+ if (keyNodeOrEntry === null) return null;
3037
+ if (keyNodeOrEntry === void 0) return;
3038
+ if (keyNodeOrEntry === this._NIL) return;
3039
+ if (this.isNode(keyNodeOrEntry)) return keyNodeOrEntry.key;
3040
+ if (this.isEntry(keyNodeOrEntry)) return keyNodeOrEntry[0];
3041
+ return keyNodeOrEntry;
3042
+ }
3043
+ /**
3044
+ * Time Complexity: O(1)
3045
+ * Space Complexity: O(1)
3046
+ *
3047
+ * The function `_setValue` sets a value in a store based on a key, handling cases where the key or
3048
+ * value is null or undefined.
3049
+ * @param {K | null | undefined} key - The `key` parameter can be of type `K`, `null`, or
3050
+ * `undefined`.
3051
+ * @param {V | undefined} value - The `value` parameter in the `_setValue` method can be of type `V`
3052
+ * or `undefined`.
3053
+ * @returns The method `_setValue` returns `false` if either the `key` is `null` or `undefined`, or
3054
+ * if the `value` is `undefined`. Otherwise, it returns the result of calling the `set` method on the
3055
+ * `_store` object with the `key` and `value` arguments.
3056
+ */
3057
+ _setValue(key, value) {
3058
+ if (key === null || key === void 0) return false;
3059
+ if (value === void 0) return false;
3060
+ return this._store.set(key, value);
3061
+ }
3062
+ /**
3063
+ * Time Complexity: O(1)
3064
+ * Space Complexity: O(1)
3065
+ *
3066
+ * The _clearNodes function sets the root node to undefined and resets the size to 0.
3067
+ */
3068
+ _clearNodes() {
3069
+ this._setRoot(void 0);
3070
+ this._size = 0;
3071
+ }
3072
+ /**
3073
+ * Time Complexity: O(1)
3074
+ * Space Complexity: O(1)
3075
+ *
3076
+ * The _clearValues function clears all values stored in the _store object.
3077
+ */
3078
+ _clearValues() {
3079
+ this._store.clear();
3080
+ }
3081
+ };
3082
+ export {
3083
+ BinaryTree,
3084
+ BinaryTreeNode
3085
+ };
3086
+ /**
3087
+ * data-structure-typed
3088
+ *
3089
+ * @author Pablo Zeng
3090
+ * @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
3091
+ * @license MIT License
3092
+ */
3093
+ /**
3094
+ * @license MIT
3095
+ * @copyright Pablo Zeng <zrwusa@gmail.com>
3096
+ * @class
3097
+ */