d-ary-heap 2.2.0 → 2.4.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/README.md +82 -1
- package/dist/index.cjs +161 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +265 -3
- package/dist/index.d.ts +265 -3
- package/dist/index.js +157 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/PriorityQueue.ts +63 -1
- package/src/comparators.ts +1 -1
- package/src/index.ts +16 -1
- package/src/instrumentation.ts +302 -0
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,230 @@
|
|
|
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, 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
|
+
* | decreasePriority | ⌊log_d(n)⌋ (upward only) |
|
|
71
|
+
* | increasePriority | d × ⌊log_d(n)⌋ (downward) |
|
|
72
|
+
*
|
|
73
|
+
* The demo visualization compares actual counts against these theoretical bounds.
|
|
74
|
+
*
|
|
75
|
+
* @module instrumentation
|
|
76
|
+
* @version 2.4.0
|
|
77
|
+
* @license Apache-2.0
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Operation types that can be tracked.
|
|
82
|
+
*
|
|
83
|
+
* Note: `increasePriority` is tracked separately because in Dijkstra's algorithm
|
|
84
|
+
* it manifests as decreasePriority (lowering distance = higher priority in min-heap).
|
|
85
|
+
*/
|
|
86
|
+
type OperationType = 'insert' | 'pop' | 'decreasePriority' | 'increasePriority';
|
|
87
|
+
/**
|
|
88
|
+
* Statistics tracking comparison counts per operation type.
|
|
89
|
+
*
|
|
90
|
+
* All counts start at zero and accumulate until `reset()` is called.
|
|
91
|
+
*/
|
|
92
|
+
interface ComparisonStats {
|
|
93
|
+
/** Comparisons during insert operations (moveUp) */
|
|
94
|
+
insert: number;
|
|
95
|
+
/** Comparisons during pop operations (moveDown + bestChildPosition) */
|
|
96
|
+
pop: number;
|
|
97
|
+
/** Comparisons during decreasePriority operations (moveUp + moveDown) */
|
|
98
|
+
decreasePriority: number;
|
|
99
|
+
/** Comparisons during increasePriority operations (moveUp) */
|
|
100
|
+
increasePriority: number;
|
|
101
|
+
/** Total comparisons across all operation types */
|
|
102
|
+
readonly total: number;
|
|
103
|
+
/** Reset all counters to zero */
|
|
104
|
+
reset(): void;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* An instrumented comparator that tracks comparison counts.
|
|
108
|
+
*
|
|
109
|
+
* This extends a regular comparator with:
|
|
110
|
+
* - `stats`: Current comparison counts
|
|
111
|
+
* - `startOperation(type)`: Begin tracking for an operation
|
|
112
|
+
* - `endOperation()`: Stop tracking current operation
|
|
113
|
+
*
|
|
114
|
+
* The comparator itself remains a valid `Comparator<T>` and can be used
|
|
115
|
+
* anywhere a regular comparator is expected.
|
|
116
|
+
*/
|
|
117
|
+
interface InstrumentedComparator<T> extends Comparator<T> {
|
|
118
|
+
/** Current comparison statistics */
|
|
119
|
+
readonly stats: ComparisonStats;
|
|
120
|
+
/**
|
|
121
|
+
* Signal the start of a heap operation.
|
|
122
|
+
* Comparisons will be attributed to this operation type until `endOperation()`.
|
|
123
|
+
*
|
|
124
|
+
* @param type - The operation type being started
|
|
125
|
+
*/
|
|
126
|
+
startOperation(type: OperationType): void;
|
|
127
|
+
/**
|
|
128
|
+
* Signal the end of the current heap operation.
|
|
129
|
+
* Subsequent comparisons will not be counted until the next `startOperation()`.
|
|
130
|
+
*/
|
|
131
|
+
endOperation(): void;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Create comparison statistics tracker.
|
|
135
|
+
*
|
|
136
|
+
* @returns Fresh stats object with all counts at zero
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```typescript
|
|
140
|
+
* const stats = createComparisonStats();
|
|
141
|
+
* stats.insert = 5;
|
|
142
|
+
* stats.pop = 10;
|
|
143
|
+
* console.log(stats.total); // 15
|
|
144
|
+
* stats.reset();
|
|
145
|
+
* console.log(stats.total); // 0
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
declare function createComparisonStats(): ComparisonStats;
|
|
149
|
+
/**
|
|
150
|
+
* Wrap a comparator with instrumentation to track comparison counts.
|
|
151
|
+
*
|
|
152
|
+
* The returned comparator:
|
|
153
|
+
* - Behaves identically to the original for comparison purposes
|
|
154
|
+
* - Tracks how many times it's called, attributed to operation types
|
|
155
|
+
* - Has zero overhead when `startOperation()` hasn't been called
|
|
156
|
+
*
|
|
157
|
+
* ## How It Works
|
|
158
|
+
*
|
|
159
|
+
* 1. Call `startOperation('insert')` before `pq.insert()`
|
|
160
|
+
* 2. The comparator increments `stats.insert` for each comparison
|
|
161
|
+
* 3. Call `endOperation()` after the operation completes
|
|
162
|
+
* 4. Repeat for other operations
|
|
163
|
+
*
|
|
164
|
+
* The `PriorityQueue` class supports `onBeforeOperation` and `onAfterOperation`
|
|
165
|
+
* hooks to automate this.
|
|
166
|
+
*
|
|
167
|
+
* ## Performance Note
|
|
168
|
+
*
|
|
169
|
+
* When `currentOperation` is null (between operations), the instrumented
|
|
170
|
+
* comparator performs only a single null check before calling the original.
|
|
171
|
+
* Modern JavaScript engines optimize this extremely well.
|
|
172
|
+
*
|
|
173
|
+
* @param comparator - The original comparator to instrument
|
|
174
|
+
* @returns An instrumented comparator with stats tracking
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```typescript
|
|
178
|
+
* import { minBy, instrumentComparator } from 'd-ary-heap';
|
|
179
|
+
*
|
|
180
|
+
* const cmp = instrumentComparator(minBy<number, number>(x => x));
|
|
181
|
+
*
|
|
182
|
+
* // Manual usage (without hooks)
|
|
183
|
+
* cmp.startOperation('insert');
|
|
184
|
+
* console.log(cmp(5, 3)); // false, and stats.insert++
|
|
185
|
+
* console.log(cmp(3, 5)); // true, and stats.insert++
|
|
186
|
+
* cmp.endOperation();
|
|
187
|
+
*
|
|
188
|
+
* console.log(cmp.stats.insert); // 2
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
declare function instrumentComparator<T>(comparator: Comparator<T>): InstrumentedComparator<T>;
|
|
192
|
+
/**
|
|
193
|
+
* Calculate theoretical comparison count for an insert operation.
|
|
194
|
+
*
|
|
195
|
+
* Insert performs at most ⌊log_d(n)⌋ comparisons (one per level during moveUp).
|
|
196
|
+
*
|
|
197
|
+
* @param n - Number of elements in heap AFTER insert
|
|
198
|
+
* @param d - Heap arity
|
|
199
|
+
* @returns Theoretical worst-case comparison count
|
|
200
|
+
*/
|
|
201
|
+
declare function theoreticalInsertComparisons(n: number, d: number): number;
|
|
202
|
+
/**
|
|
203
|
+
* Calculate theoretical comparison count for a pop operation.
|
|
204
|
+
*
|
|
205
|
+
* Pop performs at most d × ⌊log_d(n)⌋ comparisons:
|
|
206
|
+
* - At each level, find best among d children (d-1 comparisons)
|
|
207
|
+
* - Compare best child with current (1 comparison)
|
|
208
|
+
* - Total: d comparisons per level × ⌊log_d(n)⌋ levels
|
|
209
|
+
*
|
|
210
|
+
* @param n - Number of elements in heap BEFORE pop
|
|
211
|
+
* @param d - Heap arity
|
|
212
|
+
* @returns Theoretical worst-case comparison count
|
|
213
|
+
*/
|
|
214
|
+
declare function theoreticalPopComparisons(n: number, d: number): number;
|
|
215
|
+
/**
|
|
216
|
+
* Calculate theoretical comparison count for a decreasePriority operation.
|
|
217
|
+
*
|
|
218
|
+
* DecreasePriority in our implementation calls both moveUp and moveDown
|
|
219
|
+
* for safety, but typically only moveUp executes (upward movement).
|
|
220
|
+
* Worst case: ⌊log_d(n)⌋ comparisons.
|
|
221
|
+
*
|
|
222
|
+
* @param n - Number of elements in heap
|
|
223
|
+
* @param d - Heap arity
|
|
224
|
+
* @returns Theoretical worst-case comparison count (moveUp path)
|
|
225
|
+
*/
|
|
226
|
+
declare function theoreticalDecreasePriorityComparisons(n: number, d: number): number;
|
|
227
|
+
|
|
1
228
|
/**
|
|
2
229
|
* d-ary Heap Priority Queue - TypeScript Implementation
|
|
3
230
|
*
|
|
@@ -8,11 +235,13 @@
|
|
|
8
235
|
* - O(1) access to highest-priority item
|
|
9
236
|
* - O(log_d n) insert and priority increase operations
|
|
10
237
|
* - O(d · log_d n) pop and priority decrease operations
|
|
238
|
+
* - Optional instrumentation hooks for performance analysis
|
|
11
239
|
*
|
|
12
|
-
* @version 2.
|
|
240
|
+
* @version 2.4.0
|
|
13
241
|
* @license Apache-2.0
|
|
14
242
|
* @copyright 2023-2025 Eric Jacopin
|
|
15
243
|
*/
|
|
244
|
+
|
|
16
245
|
/** Type alias for position indices (cross-language consistency) */
|
|
17
246
|
type Position = number;
|
|
18
247
|
/**
|
|
@@ -37,6 +266,35 @@ interface PriorityQueueOptions<T, K> {
|
|
|
37
266
|
keyExtractor: KeyExtractor<T, K>;
|
|
38
267
|
/** Initial capacity hint for pre-allocation */
|
|
39
268
|
initialCapacity?: number;
|
|
269
|
+
/**
|
|
270
|
+
* Optional hook called before each heap operation.
|
|
271
|
+
*
|
|
272
|
+
* This enables opt-in instrumentation for performance analysis without
|
|
273
|
+
* adding overhead when not used. Pair with an instrumented comparator
|
|
274
|
+
* to track comparison counts per operation type.
|
|
275
|
+
*
|
|
276
|
+
* @param type - The operation about to be performed
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```typescript
|
|
280
|
+
* const cmp = instrumentComparator(minBy((v) => v.priority));
|
|
281
|
+
* const pq = new PriorityQueue({
|
|
282
|
+
* comparator: cmp,
|
|
283
|
+
* keyExtractor: (v) => v.id,
|
|
284
|
+
* onBeforeOperation: (op) => cmp.startOperation(op),
|
|
285
|
+
* onAfterOperation: () => cmp.endOperation(),
|
|
286
|
+
* });
|
|
287
|
+
* ```
|
|
288
|
+
*
|
|
289
|
+
* @see instrumentComparator from './instrumentation'
|
|
290
|
+
*/
|
|
291
|
+
onBeforeOperation?: (type: OperationType) => void;
|
|
292
|
+
/**
|
|
293
|
+
* Optional hook called after each heap operation completes.
|
|
294
|
+
*
|
|
295
|
+
* @see onBeforeOperation for usage example
|
|
296
|
+
*/
|
|
297
|
+
onAfterOperation?: () => void;
|
|
40
298
|
}
|
|
41
299
|
/**
|
|
42
300
|
* Generic d-ary heap priority queue with O(1) lookup.
|
|
@@ -73,6 +331,10 @@ declare class PriorityQueue<T, K = string | number> {
|
|
|
73
331
|
private readonly comparator;
|
|
74
332
|
/** Key extractor for identity-based lookup */
|
|
75
333
|
private readonly keyExtractor;
|
|
334
|
+
/** Optional hook called before operations (for instrumentation) */
|
|
335
|
+
private readonly onBeforeOperation;
|
|
336
|
+
/** Optional hook called after operations (for instrumentation) */
|
|
337
|
+
private readonly onAfterOperation;
|
|
76
338
|
/**
|
|
77
339
|
* Create a new d-ary heap priority queue.
|
|
78
340
|
*
|
|
@@ -305,7 +567,7 @@ declare class PriorityQueue<T, K = string | number> {
|
|
|
305
567
|
* Pre-built comparator factories for common use cases.
|
|
306
568
|
*
|
|
307
569
|
* @module comparators
|
|
308
|
-
* @version 2.
|
|
570
|
+
* @version 2.4.0
|
|
309
571
|
* @license Apache-2.0
|
|
310
572
|
*/
|
|
311
573
|
|
|
@@ -385,4 +647,4 @@ declare function reverse<T>(cmp: Comparator<T>): Comparator<T>;
|
|
|
385
647
|
*/
|
|
386
648
|
declare function chain<T>(...comparators: Comparator<T>[]): Comparator<T>;
|
|
387
649
|
|
|
388
|
-
export { type Comparator, type KeyExtractor, type Position, PriorityQueue, type PriorityQueueOptions, chain, maxBy, maxNumber, maxString, minBy, minNumber, minString, reverse };
|
|
650
|
+
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, theoreticalInsertComparisons, theoreticalPopComparisons };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,230 @@
|
|
|
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, 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
|
+
* | decreasePriority | ⌊log_d(n)⌋ (upward only) |
|
|
71
|
+
* | increasePriority | d × ⌊log_d(n)⌋ (downward) |
|
|
72
|
+
*
|
|
73
|
+
* The demo visualization compares actual counts against these theoretical bounds.
|
|
74
|
+
*
|
|
75
|
+
* @module instrumentation
|
|
76
|
+
* @version 2.4.0
|
|
77
|
+
* @license Apache-2.0
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Operation types that can be tracked.
|
|
82
|
+
*
|
|
83
|
+
* Note: `increasePriority` is tracked separately because in Dijkstra's algorithm
|
|
84
|
+
* it manifests as decreasePriority (lowering distance = higher priority in min-heap).
|
|
85
|
+
*/
|
|
86
|
+
type OperationType = 'insert' | 'pop' | 'decreasePriority' | 'increasePriority';
|
|
87
|
+
/**
|
|
88
|
+
* Statistics tracking comparison counts per operation type.
|
|
89
|
+
*
|
|
90
|
+
* All counts start at zero and accumulate until `reset()` is called.
|
|
91
|
+
*/
|
|
92
|
+
interface ComparisonStats {
|
|
93
|
+
/** Comparisons during insert operations (moveUp) */
|
|
94
|
+
insert: number;
|
|
95
|
+
/** Comparisons during pop operations (moveDown + bestChildPosition) */
|
|
96
|
+
pop: number;
|
|
97
|
+
/** Comparisons during decreasePriority operations (moveUp + moveDown) */
|
|
98
|
+
decreasePriority: number;
|
|
99
|
+
/** Comparisons during increasePriority operations (moveUp) */
|
|
100
|
+
increasePriority: number;
|
|
101
|
+
/** Total comparisons across all operation types */
|
|
102
|
+
readonly total: number;
|
|
103
|
+
/** Reset all counters to zero */
|
|
104
|
+
reset(): void;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* An instrumented comparator that tracks comparison counts.
|
|
108
|
+
*
|
|
109
|
+
* This extends a regular comparator with:
|
|
110
|
+
* - `stats`: Current comparison counts
|
|
111
|
+
* - `startOperation(type)`: Begin tracking for an operation
|
|
112
|
+
* - `endOperation()`: Stop tracking current operation
|
|
113
|
+
*
|
|
114
|
+
* The comparator itself remains a valid `Comparator<T>` and can be used
|
|
115
|
+
* anywhere a regular comparator is expected.
|
|
116
|
+
*/
|
|
117
|
+
interface InstrumentedComparator<T> extends Comparator<T> {
|
|
118
|
+
/** Current comparison statistics */
|
|
119
|
+
readonly stats: ComparisonStats;
|
|
120
|
+
/**
|
|
121
|
+
* Signal the start of a heap operation.
|
|
122
|
+
* Comparisons will be attributed to this operation type until `endOperation()`.
|
|
123
|
+
*
|
|
124
|
+
* @param type - The operation type being started
|
|
125
|
+
*/
|
|
126
|
+
startOperation(type: OperationType): void;
|
|
127
|
+
/**
|
|
128
|
+
* Signal the end of the current heap operation.
|
|
129
|
+
* Subsequent comparisons will not be counted until the next `startOperation()`.
|
|
130
|
+
*/
|
|
131
|
+
endOperation(): void;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Create comparison statistics tracker.
|
|
135
|
+
*
|
|
136
|
+
* @returns Fresh stats object with all counts at zero
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```typescript
|
|
140
|
+
* const stats = createComparisonStats();
|
|
141
|
+
* stats.insert = 5;
|
|
142
|
+
* stats.pop = 10;
|
|
143
|
+
* console.log(stats.total); // 15
|
|
144
|
+
* stats.reset();
|
|
145
|
+
* console.log(stats.total); // 0
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
declare function createComparisonStats(): ComparisonStats;
|
|
149
|
+
/**
|
|
150
|
+
* Wrap a comparator with instrumentation to track comparison counts.
|
|
151
|
+
*
|
|
152
|
+
* The returned comparator:
|
|
153
|
+
* - Behaves identically to the original for comparison purposes
|
|
154
|
+
* - Tracks how many times it's called, attributed to operation types
|
|
155
|
+
* - Has zero overhead when `startOperation()` hasn't been called
|
|
156
|
+
*
|
|
157
|
+
* ## How It Works
|
|
158
|
+
*
|
|
159
|
+
* 1. Call `startOperation('insert')` before `pq.insert()`
|
|
160
|
+
* 2. The comparator increments `stats.insert` for each comparison
|
|
161
|
+
* 3. Call `endOperation()` after the operation completes
|
|
162
|
+
* 4. Repeat for other operations
|
|
163
|
+
*
|
|
164
|
+
* The `PriorityQueue` class supports `onBeforeOperation` and `onAfterOperation`
|
|
165
|
+
* hooks to automate this.
|
|
166
|
+
*
|
|
167
|
+
* ## Performance Note
|
|
168
|
+
*
|
|
169
|
+
* When `currentOperation` is null (between operations), the instrumented
|
|
170
|
+
* comparator performs only a single null check before calling the original.
|
|
171
|
+
* Modern JavaScript engines optimize this extremely well.
|
|
172
|
+
*
|
|
173
|
+
* @param comparator - The original comparator to instrument
|
|
174
|
+
* @returns An instrumented comparator with stats tracking
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```typescript
|
|
178
|
+
* import { minBy, instrumentComparator } from 'd-ary-heap';
|
|
179
|
+
*
|
|
180
|
+
* const cmp = instrumentComparator(minBy<number, number>(x => x));
|
|
181
|
+
*
|
|
182
|
+
* // Manual usage (without hooks)
|
|
183
|
+
* cmp.startOperation('insert');
|
|
184
|
+
* console.log(cmp(5, 3)); // false, and stats.insert++
|
|
185
|
+
* console.log(cmp(3, 5)); // true, and stats.insert++
|
|
186
|
+
* cmp.endOperation();
|
|
187
|
+
*
|
|
188
|
+
* console.log(cmp.stats.insert); // 2
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
declare function instrumentComparator<T>(comparator: Comparator<T>): InstrumentedComparator<T>;
|
|
192
|
+
/**
|
|
193
|
+
* Calculate theoretical comparison count for an insert operation.
|
|
194
|
+
*
|
|
195
|
+
* Insert performs at most ⌊log_d(n)⌋ comparisons (one per level during moveUp).
|
|
196
|
+
*
|
|
197
|
+
* @param n - Number of elements in heap AFTER insert
|
|
198
|
+
* @param d - Heap arity
|
|
199
|
+
* @returns Theoretical worst-case comparison count
|
|
200
|
+
*/
|
|
201
|
+
declare function theoreticalInsertComparisons(n: number, d: number): number;
|
|
202
|
+
/**
|
|
203
|
+
* Calculate theoretical comparison count for a pop operation.
|
|
204
|
+
*
|
|
205
|
+
* Pop performs at most d × ⌊log_d(n)⌋ comparisons:
|
|
206
|
+
* - At each level, find best among d children (d-1 comparisons)
|
|
207
|
+
* - Compare best child with current (1 comparison)
|
|
208
|
+
* - Total: d comparisons per level × ⌊log_d(n)⌋ levels
|
|
209
|
+
*
|
|
210
|
+
* @param n - Number of elements in heap BEFORE pop
|
|
211
|
+
* @param d - Heap arity
|
|
212
|
+
* @returns Theoretical worst-case comparison count
|
|
213
|
+
*/
|
|
214
|
+
declare function theoreticalPopComparisons(n: number, d: number): number;
|
|
215
|
+
/**
|
|
216
|
+
* Calculate theoretical comparison count for a decreasePriority operation.
|
|
217
|
+
*
|
|
218
|
+
* DecreasePriority in our implementation calls both moveUp and moveDown
|
|
219
|
+
* for safety, but typically only moveUp executes (upward movement).
|
|
220
|
+
* Worst case: ⌊log_d(n)⌋ comparisons.
|
|
221
|
+
*
|
|
222
|
+
* @param n - Number of elements in heap
|
|
223
|
+
* @param d - Heap arity
|
|
224
|
+
* @returns Theoretical worst-case comparison count (moveUp path)
|
|
225
|
+
*/
|
|
226
|
+
declare function theoreticalDecreasePriorityComparisons(n: number, d: number): number;
|
|
227
|
+
|
|
1
228
|
/**
|
|
2
229
|
* d-ary Heap Priority Queue - TypeScript Implementation
|
|
3
230
|
*
|
|
@@ -8,11 +235,13 @@
|
|
|
8
235
|
* - O(1) access to highest-priority item
|
|
9
236
|
* - O(log_d n) insert and priority increase operations
|
|
10
237
|
* - O(d · log_d n) pop and priority decrease operations
|
|
238
|
+
* - Optional instrumentation hooks for performance analysis
|
|
11
239
|
*
|
|
12
|
-
* @version 2.
|
|
240
|
+
* @version 2.4.0
|
|
13
241
|
* @license Apache-2.0
|
|
14
242
|
* @copyright 2023-2025 Eric Jacopin
|
|
15
243
|
*/
|
|
244
|
+
|
|
16
245
|
/** Type alias for position indices (cross-language consistency) */
|
|
17
246
|
type Position = number;
|
|
18
247
|
/**
|
|
@@ -37,6 +266,35 @@ interface PriorityQueueOptions<T, K> {
|
|
|
37
266
|
keyExtractor: KeyExtractor<T, K>;
|
|
38
267
|
/** Initial capacity hint for pre-allocation */
|
|
39
268
|
initialCapacity?: number;
|
|
269
|
+
/**
|
|
270
|
+
* Optional hook called before each heap operation.
|
|
271
|
+
*
|
|
272
|
+
* This enables opt-in instrumentation for performance analysis without
|
|
273
|
+
* adding overhead when not used. Pair with an instrumented comparator
|
|
274
|
+
* to track comparison counts per operation type.
|
|
275
|
+
*
|
|
276
|
+
* @param type - The operation about to be performed
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```typescript
|
|
280
|
+
* const cmp = instrumentComparator(minBy((v) => v.priority));
|
|
281
|
+
* const pq = new PriorityQueue({
|
|
282
|
+
* comparator: cmp,
|
|
283
|
+
* keyExtractor: (v) => v.id,
|
|
284
|
+
* onBeforeOperation: (op) => cmp.startOperation(op),
|
|
285
|
+
* onAfterOperation: () => cmp.endOperation(),
|
|
286
|
+
* });
|
|
287
|
+
* ```
|
|
288
|
+
*
|
|
289
|
+
* @see instrumentComparator from './instrumentation'
|
|
290
|
+
*/
|
|
291
|
+
onBeforeOperation?: (type: OperationType) => void;
|
|
292
|
+
/**
|
|
293
|
+
* Optional hook called after each heap operation completes.
|
|
294
|
+
*
|
|
295
|
+
* @see onBeforeOperation for usage example
|
|
296
|
+
*/
|
|
297
|
+
onAfterOperation?: () => void;
|
|
40
298
|
}
|
|
41
299
|
/**
|
|
42
300
|
* Generic d-ary heap priority queue with O(1) lookup.
|
|
@@ -73,6 +331,10 @@ declare class PriorityQueue<T, K = string | number> {
|
|
|
73
331
|
private readonly comparator;
|
|
74
332
|
/** Key extractor for identity-based lookup */
|
|
75
333
|
private readonly keyExtractor;
|
|
334
|
+
/** Optional hook called before operations (for instrumentation) */
|
|
335
|
+
private readonly onBeforeOperation;
|
|
336
|
+
/** Optional hook called after operations (for instrumentation) */
|
|
337
|
+
private readonly onAfterOperation;
|
|
76
338
|
/**
|
|
77
339
|
* Create a new d-ary heap priority queue.
|
|
78
340
|
*
|
|
@@ -305,7 +567,7 @@ declare class PriorityQueue<T, K = string | number> {
|
|
|
305
567
|
* Pre-built comparator factories for common use cases.
|
|
306
568
|
*
|
|
307
569
|
* @module comparators
|
|
308
|
-
* @version 2.
|
|
570
|
+
* @version 2.4.0
|
|
309
571
|
* @license Apache-2.0
|
|
310
572
|
*/
|
|
311
573
|
|
|
@@ -385,4 +647,4 @@ declare function reverse<T>(cmp: Comparator<T>): Comparator<T>;
|
|
|
385
647
|
*/
|
|
386
648
|
declare function chain<T>(...comparators: Comparator<T>[]): Comparator<T>;
|
|
387
649
|
|
|
388
|
-
export { type Comparator, type KeyExtractor, type Position, PriorityQueue, type PriorityQueueOptions, chain, maxBy, maxNumber, maxString, minBy, minNumber, minString, reverse };
|
|
650
|
+
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, theoreticalInsertComparisons, theoreticalPopComparisons };
|