max-priority-queue-typed 2.4.0 → 2.4.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 (43) hide show
  1. package/dist/types/data-structures/base/linear-base.d.ts +6 -6
  2. package/dist/types/data-structures/binary-tree/binary-tree.d.ts +6 -6
  3. package/dist/types/data-structures/binary-tree/bst.d.ts +2 -1
  4. package/dist/types/data-structures/binary-tree/index.d.ts +3 -3
  5. package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +150 -20
  6. package/dist/types/data-structures/binary-tree/tree-map.d.ts +188 -0
  7. package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +238 -147
  8. package/dist/types/data-structures/binary-tree/tree-multi-set.d.ts +270 -0
  9. package/dist/types/data-structures/binary-tree/tree-set.d.ts +181 -0
  10. package/dist/types/interfaces/binary-tree.d.ts +2 -2
  11. package/dist/types/types/data-structures/binary-tree/index.d.ts +3 -3
  12. package/dist/types/types/data-structures/binary-tree/tree-map.d.ts +33 -0
  13. package/dist/types/types/data-structures/binary-tree/tree-multi-set.d.ts +16 -0
  14. package/dist/types/types/data-structures/binary-tree/tree-set.d.ts +33 -0
  15. package/package.json +2 -2
  16. package/src/data-structures/base/linear-base.ts +2 -12
  17. package/src/data-structures/binary-tree/avl-tree.ts +1 -1
  18. package/src/data-structures/binary-tree/binary-tree.ts +45 -21
  19. package/src/data-structures/binary-tree/bst.ts +85 -10
  20. package/src/data-structures/binary-tree/index.ts +3 -3
  21. package/src/data-structures/binary-tree/red-black-tree.ts +568 -76
  22. package/src/data-structures/binary-tree/tree-map.ts +439 -0
  23. package/src/data-structures/binary-tree/tree-multi-map.ts +488 -325
  24. package/src/data-structures/binary-tree/tree-multi-set.ts +502 -0
  25. package/src/data-structures/binary-tree/tree-set.ts +407 -0
  26. package/src/data-structures/queue/deque.ts +10 -0
  27. package/src/interfaces/binary-tree.ts +2 -2
  28. package/src/types/data-structures/binary-tree/index.ts +3 -3
  29. package/src/types/data-structures/binary-tree/tree-map.ts +45 -0
  30. package/src/types/data-structures/binary-tree/tree-multi-set.ts +19 -0
  31. package/src/types/data-structures/binary-tree/tree-set.ts +39 -0
  32. package/dist/types/data-structures/binary-tree/avl-tree-counter.d.ts +0 -236
  33. package/dist/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +0 -197
  34. package/dist/types/data-structures/binary-tree/tree-counter.d.ts +0 -243
  35. package/dist/types/types/data-structures/binary-tree/avl-tree-counter.d.ts +0 -2
  36. package/dist/types/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +0 -2
  37. package/dist/types/types/data-structures/binary-tree/tree-counter.d.ts +0 -2
  38. package/src/data-structures/binary-tree/avl-tree-counter.ts +0 -539
  39. package/src/data-structures/binary-tree/avl-tree-multi-map.ts +0 -438
  40. package/src/data-structures/binary-tree/tree-counter.ts +0 -575
  41. package/src/types/data-structures/binary-tree/avl-tree-counter.ts +0 -3
  42. package/src/types/data-structures/binary-tree/avl-tree-multi-map.ts +0 -3
  43. package/src/types/data-structures/binary-tree/tree-counter.ts +0 -3
@@ -2,187 +2,34 @@
2
2
  * data-structure-typed
3
3
  *
4
4
  * @author Pablo Zeng
5
- * @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
5
+ * @copyright Copyright (c) 2022 Pablo Zeng
6
6
  * @license MIT License
7
7
  */
8
8
 
9
- import type {
10
- BTNOptKeyOrNull,
11
- ElemOf,
12
- EntryCallback,
13
- FamilyPosition,
14
- RBTNColor,
15
- TreeMultiMapOptions
16
- } from '../../types';
9
+ import type { Comparator, TreeMultiMapOptions } from '../../types';
10
+ import { Range } from '../../common';
17
11
  import { RedBlackTree, RedBlackTreeNode } from './red-black-tree';
18
- import { IBinaryTree } from '../../interfaces';
12
+ import { TreeSet } from './tree-set';
19
13
 
20
14
  /**
21
- * Node used by TreeMultiMap; stores the key with a bucket of values (array).
22
- * @remarks Time O(1), Space O(1)
23
- * @template K
24
- * @template V
15
+ * Node type used by TreeMultiMap (alias to RedBlackTreeNode for backward compatibility).
16
+ *
17
+ * @deprecated Direct node manipulation is discouraged. Use TreeMultiMap methods instead.
25
18
  */
