data-structure-typed 2.4.4 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/README.md +34 -1
  3. package/dist/cjs/index.cjs +10639 -2151
  4. package/dist/cjs-legacy/index.cjs +10694 -2195
  5. package/dist/esm/index.mjs +10639 -2150
  6. package/dist/esm-legacy/index.mjs +10694 -2194
  7. package/dist/types/common/error.d.ts +23 -0
  8. package/dist/types/common/index.d.ts +1 -0
  9. package/dist/types/data-structures/base/iterable-element-base.d.ts +1 -1
  10. package/dist/types/data-structures/binary-tree/avl-tree.d.ts +128 -51
  11. package/dist/types/data-structures/binary-tree/binary-indexed-tree.d.ts +210 -164
  12. package/dist/types/data-structures/binary-tree/binary-tree.d.ts +439 -78
  13. package/dist/types/data-structures/binary-tree/bst.d.ts +311 -28
  14. package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +217 -31
  15. package/dist/types/data-structures/binary-tree/segment-tree.d.ts +218 -152
  16. package/dist/types/data-structures/binary-tree/tree-map.d.ts +1281 -5
  17. package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +1087 -201
  18. package/dist/types/data-structures/binary-tree/tree-multi-set.d.ts +858 -65
  19. package/dist/types/data-structures/binary-tree/tree-set.d.ts +1133 -5
  20. package/dist/types/data-structures/graph/abstract-graph.d.ts +44 -0
  21. package/dist/types/data-structures/graph/directed-graph.d.ts +220 -47
  22. package/dist/types/data-structures/graph/map-graph.d.ts +59 -1
  23. package/dist/types/data-structures/graph/undirected-graph.d.ts +218 -59
  24. package/dist/types/data-structures/hash/hash-map.d.ts +230 -77
  25. package/dist/types/data-structures/heap/heap.d.ts +287 -99
  26. package/dist/types/data-structures/heap/max-heap.d.ts +46 -0
  27. package/dist/types/data-structures/heap/min-heap.d.ts +59 -0
  28. package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +286 -44
  29. package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +278 -65
  30. package/dist/types/data-structures/linked-list/skip-linked-list.d.ts +415 -12
  31. package/dist/types/data-structures/matrix/matrix.d.ts +331 -0
  32. package/dist/types/data-structures/priority-queue/max-priority-queue.d.ts +57 -0
  33. package/dist/types/data-structures/priority-queue/min-priority-queue.d.ts +60 -0
  34. package/dist/types/data-structures/priority-queue/priority-queue.d.ts +60 -0
  35. package/dist/types/data-structures/queue/deque.d.ts +313 -66
  36. package/dist/types/data-structures/queue/queue.d.ts +211 -42
  37. package/dist/types/data-structures/stack/stack.d.ts +174 -32
  38. package/dist/types/data-structures/trie/trie.d.ts +213 -43
  39. package/dist/types/types/data-structures/binary-tree/segment-tree.d.ts +1 -1
  40. package/dist/types/types/data-structures/linked-list/skip-linked-list.d.ts +1 -4
  41. package/dist/types/types/data-structures/queue/deque.d.ts +6 -0
  42. package/dist/umd/data-structure-typed.js +10725 -2221
  43. package/dist/umd/data-structure-typed.min.js +4 -2
  44. package/package.json +5 -4
  45. package/src/common/error.ts +60 -0
  46. package/src/common/index.ts +2 -0
  47. package/src/data-structures/base/iterable-element-base.ts +2 -2
  48. package/src/data-structures/binary-tree/avl-tree.ts +146 -51
  49. package/src/data-structures/binary-tree/binary-indexed-tree.ts +317 -247
  50. package/src/data-structures/binary-tree/binary-tree.ts +567 -121
  51. package/src/data-structures/binary-tree/bst.ts +370 -37
  52. package/src/data-structures/binary-tree/red-black-tree.ts +328 -96
  53. package/src/data-structures/binary-tree/segment-tree.ts +378 -248
  54. package/src/data-structures/binary-tree/tree-map.ts +1411 -13
  55. package/src/data-structures/binary-tree/tree-multi-map.ts +1218 -215
  56. package/src/data-structures/binary-tree/tree-multi-set.ts +959 -69
  57. package/src/data-structures/binary-tree/tree-set.ts +1257 -15
  58. package/src/data-structures/graph/abstract-graph.ts +106 -1
  59. package/src/data-structures/graph/directed-graph.ts +233 -47
  60. package/src/data-structures/graph/map-graph.ts +59 -1
  61. package/src/data-structures/graph/undirected-graph.ts +308 -59
  62. package/src/data-structures/hash/hash-map.ts +254 -79
  63. package/src/data-structures/heap/heap.ts +305 -102
  64. package/src/data-structures/heap/max-heap.ts +48 -3
  65. package/src/data-structures/heap/min-heap.ts +59 -0
  66. package/src/data-structures/linked-list/doubly-linked-list.ts +303 -44
  67. package/src/data-structures/linked-list/singly-linked-list.ts +293 -65
  68. package/src/data-structures/linked-list/skip-linked-list.ts +707 -90
  69. package/src/data-structures/matrix/matrix.ts +433 -22
  70. package/src/data-structures/priority-queue/max-priority-queue.ts +59 -3
  71. package/src/data-structures/priority-queue/min-priority-queue.ts +60 -0
  72. package/src/data-structures/priority-queue/priority-queue.ts +60 -0
  73. package/src/data-structures/queue/deque.ts +358 -68
  74. package/src/data-structures/queue/queue.ts +223 -42
  75. package/src/data-structures/stack/stack.ts +184 -32
  76. package/src/data-structures/trie/trie.ts +227 -44
  77. package/src/types/data-structures/binary-tree/segment-tree.ts +1 -1
  78. package/src/types/data-structures/linked-list/skip-linked-list.ts +2 -1
  79. package/src/types/data-structures/queue/deque.ts +7 -0
  80. package/src/utils/utils.ts +4 -2
