d-ary-heap 2.2.0 → 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.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,254 @@
1
+ /**
2
+ * Instrumentation utilities for d-ary heap performance analysis.
3
+ *
4
+ * This module provides opt-in instrumentation to count comparisons performed
5
+ * during heap operations. It is designed for:
6
+ *
7
+ * - **Educational purposes**: Understanding the theoretical vs actual cost of heap operations
8
+ * - **Benchmarking**: Measuring real comparison counts across different arities
9
+ * - **Visualization**: Powering interactive demos that show heap behavior
10
+ *
11
+ * ## Design Philosophy: Zero-Cost When Disabled
12
+ *
13
+ * Instrumentation follows these principles:
14
+ *
15
+ * 1. **Opt-in only**: No overhead when not using instrumentation
16
+ * 2. **Non-breaking**: Existing code continues to work unchanged
17
+ * 3. **Per-operation tracking**: Distinguish insert/pop/decreasePriority comparisons
18
+ *
19
+ * ## Cross-Language Consistency
20
+ *
21
+ * Currently, instrumentation is implemented in TypeScript only. The table below
22
+ * shows the idiomatic zero-cost approach for each language, planned for v2.5.0:
23
+ *
24
+ * | Language | Mechanism | Overhead When Disabled | Status |
25
+ * |------------|----------------------------------|------------------------|--------|
26
+ * | TypeScript | Optional hooks + instrumented comparator | Zero (JIT optimization) | ✅ Implemented |
27
+ * | Go | Nil stats pointer | ~1 cycle (nil check) | Planned v2.5.0 |
28
+ * | Rust | Generic over StatsCollector trait | Zero (monomorphization) | Planned v2.5.0 |
29
+ * | C++ | Template policy class | Zero (inlining) | Planned v2.5.0 |
30
+ * | Zig | Comptime bool parameter | Zero (branch elimination) | Planned v2.5.0 |
31
+ *
32
+ * ## Usage Example
33
+ *
34
+ * ```typescript
35
+ * import { PriorityQueue, minBy, instrumentComparator } from 'd-ary-heap';
36
+ *
37
+ * // 1. Wrap your comparator with instrumentation
38
+ * const comparator = instrumentComparator(minBy((v: Vertex) => v.distance));
39
+ *
40
+ * // 2. Create priority queue with operation hooks
41
+ * const pq = new PriorityQueue({
42
+ * d: 4,
43
+ * comparator,
44
+ * keyExtractor: (v) => v.id,
45
+ * onBeforeOperation: (op) => comparator.startOperation(op),
46
+ * onAfterOperation: () => comparator.endOperation(),
47
+ * });
48
+ *
49
+ * // 3. Use normally - comparisons are tracked automatically
50
+ * pq.insert({ id: 'A', distance: 0 });
51
+ * pq.insert({ id: 'B', distance: 5 });
52
+ * pq.pop();
53
+ *
54
+ * // 4. Access statistics
55
+ * console.log(comparator.stats);
56
+ * // { insert: 1, pop: 2, decreasePriority: 0, increasePriority: 0, updatePriority: 0, total: 3 }
57
+ *
58
+ * // 5. Reset for next measurement
59
+ * comparator.stats.reset();
60
+ * ```
61
+ *
62
+ * ## Theoretical Complexity Reference
63
+ *
64
+ * For a d-ary heap with n elements:
65
+ *
66
+ * | Operation | Comparisons (worst case) |
67
+ * |------------------|-------------------------------|
68
+ * | insert | ⌊log_d(n)⌋ |
69
+ * | pop | d × ⌊log_d(n)⌋ |
70
+ * | increasePriority | ⌊log_d(n)⌋ (moveUp only) |
71
+ * | decreasePriority | d × ⌊log_d(n)⌋ (moveDown only)|
72
+ * | updatePriority | (d+1) × ⌊log_d(n)⌋ (both) |
73
+ *
74
+ * The demo visualization compares actual counts against these theoretical bounds.
75
+ *
76
+ * @module instrumentation
77
+ * @version 2.4.0
78
+ * @license Apache-2.0
79
+ */
80
+
81
+ /**
82
+ * Operation types that can be tracked.
83
+ *
84
+ * Note: `updatePriority` is tracked separately for when the caller doesn't know
85
+ * whether priority increased or decreased (checks both directions).
86
+ */
87
+ type OperationType = 'insert' | 'pop' | 'decreasePriority' | 'increasePriority' | 'updatePriority';
88
+ /**
89
+ * Statistics tracking comparison counts per operation type.
90
+ *
91
+ * All counts start at zero and accumulate until `reset()` is called.
92
+ */
93
+ interface ComparisonStats {
94
+ /** Comparisons during insert operations (moveUp) */
95
+ insert: number;
96
+ /** Comparisons during pop operations (moveDown + bestChildPosition) */
97
+ pop: number;
98
+ /** Comparisons during decreasePriority operations (moveDown only) */
99
+ decreasePriority: number;
100
+ /** Comparisons during increasePriority operations (moveUp only) */
101
+ increasePriority: number;
102
+ /** Comparisons during updatePriority operations (moveUp + moveDown) */
103
+ updatePriority: number;
104
+ /** Total comparisons across all operation types */
105
+ readonly total: number;
106
+ /** Reset all counters to zero */
107
+ reset(): void;
108
+ }
109
+ /**
110
+ * An instrumented comparator that tracks comparison counts.
111
+ *
112
+ * This extends a regular comparator with:
113
+ * - `stats`: Current comparison counts
114
+ * - `startOperation(type)`: Begin tracking for an operation
115
+ * - `endOperation()`: Stop tracking current operation
116
+ *
117
+ * The comparator itself remains a valid `Comparator<T>` and can be used
118
+ * anywhere a regular comparator is expected.
119
+ */
120
+ interface InstrumentedComparator<T> extends Comparator<T> {
121
+ /** Current comparison statistics */
122
+ readonly stats: ComparisonStats;
123
+ /**
124
+ * Signal the start of a heap operation.
125
+ * Comparisons will be attributed to this operation type until `endOperation()`.
126
+ *
127
+ * @param type - The operation type being started
128
+ */
129
+ startOperation(type: OperationType): void;
130
+ /**
131
+ * Signal the end of the current heap operation.
132
+ * Subsequent comparisons will not be counted until the next `startOperation()`.
133
+ */
134
+ endOperation(): void;
135
+ }
136
+ /**
137
+ * Create comparison statistics tracker.
138
+ *
139
+ * @returns Fresh stats object with all counts at zero
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * const stats = createComparisonStats();
144
+ * stats.insert = 5;
145
+ * stats.pop = 10;
146
+ * console.log(stats.total); // 15
147
+ * stats.reset();
148
+ * console.log(stats.total); // 0
149
+ * ```
150
+ */
151
+ declare function createComparisonStats(): ComparisonStats;
152
+ /**
153
+ * Wrap a comparator with instrumentation to track comparison counts.
154
+ *
155
+ * The returned comparator:
156
+ * - Behaves identically to the original for comparison purposes
157
+ * - Tracks how many times it's called, attributed to operation types
158
+ * - Has zero overhead when `startOperation()` hasn't been called
159
+ *
160
+ * ## How It Works
161
+ *
162
+ * 1. Call `startOperation('insert')` before `pq.insert()`
163
+ * 2. The comparator increments `stats.insert` for each comparison
164
+ * 3. Call `endOperation()` after the operation completes
165
+ * 4. Repeat for other operations
166
+ *
167
+ * The `PriorityQueue` class supports `onBeforeOperation` and `onAfterOperation`
168
+ * hooks to automate this.
169
+ *
170
+ * ## Performance Note
171
+ *
172
+ * When `currentOperation` is null (between operations), the instrumented
173
+ * comparator performs only a single null check before calling the original.
174
+ * Modern JavaScript engines optimize this extremely well.
175
+ *
176
+ * @param comparator - The original comparator to instrument
177
+ * @returns An instrumented comparator with stats tracking
178
+ *
179
+ * @example
180
+ * ```typescript
181
+ * import { minBy, instrumentComparator } from 'd-ary-heap';
182
+ *
183
+ * const cmp = instrumentComparator(minBy<number, number>(x => x));
184
+ *
185
+ * // Manual usage (without hooks)
186
+ * cmp.startOperation('insert');
187
+ * console.log(cmp(5, 3)); // false, and stats.insert++
188
+ * console.log(cmp(3, 5)); // true, and stats.insert++
189
+ * cmp.endOperation();
190
+ *
191
+ * console.log(cmp.stats.insert); // 2
192
+ * ```
193
+ */
194
+ declare function instrumentComparator<T>(comparator: Comparator<T>): InstrumentedComparator<T>;
195
+ /**
196
+ * Calculate theoretical comparison count for an insert operation.
197
+ *
198
+ * Insert performs at most ⌊log_d(n)⌋ comparisons (one per level during moveUp).
199
+ *
200
+ * @param n - Number of elements in heap AFTER insert
201
+ * @param d - Heap arity
202
+ * @returns Theoretical worst-case comparison count
203
+ */
204
+ declare function theoreticalInsertComparisons(n: number, d: number): number;
205
+ /**
206
+ * Calculate theoretical comparison count for a pop operation.
207
+ *
208
+ * Pop performs at most d × ⌊log_d(n)⌋ comparisons:
209
+ * - At each level, find best among d children (d-1 comparisons)
210
+ * - Compare best child with current (1 comparison)
211
+ * - Total: d comparisons per level × ⌊log_d(n)⌋ levels
212
+ *
213
+ * @param n - Number of elements in heap BEFORE pop
214
+ * @param d - Heap arity
215
+ * @returns Theoretical worst-case comparison count
216
+ */
217
+ declare function theoreticalPopComparisons(n: number, d: number): number;
218
+ /**
219
+ * Calculate theoretical comparison count for an increasePriority operation.
220
+ *
221
+ * IncreasePriority performs only moveUp (item became more important).
222
+ * Worst case: ⌊log_d(n)⌋ comparisons (one per level).
223
+ *
224
+ * @param n - Number of elements in heap
225
+ * @param d - Heap arity
226
+ * @returns Theoretical worst-case comparison count
227
+ */
228
+ declare function theoreticalIncreasePriorityComparisons(n: number, d: number): number;
229
+ /**
230
+ * Calculate theoretical comparison count for a decreasePriority operation.
231
+ *
232
+ * DecreasePriority performs only moveDown (item became less important).
233
+ * Worst case: d × ⌊log_d(n)⌋ comparisons.
234
+ *
235
+ * @param n - Number of elements in heap
236
+ * @param d - Heap arity
237
+ * @returns Theoretical worst-case comparison count
238
+ */
239
+ declare function theoreticalDecreasePriorityComparisons(n: number, d: number): number;
240
+ /**
241
+ * Calculate theoretical comparison count for an updatePriority operation.
242
+ *
243
+ * UpdatePriority performs both moveUp and moveDown (direction unknown).
244
+ * Worst case: (d + 1) × ⌊log_d(n)⌋ comparisons.
245
+ *
246
+ * @param n - Number of elements in heap
247
+ * @param d - Heap arity
248
+ * @returns Theoretical worst-case comparison count
249
+ */
250
+ declare function theoreticalUpdatePriorityComparisons(n: number, d: number): number;
251
+
1
252
  /**
2
253
  * d-ary Heap Priority Queue - TypeScript Implementation
3
254
  *
@@ -6,13 +257,16 @@
6
257
  * - Min-heap or max-heap behavior via comparator functions
7
258
  * - O(1) item lookup using Map for efficient priority updates
8
259
  * - O(1) access to highest-priority item
9
- * - O(log_d n) insert and priority increase operations
10
- * - O(d · log_d n) pop and priority decrease operations
260
+ * - O(log_d n) insert and increasePriority operations
261
+ * - O(d · log_d n) pop and decreasePriority operations
262
+ * - O((d+1) · log_d n) updatePriority (bidirectional)
263
+ * - Optional instrumentation hooks for performance analysis
11
264
  *
12
- * @version 2.2.0
265
+ * @version 2.4.0
13
266
  * @license Apache-2.0
14
267
  * @copyright 2023-2025 Eric Jacopin
15
268
  */
