export class CircularQueue { private startIndex: number; private endIndex: number; private _data: T[]; private _size: number; private _capacity: number; constructor( size: number, ) { // See the Mozilla documentation on sparse arrays (*not* undefined values) // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections#sparse_arrays this._data = new Array(size); this.startIndex = 0; this.endIndex = 0; this._size = 0; this._capacity = size; } size = (): number => this._size; get = (index: number): T | undefined => { if (index < 0 || index >= this._size) { return undefined; } const actualIndex = (this.startIndex + index) % this._capacity; return this._data[actualIndex]; }; appendWithSorting = ( data: T, sortingCallback: (a: T, b: T) => number ) => { if (this._size === 0) { this._data[this.startIndex] = data; this._size = 1; this.endIndex = this.startIndex; return; } const lastItem = this.get(this._size - 1); const isAlreadyInOrder = lastItem && sortingCallback(lastItem, data) <= 0; if (this._size < this._capacity) { this.endIndex = (this.endIndex + 1) % this._capacity; this._data[this.endIndex] = data; this._size++; } else { this.startIndex = (this.startIndex + 1) % this._capacity; this.endIndex = (this.endIndex + 1) % this._capacity; this._data[this.endIndex] = data; } if (!isAlreadyInOrder) { this.sortData(sortingCallback); } } popFront = () => { if (this._size === 0) { return; } this._data[this.startIndex] = undefined as any; if (this._size === 1) { this._size = 0; this.startIndex = 0; this.endIndex = 0; } else { this.startIndex = (this.startIndex + 1) % this._capacity; this._size--; } } binarySearch = ( searchKey: K, keyExtractor: (item: T) => K ): T | undefined => { if (this._size === 0) { return undefined; } let left = 0; let right = this._size - 1; while (left <= right) { const mid = Math.floor((left + right) / 2); const midItem = this.get(mid)!; const midKey = keyExtractor(midItem); if (midKey === searchKey) { return midItem; } else if (midKey < searchKey) { left = mid + 1; } else { right = mid - 1; } } return undefined; } private sortData = (sortingCallback: (a: T, b: T) => number) => { const items: T[] = []; for (let i = 0; i < this._size; i++) { const item = this.get(i); if (item !== undefined) { items.push(item); } } items.sort(sortingCallback); for (let i = 0; i < items.length; i++) { const actualIndex = (this.startIndex + i) % this._capacity; this._data[actualIndex] = items[i]; } }; }