@mark1russell7/highways 0.1.1 → 0.1.3
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/build/index.d.ts +153 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1383 -374
- package/build/index.js.map +1 -1
- package/package.json +2 -1
- package/src/bench/links.ts +191 -0
- package/src/bench/properties.ts +27 -2
- package/src/bench/run.ts +28 -0
- package/src/cases.ts +111 -0
- package/src/grid.ts +61 -2
- package/src/index.ts +2 -0
- package/src/levels.ts +379 -0
- package/src/routers/sections.ts +50 -43
- package/src/visibility.ts +325 -86
package/src/visibility.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dist, inflate, segmentHitsRect, type Point } from "./geom.ts";
|
|
1
|
+
import { dist, inflate, segmentHitsRect, type Box, type Point } from "./geom.ts";
|
|
2
2
|
import { Grid } from "./grid.ts";
|
|
3
3
|
import type { Node } from "./model.ts";
|
|
4
4
|
|
|
@@ -14,14 +14,21 @@ export class Obstacles {
|
|
|
14
14
|
|
|
15
15
|
/** This method tells if the segment `ab` stays out of each box, made larger by `pad`. The nodes in `skip` do not count. */
|
|
16
16
|
clear(a: Point, b: Point, pad: number, skip?: ReadonlySet<Node>): boolean {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
return this.hit(a, b, pad, skip) === undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* This method gives a node whose box, made larger by `pad`, the segment `ab` goes into, or `undefined` when the segment is clear.
|
|
22
|
+
* The nodes in `skip` do not count. The test stops at the first such node.
|
|
23
|
+
*/
|
|
24
|
+
hit(a: Point, b: Point, pad: number, skip?: ReadonlySet<Node>): Node | undefined {
|
|
25
|
+
let found: Node | undefined;
|
|
26
|
+
this.#grid.someSegment(a, b, pad, (n) => {
|
|
27
|
+
if (skip?.has(n) || !hits(a, b, n, pad)) return false;
|
|
28
|
+
found = n;
|
|
29
|
+
return true;
|
|
23
30
|
});
|
|
24
|
-
return
|
|
31
|
+
return found;
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
/** This method tells if the polyline stays out of each box, made larger by `pad`. The nodes in `skip` do not count. */
|
|
@@ -30,6 +37,11 @@ export class Obstacles {
|
|
|
30
37
|
return true;
|
|
31
38
|
}
|
|
32
39
|
|
|
40
|
+
/** This method gives each node whose bucket the box touches to `visit`. A node can be outside the box. */
|
|
41
|
+
query(box: Box, visit: (n: Node) => void): void {
|
|
42
|
+
this.#grid.query(box, visit);
|
|
43
|
+
}
|
|
44
|
+
|
|
33
45
|
/** This method tells if the point is inside a box, made larger by `pad`. The node `skip` does not count. */
|
|
34
46
|
inside(p: Point, pad: number, skip?: Node): boolean {
|
|
35
47
|
let hit = false;
|
|
@@ -43,6 +55,26 @@ export class Obstacles {
|
|
|
43
55
|
}
|
|
44
56
|
}
|
|
45
57
|
|
|
58
|
+
/** This function tells if the segment `ab` goes into the box of the node, made larger by `pad`. */
|
|
59
|
+
function hits(a: Point, b: Point, n: Node, pad: number): boolean {
|
|
60
|
+
const x = n.box;
|
|
61
|
+
return segmentHitsRect(a, b, x.x - pad, x.y - pad, x.x + x.w + pad, x.y + x.h + pad);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** A free point at an end of a search of `CornerGraph.pathAmong`, and the cost to reach it. */
|
|
65
|
+
export interface Terminal {
|
|
66
|
+
readonly point: Point;
|
|
67
|
+
readonly cost: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** A path of `CornerGraph.pathAmong`: its points, the index of its first point in `from`, the index of its last point in `to`, and its cost. */
|
|
71
|
+
export interface Among {
|
|
72
|
+
readonly points: Point[];
|
|
73
|
+
readonly from: number;
|
|
74
|
+
readonly to: number;
|
|
75
|
+
readonly cost: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
46
78
|
/** The options of the corner graph. */
|
|
47
79
|
export interface CornerGraphOptions {
|
|
48
80
|
/** The distance from a box to its corners. */
|
|
@@ -78,9 +110,18 @@ export class CornerGraph {
|
|
|
78
110
|
#open = new Int32Array(0);
|
|
79
111
|
#closed = new Int32Array(0);
|
|
80
112
|
#stamp = 0;
|
|
81
|
-
// The
|
|
82
|
-
#
|
|
83
|
-
|
|
113
|
+
// The corners in each bucket of the point grid, by the key of the bucket. A corner is in one bucket: the bucket of its point.
|
|
114
|
+
readonly #buckets = new Map<number, number[]>();
|
|
115
|
+
// The distance from the point of the last call of nearestVisible to each corner that the call found.
|
|
116
|
+
#dist = new Float64Array(0);
|
|
117
|
+
// The corners of one ring of nearestVisible, nearest first.
|
|
118
|
+
readonly #near = new MinHeap();
|
|
119
|
+
// The corners that each end of a path sees, by the coordinates of the end.
|
|
120
|
+
readonly #endCache = new Map<string, number[]>();
|
|
121
|
+
// The corners that each end of a path of `pathAmong` sees. Such a path has several ends, so each end takes fewer corners.
|
|
122
|
+
readonly #fewCache = new Map<string, number[]>();
|
|
123
|
+
// The connected part of the graph of each corner. The first call of `path` makes it.
|
|
124
|
+
#part: Int32Array | undefined;
|
|
84
125
|
|
|
85
126
|
/** This constructor makes the graph of the nodes. */
|
|
86
127
|
constructor(nodes: readonly Node[], obstacles: Obstacles, options: CornerGraphOptions) {
|
|
@@ -136,7 +177,12 @@ export class CornerGraph {
|
|
|
136
177
|
}
|
|
137
178
|
|
|
138
179
|
#addPoint(c: Point): void {
|
|
139
|
-
this
|
|
180
|
+
const j = this.points.length;
|
|
181
|
+
this.#pointGrid.add(j, { x: c.x, y: c.y, w: 0, h: 0 });
|
|
182
|
+
const key = bucketKey(Math.floor(c.x / this.#cell), Math.floor(c.y / this.#cell));
|
|
183
|
+
const list = this.#buckets.get(key);
|
|
184
|
+
if (list === undefined) this.#buckets.set(key, [j]);
|
|
185
|
+
else list.push(j);
|
|
140
186
|
this.points.push(c);
|
|
141
187
|
}
|
|
142
188
|
|
|
@@ -167,23 +213,81 @@ export class CornerGraph {
|
|
|
167
213
|
|
|
168
214
|
/** This method gives at most `k` corners that `p` can see, nearest first. The corner `self` does not count. */
|
|
169
215
|
nearestVisible(p: Point, k: number, self = -1): number[] {
|
|
216
|
+
return this.#nearestVisible(p, k, self, this.#pad);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* This method tests the corners in rings of radius `r` = 1, 2, 4 and 8 cells. In each ring, it tests the corners
|
|
221
|
+
* within `r` that an earlier ring did not test, nearest first, and equal distances by index.
|
|
222
|
+
* The ring of `r` takes the corners of the buckets that the square of side `2r` around `p` touches.
|
|
223
|
+
* A corner of these buckets beyond `r` waits for the next ring. So the method visits each bucket one time.
|
|
224
|
+
*/
|
|
225
|
+
#nearestVisible(p: Point, k: number, self: number, pad: number): number[] {
|
|
170
226
|
const out: number[] = [];
|
|
171
|
-
if (this.#
|
|
172
|
-
const
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
227
|
+
if (this.#dist.length < this.points.length) this.#dist = new Float64Array(this.points.length);
|
|
228
|
+
const dd = this.#dist;
|
|
229
|
+
const points = this.points;
|
|
230
|
+
const cell = this.#cell;
|
|
231
|
+
const blockers: Node[] = [];
|
|
232
|
+
const now = this.#near;
|
|
233
|
+
// The corners that the rings found beyond their radius. The next ring takes them.
|
|
234
|
+
let wait: number[] = [];
|
|
235
|
+
// The bucket range of the ring before. The first ring has none.
|
|
236
|
+
let pi0 = 1;
|
|
237
|
+
let pi1 = 0;
|
|
238
|
+
let pj0 = 1;
|
|
239
|
+
let pj1 = 0;
|
|
240
|
+
for (let r = cell; r <= cell * 8; r *= 2) {
|
|
241
|
+
// These bounds are the bounds of a query of the box at (p.x - r, p.y - r) with the side 2r.
|
|
242
|
+
const x0 = p.x - r;
|
|
243
|
+
const y0 = p.y - r;
|
|
244
|
+
const i0 = Math.floor(x0 / cell);
|
|
245
|
+
const j0 = Math.floor(y0 / cell);
|
|
246
|
+
const i1 = Math.floor((x0 + 2 * r) / cell);
|
|
247
|
+
const j1 = Math.floor((y0 + 2 * r) / cell);
|
|
248
|
+
const later: number[] = [];
|
|
249
|
+
now.clear();
|
|
250
|
+
for (const j of wait) {
|
|
251
|
+
if (dd[j]! > r) later.push(j);
|
|
252
|
+
else now.add(dd[j]!, j);
|
|
253
|
+
}
|
|
254
|
+
for (let bi = i0; bi <= i1; bi++) {
|
|
255
|
+
const before = bi >= pi0 && bi <= pi1;
|
|
256
|
+
for (let bj = j0; bj <= j1; bj++) {
|
|
257
|
+
if (before && bj >= pj0 && bj <= pj1) continue;
|
|
258
|
+
const list = this.#buckets.get(bucketKey(bi, bj));
|
|
259
|
+
if (list === undefined) continue;
|
|
260
|
+
for (const j of list) {
|
|
261
|
+
if (j === self) continue;
|
|
262
|
+
const d = dist(p, points[j]!);
|
|
263
|
+
dd[j] = d;
|
|
264
|
+
if (d > r) later.push(j);
|
|
265
|
+
else now.add(d, j);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// The heap gives the corners nearest first, and equal distances by index. A call often stops after a few corners.
|
|
270
|
+
now.build();
|
|
271
|
+
while (now.size > 0) {
|
|
272
|
+
const j = now.pop();
|
|
273
|
+
// A node that stopped an earlier segment often stops this segment too, so the test takes these nodes first.
|
|
274
|
+
// The order of the nodes does not change the result.
|
|
275
|
+
const q = points[j]!;
|
|
276
|
+
let blocked = false;
|
|
277
|
+
for (let b = 0; b < blockers.length && !blocked; b++) blocked = hits(p, q, blockers[b]!, pad);
|
|
278
|
+
if (blocked) continue;
|
|
279
|
+
const hit = this.#obstacles.hit(p, q, pad);
|
|
280
|
+
if (hit !== undefined) blockers.push(hit);
|
|
281
|
+
else {
|
|
282
|
+
out.push(j);
|
|
283
|
+
if (out.length >= k) return out;
|
|
284
|
+
}
|
|
186
285
|
}
|
|
286
|
+
wait = later;
|
|
287
|
+
pi0 = i0;
|
|
288
|
+
pi1 = i1;
|
|
289
|
+
pj0 = j0;
|
|
290
|
+
pj1 = j1;
|
|
187
291
|
}
|
|
188
292
|
return out;
|
|
189
293
|
}
|
|
@@ -196,6 +300,35 @@ export class CornerGraph {
|
|
|
196
300
|
*/
|
|
197
301
|
path(from: Point, to: Point, cost?: (i: number, j: number, d: number) => number, floor: number = 1): Point[] | undefined {
|
|
198
302
|
if (this.#obstacles.clear(from, to, this.#pad)) return [from, to];
|
|
303
|
+
return this.#search([{ point: from, cost: 0 }], [{ point: to, cost: 0 }], cost, floor, this.#k * 2)?.points;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* This method gives the shortest path from a point of `from` to a point of `to`, or `undefined` when the graph has none.
|
|
308
|
+
* The cost of a path includes the costs of its two points. One search finds the path, so this method is faster than a call of `path` for each pair.
|
|
309
|
+
*/
|
|
310
|
+
pathAmong(from: readonly Terminal[], to: readonly Terminal[]): Among | undefined {
|
|
311
|
+
const found = this.#search(from, to, undefined, 1, this.#k);
|
|
312
|
+
// A straight line between two points that costs less than the found path is better. The test takes the lines in order of cost.
|
|
313
|
+
const lines: Among[] = [];
|
|
314
|
+
from.forEach((a, i) =>
|
|
315
|
+
to.forEach((b, k) => {
|
|
316
|
+
const c = a.cost + dist(a.point, b.point) + b.cost;
|
|
317
|
+
if (!found || c < found.cost) lines.push({ points: [a.point, b.point], from: i, to: k, cost: c });
|
|
318
|
+
}),
|
|
319
|
+
);
|
|
320
|
+
lines.sort((u, v) => u.cost - v.cost);
|
|
321
|
+
return lines.find((l) => this.#obstacles.clear(l.points[0]!, l.points[1]!, this.#pad)) ?? found;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** This method is the search (A*) of `path` and `pathAmong`. */
|
|
325
|
+
#search(
|
|
326
|
+
from: readonly Terminal[],
|
|
327
|
+
to: readonly Terminal[],
|
|
328
|
+
cost: ((i: number, j: number, d: number) => number) | undefined,
|
|
329
|
+
floor: number,
|
|
330
|
+
count: number,
|
|
331
|
+
): Among | undefined {
|
|
199
332
|
const n = this.points.length;
|
|
200
333
|
if (this.#g.length < n) {
|
|
201
334
|
this.#g = new Float64Array(n);
|
|
@@ -204,28 +337,54 @@ export class CornerGraph {
|
|
|
204
337
|
this.#closed = new Int32Array(n);
|
|
205
338
|
}
|
|
206
339
|
const stamp = ++this.#stamp;
|
|
340
|
+
const points = this.points;
|
|
341
|
+
// The cost from each corner near a point of `to` to that point, and the index of that point.
|
|
207
342
|
const goal = new Map<number, number>();
|
|
208
|
-
|
|
343
|
+
const goalOf = new Map<number, number>();
|
|
344
|
+
to.forEach((b, k) => {
|
|
345
|
+
for (const j of this.#ends(b.point, count)) {
|
|
346
|
+
const tail = dist(points[j]!, b.point) + b.cost;
|
|
347
|
+
const now = goal.get(j);
|
|
348
|
+
if (now === undefined || tail < now) {
|
|
349
|
+
goal.set(j, tail);
|
|
350
|
+
goalOf.set(j, k);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
});
|
|
209
354
|
if (goal.size === 0) return undefined;
|
|
355
|
+
const starts = from.map((a) => this.#ends(a.point, count));
|
|
356
|
+
// Without a start and a goal in one connected part, A* finds no path. This test stops it before it visits the whole part.
|
|
357
|
+
if (!starts.some((list) => this.#joined(list, goal))) return undefined;
|
|
210
358
|
const heap = new MinHeap();
|
|
211
|
-
const
|
|
359
|
+
const G = this.#g;
|
|
360
|
+
const parentOf = this.#parent;
|
|
361
|
+
const open = this.#open;
|
|
362
|
+
const closed = this.#closed;
|
|
363
|
+
const estimate = (p: Point): number => {
|
|
364
|
+
let h = Infinity;
|
|
365
|
+
for (const b of to) h = Math.min(h, dist(p, b.point));
|
|
366
|
+
return floor * h;
|
|
367
|
+
};
|
|
212
368
|
const relax = (j: number, g: number, parent: number): void => {
|
|
213
|
-
if (
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
heap.push(g +
|
|
369
|
+
if (closed[j] === stamp) return;
|
|
370
|
+
if (open[j] === stamp && G[j]! <= g) return;
|
|
371
|
+
open[j] = stamp;
|
|
372
|
+
G[j] = g;
|
|
373
|
+
parentOf[j] = parent;
|
|
374
|
+
heap.push(g + estimate(points[j]!), j);
|
|
219
375
|
};
|
|
220
|
-
|
|
376
|
+
// The parent of a first corner is -1 - i, where i is the index of its point in `from`.
|
|
377
|
+
from.forEach((a, i) => {
|
|
378
|
+
for (const j of starts[i]!) relax(j, a.cost + dist(a.point, points[j]!), -1 - i);
|
|
379
|
+
});
|
|
221
380
|
let best = Infinity;
|
|
222
381
|
let bestEnd = -1;
|
|
223
382
|
while (heap.size > 0) {
|
|
224
383
|
if (heap.topKey >= best) break;
|
|
225
384
|
const i = heap.pop();
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
const gi =
|
|
385
|
+
if (closed[i] === stamp) continue;
|
|
386
|
+
closed[i] = stamp;
|
|
387
|
+
const gi = G[i]!;
|
|
229
388
|
const tail = goal.get(i);
|
|
230
389
|
if (tail !== undefined && gi + tail < best) {
|
|
231
390
|
best = gi + tail;
|
|
@@ -235,15 +394,69 @@ export class CornerGraph {
|
|
|
235
394
|
const len = this.#len[i]!;
|
|
236
395
|
for (let m = 0; m < adj.length; m++) {
|
|
237
396
|
const j = adj[m]!;
|
|
397
|
+
if (closed[j] === stamp) continue;
|
|
238
398
|
const d = len[m]!;
|
|
239
399
|
relax(j, gi + (cost ? cost(i, j, d) : d), i);
|
|
240
400
|
}
|
|
241
401
|
}
|
|
242
402
|
if (bestEnd < 0) return undefined;
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
403
|
+
const k = goalOf.get(bestEnd)!;
|
|
404
|
+
const out: Point[] = [to[k]!.point];
|
|
405
|
+
for (let i = bestEnd; ; i = parentOf[i]!) {
|
|
406
|
+
out.push(points[i]!);
|
|
407
|
+
if (parentOf[i]! < 0) {
|
|
408
|
+
const f = -1 - parentOf[i]!;
|
|
409
|
+
out.push(from[f]!.point);
|
|
410
|
+
return { points: out.reverse(), from: f, to: k, cost: best };
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/** This method tells if a corner of `starts` and a corner of `goal` are in one connected part of the graph. */
|
|
416
|
+
#joined(starts: readonly number[], goal: ReadonlyMap<number, number>): boolean {
|
|
417
|
+
const part = (this.#part ??= this.#parts());
|
|
418
|
+
const goals = new Set<number>();
|
|
419
|
+
for (const j of goal.keys()) goals.add(part[j]!);
|
|
420
|
+
for (const j of starts) if (goals.has(part[j]!)) return true;
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/** This method gives the number of the connected part of each corner. */
|
|
425
|
+
#parts(): Int32Array {
|
|
426
|
+
const part = new Int32Array(this.points.length).fill(-1);
|
|
427
|
+
const stack: number[] = [];
|
|
428
|
+
let count = 0;
|
|
429
|
+
for (let i = 0; i < part.length; i++) {
|
|
430
|
+
if (part[i] !== -1) continue;
|
|
431
|
+
part[i] = count;
|
|
432
|
+
stack.push(i);
|
|
433
|
+
while (stack.length > 0) {
|
|
434
|
+
for (const j of this.#adj[stack.pop()!]!) {
|
|
435
|
+
if (part[j] !== -1) continue;
|
|
436
|
+
part[j] = count;
|
|
437
|
+
stack.push(j);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
count++;
|
|
441
|
+
}
|
|
442
|
+
return part;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* This method gives at most `count` corners that an end of a path can see. An end near a box sees no corner with the clearance.
|
|
447
|
+
* Then the end takes the corners that it sees without the clearance, so the path does not cut through the boxes.
|
|
448
|
+
* The graph does not change, so the method keeps the corners of each end.
|
|
449
|
+
*/
|
|
450
|
+
#ends(p: Point, count: number): number[] {
|
|
451
|
+
const cache = count === this.#k * 2 ? this.#endCache : this.#fewCache;
|
|
452
|
+
const key = `${p.x},${p.y}`;
|
|
453
|
+
let out = cache.get(key);
|
|
454
|
+
if (!out) {
|
|
455
|
+
out = this.nearestVisible(p, count);
|
|
456
|
+
if (out.length === 0) out = this.#nearestVisible(p, count, -1, 0);
|
|
457
|
+
cache.set(key, out);
|
|
458
|
+
}
|
|
459
|
+
return out;
|
|
247
460
|
}
|
|
248
461
|
|
|
249
462
|
/** This method removes the vertices that a later vertex makes needless: from each vertex, it goes to the farthest vertex in view. */
|
|
@@ -261,65 +474,91 @@ export class CornerGraph {
|
|
|
261
474
|
}
|
|
262
475
|
}
|
|
263
476
|
|
|
264
|
-
/**
|
|
265
|
-
|
|
266
|
-
readonly #k: number[] = [];
|
|
267
|
-
readonly #v: number[] = [];
|
|
268
|
-
|
|
269
|
-
get size(): number {
|
|
270
|
-
return this.#k.length;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
#less(a: number, b: number): boolean {
|
|
274
|
-
return this.#k[a]! < this.#k[b]! || (this.#k[a] === this.#k[b] && this.#v[a]! < this.#v[b]!);
|
|
275
|
-
}
|
|
477
|
+
/** This function packs the two indices of a bucket into one number. Each index stays within ±2^20. */
|
|
478
|
+
const bucketKey = (i: number, j: number): number => (i + 1_048_576) * 2_097_152 + (j + 1_048_576);
|
|
276
479
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
480
|
+
/**
|
|
481
|
+
* A binary min-heap of (key, value) pairs in typed arrays. The pairs come out by key, and equal keys by value, so the order is deterministic.
|
|
482
|
+
* The method `push` adds a pair to the heap. The method `add` only appends a pair, and the method `build` then makes the heap of all the pairs.
|
|
483
|
+
*/
|
|
484
|
+
class MinHeap {
|
|
485
|
+
#d = new Float64Array(64);
|
|
486
|
+
#j = new Int32Array(64);
|
|
487
|
+
size = 0;
|
|
285
488
|
|
|
286
489
|
/** The smallest key. */
|
|
287
490
|
get topKey(): number {
|
|
288
|
-
return this.#
|
|
491
|
+
return this.#d[0]!;
|
|
289
492
|
}
|
|
290
493
|
|
|
291
494
|
push(key: number, value: number): void {
|
|
292
|
-
this
|
|
293
|
-
this.#
|
|
294
|
-
|
|
495
|
+
this.add(key, value);
|
|
496
|
+
const d = this.#d;
|
|
497
|
+
const jj = this.#j;
|
|
498
|
+
let i = this.size - 1;
|
|
295
499
|
while (i > 0) {
|
|
296
500
|
const p = (i - 1) >> 1;
|
|
297
|
-
if (!
|
|
298
|
-
|
|
501
|
+
if (!(key < d[p]! || (key === d[p] && value < jj[p]!))) break;
|
|
502
|
+
d[i] = d[p]!;
|
|
503
|
+
jj[i] = jj[p]!;
|
|
299
504
|
i = p;
|
|
300
505
|
}
|
|
506
|
+
d[i] = key;
|
|
507
|
+
jj[i] = value;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
clear(): void {
|
|
511
|
+
this.size = 0;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
add(d: number, j: number): void {
|
|
515
|
+
if (this.size === this.#d.length) {
|
|
516
|
+
const dd = new Float64Array(2 * this.size);
|
|
517
|
+
dd.set(this.#d);
|
|
518
|
+
this.#d = dd;
|
|
519
|
+
const jj = new Int32Array(2 * this.size);
|
|
520
|
+
jj.set(this.#j);
|
|
521
|
+
this.#j = jj;
|
|
522
|
+
}
|
|
523
|
+
this.#d[this.size] = d;
|
|
524
|
+
this.#j[this.size] = j;
|
|
525
|
+
this.size++;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
build(): void {
|
|
529
|
+
for (let i = (this.size >> 1) - 1; i >= 0; i--) this.#down(i);
|
|
301
530
|
}
|
|
302
531
|
|
|
303
532
|
/** This method removes the pair with the smallest key and gives its value. */
|
|
304
533
|
pop(): number {
|
|
305
|
-
const top = this.#
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
this.#
|
|
310
|
-
this.#
|
|
311
|
-
let i = 0;
|
|
312
|
-
for (;;) {
|
|
313
|
-
const l = 2 * i + 1;
|
|
314
|
-
const r = l + 1;
|
|
315
|
-
let m = i;
|
|
316
|
-
if (l < this.#k.length && this.#less(l, m)) m = l;
|
|
317
|
-
if (r < this.#k.length && this.#less(r, m)) m = r;
|
|
318
|
-
if (m === i) break;
|
|
319
|
-
this.#swap(i, m);
|
|
320
|
-
i = m;
|
|
321
|
-
}
|
|
534
|
+
const top = this.#j[0]!;
|
|
535
|
+
this.size--;
|
|
536
|
+
if (this.size > 0) {
|
|
537
|
+
this.#d[0] = this.#d[this.size]!;
|
|
538
|
+
this.#j[0] = this.#j[this.size]!;
|
|
539
|
+
this.#down(0);
|
|
322
540
|
}
|
|
323
541
|
return top;
|
|
324
542
|
}
|
|
543
|
+
|
|
544
|
+
#down(i: number): void {
|
|
545
|
+
const d = this.#d;
|
|
546
|
+
const jj = this.#j;
|
|
547
|
+
const n = this.size;
|
|
548
|
+
const di = d[i]!;
|
|
549
|
+
const ji = jj[i]!;
|
|
550
|
+
for (;;) {
|
|
551
|
+
const l = 2 * i + 1;
|
|
552
|
+
if (l >= n) break;
|
|
553
|
+
const r = l + 1;
|
|
554
|
+
let m = l;
|
|
555
|
+
if (r < n && (d[r]! < d[l]! || (d[r] === d[l] && jj[r]! < jj[l]!))) m = r;
|
|
556
|
+
if (!(d[m]! < di || (d[m] === di && jj[m]! < ji))) break;
|
|
557
|
+
d[i] = d[m]!;
|
|
558
|
+
jj[i] = jj[m]!;
|
|
559
|
+
i = m;
|
|
560
|
+
}
|
|
561
|
+
d[i] = di;
|
|
562
|
+
jj[i] = ji;
|
|
563
|
+
}
|
|
325
564
|
}
|