@@ -1,171 +1,788 @@
1
- import type { SkipLinkedListOptions } from '../../types';
1
+ /**
2
+ * data-structure-typed
3
+ *
4
+ * @author Pablo Zeng
5
+ * @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
6
+ * @license MIT License
7
+ */
8
+
9
+ import type { Comparator, EntryCallback } from '../../types';
10
+ import { ERR } from '../../common';
11
+ import { IterableEntryBase } from '../base';
2
12
 
3
13
  export class SkipListNode<K, V> {
4
14
  key: K;
5
15
  value: V;
6
- forward: SkipListNode<K, V>[];
16
+ forward: (SkipListNode<K, V> | undefined)[];
7
17
 
8
18
  constructor(key: K, value: V, level: number) {
9
19
  this.key = key;
10
20
  this.value = value;
11
- this.forward = new Array(level);
21
+ this.forward = new Array(level).fill(undefined);
12
22
  }
13
23
  }
14
24
 
15
- export class SkipList<K, V> {
16
- constructor(elements: Iterable<[K, V]> = [], options?: SkipLinkedListOptions) {
17
- if (options) {
18
- const { maxLevel, probability } = options;
19
- if (typeof maxLevel === 'number') this._maxLevel = maxLevel;
20
- if (typeof probability === 'number') this._probability = probability;
25
+ export type SkipListOptions<K, V, R = [K, V]> = {
26
+ comparator?: Comparator<K>;
27
+ toEntryFn?: (rawElement: R) => [K, V];
28
+ maxLevel?: number;
29
+ probability?: number;
30
+ };
31
+
32
+ export type SkipListRangeOptions = {
33
+ lowInclusive?: boolean;
34
+ highInclusive?: boolean;
35
+ };
36
+
37
+
38
+ /**
39
+ * SkipList — a probabilistic sorted key-value container.
40
+ *
41
+ * API mirrors TreeMap: users can swap `TreeMap` ↔ `SkipList` with zero code changes.
42
+ * Reference: Java ConcurrentSkipListMap (NavigableMap interface).
43
+ *
44
+ * @example
45
+ * // Display skip list
46
+ * const sl = new SkipList<number, string>([[1, 'a']]);
47
+ * expect(() => sl.print()).not.toThrow();
48
+ */
49
+ export class SkipList<K = any, V = any, R = [K, V]> extends IterableEntryBase<K, V | undefined> {
50
+ readonly #comparator: Comparator<K>;
51
+ readonly #isDefaultComparator: boolean;
52
+
53
+ constructor(
54
+ entries: Iterable<R> | Iterable<[K, V | undefined]> = [],
55
+ options: SkipListOptions<K, V, R> = {}
56
+ ) {
57
+ super();
58
+ const { comparator, toEntryFn, maxLevel, probability } = options;
59
+
60
+ if (typeof maxLevel === 'number' && maxLevel > 0) this._maxLevel = maxLevel;
61
+ if (typeof probability === 'number' && probability > 0 && probability < 1) this._probability = probability;
62
+
63
+ this.#isDefaultComparator = comparator === undefined;
64
+ this.#comparator = comparator ?? SkipList.createDefaultComparator<K>();
65
+
66
+ this._head = new SkipListNode<K, V>(undefined as K, undefined as V, this._maxLevel);
67
+
68
+ for (const item of entries) {
69
+ let k: K;
70
+ let v: V | undefined;
71
+ if (toEntryFn) {
72
+ [k, v] = toEntryFn(item as R);
73
+ } else {
74
+ if (!Array.isArray(item) || item.length < 2) {
75
+ throw new TypeError(ERR.invalidEntry('SkipList'));
76
+ }
77
+ [k, v] = item as [K, V];
78
+ }
79
+ this.set(k, v as V);
21
80
  }
81
+ }
22
82
 
23
- if (elements) {
24
- for (const [key, value] of elements) this.add(key, value);
25
- }
83
+ /**
84
+ * Creates a default comparator supporting number, string, Date, and bigint.
85
+ */
86
+ static createDefaultComparator<K>(): Comparator<K> {
87
+ return (a: K, b: K): number => {
88
+ if (typeof a === 'number' && typeof b === 'number') {
89
+ if (Number.isNaN(a) || Number.isNaN(b)) throw new TypeError(ERR.invalidNaN('SkipList'));
90
+ return a - b;
91
+ }
92
+ if (typeof a === 'string' && typeof b === 'string') {
93
+ return a < b ? -1 : a > b ? 1 : 0;
94
+ }
95
+ if (a instanceof Date && b instanceof Date) {
96
+ const ta = a.getTime(),
97
+ tb = b.getTime();
98
+ if (Number.isNaN(ta) || Number.isNaN(tb)) throw new TypeError(ERR.invalidDate('SkipList'));
99
+ return ta - tb;
100
+ }
101
+ if (typeof a === 'bigint' && typeof b === 'bigint') {
102
+ return a < b ? -1 : a > b ? 1 : 0;
103
+ }
104
+ throw new TypeError(ERR.comparatorRequired('SkipList'));
105
+ };
26
106
  }
27
107
 
28
- protected _head: SkipListNode<K, V> = new SkipListNode<K, V>(undefined as K, undefined as V, this.maxLevel);
108
+ // ─── Internal state ──────────────────────────────────────────
29
109
 
30
- get head(): SkipListNode<K, V> {
31
- return this._head;
32
- }
110
+ protected _head: SkipListNode<K, V>;
33
111
 
34
112
  protected _level: number = 0;
35
113
 
36
- get level(): number {
37
- return this._level;
38
- }
114
+ protected _size: number = 0;
39
115
 
40
116
  protected _maxLevel: number = 16;
41
117
 
118
+ protected _probability: number = 0.5;
119
+
120
+ // ─── Size & lifecycle ────────────────────────────────────────
121
+
122
+ get size(): number {
123
+ return this._size;
124
+ }
125
+
42
126
  get maxLevel(): number {
43
127
  return this._maxLevel;
44
128
  }
45
129
 
46
- protected _probability: number = 0.5;
47
-
48
130
  get probability(): number {
49
131
  return this._probability;
50
132
  }
51
133
 
52
- get first(): V | undefined {
53
- const firstNode = this.head.forward[0];
54
- return firstNode ? firstNode.value : undefined;
134
+ get comparator(): Comparator<K> {
135
+ return this.#comparator;
55
136
  }
56
137
 
57
- get last(): V | undefined {
58
- let current = this.head;
59
- for (let i = this.level - 1; i >= 0; i--) {
60
- while (current.forward[i]) {
61
- current = current.forward[i];
62
- }
63
- }
64
- return current.value;
138
+ /**
139
+ * Check if empty
140
+
141
+
142
+
143
+
144
+
145
+
146
+
147
+
148
+
149
+ * @example
150
+ * // Check if empty
151
+ * const sl = new SkipList<number, string>();
152
+ * console.log(sl.isEmpty()); // true;
153
+ */
154
+ isEmpty(): boolean {
155
+ return this._size === 0;
65
156
  }
66
157
 
67
- add(key: K, value: V): void {
68
- const newNode = new SkipListNode(key, value, this._randomLevel());
69
- const update: SkipListNode<K, V>[] = new Array(this.maxLevel).fill(this.head);
70
- let current = this.head;
158
+ /**
159
+ * Remove all entries
160
+
161
+
162
+
163
+
164
+
165
+
166
+
167
+
168
+
169
+ * @example
170
+ * // Remove all entries
171
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b']]);
172
+ * sl.clear();
173
+ * console.log(sl.isEmpty()); // true;
174
+ */
175
+ clear(): void {
176
+ this._head = new SkipListNode<K, V>(undefined as K, undefined as V, this._maxLevel);
177
+ this._level = 0;
178
+ this._size = 0;
179
+ }
180
+
181
+ /**
182
+ * Create independent copy
183
+
184
+
185
+
186
+
187
+
188
+
189
+
190
+
191
+
192
+ * @example
193
+ * // Create independent copy
194
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b']]);
195
+ * const copy = sl.clone();
196
+ * copy.delete(1);
197
+ * console.log(sl.has(1)); // true;
198
+ */
199
+ clone(): this {
200
+ return new SkipList<K, V, R>(this as Iterable<[K, V | undefined]>, {
201
+ comparator: this.#isDefaultComparator ? undefined : this.#comparator,
202
+ maxLevel: this._maxLevel,
203
+ probability: this._probability
204
+ }) as this;
205
+ }
206
+
207
+ // ─── Core CRUD ───────────────────────────────────────────────
208
+
209
+ /**
210
+ * Insert or update a key-value pair. Returns `this` for chaining.
211
+ * Unique keys only — if key exists, value is updated in place.
212
+
213
+
214
+
215
+
216
+
217
+
218
+
219
+
220
+
221
+
222
+
223
+
224
+ * @example
225
+ * // In-memory sorted key-value store
226
+ * const store = new SkipList<number, string>();
227
+ *
228
+ * store.set(3, 'three');
229
+ * store.set(1, 'one');
230
+ * store.set(5, 'five');
231
+ * store.set(2, 'two');
232
+ *
233
+ * console.log(store.get(3)); // 'three';
234
+ * console.log(store.get(1)); // 'one';
235
+ * console.log(store.get(5)); // 'five';
236
+ *
237
+ * // Update existing key
238
+ * store.set(3, 'THREE');
239
+ * console.log(store.get(3)); // 'THREE';
240
+ */
241
+ set(key: K, value: V): this {
242
+ const cmp = this.#comparator;
243
+ const update = this._findUpdate(key);
244
+
245
+ // If key already exists, update value in place
246
+ const existing = update[0].forward[0];
247
+ if (existing && cmp(existing.key, key) === 0) {
248
+ existing.value = value;
249
+ return this;
250
+ }
71
251
 
72
- for (let i = this.level - 1; i >= 0; i--) {
73
- while (current.forward[i] && current.forward[i].key < key) {
74
- current = current.forward[i];
252
+ const newLevel = this._randomLevel();
253
+ const newNode = new SkipListNode(key, value, newLevel);
254
+
255
+ if (newLevel > this._level) {
256
+ for (let i = this._level; i < newLevel; i++) {
257
+ update[i] = this._head;
75
258
  }
76
- update[i] = current;
259
+ this._level = newLevel;
77
260
  }
78
261
 
79
- for (let i = 0; i < newNode.forward.length; i++) {
262
+ for (let i = 0; i < newLevel; i++) {
80
263
  newNode.forward[i] = update[i].forward[i];
81
264
  update[i].forward[i] = newNode;
82
265
  }
83
266
 
84
- if (!newNode.forward[0]) {
85
- this._level = Math.max(this.level, newNode.forward.length);
267
+ this._size++;
268
+ return this;
269
+ }
270
+
271
+ /**
272
+ * Get the value for a key, or `undefined` if not found.
273
+ * Overrides base O(n) with O(log n) skip-list search.
274
+
275
+
276
+
277
+
278
+
279
+
280
+
281
+
282
+
283
+
284
+
285
+
286
+ * @example
287
+ * // Building a sorted index
288
+ * type Product = { id: number; name: string; price: number };
289
+ * const products: Product[] = [
290
+ * { id: 1, name: 'Widget', price: 25 },
291
+ * { id: 2, name: 'Gadget', price: 50 },
292
+ * { id: 3, name: 'Doohickey', price: 15 }
293
+ * ];
294
+ *
295
+ * const index = new SkipList<number, Product>(products as any, {
296
+ * toEntryFn: (p: any) => [p.price, p]
297
+ * });
298
+ *
299
+ * // Iterate in sorted order by price
300
+ * const names = [...index.values()].map(p => p!.name);
301
+ * console.log(names); // ['Doohickey', 'Widget', 'Gadget'];
302
+ *
303
+ * // Range search: products between $20 and $60
304
+ * const range = index.rangeSearch([20, 60]);
305
+ * console.log(range.map(([, p]) => p!.name)); // ['Widget', 'Gadget'];
306
+ */
307
+ override get(key: K): V | undefined {
308
+ const node = this._findNode(key);
309
+ return node ? node.value : undefined;
310
+ }
311
+
312
+ /**
313
+ * Check if a key exists.
314
+ * Overrides base O(n) with O(log n) skip-list search.
315
+
316
+
317
+
318
+
319
+
320
+
321
+
322
+
323
+
324
+
325
+
326
+
327
+ * @example
328
+ * // Check key existence
329
+ * const sl = new SkipList<number, string>([[1, 'a'], [3, 'c'], [5, 'e']]);
330
+ * console.log(sl.has(3)); // true;
331
+ * console.log(sl.has(4)); // false;
332
+ */
333
+ override has(key: K): boolean {
334
+ return this._findNode(key) !== undefined;
335
+ }
336
+
337
+ /**
338
+ * Delete a key. Returns `true` if the key was found and removed.
339
+
340
+
341
+
342
+
343
+
344
+
345
+
346
+
347
+
348
+
349
+
350
+
351
+ * @example
352
+ * // Fast lookup with deletion
353
+ * const cache = new SkipList<string, number>();
354
+ *
355
+ * cache.set('alpha', 1);
356
+ * cache.set('beta', 2);
357
+ * cache.set('gamma', 3);
358
+ *
359
+ * console.log(cache.has('beta')); // true;
360
+ * cache.delete('beta');
361
+ * console.log(cache.has('beta')); // false;
362
+ * console.log(cache.size); // 2;
363
+ */
364
+ delete(key: K): boolean {
365
+ const cmp = this.#comparator;
366
+ const update = this._findUpdate(key);
367
+
368
+ const target = update[0].forward[0];
369
+ if (!target || cmp(target.key, key) !== 0) return false;
370
+
371
+ for (let i = 0; i < this._level; i++) {
372
+ if (update[i].forward[i] !== target) break;
373
+ update[i].forward[i] = target.forward[i];
374
+ }
375
+
376
+ while (this._level > 0 && !this._head.forward[this._level - 1]) {
377
+ this._level--;
86
378
  }
379
+
380
+ this._size--;
381
+ return true;
87
382
  }
88
383
 
89
- get(key: K): V | undefined {
90
- let current = this.head;
91
- for (let i = this.level - 1; i >= 0; i--) {
92
- while (current.forward[i] && current.forward[i].key < key) {
93
- current = current.forward[i];
384
+ // ─── Navigation ──────────────────────────────────────────────
385
+
386
+ /**
387
+ * Returns the first (smallest key) entry, or `undefined` if empty.
388
+
389
+
390
+
391
+
392
+
393
+
394
+
395
+
396
+
397
+
398
+
399
+
400
+ * @example
401
+ * // Access the minimum entry
402
+ * const sl = new SkipList<number, string>([[5, 'e'], [1, 'a'], [3, 'c']]);
403
+ * console.log(sl.first()); // [1, 'a'];
404
+ */
405
+ first(): [K, V | undefined] | undefined {
406
+ const node = this._head.forward[0];
407
+ return node ? [node.key, node.value] : undefined;
408
+ }
409
+
410
+ /**
411
+ * Returns the last (largest key) entry, or `undefined` if empty.
412
+
413
+
414
+
415
+
416
+
417
+
418
+
419
+
420
+
421
+
422
+
423
+
424
+ * @example
425
+ * // Access the maximum entry
426
+ * const sl = new SkipList<number, string>([[5, 'e'], [1, 'a'], [3, 'c']]);
427
+ * console.log(sl.last()); // [5, 'e'];
428
+ */
429
+ last(): [K, V | undefined] | undefined {
430
+ let current = this._head;
431
+ for (let i = this._level - 1; i >= 0; i--) {
432
+ while (current.forward[i]) {
433
+ current = current.forward[i]!;
94
434
  }
95
435
  }
436
+ return current === this._head ? undefined : [current.key, current.value];
437
+ }
438
+
439
+ /**
440
+ * Remove and return the first (smallest key) entry.
441
+
442
+
443
+
444
+
445
+
446
+
447
+
448
+
449
+
450
+ * @example
451
+ * // Remove and return smallest
452
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b'], [3, 'c']]);
453
+ * console.log(sl.pollFirst()); // [1, 'a'];
454
+ * console.log(sl.size); // 2;
455
+ */
456
+ pollFirst(): [K, V | undefined] | undefined {
457
+ const entry = this.first();
458
+ if (!entry) return undefined;
459
+ this.delete(entry[0]);
460
+ return entry;
461
+ }
462
+
463
+ /**
464
+ * Remove and return the last (largest key) entry.
465
+
466
+
467
+
468
+
469
+
470
+
471
+
472
+
473
+
474
+ * @example
475
+ * // Remove and return largest
476
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b'], [3, 'c']]);
477
+ * console.log(sl.pollLast()); // [3, 'c'];
478
+ * console.log(sl.size); // 2;
479
+ */
480
+ pollLast(): [K, V | undefined] | undefined {
481
+ const entry = this.last();
482
+ if (!entry) return undefined;
483
+ this.delete(entry[0]);
484
+ return entry;
485
+ }
96
486
 
97
- current = current.forward[0];
487
+ /**
488
+ * Least entry ≥ key, or `undefined`.
489
+
490
+
491
+
492
+
493
+
494
+
495
+
496
+
497
+
498
+
499
+
500
+
501
+ * @example
502
+ * // Least entry ≥ key
503
+ * const sl = new SkipList<number, string>([[10, 'a'], [20, 'b'], [30, 'c']]);
504
+ * console.log(sl.ceiling(15)); // [20, 'b'];
505
+ * console.log(sl.ceiling(20)); // [20, 'b'];
506
+ */
507
+ ceiling(key: K): [K, V | undefined] | undefined {
508
+ const cmp = this.#comparator;
509
+ let current = this._head;
510
+ for (let i = this._level - 1; i >= 0; i--) {
511
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) < 0) {
512
+ current = current.forward[i]!;
513
+ }
514
+ }
515
+ const node = current.forward[0];
516
+ return node ? [node.key, node.value] : undefined;
517
+ }
98
518
 
99
- if (current && current.key === key) {
100
- return current.value;
519
+ /**
520
+ * Greatest entry ≤ key, or `undefined`.
521
+
522
+
523
+
524
+
525
+
526
+
527
+
528
+
529
+
530
+
531
+
532
+
533
+ * @example
534
+ * // Greatest entry ≤ key
535
+ * const sl = new SkipList<number, string>([[10, 'a'], [20, 'b'], [30, 'c']]);
536
+ * console.log(sl.floor(25)); // [20, 'b'];
537
+ * console.log(sl.floor(5)); // undefined;
538
+ */
539
+ floor(key: K): [K, V | undefined] | undefined {
540
+ const cmp = this.#comparator;
541
+ let current = this._head;
542
+ for (let i = this._level - 1; i >= 0; i--) {
543
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) <= 0) {
544
+ current = current.forward[i]!;
545
+ }
101
546
  }
547
+ const result = current === this._head ? undefined : current;
102
548
 
549
+ // Check if we're exactly at or before key
550
+ if (result && cmp(result.key, key) <= 0) return [result.key, result.value];
103
551
  return undefined;
104
552
  }
105
553
 
106
- has(key: K): boolean {
107
- return this.get(key) !== undefined;
554
+ /**
555
+ * Least entry strictly > key, or `undefined`.
556
+
557
+
558
+
559
+
560
+
561
+
562
+
563
+
564
+
565
+ * @example
566
+ * // Strictly greater entry
567
+ * const sl = new SkipList<number, string>([[10, 'a'], [20, 'b'], [30, 'c']]);
568
+ * console.log(sl.higher(15)); // [20, 'b'];
569
+ * console.log(sl.higher(30)); // undefined;
570
+ */
571
+ higher(key: K): [K, V | undefined] | undefined {
572
+ const cmp = this.#comparator;
573
+ let current = this._head;
574
+ for (let i = this._level - 1; i >= 0; i--) {
575
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) <= 0) {
576
+ current = current.forward[i]!;
577
+ }
578
+ }
579
+ const node = current.forward[0];
580
+ return node ? [node.key, node.value] : undefined;
108
581
  }
109
582
 
110
- delete(key: K): boolean {
111
- const update: SkipListNode<K, V>[] = new Array(this.maxLevel).fill(this.head);
112
- let current = this.head;
583
+ /**
584
+ * Greatest entry strictly < key, or `undefined`.
585
+
586
+
587
+
588
+
589
+
590
+
591
+
592
+
593
+
594
+ * @example
595
+ * // Strictly less entry
596
+ * const sl = new SkipList<number, string>([[10, 'a'], [20, 'b'], [30, 'c']]);
597
+ * console.log(sl.lower(25)); // [20, 'b'];
598
+ * console.log(sl.lower(10)); // undefined;
599
+ */
600
+ lower(key: K): [K, V | undefined] | undefined {
601
+ const cmp = this.#comparator;
602
+ let current = this._head;
603
+ let result: SkipListNode<K, V> | undefined;
604
+ for (let i = this._level - 1; i >= 0; i--) {
605
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) < 0) {
606
+ current = current.forward[i]!;
607
+ }
608
+ if (current !== this._head && cmp(current.key, key) < 0) {
609
+ result = current;
610
+ }
611
+ }
612
+ return result ? [result.key, result.value] : undefined;
613
+ }
113
614
 
114
- for (let i = this.level - 1; i >= 0; i--) {
115
- while (current.forward[i] && current.forward[i].key < key) {
116
- current = current.forward[i];
615
+ /**
616
+ * Returns entries within the given key range.
617
+
618
+
619
+
620
+
621
+
622
+
623
+
624
+
625
+
626
+
627
+
628
+
629
+ * @example
630
+ * // Find entries in a range
631
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b'], [3, 'c'], [4, 'd'], [5, 'e']]);
632
+ * const result = sl.rangeSearch([2, 4]);
633
+ * console.log(result); // [[2, 'b'], [3, 'c'], [4, 'd']];
634
+ */
635
+ rangeSearch(range: [K, K], options: SkipListRangeOptions = {}): Array<[K, V | undefined]> {
636
+ const { lowInclusive = true, highInclusive = true } = options;
637
+ const [low, high] = range;
638
+ const cmp = this.#comparator;
639
+ const out: Array<[K, V | undefined]> = [];
640
+
641
+ // Start from the first node >= low
642
+ let current = this._head;
643
+ for (let i = this._level - 1; i >= 0; i--) {
644
+ while (current.forward[i] && cmp(current.forward[i]!.key, low) < 0) {
645
+ current = current.forward[i]!;
117
646
  }
118
- update[i] = current;
119
647
  }
648
+ current = current.forward[0]!;
120
649
 
121
- current = current.forward[0];
650
+ while (current) {
651
+ const cmpHigh = cmp(current.key, high);
652
+ if (cmpHigh > 0) break;
653
+ if (cmpHigh === 0 && !highInclusive) break;
122
654
 
123
- if (current && current.key === key) {
124
- for (let i = 0; i < this.level; i++) {
125
- if (update[i].forward[i] !== current) {
126
- break;
127
- }
128
- update[i].forward[i] = current.forward[i];
129
- }
130
- while (this.level > 0 && !this.head.forward[this.level - 1]) {
131
- this._level--;
655
+ const cmpLow = cmp(current.key, low);
656
+ if (cmpLow > 0 || (cmpLow === 0 && lowInclusive)) {
657
+ out.push([current.key, current.value]);
132
658
  }
133
- return true;
659
+
660
+ current = current.forward[0]!;
134
661
  }
135
662
 
136
- return false;
663
+ return out;
137
664
  }
138
665
 
139
- higher(key: K): V | undefined {
140
- let current = this.head;
141
- for (let i = this.level - 1; i >= 0; i--) {
142
- while (current.forward[i] && current.forward[i].key <= key) {
143
- current = current.forward[i];
144
- }
666
+ // ─── Functional (overrides) ──────────────────────────────────
667
+
668
+ /**
669
+ * Creates a new SkipList with entries transformed by callback.
670
+
671
+
672
+
673
+
674
+
675
+
676
+
677
+
678
+
679
+ * @example
680
+ * // Transform entries
681
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b']]);
682
+ * const mapped = sl.map((v, k) => [k, v?.toUpperCase()] as [number, string]);
683
+ * console.log([...mapped.values()]); // ['A', 'B'];
684
+ */
685
+ map<MK, MV>(
686
+ callback: EntryCallback<K, V | undefined, [MK, MV]>,
687
+ options?: SkipListOptions<MK, MV>
688
+ ): SkipList<MK, MV> {
689
+ const out = new SkipList<MK, MV>([], options ?? {});
690
+ let i = 0;
691
+ for (const [k, v] of this) {
692
+ const [nk, nv] = callback(v, k, i++, this);
693
+ out.set(nk, nv);
694
+ }
695
+ return out;
696
+ }
697
+
698
+ /**
699
+ * Creates a new SkipList with entries that pass the predicate.
700
+
701
+
702
+
703
+
704
+
705
+
706
+
707
+
708
+
709
+ * @example
710
+ * // Filter entries
711
+ * const sl = new SkipList<number, string>([[1, 'a'], [2, 'b'], [3, 'c']]);
712
+ * const result = sl.filter((v, k) => k > 1);
713
+ * console.log(result.size); // 2;
714
+ */
715
+ filter(
716
+ callbackfn: EntryCallback<K, V | undefined, boolean>,
717
+ thisArg?: unknown
718
+ ): this {
719
+ const out = new SkipList<K, V, R>([], {
720
+ comparator: this.#isDefaultComparator ? undefined : this.#comparator,
721
+ maxLevel: this._maxLevel,
722
+ probability: this._probability
723
+ });
724
+ let i = 0;
725
+ for (const [k, v] of this) {
726
+ const ok = callbackfn.call(thisArg, v, k, i++, this);
727
+ if (ok) out.set(k, v as V);
145
728
  }
146
- const nextNode = current.forward[0];
147
- return nextNode ? nextNode.value : undefined;
729
+ return out as this;
148
730
  }
149
731
 
150
- lower(key: K): V | undefined {
151
- let current = this.head;
152
- let lastLess = undefined;
732
+ // ─── Iterator (required by IterableEntryBase) ────────────────
153
733
 
154
- for (let i = this.level - 1; i >= 0; i--) {
155
- while (current.forward[i] && current.forward[i].key < key) {
156
- current = current.forward[i];
734
+ protected _getIterator(): IterableIterator<[K, V | undefined]> {
735
+ const head = this._head;
736
+ return (function* () {
737
+ let node = head.forward[0];
738
+ while (node) {
739
+ yield [node.key, node.value] as [K, V | undefined];
740
+ node = node.forward[0];
157
741
  }
158
- if (current.key < key) {
159
- lastLess = current;
742
+ })();
743
+ }
744
+
745
+ // ─── Internal helpers ────────────────────────────────────────
746
+
747
+ /**
748
+ * Finds the update array (predecessors at each level) for a given key.
749
+ */
750
+ protected _findUpdate(key: K): SkipListNode<K, V>[] {
751
+ const cmp = this.#comparator;
752
+ const update: SkipListNode<K, V>[] = new Array(this._maxLevel).fill(this._head);
753
+ let current = this._head;
754
+
755
+ for (let i = this._level - 1; i >= 0; i--) {
756
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) < 0) {
757
+ current = current.forward[i]!;
160
758
  }
759
+ update[i] = current;
161
760
  }
162
761
 
163
- return lastLess ? lastLess.value : undefined;
762
+ return update;
763
+ }
764
+
765
+ /**
766
+ * Finds the node for a given key, or undefined.
767
+ */
768
+ protected _findNode(key: K): SkipListNode<K, V> | undefined {
769
+ const cmp = this.#comparator;
770
+ let current = this._head;
771
+
772
+ for (let i = this._level - 1; i >= 0; i--) {
773
+ while (current.forward[i] && cmp(current.forward[i]!.key, key) < 0) {
774
+ current = current.forward[i]!;
775
+ }
776
+ }
777
+
778
+ const candidate = current.forward[0];
779
+ if (candidate && cmp(candidate.key, key) === 0) return candidate;
780
+ return undefined;
164
781
  }
165
782
 
166
783
  protected _randomLevel(): number {
167
784
  let level = 1;
168
- while (Math.random() < this.probability && level < this.maxLevel) {
785
+ while (Math.random() < this._probability && level < this._maxLevel) {
169
786
  level++;
170
787
  }
171
788
  return level;