269
+
16
270
  /** Type alias for position indices (cross-language consistency) */
17
271
  type Position = number;
18
272
  /**
@@ -37,6 +291,35 @@ interface PriorityQueueOptions<T, K> {
37
291
  keyExtractor: KeyExtractor<T, K>;
38
292
  /** Initial capacity hint for pre-allocation */
39
293
  initialCapacity?: number;
294
+ /**
295
+ * Optional hook called before each heap operation.
296
+ *
297
+ * This enables opt-in instrumentation for performance analysis without
298
+ * adding overhead when not used. Pair with an instrumented comparator
299
+ * to track comparison counts per operation type.
300
+ *
301
+ * @param type - The operation about to be performed
302
+ *
303
+ * @example
304
+ * ```typescript
305
+ * const cmp = instrumentComparator(minBy((v) => v.priority));
306
+ * const pq = new PriorityQueue({
307
+ * comparator: cmp,
308
+ * keyExtractor: (v) => v.id,
309
+ * onBeforeOperation: (op) => cmp.startOperation(op),
310
+ * onAfterOperation: () => cmp.endOperation(),
311
+ * });
312
+ * ```
313
+ *
314
+ * @see instrumentComparator from './instrumentation'
315
+ */
316
+ onBeforeOperation?: (type: OperationType) => void;
317
+ /**
318
+ * Optional hook called after each heap operation completes.
319
+ *
320
+ * @see onBeforeOperation for usage example
321
+ */
322
+ onAfterOperation?: () => void;
40
323
  }
