"use strict";
const SortableSet = require("./SortableSet");
class LazyBucketSortedSet {
constructor(getKey, comparator, ...args) {
this._getKey = getKey;
this._innerArgs = args;
this._leaf = args.length <= 1;
this._keys = new SortableSet(undefined, comparator);
this._map = new Map();
this._unsortedItems = new Set();
this.size = 0;
}
add(item) {
this.size++;
this._unsortedItems.add(item);
}
_addInternal(key, item) {
let entry = this._map.get(key);
if (entry === undefined) {
entry = this._leaf
? new SortableSet(undefined, this._innerArgs[0])
: new (LazyBucketSortedSet)(...this._innerArgs);
this._keys.add(key);
this._map.set(key, entry);
}
entry.add(item);
}
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);
}
}
_deleteKey(key) {
this._keys.delete(key);
this._map.delete(key);
}
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 = (entry);
leafEntry.sort();
const item = leafEntry.values().next().value;
leafEntry.delete(item);
if (leafEntry.size === 0) {
this._deleteKey(key);
}
return item;
} else {
const nodeEntry = (entry);
const item = nodeEntry.popFirst();
if (nodeEntry.size === 0) {
this._deleteKey(key);
}
return item;
}
}
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 = (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) {
oldEntry.add(item);
} else {
oldEntry.delete(item);
if (oldEntry.size === 0) {
this._deleteKey(key);
}
this._addInternal(newKey, item);
}
};
} else {
const oldEntry = (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);
}
};
}
}
_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 = (entry);
const iterator = leafEntry[Symbol.iterator]();
iterators.push(iterator);
} else {
const nodeEntry = (entry);
nodeEntry._appendIterators(iterators);
}
}
}
[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;