class SearchHeap { final K Function(T) getKey; final int Function(T) getPriority; List<(T, K, int)> _items; Map _positionByKey; SearchHeap({ required this.getKey, required this.getPriority, }): _items = [], _positionByKey = {}; factory SearchHeap.fromItems( List items, { required K Function(T) getKey, required int Function(T) getPriority, } ) { SearchHeap heap = SearchHeap(getKey: getKey, getPriority: getPriority); for (final T item in items) { heap.add(item); } return heap; } T get first => _items.first.$1; T? get firstOrNull => _items.firstOrNull?.$1; int get length => _items.length; bool get isEmpty => _items.isEmpty; bool get isNotEmpty => _items.isNotEmpty; void add(T item) { final (T, K, int) heapItem = (item, getKey(item), getPriority(item)); final int thisKey = heapItem.$3; int i = _items.length; _items.add(heapItem); while (i > 0 && _items[(i-1) >> 1].$3 > thisKey) { _items[i] = _items[(i-1) >> 1]; _positionByKey[_items[i].$2] = i; i = (i-1) >> 1; } _items[i] = heapItem; _positionByKey[heapItem.$2] = i; } T removeFirst() { if (_items.isEmpty) { throw StateError("No element"); } final T firstItem = _items[0].$1; _positionByKey.remove(_items[0].$2); if (_items.length < 2) { _items.length--; return firstItem; } final (T, K, int) itemToMove = _items.last; _items.length--; int i = 0; int leftIndex, rightIndex; bool stop = false; while (!stop) { rightIndex = (i+1) << 1; leftIndex = rightIndex-1; if (leftIndex < _items.length && itemToMove.$3 > _items[leftIndex].$3) { if (rightIndex < _items.length && _items[leftIndex].$3 > _items[rightIndex].$3) { _items[i] = _items[rightIndex]; _positionByKey[_items[i].$2] = i; i = rightIndex; } else { _items[i] = _items[leftIndex]; _positionByKey[_items[i].$2] = i; i = leftIndex; } } else if (rightIndex < _items.length && _items[leftIndex].$3 > _items[rightIndex].$3) { _items[i] = _items[rightIndex]; _positionByKey[_items[i].$2] = i; i = rightIndex; } else { stop = true; } } _items[i] = itemToMove; _positionByKey[itemToMove.$2] = i; return firstItem; } void updateElement(K key) { int i = _positionByKey[key]!; final (T, K, int) item = (_items[i].$1, _items[i].$2, getPriority(_items[i].$1)); bool dontBubbleDown = false; while (i > 0 && _items[(i-1) >> 1].$3 > item.$3) { dontBubbleDown = true; _items[i] = _items[(i-1) >> 1]; _positionByKey[_items[i].$2] = i; i = (i-1) >> 1; } int leftIndex, rightIndex; while (!dontBubbleDown) { rightIndex = (i+1) << 1; leftIndex = rightIndex-1; if (leftIndex < _items.length && item.$3 > _items[leftIndex].$3) { if (rightIndex < _items.length && _items[leftIndex].$3 > _items[rightIndex].$3) { _items[i] = _items[rightIndex]; _positionByKey[_items[i].$2] = i; i = rightIndex; } else { _items[i] = _items[leftIndex]; _positionByKey[_items[i].$2] = i; i = leftIndex; } } else if (rightIndex < _items.length && _items[leftIndex].$3 > _items[rightIndex].$3) { _items[i] = _items[rightIndex]; _positionByKey[_items[i].$2] = i; i = rightIndex; } else { dontBubbleDown = true; } } _items[i] = item; _positionByKey[item.$2] = i; } @override String toString() => _items.map(((T, K, int) item) => item.$1).toString(); List get debugItems => _items.map(((T, K, int) item) => item.$1).toList(); }