41
324
  /**
42
325
  * Generic d-ary heap priority queue with O(1) lookup.
@@ -56,6 +339,7 @@ interface PriorityQueueOptions<T, K> {
56
339
  * - pop(): O(d · log_d n)
57
340
  * - increasePriority(): O(log_d n)
58
341
  * - decreasePriority(): O(d · log_d n)
342
+ * - updatePriority(): O((d+1) · log_d n)
59
343
  * - contains(): O(1)
60
344
  * - len(), isEmpty(), d(): O(1)
61
345
  *
@@ -73,6 +357,10 @@ declare class PriorityQueue<T, K = string | number> {
73
357
  private readonly comparator;
74
358
  /** Key extractor for identity-based lookup */
75
359
  private readonly keyExtractor;
360
+ /** Optional hook called before operations (for instrumentation) */
361
+ private readonly onBeforeOperation;
362
+ /** Optional hook called after operations (for instrumentation) */
363
+ private readonly onAfterOperation;
76
364
  /**
77
365
  * Create a new d-ary heap priority queue.
78
366
  *
@@ -222,6 +510,21 @@ declare class PriorityQueue<T, K = string | number> {
222
510
  increasePriorityByIndex(index: number): void;
223
511
  /** Alias for increasePriorityByIndex() - snake_case for cross-language consistency */
