@pilates/core 1.0.1 → 2.0.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/algorithm/cache.d.ts +7 -19
- package/dist/algorithm/cache.d.ts.map +1 -1
- package/dist/algorithm/cache.js +31 -27
- package/dist/algorithm/cache.js.map +1 -1
- package/dist/algorithm/index.d.ts +31 -0
- package/dist/algorithm/index.d.ts.map +1 -1
- package/dist/algorithm/index.js +105 -13
- package/dist/algorithm/index.js.map +1 -1
- package/dist/algorithm/round.d.ts +13 -0
- package/dist/algorithm/round.d.ts.map +1 -1
- package/dist/algorithm/round.js +33 -16
- package/dist/algorithm/round.js.map +1 -1
- package/dist/algorithm/spineless/classify.d.ts +47 -0
- package/dist/algorithm/spineless/classify.d.ts.map +1 -0
- package/dist/algorithm/spineless/classify.js +109 -0
- package/dist/algorithm/spineless/classify.js.map +1 -0
- package/dist/algorithm/spineless/field-id-pool.d.ts +28 -0
- package/dist/algorithm/spineless/field-id-pool.d.ts.map +1 -0
- package/dist/algorithm/spineless/field-id-pool.js +35 -0
- package/dist/algorithm/spineless/field-id-pool.js.map +1 -0
- package/dist/algorithm/spineless/flex-grammar.d.ts +394 -0
- package/dist/algorithm/spineless/flex-grammar.d.ts.map +1 -0
- package/dist/algorithm/spineless/flex-grammar.js +2509 -0
- package/dist/algorithm/spineless/flex-grammar.js.map +1 -0
- package/dist/algorithm/spineless/grammar.d.ts +156 -0
- package/dist/algorithm/spineless/grammar.d.ts.map +1 -0
- package/dist/algorithm/spineless/grammar.js +145 -0
- package/dist/algorithm/spineless/grammar.js.map +1 -0
- package/dist/algorithm/spineless/layout.d.ts +167 -0
- package/dist/algorithm/spineless/layout.d.ts.map +1 -0
- package/dist/algorithm/spineless/layout.js +893 -0
- package/dist/algorithm/spineless/layout.js.map +1 -0
- package/dist/algorithm/spineless/order-maintenance.bench.d.ts +25 -0
- package/dist/algorithm/spineless/order-maintenance.bench.d.ts.map +1 -0
- package/dist/algorithm/spineless/order-maintenance.bench.js +78 -0
- package/dist/algorithm/spineless/order-maintenance.bench.js.map +1 -0
- package/dist/algorithm/spineless/order-maintenance.d.ts +201 -0
- package/dist/algorithm/spineless/order-maintenance.d.ts.map +1 -0
- package/dist/algorithm/spineless/order-maintenance.js +300 -0
- package/dist/algorithm/spineless/order-maintenance.js.map +1 -0
- package/dist/algorithm/spineless/priority-queue.bench.d.ts +17 -0
- package/dist/algorithm/spineless/priority-queue.bench.d.ts.map +1 -0
- package/dist/algorithm/spineless/priority-queue.bench.js +57 -0
- package/dist/algorithm/spineless/priority-queue.bench.js.map +1 -0
- package/dist/algorithm/spineless/priority-queue.d.ts +73 -0
- package/dist/algorithm/spineless/priority-queue.d.ts.map +1 -0
- package/dist/algorithm/spineless/priority-queue.js +149 -0
- package/dist/algorithm/spineless/priority-queue.js.map +1 -0
- package/dist/algorithm/spineless/runtime.d.ts +292 -0
- package/dist/algorithm/spineless/runtime.d.ts.map +1 -0
- package/dist/algorithm/spineless/runtime.js +609 -0
- package/dist/algorithm/spineless/runtime.js.map +1 -0
- package/dist/algorithm/spineless/style-dirty.d.ts +65 -0
- package/dist/algorithm/spineless/style-dirty.d.ts.map +1 -0
- package/dist/algorithm/spineless/style-dirty.js +75 -0
- package/dist/algorithm/spineless/style-dirty.js.map +1 -0
- package/dist/dirty-flags.d.ts +30 -0
- package/dist/dirty-flags.d.ts.map +1 -0
- package/dist/dirty-flags.js +35 -0
- package/dist/dirty-flags.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +27 -0
- package/dist/inspect.d.ts.map +1 -0
- package/dist/inspect.js +61 -0
- package/dist/inspect.js.map +1 -0
- package/dist/layout-pool.d.ts +49 -0
- package/dist/layout-pool.d.ts.map +1 -0
- package/dist/layout-pool.js +75 -0
- package/dist/layout-pool.js.map +1 -0
- package/dist/node.d.ts +20 -3
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +63 -42
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Order Maintenance (OM) data structure.
|
|
3
|
+
*
|
|
4
|
+
* Maintains a totally-ordered set of items under insertions, deletions,
|
|
5
|
+
* and `compare(a, b)` queries that return the relative order in O(1) (or
|
|
6
|
+
* O(1) amortized). Foundational primitive for Spineless Traversal
|
|
7
|
+
* (Kirisame, Wang, Panchekha — PLDI 2025): the priority queue that
|
|
8
|
+
* drives incremental layout invalidation is keyed on OM timestamps,
|
|
9
|
+
* so a fast OM comparator dominates per-operation cost.
|
|
10
|
+
*
|
|
11
|
+
* Reference: Bender, Cole, Demaine, Farach-Colton, Zito. "Two
|
|
12
|
+
* simplified algorithms for maintaining order in a list." ESA 2002.
|
|
13
|
+
*
|
|
14
|
+
* ## What this file ships
|
|
15
|
+
*
|
|
16
|
+
* **The interface** — `OMNode`, `OrderMaintenance` — is the surface the
|
|
17
|
+
* Spineless runtime will consume. We design it so the implementation
|
|
18
|
+
* can be swapped (naive ↔ Bender amortized O(1)) without ripple.
|
|
19
|
+
*
|
|
20
|
+
* **The naive implementation** — `NaiveOrderMaintenance` — uses a single
|
|
21
|
+
* doubly-linked list with sequential integer tags assigned on insert.
|
|
22
|
+
* Inserts trigger O(N) renumbering of trailing nodes. Compare is O(1).
|
|
23
|
+
*
|
|
24
|
+
* This is the **scaffolding implementation**: correct, simple, lets us
|
|
25
|
+
* write all the consumer code (priority queue, runtime, attribute grammar
|
|
26
|
+
* interpreter) without waiting for the optimized impl. The Bender et al.
|
|
27
|
+
* amortized-O(1) implementation lands as a drop-in replacement once the
|
|
28
|
+
* Spineless runtime is wired and we can benchmark end-to-end. The naive
|
|
29
|
+
* impl stays in tree forever as a reference oracle: every Bender op is
|
|
30
|
+
* differentially compared against naive in the property-based test.
|
|
31
|
+
*
|
|
32
|
+
* ## Why JS and not WASM
|
|
33
|
+
*
|
|
34
|
+
* The paper's reference (Megatron) emits C++ and uses inline `cmov` for
|
|
35
|
+
* branchless compare. We can't use inline assembly in JS, but we don't
|
|
36
|
+
* have to: V8 already lowers `a < b` on 32-bit-integer hidden classes
|
|
37
|
+
* to a branchless `cmp` + `setb` sequence on x86-64. The TS-side cost is
|
|
38
|
+
* predominantly in the `lookup` (Map.get) and the unboxed compare itself.
|
|
39
|
+
*
|
|
40
|
+
* @internal
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* Naive O(N)-per-insert Order Maintenance. Correctness-only impl
|
|
44
|
+
* for scaffolding and as a fuzzer oracle for the future Bender impl.
|
|
45
|
+
*
|
|
46
|
+
* Invariants:
|
|
47
|
+
* - `_omTag` is strictly increasing along the `next` chain.
|
|
48
|
+
* - `_omTag` values are arbitrary integers but always orderable.
|
|
49
|
+
* - Insert renumbers all trailing nodes to maintain monotonicity.
|
|
50
|
+
*
|
|
51
|
+
* @internal
|
|
52
|
+
*/
|
|
53
|
+
export class NaiveOrderMaintenance {
|
|
54
|
+
firstNode = null;
|
|
55
|
+
nodeCount = 0;
|
|
56
|
+
get size() {
|
|
57
|
+
return this.nodeCount;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Returns the first node in the order, or `null` if empty. Exposed
|
|
61
|
+
* for tests and for the priority queue's extract-min operation.
|
|
62
|
+
*
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
first() {
|
|
66
|
+
return this.firstNode;
|
|
67
|
+
}
|
|
68
|
+
init() {
|
|
69
|
+
const first = { _omTag: 0, prev: null, next: null };
|
|
70
|
+
this.firstNode = first;
|
|
71
|
+
this.nodeCount = 1;
|
|
72
|
+
return first;
|
|
73
|
+
}
|
|
74
|
+
insertAfter(after) {
|
|
75
|
+
const afterNode = after;
|
|
76
|
+
const newNode = {
|
|
77
|
+
_omTag: afterNode._omTag + 1,
|
|
78
|
+
prev: afterNode,
|
|
79
|
+
next: afterNode.next,
|
|
80
|
+
};
|
|
81
|
+
if (afterNode.next !== null) {
|
|
82
|
+
afterNode.next.prev = newNode;
|
|
83
|
+
}
|
|
84
|
+
afterNode.next = newNode;
|
|
85
|
+
// Renumber trailing nodes to maintain strict monotonicity.
|
|
86
|
+
// O(N) worst case; this is the scaffolding-only cost the Bender
|
|
87
|
+
// impl removes.
|
|
88
|
+
let cursor = newNode.next;
|
|
89
|
+
let nextTag = newNode._omTag + 1;
|
|
90
|
+
while (cursor !== null) {
|
|
91
|
+
cursor._omTag = nextTag;
|
|
92
|
+
nextTag++;
|
|
93
|
+
cursor = cursor.next;
|
|
94
|
+
}
|
|
95
|
+
this.nodeCount++;
|
|
96
|
+
return newNode;
|
|
97
|
+
}
|
|
98
|
+
delete(node) {
|
|
99
|
+
const n = node;
|
|
100
|
+
if (n.prev !== null) {
|
|
101
|
+
n.prev.next = n.next;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
this.firstNode = n.next;
|
|
105
|
+
}
|
|
106
|
+
if (n.next !== null) {
|
|
107
|
+
n.next.prev = n.prev;
|
|
108
|
+
}
|
|
109
|
+
// Renumbering is not strictly required after delete (gaps in tags
|
|
110
|
+
// are harmless), but consistency with insertAfter's invariant
|
|
111
|
+
// (consecutive tags) simplifies reasoning. Skipped for now to
|
|
112
|
+
// avoid an extra O(N) walk; tests should not rely on consecutive
|
|
113
|
+
// tags after deletes.
|
|
114
|
+
this.nodeCount--;
|
|
115
|
+
}
|
|
116
|
+
compare(a, b) {
|
|
117
|
+
const ta = a._omTag;
|
|
118
|
+
const tb = b._omTag;
|
|
119
|
+
return ta - tb;
|
|
120
|
+
}
|
|
121
|
+
predecessor(node) {
|
|
122
|
+
return node.prev;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// --- Bender et al. 2002 Algorithm 1 (list-labeling with windowed relabel) ---
|
|
126
|
+
/**
|
|
127
|
+
* Label space. Labels live in `[0, LABEL_MAX)`. Using 2^30 keeps every
|
|
128
|
+
* arithmetic op (midpoint, double, mask) in V8's 31-bit SMI range, so
|
|
129
|
+
* the JIT lowers everything to native int instructions. At 2^30 we can
|
|
130
|
+
* host ~50M items before any global rebalance is forced — far above
|
|
131
|
+
* typical TUI scale.
|
|
132
|
+
*
|
|
133
|
+
* @internal
|
|
134
|
+
*/
|
|
135
|
+
const LABEL_MAX = 1 << 30;
|
|
136
|
+
/**
|
|
137
|
+
* Bender, Cole, Demaine, Farach-Colton, Zito (2002): "Two simplified
|
|
138
|
+
* algorithms for maintaining order in a list." Algorithm 1 — list-
|
|
139
|
+
* labeling with exponentially-growing window relabel.
|
|
140
|
+
*
|
|
141
|
+
* Operations:
|
|
142
|
+
* - `compare`: O(1) integer subtract.
|
|
143
|
+
* - `insertAfter`: amortized O(log N) per operation (looser than the
|
|
144
|
+
* paper's O(1) amortized when using density bound (3/2)^d at depth d;
|
|
145
|
+
* we trade a log factor for simpler integer arithmetic and avoid
|
|
146
|
+
* BigInt).
|
|
147
|
+
* - `delete`: O(1).
|
|
148
|
+
*
|
|
149
|
+
* Cross-validated against `NaiveOrderMaintenance` via property fuzzer.
|
|
150
|
+
*
|
|
151
|
+
* @internal
|
|
152
|
+
*/
|
|
153
|
+
export class BenderOrderMaintenance {
|
|
154
|
+
firstNode = null;
|
|
155
|
+
nodeCount = 0;
|
|
156
|
+
get size() {
|
|
157
|
+
return this.nodeCount;
|
|
158
|
+
}
|
|
159
|
+
first() {
|
|
160
|
+
return this.firstNode;
|
|
161
|
+
}
|
|
162
|
+
init() {
|
|
163
|
+
// Start label at the middle of the space so first inserts have room
|
|
164
|
+
// both before (via the global rebalance path) and after.
|
|
165
|
+
const first = { _omTag: LABEL_MAX >> 1, prev: null, next: null };
|
|
166
|
+
this.firstNode = first;
|
|
167
|
+
this.nodeCount = 1;
|
|
168
|
+
return first;
|
|
169
|
+
}
|
|
170
|
+
insertAfter(after) {
|
|
171
|
+
const pred = after;
|
|
172
|
+
const succ = pred.next;
|
|
173
|
+
const succLabel = succ === null ? LABEL_MAX : succ._omTag;
|
|
174
|
+
const predLabel = pred._omTag;
|
|
175
|
+
// Midpoint between predecessor and successor labels. Integer-only.
|
|
176
|
+
const newLabel = (predLabel + succLabel) >> 1;
|
|
177
|
+
const newNode = { _omTag: newLabel, prev: pred, next: succ };
|
|
178
|
+
pred.next = newNode;
|
|
179
|
+
if (succ !== null)
|
|
180
|
+
succ.prev = newNode;
|
|
181
|
+
this.nodeCount++;
|
|
182
|
+
if (newLabel === predLabel) {
|
|
183
|
+
// No room between pred and succ — trigger a windowed relabel.
|
|
184
|
+
// Relabel updates newNode._omTag (and others in the window) to
|
|
185
|
+
// spread out across available label space.
|
|
186
|
+
this.relabel(newNode);
|
|
187
|
+
}
|
|
188
|
+
return newNode;
|
|
189
|
+
}
|
|
190
|
+
delete(node) {
|
|
191
|
+
const n = node;
|
|
192
|
+
if (n.prev !== null) {
|
|
193
|
+
n.prev.next = n.next;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
this.firstNode = n.next;
|
|
197
|
+
}
|
|
198
|
+
if (n.next !== null) {
|
|
199
|
+
n.next.prev = n.prev;
|
|
200
|
+
}
|
|
201
|
+
this.nodeCount--;
|
|
202
|
+
// Delete only creates extra label room; no rebalance needed.
|
|
203
|
+
}
|
|
204
|
+
compare(a, b) {
|
|
205
|
+
return a._omTag - b._omTag;
|
|
206
|
+
}
|
|
207
|
+
predecessor(node) {
|
|
208
|
+
return node.prev;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Walk exponentially-growing windows around `pivot` until a window
|
|
212
|
+
* with low enough density to redistribute is found. Redistribute
|
|
213
|
+
* labels uniformly across that window.
|
|
214
|
+
*
|
|
215
|
+
* Density bound: `count * 2 ≤ span`. This guarantees:
|
|
216
|
+
* - Every node gets a distinct integer label (count ≤ span).
|
|
217
|
+
* - Each interval between consecutive labels has at least 2 units
|
|
218
|
+
* of room, so subsequent midpoint inserts succeed without
|
|
219
|
+
* triggering relabel again at the same depth.
|
|
220
|
+
*
|
|
221
|
+
* Window definition at depth d:
|
|
222
|
+
* span = 2^d
|
|
223
|
+
* tagLow = pivot._omTag aligned down to multiple of span
|
|
224
|
+
* tagHigh = tagLow + span
|
|
225
|
+
*
|
|
226
|
+
* Amortized cost: O(log N) per insert. (Tighter than the paper's
|
|
227
|
+
* O(1) amortized with density bound (3/2)^d, but the bound here uses
|
|
228
|
+
* pure integer arithmetic for V8-friendly speed.)
|
|
229
|
+
*
|
|
230
|
+
* @internal
|
|
231
|
+
*/
|
|
232
|
+
relabel(pivot) {
|
|
233
|
+
let depth = 1;
|
|
234
|
+
let span = 2;
|
|
235
|
+
while (true) {
|
|
236
|
+
// Find aligned window boundaries around the pivot's current label.
|
|
237
|
+
const mask = (LABEL_MAX - 1) ^ (span - 1);
|
|
238
|
+
const tagLow = pivot._omTag & mask;
|
|
239
|
+
const tagHigh = tagLow + span;
|
|
240
|
+
// Find leftmost node in the window by walking back from pivot.
|
|
241
|
+
let firstInWindow = pivot;
|
|
242
|
+
while (firstInWindow.prev !== null && firstInWindow.prev._omTag >= tagLow) {
|
|
243
|
+
firstInWindow = firstInWindow.prev;
|
|
244
|
+
}
|
|
245
|
+
// Count nodes in the window and collect them in-order.
|
|
246
|
+
const inWindow = [];
|
|
247
|
+
let cursor = firstInWindow;
|
|
248
|
+
while (cursor !== null && cursor._omTag < tagHigh) {
|
|
249
|
+
inWindow.push(cursor);
|
|
250
|
+
cursor = cursor.next;
|
|
251
|
+
}
|
|
252
|
+
const count = inWindow.length;
|
|
253
|
+
// Density bound: redistribute only when there's at least 2x
|
|
254
|
+
// headroom in the window. This guarantees distinct labels +
|
|
255
|
+
// future-insert room without immediate re-relabel.
|
|
256
|
+
if (count * 2 <= span) {
|
|
257
|
+
const step = Math.max(1, (span / count) | 0);
|
|
258
|
+
// Center the assigned range: leave equal padding at both ends.
|
|
259
|
+
const used = (count - 1) * step + 1;
|
|
260
|
+
const startPad = Math.max(0, (span - used) >> 1);
|
|
261
|
+
let label = tagLow + startPad;
|
|
262
|
+
for (const node of inWindow) {
|
|
263
|
+
node._omTag = label;
|
|
264
|
+
label += step;
|
|
265
|
+
}
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
// Window overcrowded — expand. Doubling depth doubles span.
|
|
269
|
+
// If span exceeds the label space, fall back to a global relabel.
|
|
270
|
+
depth++;
|
|
271
|
+
span <<= 1;
|
|
272
|
+
if (span >= LABEL_MAX) {
|
|
273
|
+
this.globalRelabel();
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Fallback when the windowed relabel walks all the way up to the
|
|
280
|
+
* full label space without finding a sparse-enough window. Spreads
|
|
281
|
+
* every node uniformly across [0, LABEL_MAX).
|
|
282
|
+
*
|
|
283
|
+
* @internal
|
|
284
|
+
*/
|
|
285
|
+
globalRelabel() {
|
|
286
|
+
const all = [];
|
|
287
|
+
let cursor = this.firstNode;
|
|
288
|
+
while (cursor !== null) {
|
|
289
|
+
all.push(cursor);
|
|
290
|
+
cursor = cursor.next;
|
|
291
|
+
}
|
|
292
|
+
const stride = Math.max(1, (LABEL_MAX / (all.length + 1)) | 0);
|
|
293
|
+
let label = stride;
|
|
294
|
+
for (const node of all) {
|
|
295
|
+
node._omTag = label;
|
|
296
|
+
label += stride;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
//# sourceMappingURL=order-maintenance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"order-maintenance.js","sourceRoot":"","sources":["../../../src/algorithm/spineless/order-maintenance.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Microbenchmark for the OM-keyed priority queue.
|
|
3
|
+
*
|
|
4
|
+
* Targets (Phase 5 viability):
|
|
5
|
+
* - `push`: < 2µs/op at N=10k (called once per dirty field per layout)
|
|
6
|
+
* - `popMin`: < 2µs/op at N=10k (called once per recompute step)
|
|
7
|
+
*
|
|
8
|
+
* Each layout pass under Spineless may process up to ~5k field-recomputes
|
|
9
|
+
* on a 1k-node TUI tree. Total PQ overhead must stay well under the
|
|
10
|
+
* 50-100µs target for incremental layout to beat imperative.
|
|
11
|
+
*
|
|
12
|
+
* Run with: `tsx packages/core/src/algorithm/spineless/priority-queue.bench.ts`
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=priority-queue.bench.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"priority-queue.bench.d.ts","sourceRoot":"","sources":["../../../src/algorithm/spineless/priority-queue.bench.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Microbenchmark for the OM-keyed priority queue.
|
|
3
|
+
*
|
|
4
|
+
* Targets (Phase 5 viability):
|
|
5
|
+
* - `push`: < 2µs/op at N=10k (called once per dirty field per layout)
|
|
6
|
+
* - `popMin`: < 2µs/op at N=10k (called once per recompute step)
|
|
7
|
+
*
|
|
8
|
+
* Each layout pass under Spineless may process up to ~5k field-recomputes
|
|
9
|
+
* on a 1k-node TUI tree. Total PQ overhead must stay well under the
|
|
10
|
+
* 50-100µs target for incremental layout to beat imperative.
|
|
11
|
+
*
|
|
12
|
+
* Run with: `tsx packages/core/src/algorithm/spineless/priority-queue.bench.ts`
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
import { BenderOrderMaintenance } from './order-maintenance.js';
|
|
17
|
+
import { OmPriorityQueue } from './priority-queue.js';
|
|
18
|
+
function median(xs) {
|
|
19
|
+
const sorted = [...xs].sort((a, b) => a - b);
|
|
20
|
+
return sorted[Math.floor(sorted.length / 2)];
|
|
21
|
+
}
|
|
22
|
+
function bench(label, sizes, opsPerTrial, trials) {
|
|
23
|
+
console.log(`\n## ${label}`);
|
|
24
|
+
for (const N of sizes) {
|
|
25
|
+
const trialMsPush = [];
|
|
26
|
+
const trialMsPop = [];
|
|
27
|
+
for (let t = 0; t < trials; t++) {
|
|
28
|
+
const om = new BenderOrderMaintenance();
|
|
29
|
+
const nodes = [om.init()];
|
|
30
|
+
for (let i = 1; i < N; i++)
|
|
31
|
+
nodes.push(om.insertAfter(nodes[i - 1]));
|
|
32
|
+
const pq = new OmPriorityQueue(om);
|
|
33
|
+
// Push phase
|
|
34
|
+
let s = performance.now();
|
|
35
|
+
for (let k = 0; k < opsPerTrial; k++) {
|
|
36
|
+
// Use a deterministic but spread-out order so the heap actually re-orders.
|
|
37
|
+
const i = (k * 2654435761) % N;
|
|
38
|
+
pq.push(k, nodes[i]);
|
|
39
|
+
}
|
|
40
|
+
let e = performance.now();
|
|
41
|
+
trialMsPush.push(e - s);
|
|
42
|
+
// Pop phase (extract everything)
|
|
43
|
+
s = performance.now();
|
|
44
|
+
while (!pq.isEmpty())
|
|
45
|
+
pq.popMin();
|
|
46
|
+
e = performance.now();
|
|
47
|
+
trialMsPop.push(e - s);
|
|
48
|
+
}
|
|
49
|
+
const usPush = (median(trialMsPush) * 1000) / opsPerTrial;
|
|
50
|
+
const usPop = (median(trialMsPop) * 1000) / opsPerTrial;
|
|
51
|
+
console.log(` N=${N.toString().padStart(6)} push=${usPush.toFixed(3).padStart(7)} µs popMin=${usPop.toFixed(3).padStart(7)} µs`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
console.log('OmPriorityQueue microbench (Bender OM, single-threaded JS)');
|
|
55
|
+
console.log('Targets: push < 2µs, popMin < 2µs at N=10k');
|
|
56
|
+
bench('push + popMin', [100, 1000, 10000], 10_000, 5);
|
|
57
|
+
//# sourceMappingURL=priority-queue.bench.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"priority-queue.bench.js","sourceRoot":"","sources":["../../../src/algorithm/spineless/priority-queue.bench.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Order-Maintenance-keyed priority queue.
|
|
3
|
+
*
|
|
4
|
+
* The hot data structure of the Spineless Traversal runtime (Kirisame,
|
|
5
|
+
* Wang, Panchekha — PLDI 2025). Each item is keyed by an {@link OMNode}
|
|
6
|
+
* representing its position in the layout-field traversal order; the
|
|
7
|
+
* queue extracts items in OM-order, so the runtime always processes
|
|
8
|
+
* the earliest-dirty field next.
|
|
9
|
+
*
|
|
10
|
+
* Implementation: binary min-heap over `{ value, omNode }` records.
|
|
11
|
+
* Ordering uses `om.compare(a.omNode, b.omNode)`. Standard O(log N)
|
|
12
|
+
* push/popMin, O(1) peek/size.
|
|
13
|
+
*
|
|
14
|
+
* Membership tracking: an internal `Set` allows O(1) `has(value)`
|
|
15
|
+
* queries so the Spineless runtime can avoid enqueueing the same
|
|
16
|
+
* field twice. Important: this assumes `value` instances are stable
|
|
17
|
+
* (used as Map keys via reference equality), which fits the Spineless
|
|
18
|
+
* "one OMNode per (Node, field-name) pair, allocated once" model.
|
|
19
|
+
*
|
|
20
|
+
* ## Correctness under OM relabel
|
|
21
|
+
*
|
|
22
|
+
* The OM data structure may relabel internal tags on insert
|
|
23
|
+
* (`BenderOrderMaintenance`'s windowed redistribute). The heap
|
|
24
|
+
* invariant is "parent ≤ all descendants by OM compare". A relabel
|
|
25
|
+
* preserves the RELATIVE order of all live OM nodes, so
|
|
26
|
+
* `sign(compare(a, b))` is unchanged — the heap invariant holds.
|
|
27
|
+
*
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
import type { OMNode, OrderMaintenance } from './order-maintenance.js';
|
|
31
|
+
/**
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
export declare class OmPriorityQueue<T> {
|
|
35
|
+
private readonly om;
|
|
36
|
+
private readonly heapValue;
|
|
37
|
+
private readonly heapOmNode;
|
|
38
|
+
private readonly members;
|
|
39
|
+
constructor(om: OrderMaintenance);
|
|
40
|
+
get size(): number;
|
|
41
|
+
isEmpty(): boolean;
|
|
42
|
+
has(value: T): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Push a value at the given OM position. Caller is responsible for
|
|
45
|
+
* checking `has(value)` first if dedup matters (some workflows
|
|
46
|
+
* intentionally re-enqueue after deletion).
|
|
47
|
+
*
|
|
48
|
+
* Returns `true` if added, `false` if `value` was already present
|
|
49
|
+
* (no-op — the queue is unchanged, original OM position retained).
|
|
50
|
+
*/
|
|
51
|
+
push(value: T, omNode: OMNode): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Return (without removing) the value at OM-minimum. Returns
|
|
54
|
+
* `undefined` if the queue is empty.
|
|
55
|
+
*/
|
|
56
|
+
peek(): T | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Remove and return the OM-minimum value. Returns `undefined` if
|
|
59
|
+
* the queue is empty.
|
|
60
|
+
*/
|
|
61
|
+
popMin(): T | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* Sift the element at index `start` up the heap until heap property
|
|
64
|
+
* is restored. O(log N).
|
|
65
|
+
*/
|
|
66
|
+
private siftUp;
|
|
67
|
+
/**
|
|
68
|
+
* Sift the element at index `start` down the heap until heap property
|
|
69
|
+
* is restored. O(log N).
|
|
70
|
+
*/
|
|
71
|
+
private siftDown;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=priority-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"priority-queue.d.ts","sourceRoot":"","sources":["../../../src/algorithm/spineless/priority-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAEvE;;GAEG;AACH,qBAAa,eAAe,CAAC,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAmB;IAMtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgB;IAE3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;gBAEjC,EAAE,EAAE,gBAAgB;IAIhC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,IAAI,OAAO;IAIlB,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO;IAItB;;;;;;;OAOG;IACH,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO;IASvC;;;OAGG;IACH,IAAI,IAAI,CAAC,GAAG,SAAS;IAIrB;;;OAGG;IACH,MAAM,IAAI,CAAC,GAAG,SAAS;IAgBvB;;;OAGG;IACH,OAAO,CAAC,MAAM;IAgBd;;;OAGG;IACH,OAAO,CAAC,QAAQ;CAsBjB"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Order-Maintenance-keyed priority queue.
|
|
3
|
+
*
|
|
4
|
+
* The hot data structure of the Spineless Traversal runtime (Kirisame,
|
|
5
|
+
* Wang, Panchekha — PLDI 2025). Each item is keyed by an {@link OMNode}
|
|
6
|
+
* representing its position in the layout-field traversal order; the
|
|
7
|
+
* queue extracts items in OM-order, so the runtime always processes
|
|
8
|
+
* the earliest-dirty field next.
|
|
9
|
+
*
|
|
10
|
+
* Implementation: binary min-heap over `{ value, omNode }` records.
|
|
11
|
+
* Ordering uses `om.compare(a.omNode, b.omNode)`. Standard O(log N)
|
|
12
|
+
* push/popMin, O(1) peek/size.
|
|
13
|
+
*
|
|
14
|
+
* Membership tracking: an internal `Set` allows O(1) `has(value)`
|
|
15
|
+
* queries so the Spineless runtime can avoid enqueueing the same
|
|
16
|
+
* field twice. Important: this assumes `value` instances are stable
|
|
17
|
+
* (used as Map keys via reference equality), which fits the Spineless
|
|
18
|
+
* "one OMNode per (Node, field-name) pair, allocated once" model.
|
|
19
|
+
*
|
|
20
|
+
* ## Correctness under OM relabel
|
|
21
|
+
*
|
|
22
|
+
* The OM data structure may relabel internal tags on insert
|
|
23
|
+
* (`BenderOrderMaintenance`'s windowed redistribute). The heap
|
|
24
|
+
* invariant is "parent ≤ all descendants by OM compare". A relabel
|
|
25
|
+
* preserves the RELATIVE order of all live OM nodes, so
|
|
26
|
+
* `sign(compare(a, b))` is unchanged — the heap invariant holds.
|
|
27
|
+
*
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
export class OmPriorityQueue {
|
|
34
|
+
om;
|
|
35
|
+
// Parallel arrays for the heap. heapValue[i] is the user-supplied
|
|
36
|
+
// value; heapOmNode[i] is its OM position. Using parallel arrays
|
|
37
|
+
// rather than {value, omNode} pair objects avoids per-push
|
|
38
|
+
// allocation, which matters at Spineless-runtime scale (10k+
|
|
39
|
+
// pushes per layout pass at the largest TUI tree size).
|
|
40
|
+
heapValue = [];
|
|
41
|
+
heapOmNode = [];
|
|
42
|
+
// Membership set keyed by reference. Allows O(1) `has` checks.
|
|
43
|
+
members = new Set();
|
|
44
|
+
constructor(om) {
|
|
45
|
+
this.om = om;
|
|
46
|
+
}
|
|
47
|
+
get size() {
|
|
48
|
+
return this.heapValue.length;
|
|
49
|
+
}
|
|
50
|
+
isEmpty() {
|
|
51
|
+
return this.heapValue.length === 0;
|
|
52
|
+
}
|
|
53
|
+
has(value) {
|
|
54
|
+
return this.members.has(value);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Push a value at the given OM position. Caller is responsible for
|
|
58
|
+
* checking `has(value)` first if dedup matters (some workflows
|
|
59
|
+
* intentionally re-enqueue after deletion).
|
|
60
|
+
*
|
|
61
|
+
* Returns `true` if added, `false` if `value` was already present
|
|
62
|
+
* (no-op — the queue is unchanged, original OM position retained).
|
|
63
|
+
*/
|
|
64
|
+
push(value, omNode) {
|
|
65
|
+
if (this.members.has(value))
|
|
66
|
+
return false;
|
|
67
|
+
this.heapValue.push(value);
|
|
68
|
+
this.heapOmNode.push(omNode);
|
|
69
|
+
this.members.add(value);
|
|
70
|
+
this.siftUp(this.heapValue.length - 1);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Return (without removing) the value at OM-minimum. Returns
|
|
75
|
+
* `undefined` if the queue is empty.
|
|
76
|
+
*/
|
|
77
|
+
peek() {
|
|
78
|
+
return this.heapValue[0];
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Remove and return the OM-minimum value. Returns `undefined` if
|
|
82
|
+
* the queue is empty.
|
|
83
|
+
*/
|
|
84
|
+
popMin() {
|
|
85
|
+
const n = this.heapValue.length;
|
|
86
|
+
if (n === 0)
|
|
87
|
+
return undefined;
|
|
88
|
+
const top = this.heapValue[0];
|
|
89
|
+
const last = n - 1;
|
|
90
|
+
if (last > 0) {
|
|
91
|
+
this.heapValue[0] = this.heapValue[last];
|
|
92
|
+
this.heapOmNode[0] = this.heapOmNode[last];
|
|
93
|
+
}
|
|
94
|
+
this.heapValue.length = last;
|
|
95
|
+
this.heapOmNode.length = last;
|
|
96
|
+
this.members.delete(top);
|
|
97
|
+
if (last > 0)
|
|
98
|
+
this.siftDown(0);
|
|
99
|
+
return top;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Sift the element at index `start` up the heap until heap property
|
|
103
|
+
* is restored. O(log N).
|
|
104
|
+
*/
|
|
105
|
+
siftUp(start) {
|
|
106
|
+
let i = start;
|
|
107
|
+
const value = this.heapValue[i];
|
|
108
|
+
const omNode = this.heapOmNode[i];
|
|
109
|
+
while (i > 0) {
|
|
110
|
+
const parent = (i - 1) >> 1;
|
|
111
|
+
const parentOm = this.heapOmNode[parent];
|
|
112
|
+
if (this.om.compare(omNode, parentOm) >= 0)
|
|
113
|
+
break;
|
|
114
|
+
this.heapValue[i] = this.heapValue[parent];
|
|
115
|
+
this.heapOmNode[i] = parentOm;
|
|
116
|
+
i = parent;
|
|
117
|
+
}
|
|
118
|
+
this.heapValue[i] = value;
|
|
119
|
+
this.heapOmNode[i] = omNode;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Sift the element at index `start` down the heap until heap property
|
|
123
|
+
* is restored. O(log N).
|
|
124
|
+
*/
|
|
125
|
+
siftDown(start) {
|
|
126
|
+
let i = start;
|
|
127
|
+
const n = this.heapValue.length;
|
|
128
|
+
const value = this.heapValue[i];
|
|
129
|
+
const omNode = this.heapOmNode[i];
|
|
130
|
+
const halfN = n >> 1;
|
|
131
|
+
while (i < halfN) {
|
|
132
|
+
let bestChild = (i << 1) | 1; // 2i + 1, left child
|
|
133
|
+
const right = bestChild + 1;
|
|
134
|
+
if (right < n) {
|
|
135
|
+
if (this.om.compare(this.heapOmNode[right], this.heapOmNode[bestChild]) < 0) {
|
|
136
|
+
bestChild = right;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (this.om.compare(this.heapOmNode[bestChild], omNode) >= 0)
|
|
140
|
+
break;
|
|
141
|
+
this.heapValue[i] = this.heapValue[bestChild];
|
|
142
|
+
this.heapOmNode[i] = this.heapOmNode[bestChild];
|
|
143
|
+
i = bestChild;
|
|
144
|
+
}
|
|
145
|
+
this.heapValue[i] = value;
|
|
146
|
+
this.heapOmNode[i] = omNode;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=priority-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"priority-queue.js","sourceRoot":"","sources":["../../../src/algorithm/spineless/priority-queue.ts"],"names":[],"mappings":"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"}
|