100 lines
2.8 KiB
TypeScript
100 lines
2.8 KiB
TypeScript
|
|
// SOURCE: ARCHITECTURE/32-LANGUAGE-OFFICIAL-13-IMPLEMENTATION-3.md
|
||
|
|
// AUTO-EXTRACTED - Do not edit directly. Edit the source document.
|
||
|
|
|
||
|
|
import type { Job } from "./types";
|
||
|
|
|
||
|
|
export interface Frontier {
|
||
|
|
push(job: Job): void;
|
||
|
|
pop(): Job | undefined;
|
||
|
|
size(): number;
|
||
|
|
clear(): void;
|
||
|
|
}
|
||
|
|
|
||
|
|
export class StackFrontier implements Frontier {
|
||
|
|
private xs: Job[] = [];
|
||
|
|
push(job: Job) { this.xs.push(job); }
|
||
|
|
pop() { return this.xs.pop(); }
|
||
|
|
size() { return this.xs.length; }
|
||
|
|
clear() { this.xs = []; }
|
||
|
|
}
|
||
|
|
|
||
|
|
export class QueueFrontier implements Frontier {
|
||
|
|
private xs: Job[] = [];
|
||
|
|
private head = 0;
|
||
|
|
push(job: Job) { this.xs.push(job); }
|
||
|
|
pop() {
|
||
|
|
if (this.head >= this.xs.length) return undefined;
|
||
|
|
const j = this.xs[this.head];
|
||
|
|
this.head += 1;
|
||
|
|
// compact occasionally
|
||
|
|
if (this.head > 1024 && this.head * 2 > this.xs.length) {
|
||
|
|
this.xs = this.xs.slice(this.head);
|
||
|
|
this.head = 0;
|
||
|
|
}
|
||
|
|
return j;
|
||
|
|
}
|
||
|
|
size() { return this.xs.length - this.head; }
|
||
|
|
clear() { this.xs = []; this.head = 0; }
|
||
|
|
}
|
||
|
|
|
||
|
|
export class PriorityFrontier implements Frontier {
|
||
|
|
// Max-heap by job.score (reference grade)
|
||
|
|
private heap: Job[] = [];
|
||
|
|
push(job: Job) { heapPush(this.heap, job); }
|
||
|
|
pop() { return heapPop(this.heap); }
|
||
|
|
size() { return this.heap.length; }
|
||
|
|
clear() { this.heap = []; }
|
||
|
|
}
|
||
|
|
|
||
|
|
function heapPush(heap: Job[], job: Job) {
|
||
|
|
heap.push(job);
|
||
|
|
let i = heap.length - 1;
|
||
|
|
while (i > 0) {
|
||
|
|
const p = Math.floor((i - 1) / 2);
|
||
|
|
if (heap[p].score >= heap[i].score) break;
|
||
|
|
[heap[p], heap[i]] = [heap[i], heap[p]];
|
||
|
|
i = p;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function heapPop(heap: Job[]): Job | undefined {
|
||
|
|
if (heap.length === 0) return undefined;
|
||
|
|
const top = heap[0];
|
||
|
|
const last = heap.pop()!;
|
||
|
|
if (heap.length > 0) {
|
||
|
|
heap[0] = last;
|
||
|
|
let i = 0;
|
||
|
|
while (true) {
|
||
|
|
const l = 2 * i + 1, r = 2 * i + 2;
|
||
|
|
let m = i;
|
||
|
|
if (l < heap.length && heap[l].score > heap[m].score) m = l;
|
||
|
|
if (r < heap.length && heap[r].score > heap[m].score) m = r;
|
||
|
|
if (m === i) break;
|
||
|
|
[heap[i], heap[m]] = [heap[m], heap[i]];
|
||
|
|
i = m;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return top;
|
||
|
|
}
|
||
|
|
|
||
|
|
export class BeamFrontier implements Frontier {
|
||
|
|
private heap: Job[] = [];
|
||
|
|
constructor(private readonly width: number) {}
|
||
|
|
push(job: Job) {
|
||
|
|
heapPush(this.heap, job);
|
||
|
|
if (this.heap.length > this.width) {
|
||
|
|
// drop worst by rebuilding heap after removing min:
|
||
|
|
// reference-grade: sort descending, truncate, rebuild
|
||
|
|
this.heap.sort((a, b) => b.score - a.score);
|
||
|
|
this.heap.length = this.width;
|
||
|
|
this.heap = this.heap.slice();
|
||
|
|
// rebuild heap structure
|
||
|
|
const xs = this.heap.slice().sort((a, b) => a.score - b.score);
|
||
|
|
this.heap = [];
|
||
|
|
for (const j of xs) heapPush(this.heap, j);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
pop() { return heapPop(this.heap); }
|
||
|
|
size() { return this.heap.length; }
|
||
|
|
clear() { this.heap = []; }
|
||
|
|
}
|