224
512
  increase_priority_by_index(index: number): void;
513
+ /**
514
+ * Decrease the priority of the item at the given index.
515
+ * Time complexity: O(d · log_d n)
516
+ *
517
+ * @param index - Index of the item in the heap array
518
+ * @throws Error if index is out of bounds
519
+ *
520
+ * @remarks
521
+ * This is a lower-level method. Prefer decreasePriority() with the item itself.
522
+ * The item at the given index should already have its priority value updated
523
+ * in the container before calling this method.
524
+ */
525
+ decreasePriorityByIndex(index: number): void;
526
+ /** Alias for decreasePriorityByIndex() - snake_case for cross-language consistency */
527
+ decrease_priority_by_index(index: number): void;
225
528
  /**
226
529
  * Decrease the priority of an existing item (move toward leaves).
227
530
  * Time complexity: O(d · log_d n)
@@ -232,11 +535,26 @@ declare class PriorityQueue<T, K = string | number> {
232
535
  * @remarks
233
536
  * For min-heap: increasing the priority value decreases importance.
234
537
  * For max-heap: decreasing the priority value decreases importance.
235
- * This method checks both directions for robustness.
538
+ * This method only moves items downward. Use updatePriority() if direction is unknown.
236
539
  */
237
540
  decreasePriority(updatedItem: T): void;
238
541
  /** Alias for decreasePriority() - snake_case for cross-language consistency */
239
542
  decrease_priority(updatedItem: T): void;
543
+ /**
544
+ * Update the priority of an existing item when direction is unknown.
545
+ * Time complexity: O((d+1) · log_d n) - checks both directions
546
+ *
547
+ * @param updatedItem - Item with same identity but updated priority
548
+ * @throws Error if item not found
549
+ *
550
+ * @remarks
551
+ * Use this method when you don't know whether the priority increased or decreased.
552
+ * If you know the direction, prefer increasePriority() or decreasePriority() for
553
+ * better performance (log_d n vs (d+1) · log_d n comparisons).
554
+ */
555
+ updatePriority(updatedItem: T): void;
556
+ /** Alias for updatePriority() - snake_case for cross-language consistency */
557
+ update_priority(updatedItem: T): void;
240
558
  /**
241
559
  * Remove and return the highest-priority item.
242
560
  * Time complexity: O(d · log_d n)
@@ -305,7 +623,7 @@ declare class PriorityQueue<T, K = string | number> {
305
623
  * Pre-built comparator factories for common use cases.
306
624
  *
307
625
  * @module comparators
308
- * @version 2.2.0
626
+ * @version 2.4.0
309
627
  * @license Apache-2.0
310
628
  */
311
629
 
@@ -385,4 +703,4 @@ declare function reverse<T>(cmp: Comparator<T>): Comparator<T>;
385
703
  */
386
704
  declare function chain<T>(...comparators: Comparator<T>[]): Comparator<T>;
387
705
 
388
- export { type Comparator, type KeyExtractor, type Position, PriorityQueue, type PriorityQueueOptions, chain, maxBy, maxNumber, maxString, minBy, minNumber, minString, reverse };
706
+ export { type Comparator, type ComparisonStats, type InstrumentedComparator, type KeyExtractor, type OperationType, type Position, PriorityQueue, type PriorityQueueOptions, chain, createComparisonStats, instrumentComparator, maxBy, maxNumber, maxString, minBy, minNumber, minString, reverse, theoreticalDecreasePriorityComparisons, theoreticalIncreasePriorityComparisons, theoreticalInsertComparisons, theoreticalPopComparisons, theoreticalUpdatePriorityComparisons };