omnichess/lib/data_structures/search_heap.dart

154 lines
4.1 KiB
Dart

class SearchHeap<K, T> {
final K Function(T) getKey;
final int Function(T) getPriority;
List<(T, K, int)> _items;
Map<K, int> _indexByKey;
SearchHeap({
required this.getKey,
required this.getPriority,
}):
_items = [],
_indexByKey = {};
factory SearchHeap.fromItems(
List<T> items,
{
required K Function(T) getKey,
required int Function(T) getPriority,
}
) {
SearchHeap<K, T> 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];
_indexByKey[_items[i].$2] = i;
i = (i-1) >> 1;
}
_items[i] = heapItem;
_indexByKey[heapItem.$2] = i;
}
T removeFirst() {
if (_items.isEmpty) {
throw StateError("No element");
}
final T firstItem = _items[0].$1;
_indexByKey.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];
_indexByKey[_items[i].$2] = i;
i = rightIndex;
} else {
_items[i] = _items[leftIndex];
_indexByKey[_items[i].$2] = i;
i = leftIndex;
}
} else if (rightIndex < _items.length && itemToMove.$3 > _items[rightIndex].$3) {
_items[i] = _items[rightIndex];
_indexByKey[_items[i].$2] = i;
i = rightIndex;
} else {
stop = true;
}
}
_items[i] = itemToMove;
_indexByKey[itemToMove.$2] = i;
return firstItem;
}
void updateElement(K key) {
int i = _indexByKey[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];
_indexByKey[_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];
_indexByKey[_items[i].$2] = i;
i = rightIndex;
} else {
_items[i] = _items[leftIndex];
_indexByKey[_items[i].$2] = i;
i = leftIndex;
}
} else if (rightIndex < _items.length && item.$3 > _items[rightIndex].$3) {
_items[i] = _items[rightIndex];
_indexByKey[_items[i].$2] = i;
i = rightIndex;
} else {
dontBubbleDown = true;
}
}
_items[i] = item;
_indexByKey[item.$2] = i;
}
@override
String toString() => _items.map(((T, K, int) item) => item.$1).toString();
List<T> get debugItems => _items.map(((T, K, int) item) => item.$1).toList();
Map<K, int> get debugPositionByKey => _indexByKey;
bool get debugHeapValidity {
for (int i = 1; i < _items.length; i++) {
if (_items[i].$3 < _items[(i-1) >> 1].$3) {
return false;
}
}
return true;
}
bool get debugDictionaryValidity {
for (final MapEntry<K, int>(key: K key, value: int index) in _indexByKey.entries) {
if (_items[index].$2 != key) {
return false;
}
}
return true;
}
}