spica
Version:
Supervisor, Coroutine, Channel, select, AtomicPromise, Cancellation, Cache, List, Queue, Stack, and some utils.
307 lines (296 loc) • 8.44 kB
text/typescript
import { List } from './list';
import { memoize } from './memoize';
// Max heap
interface Node<T, O> {
index: number;
order: O;
value: T;
}
export namespace Heap {
export interface Options {
stable?: boolean;
}
export interface Node<T, O = T> {
readonly order: O;
value: T;
}
}
export class Heap<T, O = T> {
public static readonly max = <O>(a: O, b: O): number => a > b ? -1 : a < b ? 1 : 0;
public static readonly min = <O>(a: O, b: O): number => a > b ? 1 : a < b ? -1 : 0;
constructor(
private readonly cmp: (a: O, b: O) => number = Heap.max,
options?: Heap.Options,
) {
this.stable = options?.stable ?? false;
}
private readonly stable: boolean;
private array: Record<number, Node<T, O>> = {};
private $length = 0;
public get length(): number {
return this.$length;
}
public isEmpty(): boolean {
return this.array[0] !== undefined;
}
public peek(): Heap.Node<T, O> | undefined {
return this.array[0];
}
public insert(this: Heap<T, T>, value: T): Heap.Node<T, O>;
public insert(value: T, order: O): Heap.Node<T, O>;
public insert(value: T, order?: O): Heap.Node<T, O> {
if (arguments.length === 1) {
order = value as any;
}
assert([order = order!]);
const array = this.array;
const node = array[this.$length] = {
index: ++this.$length,
order,
value,
};
upHeapify(this.cmp, array, this.$length);
return node;
}
public replace(this: Heap<T, T>, value: T): T | undefined;
public replace(value: T, order: O): T | undefined;
public replace(value: T, order?: O): T | undefined {
if (arguments.length === 1) {
order = value as any;
}
assert([order = order!]);
if (this.$length === 0) return void this.insert(value, order);
const array = this.array;
const node = array[0];
const val = node.value;
node.order = order;
node.value = value;
downHeapify(this.cmp, array, 1, this.$length, this.stable);
return val;
}
public extract(): T | undefined {
if (this.$length === 0) return;
const node = this.array[0];
this.delete(node);
return node.value;
}
public delete(node: Heap.Node<T, O>): T;
public delete(node: Node<T, O>): T {
const array = this.array;
const index = node.index;
if (array[index - 1] !== node) throw new Error('Invalid node');
swap(array, index, this.$length--);
sort(this.cmp, array, index, this.$length, this.stable);
array[this.$length] = undefined as any;
return node.value;
}
public update(this: Heap<T, T>, node: Heap.Node<T, O>): void;
public update(node: Heap.Node<T, O>, order: O, value?: T): void;
public update(node: Node<T, O>, order?: O, value?: T): void {
const array = this.array;
const index = node.index;
if (array[index - 1] !== node) throw new Error('Invalid node');
if (arguments.length === 1) {
order = node.order;
}
assert([order = order!]);
if (arguments.length >= 3) {
node.value = value!;
}
if (this.cmp(node.order, node.order = order) === 0) return;
sort(this.cmp, array, index, this.$length, this.stable);
}
public clear(): void {
this.array = {};
this.$length = 0;
}
}
function sort<T, O>(
cmp: (a: O, b: O) => number,
array: Record<number, Node<T, O>>,
index: number,
length: number,
stable: boolean,
): boolean {
assert(index);
if (length === 0) return false;
switch (index) {
case 1:
return false
|| downHeapify(cmp, array, index, length, stable);
case length:
return upHeapify(cmp, array, index);
default:
return upHeapify(cmp, array, index)
|| downHeapify(cmp, array, index, length, stable);
}
}
function upHeapify<T, O>(
cmp: (a: O, b: O) => number,
array: Record<number, Node<T, O>>,
index: number,
): boolean {
assert(index);
const order = array[index - 1].order;
let changed = false;
while (index > 1) {
const parent = index / 2 | 0;
if (cmp(array[parent - 1].order, order) <= 0) break;
swap(array, index, parent);
index = parent;
changed ||= true;
}
return changed;
}
function downHeapify<T, O>(
cmp: (a: O, b: O) => number,
array: Record<number, Node<T, O>>,
index: number,
length: number,
stable: boolean,
): boolean {
assert(index);
let changed = false;
while (index < length) {
const left = index * 2;
const right = index * 2 + 1;
let min = index;
if (left <= length) {
const result = cmp(array[left - 1].order, array[min - 1].order);
if (stable ? result <= 0 : result < 0) {
min = left;
}
}
if (right <= length) {
const result = cmp(array[right - 1].order, array[min - 1].order);
if (stable ? result <= 0 : result < 0) {
min = right;
}
}
if (min === index) break;
swap(array, index, min);
index = min;
changed ||= true;
}
return changed;
}
function swap<T, O>(array: Record<number, Node<T, O>>, index1: number, index2: number): void {
assert(index1 && index2);
if (index1 === index2) return;
const pos1 = index1 - 1;
const pos2 = index2 - 1;
const node1 = array[pos1];
const node2 = array[pos2];
array[pos1] = node2;
array[pos2] = node1;
node1.index = index2;
node2.index = index1;
}
class MList<T, O> extends List<MNode<T, O>> {
constructor(
public readonly order: O,
heap: Heap<MList<T, O>, O>,
) {
super();
this.heap = heap.insert(this, order);
}
public readonly heap: Heap.Node<MList<T, O>, O>;
}
class MNode<T, O> implements List.Node {
constructor(
public list: MList<T, O>,
public order: O,
public value: T,
) {
}
public next?: this = undefined;
public prev?: this = undefined;
}
// 1e6要素で落ちるため実用不可
export namespace MultiHeap {
export interface Node<T, O = T> {
readonly order: O;
value: T;
}
export interface Options {
clean?: boolean;
}
}
export class MultiHeap<T, O = T> {
public static readonly max = Heap.max;
public static readonly min = Heap.min;
constructor(
private readonly cmp: (a: O, b: O) => number = MultiHeap.max,
options?: MultiHeap.Options,
) {
this.clean = options?.clean ?? true;
this.heap = new Heap(this.cmp);
}
private readonly clean: boolean;
private readonly heap: Heap<MList<T, O>, O>;
private readonly dict = new Map<O, MList<T, O>>();
private readonly list = memoize<O, MList<T, O>>(order => {
return new MList<T, O>(order, this.heap);
}, this.dict);
private $length = 0;
public get length(): number {
return this.$length;
}
public isEmpty(): boolean {
return this.heap.isEmpty();
}
public peek(): MultiHeap.Node<T, O> | undefined {
return this.heap.peek()?.value.head;
}
public insert(this: MultiHeap<T, T>, value: T): MultiHeap.Node<T, O>;
public insert(value: T, order: O): MultiHeap.Node<T, O>;
public insert(value: T, order?: O): MNode<T, O> {
if (arguments.length === 1) {
order = value as any;
}
assert([order = order!]);
++this.$length;
const node = new MNode(this.list(order), order, value);
node.list.push(node);
return node;
}
public extract(): T | undefined {
if (this.$length === 0) return;
--this.$length;
const list = this.heap.peek()?.value!;
const value = list.shift()!.value;
if (list.length === 0) {
this.heap.extract();
this.clean && this.dict.delete(list.order);
}
return value;
}
public delete(node: MultiHeap.Node<T, O>): T;
public delete(node: MNode<T, O>): T {
if (node.next === undefined) throw new Error('Invalid node');
const list = node.list;
--this.$length;
if (list.length === 1) {
this.heap.delete(list.heap);
this.clean && this.dict.delete(list.order);
}
return list.delete(node).value;
}
public update(node: MultiHeap.Node<T, O>, order: O, value?: T): MultiHeap.Node<T, O>;
public update(node: MNode<T, O>, order?: O, value?: T): MultiHeap.Node<T, O> {
const list = node.list;
if (list === undefined) throw new Error('Invalid node');
assert([order = order!]);
if (arguments.length >= 3) {
node.value = value!;
}
if (this.cmp(list.order, order) === 0) return node;
this.delete(node);
return this.insert(node.value, order);
}
public clear(): void {
this.heap.clear();
this.dict.clear();
this.$length = 0;
}
}