| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 | /*	MIT License http://www.opensource.org/licenses/mit-license.php	Author Tobias Koppers @sokra*/"use strict";const SortableSet = require("./SortableSet");/** * @template T * @template K * Multi layer bucket sorted set * Supports adding non-existing items (DO NOT ADD ITEM TWICE) * Supports removing exiting items (DO NOT REMOVE ITEM NOT IN SET) * Supports popping the first items according to defined order * Supports iterating all items without order * Supports updating an item in an efficient way * Supports size property, which is the number of items * Items are lazy partially sorted when needed */class LazyBucketSortedSet {	/**	 * @param {function(T): K} getKey function to get key from item	 * @param {function(K, K): number} comparator comparator to sort keys	 * @param  {...((function(T): any) | (function(any, any): number))} args more pairs of getKey and comparator plus optional final comparator for the last layer	 */	constructor(getKey, comparator, ...args) {		this._getKey = getKey;		this._innerArgs = args;		this._leaf = args.length <= 1;		this._keys = new SortableSet(undefined, comparator);		/** @type {Map<K, LazyBucketSortedSet<T, any> | SortableSet<T>>} */		this._map = new Map();		this._unsortedItems = new Set();		this.size = 0;	}	/**	 * @param {T} item an item	 * @returns {void}	 */	add(item) {		this.size++;		this._unsortedItems.add(item);	}	/**	 * @param {K} key key of item	 * @param {T} item the item	 * @returns {void}	 */	_addInternal(key, item) {		let entry = this._map.get(key);		if (entry === undefined) {			entry = this._leaf				? new SortableSet(undefined, this._innerArgs[0])				: new /** @type {any} */ (LazyBucketSortedSet)(...this._innerArgs);			this._keys.add(key);			this._map.set(key, entry);		}		entry.add(item);	}	/**	 * @param {T} item an item	 * @returns {void}	 */	delete(item) {		this.size--;		if (this._unsortedItems.has(item)) {			this._unsortedItems.delete(item);			return;		}		const key = this._getKey(item);		const entry = this._map.get(key);		entry.delete(item);		if (entry.size === 0) {			this._deleteKey(key);		}	}	/**	 * @param {K} key key to be removed	 * @returns {void}	 */	_deleteKey(key) {		this._keys.delete(key);		this._map.delete(key);	}	/**	 * @returns {T | undefined} an item	 */	popFirst() {		if (this.size === 0) return undefined;		this.size--;		if (this._unsortedItems.size > 0) {			for (const item of this._unsortedItems) {				const key = this._getKey(item);				this._addInternal(key, item);			}			this._unsortedItems.clear();		}		this._keys.sort();		const key = this._keys.values().next().value;		const entry = this._map.get(key);		if (this._leaf) {			const leafEntry = /** @type {SortableSet<T>} */ (entry);			leafEntry.sort();			const item = leafEntry.values().next().value;			leafEntry.delete(item);			if (leafEntry.size === 0) {				this._deleteKey(key);			}			return item;		} else {			const nodeEntry = /** @type {LazyBucketSortedSet<T, any>} */ (entry);			const item = nodeEntry.popFirst();			if (nodeEntry.size === 0) {				this._deleteKey(key);			}			return item;		}	}	/**	 * @param {T} item to be updated item	 * @returns {function(true=): void} finish update	 */	startUpdate(item) {		if (this._unsortedItems.has(item)) {			return remove => {				if (remove) {					this._unsortedItems.delete(item);					this.size--;					return;				}			};		}		const key = this._getKey(item);		if (this._leaf) {			const oldEntry = /** @type {SortableSet<T>} */ (this._map.get(key));			return remove => {				if (remove) {					this.size--;					oldEntry.delete(item);					if (oldEntry.size === 0) {						this._deleteKey(key);					}					return;				}				const newKey = this._getKey(item);				if (key === newKey) {					// This flags the sortable set as unordered					oldEntry.add(item);				} else {					oldEntry.delete(item);					if (oldEntry.size === 0) {						this._deleteKey(key);					}					this._addInternal(newKey, item);				}			};		} else {			const oldEntry = /** @type {LazyBucketSortedSet<T, any>} */ (this._map.get(				key			));			const finishUpdate = oldEntry.startUpdate(item);			return remove => {				if (remove) {					this.size--;					finishUpdate(true);					if (oldEntry.size === 0) {						this._deleteKey(key);					}					return;				}				const newKey = this._getKey(item);				if (key === newKey) {					finishUpdate();				} else {					finishUpdate(true);					if (oldEntry.size === 0) {						this._deleteKey(key);					}					this._addInternal(newKey, item);				}			};		}	}	/**	 * @param {Iterator<T>[]} iterators list of iterators to append to	 * @returns {void}	 */	_appendIterators(iterators) {		if (this._unsortedItems.size > 0)			iterators.push(this._unsortedItems[Symbol.iterator]());		for (const key of this._keys) {			const entry = this._map.get(key);			if (this._leaf) {				const leafEntry = /** @type {SortableSet<T>} */ (entry);				const iterator = leafEntry[Symbol.iterator]();				iterators.push(iterator);			} else {				const nodeEntry = /** @type {LazyBucketSortedSet<T, any>} */ (entry);				nodeEntry._appendIterators(iterators);			}		}	}	/**	 * @returns {Iterator<T>} the iterator	 */	[Symbol.iterator]() {		const iterators = [];		this._appendIterators(iterators);		iterators.reverse();		let currentIterator = iterators.pop();		return {			next: () => {				const res = currentIterator.next();				if (res.done) {					if (iterators.length === 0) return res;					currentIterator = iterators.pop();					return currentIterator.next();				}				return res;			}		};	}}module.exports = LazyBucketSortedSet;
 |