26
- export class TreeMultiMapNode<K = any, V = any> {
27
- key: K;
28
- value?: V[];
29
- parent?: TreeMultiMapNode<K, V> = undefined;
30
-
31
- /**
32
- * Create a TreeMultiMap node with an optional value bucket.
33
- * @remarks Time O(1), Space O(1)
34
- * @param key - Key of the node.
35
- * @param [value] - Initial array of values.
36
- * @returns New TreeMultiMapNode instance.
37
- */
38
- constructor(key: K, value: V[] = [], color: RBTNColor = 'BLACK') {
39
- this.key = key;
40
- this.value = value;
41
- this.color = color;
42
- }
43
-
44
- _left?: TreeMultiMapNode<K, V> | null | undefined = undefined;
45
-
46
- /**
47
- * Get the left child pointer.
48
- * @remarks Time O(1), Space O(1)
49
- * @returns Left child node, or null/undefined.
50
- */
51
- get left(): TreeMultiMapNode<K, V> | null | undefined {
52
- return this._left;
53
- }
54
-
55
- /**
56
- * Set the left child and update its parent pointer.
57
- * @remarks Time O(1), Space O(1)
58
- * @param v - New left child node, or null/undefined.
59
- * @returns void
60
- */
61
- set left(v: TreeMultiMapNode<K, V> | null | undefined) {
62
- if (v) {
63
- v.parent = this;
64
- }
65
- this._left = v;
66
- }
67
-
68
- _right?: TreeMultiMapNode<K, V> | null | undefined = undefined;
69
-
70
- /**
71
- * Get the right child pointer.
72
- * @remarks Time O(1), Space O(1)
73
- * @returns Right child node, or null/undefined.
74
- */
75
- get right(): TreeMultiMapNode<K, V> | null | undefined {
76
- return this._right;
77
- }
78
-
79
- /**
80
- * Set the right child and update its parent pointer.
81
- * @remarks Time O(1), Space O(1)
82
- * @param v - New right child node, or null/undefined.
83
- * @returns void
84
- */
85
- set right(v: TreeMultiMapNode<K, V> | null | undefined) {
86
- if (v) {
87
- v.parent = this;
88
- }
89
- this._right = v;
90
- }
91
-
92
- _height: number = 0;
93
-
94
- /**
95
- * Gets the height of the node (used in self-balancing trees).
96
- * @remarks Time O(1), Space O(1)
97
- *
98
- * @returns The height.
99
- */
100
- get height(): number {
101
- return this._height;
102
- }
103
-
104
- /**
105
- * Sets the height of the node.
106
- * @remarks Time O(1), Space O(1)
107
- *
108
- * @param value - The new height.
109
- */
110
- set height(value: number) {
111
- this._height = value;
112
- }
113
-
114
- _color: RBTNColor = 'BLACK';
115
-
116
- /**
117
- * Gets the color of the node (used in Red-Black trees).
118
- * @remarks Time O(1), Space O(1)
119
- *
120
- * @returns The node's color.
121
- */
122
- get color(): RBTNColor {
123
- return this._color;
124
- }
125
-
126
- /**
127
- * Sets the color of the node.
128
- * @remarks Time O(1), Space O(1)
129
- *
130
- * @param value - The new color.
131
- */
132
- set color(value: RBTNColor) {
133
- this._color = value;
134
- }
135
-
136
- _count: number = 1;
137
-
138
- /**
139
- * Gets the count of nodes in the subtree rooted at this node (used in order-statistic trees).
140
- * @remarks Time O(1), Space O(1)
141
- *
142
- * @returns The subtree node count.
143
- */
144
- get count(): number {
145
- return this._count;
146
- }
147
-
148
- /**
149
- * Sets the count of nodes in the subtree.
150
- * @remarks Time O(1), Space O(1)
151
- *
152
- * @param value - The new count.
153
- */
154
- set count(value: number) {
155
- this._count = value;
156
- }
157
-
158
- /**
159
- * Gets the position of the node relative to its parent.
160
- * @remarks Time O(1), Space O(1)
161
- *
162
- * @returns The family position (e.g., 'ROOT', 'LEFT', 'RIGHT').
163
- */
164
- get familyPosition(): FamilyPosition {
165
- if (!this.parent) {
166
- return this.left || this.right ? 'ROOT' : 'ISOLATED';
167
- }
168
-
169
- if (this.parent.left === this) {
170
- return this.left || this.right ? 'ROOT_LEFT' : 'LEFT';
171
- } else if (this.parent.right === this) {
172
- return this.left || this.right ? 'ROOT_RIGHT' : 'RIGHT';
173
- }
174
-
175
- return 'MAL_NODE';
19
+ export class TreeMultiMapNode<K = any, V = any> extends RedBlackTreeNode<K, V[]> {
20
+ constructor(key: K, value: V[] = []) {
21
+ super(key, value);
176
22
  }
177
23
  }
178
24
 
179
25
  /**
180
- * Red-Black Tree–based multimap (key → array of values). Preserves O(log N) updates and supports map-like mode.
181
- * @remarks Time O(1), Space O(1)
182
- * @template K
183
- * @template V
184
- * @template R
26
+ * TreeMultiMap (ordered MultiMap) key → bucket (Array of values).
185
27
  *
28
+ * Semantics (RFC):
29
+ * - Bucketed design: each key appears once; duplicates live in the bucket.
30
+ * - `get(key)` returns a **live** bucket reference.
31
+ * - Default iteration yields bucket entries: `[K, V[]]`.
32
+ * - Navigable operations (`first/last/ceiling/...`) return entry tuples like TreeMap.
186
33
  * @example
187
34
  * // players ranked by score with their equipment
188
35
  * type Equipment = {
@@ -310,7 +157,7 @@ export class TreeMultiMapNode<K = any, V = any> {
310
157
  * isMapMode: false
311
158
  * });
312
159
  *
313
- * const topPlayersEquipments = playerRankings.rangeSearch([8900, 10000], node => playerRankings.get(node));
160
+ * const topPlayersEquipments = playerRankings.rangeSearch([8900, 10000], node => playerRankings.get(node.key));
314
161
  * console.log(topPlayersEquipments); // [
315
162
  * // [
316
163
  * // {
@@ -348,202 +195,518 @@ export class TreeMultiMapNode<K = any, V = any> {
348
195
  * // ]
349
196
  * // ];
350
197
  */
351
- export class TreeMultiMap<K = any, V = any, R = any> extends RedBlackTree<K, V[], R> implements IBinaryTree<K, V[], R> {
198
+ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]]> {
199
+ readonly #core: RedBlackTree<K, V[], R>;
200
+ readonly #isDefaultComparator: boolean;
201
+
352
202
  /**
353
- * Create a TreeMultiMap and optionally bulk-insert items.
354
- * @remarks Time O(N log N), Space O(N)
355
- * @param [keysNodesEntriesOrRaws] - Iterable of keys/nodes/entries/raw items to insert.
356
- * @param [options] - Options for TreeMultiMap (comparator, reverse, map mode).
357
- * @returns New TreeMultiMap instance.
203
+ * Creates a new TreeMultiMap.
204
+ * @param keysNodesEntriesOrRaws - Initial entries, or raw elements if `toEntryFn` is provided.
205
+ * @param options - Configuration options including optional `toEntryFn` to transform raw elements.
206
+ * @remarks Time O(m log m), Space O(m) where m is the number of initial entries
207
+ * @example
208
+ * // Standard usage with entries
209
+ * const mmap = new TreeMultiMap([['a', ['x', 'y']], ['b', ['z']]]);
210
+ *
211
+ * // Using toEntryFn to transform raw objects
212
+ * const players = [{ score: 100, items: ['sword'] }, { score: 200, items: ['shield', 'bow'] }];
213
+ * const mmap = new TreeMultiMap(players, { toEntryFn: p => [p.score, p.items] });
358
214
  */
359
215
  constructor(
360
- keysNodesEntriesOrRaws: Iterable<
361
- K | TreeMultiMapNode<K, V> | [K | null | undefined, V[] | undefined] | null | undefined | R
362
- > = [],
363
- options?: TreeMultiMapOptions<K, V[], R>
216
+ keysNodesEntriesOrRaws: Iterable<K | [K | null | undefined, V[] | undefined] | null | undefined | R> = [],
217
+ options: TreeMultiMapOptions<K, V[], R> = {}
364
218
  ) {
365
- super([], { ...options });
366
- if (keysNodesEntriesOrRaws) {
367
- this.setMany(keysNodesEntriesOrRaws);
219
+ const comparator = options.comparator ?? TreeSet.createDefaultComparator<K>();
220
+ this.#isDefaultComparator = options.comparator === undefined;
221
+ const toEntryFn = options.toEntryFn;
222
+ this.#core = new RedBlackTree<K, V[], R>([], { ...options, comparator, isMapMode: options.isMapMode });
223
+
224
+ for (const x of keysNodesEntriesOrRaws) {
225
+ if (x === null || x === undefined) continue;
226
+
227
+ // If toEntryFn is provided, use it to transform raw element
228
+ if (toEntryFn) {
229
+ const [k, bucket] = toEntryFn(x as R);
230
+ if (k === null || k === undefined) continue;
231
+ if (bucket !== undefined) {
232
+ this.#core.set(k as K, Array.isArray(bucket) ? [...bucket] : [bucket] as V[]);
233
+ } else {
234
+ this.#core.set(k as K, [] as V[]);
235
+ }
236
+ continue;
237
+ }
238
+
239
+ if (Array.isArray(x)) {
240
+ const [k, bucket] = x;
241
+ if (k === null || k === undefined) continue;
242
+ if (bucket !== undefined) {
243
+ // seed bucket (copy)
244
+ this.#core.set(k as K, [...bucket] as V[]);
245
+ } else {
246
+ this.#core.set(k as K, [] as V[]);
247
+ }
248
+ continue;
249
+ }
250
+ // key-only
251
+ this.#core.set(x as K, [] as V[]);
368
252
  }
369
253
  }
370
254
 
371
- override createNode(key: K, value: V[] = []): TreeMultiMapNode<K, V> {
372
- return new TreeMultiMapNode<K, V>(key, this._isMapMode ? [] : value);
255
+ /**
256
+ * Validates the key against the default comparator rules.
257
+ * @remarks Time O(1), Space O(1)
258
+ */
259
+ private _validateKey(key: K): void {
260
+ if (!this.#isDefaultComparator) return;
261
+ // reuse TreeSet strict validation (same policy)
262
+ // NOTE: TreeSet._validateKey is private, so we replicate the checks.
263
+ if (typeof key === 'number') {
264
+ if (Number.isNaN(key)) throw new TypeError('TreeMultiMap: NaN is not a valid key');
265
+ return;
266
+ }
267
+ if (typeof key === 'string') return;
268
+ if (key instanceof Date) {
269
+ if (Number.isNaN(key.getTime())) throw new TypeError('TreeMultiMap: invalid Date key');
270
+ return;
271
+ }
272
+ throw new TypeError('TreeMultiMap: comparator is required for non-number/non-string/non-Date keys');
373
273
  }
374
274
 
375
275
  /**
376
- * Checks if the given item is a `TreeMultiMapNode` instance.
276
+ * Number of distinct keys.
377
277
  * @remarks Time O(1), Space O(1)
378
- *
379
- * @param keyNodeOrEntry - The item to check.
380
- * @returns True if it's a TreeMultiMapNode, false otherwise.
381
278
  */
382
- override isNode(
383
- keyNodeOrEntry: K | TreeMultiMapNode<K, V> | [K | null | undefined, V[] | undefined] | null | undefined
384
- ): keyNodeOrEntry is TreeMultiMapNode<K, V> {
385
- return keyNodeOrEntry instanceof TreeMultiMapNode;
279
+ get size(): number {
280
+ return this.#core.size;
386
281
  }
387
282
 
388
- override set(
389
- keyNodeOrEntry: K | TreeMultiMapNode<K, V> | [K | null | undefined, V[] | undefined] | null | undefined
390
- ): boolean;
283
+ /**
284
+ * Whether the map is empty.
285
+ * @remarks Time O(1), Space O(1)
286
+ */
287
+ isEmpty(): boolean {
288
+ return this.size === 0;
289
+ }
290
+
291
+ /**
292
+ * Removes all entries from the map.
293
+ * @remarks Time O(1), Space O(1)
294
+ */
295
+ clear(): void {
296
+ this.#core.clear();
297
+ }
391
298
 
392
- override set(key: K, value: V): boolean;
299
+ /**
300
+ * Bucket length for a key (missing => 0).
301
+ * @remarks Time O(log n), Space O(1)
302
+ */
303
+ count(key: K): number {
304
+ const b = this.get(key);
305
+ return Array.isArray(b) ? b.length : 0;
306
+ }
393
307
 
394
308
  /**
395
- * Insert a value or a list of values into the multimap. If the key exists, values are appended.
396
- * @remarks Time O(log N + M), Space O(1)
397
- * @param keyNodeOrEntry - Key, node, or [key, values] entry.
398
- * @param [value] - Single value to set when a bare key is provided.
399
- * @returns True if inserted or appended; false if ignored.
309
+ * Total number of values across all buckets bucket.length).
310
+ * @remarks Time O(n), Space O(1)
400
311
  */
401
- override set(
402
- keyNodeOrEntry: K | TreeMultiMapNode<K, V> | [K | null | undefined, V[] | undefined] | null | undefined,
403
- value?: V
404
- ): boolean {
405
- if (this.isRealNode(keyNodeOrEntry)) return super.set(keyNodeOrEntry);
312
+ get totalSize(): number {
313
+ let sum = 0;
314
+ for (const [, bucket] of this) sum += bucket.length;
315
+ return sum;
316
+ }
406
317
 
407
- const _commonAdd = (key?: BTNOptKeyOrNull<K>, values?: V[]) => {
408
- if (key === undefined || key === null) return false;
318
+ /**
319
+ * Whether the map contains the given key.
320
+ * @remarks Time O(log n), Space O(1)
321
+ */
322
+ has(key: K): boolean {
323
+ this._validateKey(key);
324
+ return this.#core.has(key);
325
+ }
409
326
 
410
- const _setToValues = () => {
411
- const existingValues = this.get(key);
412
- if (existingValues !== undefined && values !== undefined) {
413
- for (const value of values) existingValues.push(value);
414
- return true;
415
- }
416
- return false;
417
- };
418
-
419
- const _setByNode = () => {
420
- const existingNode = this.getNode(key);
421
- if (this.isRealNode(existingNode)) {
422
- const existingValues = this.get(existingNode);
423
- if (existingValues === undefined) {
424
- super.set(key, values);
425
- return true;
426
- }
427
- if (values !== undefined) {
428
- for (const value of values) existingValues.push(value);
429
- return true;
430
- } else {
431
- return false;
432
- }
433
- } else {
434
- return super.set(key, values);
435
- }
436
- };
327
+ /**
328
+ * Live bucket reference (do not auto-delete key if bucket becomes empty via mutation).
329
+ * @remarks Time O(log n), Space O(1)
330
+ */
331
+ get(key: K): V[] | undefined {
332
+ this._validateKey(key);
333
+ return this.#core.get(key);
334
+ }
335
+
336
+ /**
337
+ * Append a single value.
338
+ * @remarks Time O(log n), Space O(1)
339
+ */
340
+ add(key: K, value: V): boolean {
341
+ this._validateKey(key);
342
+ const bucket = this.#core.get(key);
343
+ if (bucket) {
344
+ bucket.push(value);
345
+ return true;
346
+ }
347
+ return this.#core.set(key, [value]);
348
+ }
437
349
 
438
- if (this._isMapMode) {
439
- return _setByNode() || _setToValues();
350
+ /**
351
+ * Alias for compatibility with existing TreeMultiMap semantics.
352
+ * @remarks Time O(log n), Space O(1) for single value; O(log n + m) for bucket append
353
+ */
354
+ set(entry: [K | null | undefined, V[] | undefined] | K | null | undefined, value?: V): boolean;
355
+ set(key: K, value: V): boolean;
356
+ set(entry: [K | null | undefined, V[] | undefined] | K | null | undefined, value?: V): boolean {
357
+ if (entry === null || entry === undefined) return false;
358
+ if (Array.isArray(entry)) {
359
+ const [k, bucket] = entry;
360
+ if (k === null || k === undefined) return false;
361
+ if (value !== undefined) return this.add(k as K, value);
362
+ if (bucket === undefined) {
363
+ // ensure key exists
364
+ return this.#core.set(k as K, [] as V[]);
365
+ }
366
+ // append bucket
367
+ const existing = this.#core.get(k as K);
368
+ if (existing) {
369
+ existing.push(...bucket);
370
+ return true;
440
371
  }
441
- return _setToValues() || _setByNode();
442
- };
372
+ return this.#core.set(k as K, [...bucket] as V[]);
373
+ }
374
+ // key-only or key+value
375
+ if (value !== undefined) return this.add(entry as K, value);
376
+ return this.#core.set(entry as K, [] as V[]);
377
+ }
443
378
 
444
- if (this.isEntry(keyNodeOrEntry)) {
445
- const [key, values] = keyNodeOrEntry;
446
- return _commonAdd(key, value !== undefined ? [value] : values);
379
+ /**
380
+ * Deletes a key and its entire bucket.
381
+ * @remarks Time O(log n), Space O(1)
382
+ */
383
+ delete(key: K): boolean {
384
+ this._validateKey(key);
385
+ return this.#core.delete(key).length > 0;
386
+ }
387
+
388
+ /**
389
+ * Check if a specific value exists in a key's bucket.
390
+ * @remarks Time O(log n + m), Space O(1) where m is bucket size
391
+ */
392
+ hasEntry(key: K, value: V, eq: (a: V, b: V) => boolean = Object.is): boolean {
393
+ const bucket = this.get(key);
394
+ if (!Array.isArray(bucket)) return false;
395
+ return bucket.some(v => eq(v, value));
396
+ }
397
+
398
+ /**
399
+ * Delete a single occurrence of a value from a key's bucket.
400
+ * @remarks Time O(log n + m), Space O(1) where m is bucket size
401
+ */
402
+ deleteValue(key: K, value: V, eq: (a: V, b: V) => boolean = Object.is): boolean {
403
+ const bucket = this.get(key);
404
+ if (!Array.isArray(bucket)) return false;
405
+ const idx = bucket.findIndex(v => eq(v, value));
406
+ if (idx === -1) return false;
407
+ bucket.splice(idx, 1);
408
+ if (bucket.length === 0) this.delete(key);
409
+ return true;
410
+ }
411
+
412
+ /**
413
+ * Delete all occurrences of a value from a key's bucket.
414
+ * @remarks Time O(log n + m), Space O(1) where m is bucket size
415
+ */
416
+ deleteValues(key: K, value: V, eq: (a: V, b: V) => boolean = Object.is): number {
417
+ const bucket = this.get(key);
418
+ if (!Array.isArray(bucket) || bucket.length === 0) return 0;
419
+ let removed = 0;
420
+ for (let i = bucket.length - 1; i >= 0; i--) {
421
+ if (eq(bucket[i] as V, value)) {
422
+ bucket.splice(i, 1);
423
+ removed++;
424
+ }
447
425
  }
426
+ if (bucket.length === 0 && removed > 0) this.delete(key);
427
+ return removed;
428
+ }
429
+
430
+ // ---- iteration (bucket view) ----
448
431
 
449
- return _commonAdd(keyNodeOrEntry, value !== undefined ? [value] : undefined);
432
+ /**
433
+ * Iterates over all entries as [key, bucket] pairs.
434
+ * @remarks Time O(n), Space O(1)
435
+ */
436
+ *[Symbol.iterator](): Iterator<[K, V[]]> {
437
+ for (const [k, v] of this.#core) {
438
+ // core always stores buckets, but guard anyway
439
+ yield [k, v ?? ([] as V[])];
440
+ }
441
+ }
442
+
443
+ /**
444
+ * Iterates over all keys.
445
+ * @remarks Time O(n), Space O(1)
446
+ */
447
+ *keys(): IterableIterator<K> {
448
+ yield* this.#core.keys();
450
449
  }
451
450
 
452
451
  /**
453
- * Delete a single value from the bucket at a given key. Removes the key if the bucket becomes empty.
454
- * @remarks Time O(log N), Space O(1)
455
- * @param keyNodeOrEntry - Key, node, or [key, values] entry to locate the bucket.
456
- * @param value - Value to remove from the bucket.
457
- * @returns True if the value was removed; false if not found.
452
+ * Iterates over all buckets.
453
+ * @remarks Time O(n), Space O(1)
458
454
  */
459
- deleteValue(
460
- keyNodeOrEntry: K | TreeMultiMapNode<K, V> | [K | null | undefined, V[] | undefined] | null | undefined,
461
- value: V
462
- ): boolean {
463
- const values = this.get(keyNodeOrEntry);
464
- if (Array.isArray(values)) {
465
- const index = values.indexOf(value);
466
- if (index === -1) return false;
467
- values.splice(index, 1);
455
+ *values(): IterableIterator<V[]> {
456
+ for (const [, bucket] of this) yield bucket;
457
+ }
468
458
 
469
- if (values.length === 0) this.delete(keyNodeOrEntry);
459
+ // ---- entry-flat views ----
470
460
 
471
- return true;
461
+ /**
462
+ * Iterates over all entries for a specific key.
463
+ * @remarks Time O(log n + m), Space O(1) where m is bucket size
464
+ */
465
+ *entriesOf(key: K): IterableIterator<[K, V]> {
466
+ const bucket = this.get(key);
467
+ if (!Array.isArray(bucket)) return;
468
+ for (const v of bucket) yield [key, v];
469
+ }
470
+
471
+ /**
472
+ * Iterates over all values for a specific key.
473
+ * @remarks Time O(log n + m), Space O(1) where m is bucket size
474
+ */
475
+ *valuesOf(key: K): IterableIterator<V> {
476
+ const bucket = this.get(key);
477
+ if (!Array.isArray(bucket)) return;
478
+ yield* bucket;
479
+ }
480
+
481
+ /**
482
+ * Iterates over all [key, value] pairs (flattened from buckets).
483
+ * @remarks Time O(T), Space O(1) where T is totalSize
484
+ */
485
+ *flatEntries(): IterableIterator<[K, V]> {
486
+ for (const [k, bucket] of this) {
487
+ for (const v of bucket) yield [k, v];
488
+ }
489
+ }
490
+
491
+ // ━━━ Navigable methods (return [K, V[]] | undefined) ━━━
492
+
493
+ /**
494
+ * Returns the entry with the smallest key.
495
+ * @remarks Time O(log n), Space O(1)
496
+ * @example
497
+ * const map = new TreeMultiMap([[1, ['a']], [2, ['b']]]);
498
+ * map.first(); // [1, ['a']]
499
+ */
500
+ first(): [K, V[]] | undefined {
501
+ const k = this.#core.getLeftMost();
502
+ if (k === undefined) return undefined;
503
+ const b = this.get(k);
504
+ return b === undefined ? undefined : [k, b];
505
+ }
506
+
507
+ /**
508
+ * Returns the entry with the largest key.
509
+ * @remarks Time O(log n), Space O(1)
510
+ * @example
511
+ * const map = new TreeMultiMap([[1, ['a']], [2, ['b']]]);
512
+ * map.last(); // [2, ['b']]
513
+ */
514
+ last(): [K, V[]] | undefined {
515
+ const k = this.#core.getRightMost();
516
+ if (k === undefined) return undefined;
517
+ const b = this.get(k);
518
+ return b === undefined ? undefined : [k, b];
519
+ }
520
+
521
+ /**
522
+ * Removes and returns the entry with the smallest key.
523
+ * @remarks Time O(log n), Space O(1)
524
+ * @example
525
+ * const map = new TreeMultiMap([[1, ['a']], [2, ['b']]]);
526
+ * map.pollFirst(); // [1, ['a']]
527
+ * map.has(1); // false
528
+ */
529
+ pollFirst(): [K, V[]] | undefined {
530
+ const e = this.first();
531
+ if (!e) return undefined;
532
+ this.delete(e[0]);
533
+ return e;
534
+ }
535
+
536
+ /**
537
+ * Removes and returns the entry with the largest key.
538
+ * @remarks Time O(log n), Space O(1)
539
+ * @example
540
+ * const map = new TreeMultiMap([[1, ['a']], [2, ['b']]]);
541
+ * map.pollLast(); // [2, ['b']]
542
+ * map.has(2); // false
543
+ */
544
+ pollLast(): [K, V[]] | undefined {
545
+ const e = this.last();
546
+ if (!e) return undefined;
547
+ this.delete(e[0]);
548
+ return e;
549
+ }
550
+
551
+ /**
552
+ * Returns the entry with the smallest key >= given key.
553
+ * @remarks Time O(log n), Space O(1)
554
+ * @example
555
+ * const map = new TreeMultiMap([[10, ['a']], [20, ['b']], [30, ['c']]]);
556
+ * map.ceiling(15); // [20, ['b']]
557
+ * map.ceiling(20); // [20, ['b']]
558
+ */
559
+ ceiling(key: K): [K, V[]] | undefined {
560
+ this._validateKey(key);
561
+ const k = this.#core.ceiling(key);
562
+ if (k === undefined) return undefined;
563
+ const b = this.get(k);
564
+ return b === undefined ? undefined : [k, b];
565
+ }
566
+
567
+ /**
568
+ * Returns the entry with the largest key <= given key.
569
+ * @remarks Time O(log n), Space O(1)
570
+ * @example
571
+ * const map = new TreeMultiMap([[10, ['a']], [20, ['b']], [30, ['c']]]);
572
+ * map.floor(25); // [20, ['b']]
573
+ * map.floor(20); // [20, ['b']]
574
+ */
575
+ floor(key: K): [K, V[]] | undefined {
576
+ this._validateKey(key);
577
+ const k = this.#core.floor(key);
578
+ if (k === undefined) return undefined;
579
+ const b = this.get(k);
580
+ return b === undefined ? undefined : [k, b];
581
+ }
582
+
583
+ /**
584
+ * Returns the entry with the smallest key > given key.
585
+ * @remarks Time O(log n), Space O(1)
586
+ * @example
587
+ * const map = new TreeMultiMap([[10, ['a']], [20, ['b']], [30, ['c']]]);
588
+ * map.higher(10); // [20, ['b']]
589
+ * map.higher(15); // [20, ['b']]
590
+ */
591
+ higher(key: K): [K, V[]] | undefined {
592
+ this._validateKey(key);
593
+ const k = this.#core.higher(key);
594
+ if (k === undefined) return undefined;
595
+ const b = this.get(k);
596
+ return b === undefined ? undefined : [k, b];
597
+ }
598
+
599
+ /**
600
+ * Returns the entry with the largest key < given key.
601
+ * @remarks Time O(log n), Space O(1)
602
+ * @example
603
+ * const map = new TreeMultiMap([[10, ['a']], [20, ['b']], [30, ['c']]]);
604
+ * map.lower(20); // [10, ['a']]
605
+ * map.lower(15); // [10, ['a']]
606
+ */
607
+ lower(key: K): [K, V[]] | undefined {
608
+ this._validateKey(key);
609
+ const k = this.#core.lower(key);
610
+ if (k === undefined) return undefined;
611
+ const b = this.get(k);
612
+ return b === undefined ? undefined : [k, b];
613
+ }
614
+
615
+ // ━━━ Tree utilities ━━━
616
+
617
+ /**
618
+ * Prints the internal tree structure (for debugging).
619
+ * @remarks Time O(n), Space O(n)
620
+ */
621
+ print(): void {
622
+ this.#core.print();
623
+ }
624
+
625
+ /**
626
+ * Executes a callback for each entry.
627
+ * @remarks Time O(n), Space O(1)
628
+ */
629
+ forEach(callback: (value: V[], key: K, map: this) => void): void {
630
+ for (const [k, v] of this) {
631
+ callback(v, k, this);
632
+ }
633
+ }
634
+
635
+ /**
636
+ * Creates a new map with entries that pass the predicate.
637
+ * @remarks Time O(n), Space O(n)
638
+ */
639
+ filter(predicate: (value: V[], key: K, map: this) => boolean): TreeMultiMap<K, V, R> {
640
+ const filtered: [K, V[]][] = [];
641
+ for (const [k, v] of this) {
642
+ if (predicate(v, k, this)) filtered.push([k, v]);
643
+ }
644
+ return new TreeMultiMap<K, V, R>(filtered, { comparator: this.comparator });
645
+ }
646
+
647
+ /**
648
+ * Creates a new map by transforming each entry.
649
+ * @remarks Time O(n log n), Space O(n)
650
+ */
651
+ map<V2>(
652
+ mapper: (value: V[], key: K, map: this) => [K, V2[]]
653
+ ): TreeMultiMap<K, V2, R> {
654
+ const mapped: [K, V2[]][] = [];
655
+ for (const [k, v] of this) {
656
+ mapped.push(mapper(v, k, this));
472
657
  }
473
- return false;
658
+ return new TreeMultiMap<K, V2, R>(mapped, { comparator: this.comparator });
474
659
  }
475
660
 
476
- override map<MK = K, MVArr extends unknown[] = V[], MR = any>(
477
- callback: EntryCallback<K, V[] | undefined, [MK, MVArr]>,
478
- options?: Partial<TreeMultiMapOptions<MK, MVArr, MR>>,
479
- thisArg?: unknown
480
- ): TreeMultiMap<MK, ElemOf<MVArr>, MR>;
661
+ /**
662
+ * Reduces all entries to a single value.
663
+ * @remarks Time O(n), Space O(1)
664
+ */
665
+ reduce<U>(callback: (accumulator: U, value: V[], key: K, map: this) => U, initialValue: U): U {
666
+ let acc = initialValue;
667
+ for (const [k, v] of this) {
668
+ acc = callback(acc, v, k, this);
669
+ }
670
+ return acc;
671
+ }
672
+
673
+ /**
674
+ * Sets multiple entries at once.
675
+ * @remarks Time O(m log n), Space O(m) where m is input size
676
+ */
677
+ setMany(keysNodesEntriesOrRaws: Iterable<K | [K | null | undefined, V[] | undefined]>): boolean[] {
678
+ const results: boolean[] = [];
679
+ for (const x of keysNodesEntriesOrRaws) {
680
+ // Call implementation directly: entry can be K or [K, V[]] or [K, undefined]
681
+ results.push(this.set(x));
682
+ }
683
+ return results;
684
+ }
481
685
 
482
- override map<MK = K, MV = V[], MR = any>(
483
- callback: EntryCallback<K, V[] | undefined, [MK, MV]>,
484
- options?: Partial<TreeMultiMapOptions<MK, MV, MR>>,
485
- thisArg?: unknown
486
- ): RedBlackTree<MK, MV, MR>;
686
+ /**
687
+ * Searches for entries within a key range.
688
+ * @remarks Time O(log n + k), Space O(k) where k is result size
689
+ */
690
+ rangeSearch<C extends (node: RedBlackTreeNode<K, V[]>) => unknown>(
691
+ range: Range<K> | [K, K],
692
+ callback?: C
693
+ ): ReturnType<C>[] {
694
+ return this.#core.rangeSearch(range, callback as (node: RedBlackTreeNode<K, V[]>) => ReturnType<C>);
695
+ }
487
696
 
488
697
  /**
489
- * Create a new tree by mapping each [key, values] bucket.
490
- * @remarks Time O(N log N), Space O(N)
491
- * @template MK
492
- * @template MV
493
- * @template MR
494
- * @param callback - Function mapping (key, values, index, tree) → [newKey, newValue].
495
- * @param [options] - Options for the output tree.
496
- * @param [thisArg] - Value for `this` inside the callback.
497
- * @returns A new RedBlackTree (or TreeMultiMap when mapping to array values; see overloads).
698
+ * Creates a shallow clone of this map.
699
+ * @remarks Time O(n log n), Space O(n)
498
700
  */
499
- override map<MK, MV, MR extends object>(
500
- callback: EntryCallback<K, V[] | undefined, [MK, MV]>,
501
- options?: Partial<TreeMultiMapOptions<MK, MV, MR>>,
502
- thisArg?: unknown
503
- ): RedBlackTree<MK, MV, MR> {
504
- const out = this._createLike<MK, MV, MR>([], options);
505
- let i = 0;
506
- for (const [k, v] of this) out.set(callback.call(thisArg, v, k, i++, this));
507
- return out;
701
+ clone(): TreeMultiMap<K, V, R> {
702
+ return new TreeMultiMap<K, V, R>(this, { comparator: this.comparator, isMapMode: this.#core.isMapMode });
508
703
  }
509
704
 
510
705
  /**
511
- * (Protected) Create an empty instance of the same concrete class.
706
+ * Expose comparator for advanced usage/testing (read-only).
512
707
  * @remarks Time O(1), Space O(1)
513
- * @template TK
514
- * @template TV
515
- * @template TR
516
- * @param [options] - Optional constructor options for the like-kind instance.
517
- * @returns An empty like-kind instance.
518
- */
519
- protected override _createInstance<TK = K, TV = V, TR = R>(options?: Partial<TreeMultiMapOptions<TK, TV, TR>>): this {
520
- const Ctor = this.constructor as unknown as new (
521
- iter?: Iterable<TK | RedBlackTreeNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR>,
522
- opts?: TreeMultiMapOptions<TK, TV, TR>
523
- ) => RedBlackTree<TK, TV, TR>;
524
- return new Ctor([], { ...(this._snapshotOptions?.<TK, TV, TR>() ?? {}), ...(options ?? {}) }) as unknown as this;
525
- }
526
-
527
- /**
528
- * (Protected) Create a like-kind instance and seed it from an iterable.
529
- * @remarks Time O(N log N), Space O(N)
530
- * @template TK
531
- * @template TV
532
- * @template TR
533
- * @param iter - Iterable used to seed the new tree.
534
- * @param [options] - Options merged with the current snapshot.
535
- * @returns A like-kind RedBlackTree built from the iterable.
536
- */
537
- protected override _createLike<TK = K, TV = V, TR = R>(
538
- iter: Iterable<
539
- TK | RedBlackTreeNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR
540
- > = [],
541
- options?: Partial<TreeMultiMapOptions<TK, TV, TR>>
542
- ): RedBlackTree<TK, TV, TR> {
543
- const Ctor = this.constructor as unknown as new (
544
- iter?: Iterable<TK | RedBlackTreeNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR>,
545
- opts?: TreeMultiMapOptions<TK, TV, TR>
546
- ) => RedBlackTree<TK, TV, TR>;
547
- return new Ctor(iter, { ...(this._snapshotOptions?.<TK, TV, TR>() ?? {}), ...(options ?? {}) });
708
+ */
709
+ get comparator(): Comparator<K> {
710
+ return this.#core.comparator;
548
711
  }
549
712
  }