X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fpriority-queue.ts;h=ed4d63f5fa9cb03b71f1e91b472f84d1978d2573;hb=e75373e6b8ed2882c1b8dd7eedc8ca9217582121;hp=cf4fb150e1e1caba20ff9e9d579128c110c40e73;hpb=e41b02718bb9355f7934f0198568cc0ac6f70f9a;p=poolifier.git diff --git a/src/priority-queue.ts b/src/priority-queue.ts index cf4fb150..ed4d63f5 100644 --- a/src/priority-queue.ts +++ b/src/priority-queue.ts @@ -1,42 +1,71 @@ +// Copyright Jerome Benoit. 2024. All Rights Reserved. + +import { FixedPriorityQueue } from './fixed-priority-queue.js' + /** + * Default bucket size. + */ +export const defaultBucketSize = 2048 + +/** + * Priority queue node. + * + * @typeParam T - Type of priority queue node data. * @internal */ -interface PriorityQueueNode { - data: T - priority: number +export interface PriorityQueueNode extends FixedPriorityQueue { + next?: FixedPriorityQueue } /** - * k-priority queue. + * Priority queue. * * @typeParam T - Type of priority queue data. * @internal */ export class PriorityQueue { - private nodeArray!: Array> - /** Prioritized bucket size. */ - private readonly k: number - /** The size of the priority queue. */ - public size!: number - /** The maximum size of the priority queue. */ + private head!: PriorityQueueNode + private tail!: PriorityQueueNode + private readonly bucketSize: number public maxSize!: number /** - * Constructs a k-priority queue. + * Constructs a priority queue. * - * @param k - Prioritized bucket size. + * @param bucketSize - Prioritized bucket size. @defaultValue defaultBucketSize + * @returns PriorityQueue. */ - public constructor (k = Infinity) { - if (k !== Infinity && !Number.isSafeInteger(k)) { - throw new TypeError('k must be an integer') + public constructor (bucketSize: number = defaultBucketSize) { + if (!Number.isSafeInteger(bucketSize)) { + throw new TypeError('bucketSize must be an integer') } - if (k < 1) { - throw new RangeError('k must be greater than or equal to 1') + if (bucketSize < 1) { + throw new RangeError('bucketSize must be greater than or equal to 1') } - this.k = k + this.bucketSize = bucketSize this.clear() } + /** + * The size of the priority queue. + */ + public get size (): number { + let node: PriorityQueueNode | undefined = this.tail + let size = 0 + while (node != null) { + size += node.size + node = node.next + } + return size + } + + /** + * The number of filled prioritized buckets. + */ + public get buckets (): number { + return Math.trunc(this.size / this.bucketSize) + } + /** * Enqueue data into the priority queue. * @@ -45,72 +74,94 @@ export class PriorityQueue { * @returns The new size of the priority queue. */ public enqueue (data: T, priority?: number): number { - priority = priority ?? 0 - const startIndex = - this.k === Infinity || this.nodeArray.length / this.k < 1 - ? 0 - : Math.trunc(this.nodeArray.length / this.k) * this.k - let inserted = false - for (let index = startIndex; index < this.nodeArray.length; index++) { - if (this.nodeArray[index].priority > priority) { - this.nodeArray.splice(index, 0, { data, priority }) - inserted = true - break - } + if (this.head.full()) { + this.head = this.head.next = new FixedPriorityQueue(this.bucketSize) } - if (!inserted) { - this.nodeArray.push({ data, priority }) + this.head.enqueue(data, priority) + const size = this.size + if (size > this.maxSize) { + this.maxSize = size } - return this.incrementSize() + return size } /** * Dequeue data from the priority queue. * + * @param bucket - The prioritized bucket to dequeue from. * @returns The dequeued data or `undefined` if the priority queue is empty. */ - public dequeue (): T | undefined { - if (this.size > 0) { - --this.size + public dequeue (bucket?: number): T | undefined { + let tail: PriorityQueueNode | undefined = this.tail + if (bucket != null) { + let currentBucket = 0 + while (tail != null) { + ++currentBucket + if (currentBucket === bucket) { + break + } + tail = tail.next + } } - return this.nodeArray.shift()?.data - } - - /** - * Peeks at the first data. - * @returns The first data or `undefined` if the priority queue is empty. - */ - public peekFirst (): T | undefined { - return this.nodeArray[0]?.data - } - - /** - * Peeks at the last data. - * @returns The last data or `undefined` if the priority queue is empty. - */ - public peekLast (): T | undefined { - return this.nodeArray[this.nodeArray.length - 1]?.data + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const data = tail!.dequeue() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (tail!.empty() && tail!.next != null) { + if (tail === this.tail) { + this.tail = tail.next + } else { + let node: PriorityQueueNode | undefined = this.tail + while (node != null) { + if (node.next === tail) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + node.next = tail!.next + break + } + node = node.next + } + } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + delete tail!.next + } + return data } /** * Clears the priority queue. */ public clear (): void { - this.nodeArray = [] - this.size = 0 + this.head = this.tail = new FixedPriorityQueue(this.bucketSize) this.maxSize = 0 } /** - * Increments the size of the deque. + * Returns an iterator for the priority queue. * - * @returns The new size of the deque. + * @returns An iterator for the priority queue. + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols */ - private incrementSize (): number { - ++this.size - if (this.size > this.maxSize) { - this.maxSize = this.size + public [Symbol.iterator] (): Iterator { + let i = 0 + let node = this.tail + return { + next: () => { + if (node.empty() || (i >= node.capacity && node.next == null)) { + return { + value: undefined, + done: true + } + } + const value = node.dequeue() as T + ++i + if (i === node.capacity && node.next != null) { + node = node.next + i = 0 + } + return { + value, + done: false + } + } } - return this.size } }