diff --git a/packages/heaps/package.json b/packages/heaps/package.json index a878ab3b87..de71c29a3c 100644 --- a/packages/heaps/package.json +++ b/packages/heaps/package.json @@ -40,7 +40,8 @@ }, "dependencies": { "@thi.ng/api": "^7.1.9", - "@thi.ng/compare": "^1.3.33" + "@thi.ng/compare": "^1.3.33", + "@thi.ng/equiv": "^1.0.45" }, "files": [ "*.js", diff --git a/packages/heaps/src/api.ts b/packages/heaps/src/api.ts index b42f62b095..883996ea28 100644 --- a/packages/heaps/src/api.ts +++ b/packages/heaps/src/api.ts @@ -1,7 +1,8 @@ -import type { Comparator } from "@thi.ng/api"; +import type { Comparator, Predicate2 } from "@thi.ng/api"; export interface HeapOpts { compare: Comparator; + equiv: Predicate2; } export interface DHeapOpts extends HeapOpts { diff --git a/packages/heaps/src/dheap.ts b/packages/heaps/src/dheap.ts index 65585b3e8b..07c5ffbaef 100644 --- a/packages/heaps/src/dheap.ts +++ b/packages/heaps/src/dheap.ts @@ -1,8 +1,12 @@ import type { ICopy, IEmpty, IStack } from "@thi.ng/api"; -import { compare } from "@thi.ng/compare"; import type { DHeapOpts } from "./api"; import { Heap } from "./heap"; +export const defDHeap = ( + values?: Iterable | null, + opts?: Partial> +) => new DHeap(values, opts); + /** * Generic d-ary heap / priority queue with configurable arity (default * = 4) and ordering via user-supplied comparator. @@ -17,7 +21,8 @@ import { Heap } from "./heap"; */ export class DHeap extends Heap - implements ICopy>, IEmpty>, IStack> { + implements ICopy>, IEmpty>, IStack> +{ /** * Returns index of parent node or -1 if `idx < 1`. * @@ -41,12 +46,10 @@ export class DHeap protected d: number; constructor(values?: Iterable | null, opts?: Partial>) { - super(undefined, { compare, ...opts }); + super(undefined, opts); this.d = (opts && opts.d) || 4; this.values = []; - if (values) { - this.into(values); - } + values && this.into(values); } copy(): DHeap { @@ -78,7 +81,7 @@ export class DHeap } heapify(vals = this.values) { - for (var i = ((vals.length - 1) / this.d) | 0; i >= 0; i--) { + for (let i = ((vals.length - 1) / this.d) | 0; i >= 0; i--) { this.percolateDown(i, vals); } } diff --git a/packages/heaps/src/heap.ts b/packages/heaps/src/heap.ts index 887d9b393b..38e57813d4 100644 --- a/packages/heaps/src/heap.ts +++ b/packages/heaps/src/heap.ts @@ -1,14 +1,23 @@ -import { compare } from "@thi.ng/compare"; import type { Comparator, IClear, ICopy, IEmpty, + IInto, ILength, IStack, + Predicate, + Predicate2, } from "@thi.ng/api"; +import { compare } from "@thi.ng/compare"; +import { equiv } from "@thi.ng/equiv"; import type { HeapOpts } from "./api"; +export const defHeap = ( + values?: Iterable | null, + opts?: Partial> +) => new Heap(values, opts); + /** * Generic binary heap / priority queue with customizable ordering via * user-supplied comparator. By default, implements min-heap ordering @@ -32,8 +41,10 @@ export class Heap IClear, ICopy>, IEmpty>, + IInto>, ILength, - IStack> { + IStack> +{ static parentIndex(idx: number) { return idx > 0 ? (idx - 1) >> 1 : -1; } @@ -44,10 +55,12 @@ export class Heap values: T[]; compare: Comparator; + equiv: Predicate2; - constructor(values?: Iterable | null, opts?: HeapOpts) { - opts = Object.assign({ compare: compare }, opts); - this.compare = opts.compare; + constructor(values?: Iterable | null, opts?: Partial>) { + opts = { compare, equiv, ...opts }; + this.compare = opts.compare!; + this.equiv = opts.equiv!; this.values = []; if (values) { this.into(values); @@ -139,8 +152,36 @@ export class Heap return res; } + remove(val: T) { + const { values, equiv } = this; + for (let i = values.length; --i >= 0; ) { + if (equiv(values[i], val)) { + this.values.splice(i, 1); + this.heapify(); + return true; + } + } + return false; + } + + find(val: T) { + const { values, equiv } = this; + for (let i = values.length; --i >= 0; ) { + if (equiv(values[i], val)) { + return values[i]; + } + } + } + + findWith(pred: Predicate) { + const values = this.values; + for (let i = values.length; --i >= 0; ) { + if (pred(values[i])) return values[i]; + } + } + heapify(vals = this.values) { - for (var i = (vals.length - 1) >> 1; i >= 0; i--) { + for (let i = (vals.length - 1) >> 1; i >= 0; i--) { this.percolateDown(i, vals); } } diff --git a/packages/heaps/src/index.ts b/packages/heaps/src/index.ts index 79de52ed55..538fcdb4f1 100644 --- a/packages/heaps/src/index.ts +++ b/packages/heaps/src/index.ts @@ -2,3 +2,4 @@ export * from "./api"; export * from "./heap"; export * from "./dheap"; export * from "./pairing"; +export * from "./priority-queue"; diff --git a/packages/heaps/src/pairing.ts b/packages/heaps/src/pairing.ts index d08630a648..2d0765320e 100644 --- a/packages/heaps/src/pairing.ts +++ b/packages/heaps/src/pairing.ts @@ -6,8 +6,11 @@ import type { IEmpty, ILength, IStack, + Predicate, + Predicate2, } from "@thi.ng/api"; import { compare } from "@thi.ng/compare"; +import { equiv } from "@thi.ng/equiv"; import type { HeapOpts } from "./api"; interface Node { @@ -16,6 +19,11 @@ interface Node { p?: Node; } +export const defPairingHeap = ( + values?: Iterable | null, + opts?: Partial> +) => new PairingHeap(values, opts); + export class PairingHeap implements Iterable, @@ -23,14 +31,17 @@ export class PairingHeap ICopy>, IEmpty>, ILength, - IStack> { + IStack> +{ protected compare: Comparator; + protected equiv: Predicate2; protected root!: Node; protected _size!: number; - constructor(vals?: Iterable, opts?: HeapOpts) { - opts = Object.assign({ compare: compare }, opts); - this.compare = opts.compare; + constructor(vals?: Iterable | null, opts?: Partial>) { + opts = { compare, equiv, ...opts }; + this.compare = opts.compare!; + this.equiv = opts.equiv!; this.clear(); vals && this.into(vals); } @@ -51,7 +62,7 @@ export class PairingHeap } empty() { - return new PairingHeap(undefined, { compare }); + return new PairingHeap(null, { compare, equiv }); } copy() { @@ -103,6 +114,18 @@ export class PairingHeap return this; } + find(val: T) { + let found: T | undefined; + this.visit((x) => (this.equiv(x, val) ? ((found = x), false) : true)); + return found; + } + + findWith(fn: Predicate) { + let found: T | undefined; + this.visit((x) => (fn(x) ? ((found = x), false) : true)); + return found; + } + /** * Computes union with given heap and clears `heap`, i.e. this heap * will take ownership of `heap`'s items (if any).