/*
* Copyright (c) 2003-onwards Shaven Puppy Ltd
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'Shaven Puppy' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The original code to this class I think came from pjt33 on java-gaming.org - many thanks!
*/
package worm.path;
import java.io.Serializable;
import worm.util.map.IntKeyOpenHashMap;
class BinaryHeap implements Serializable {
private static final long serialVersionUID = 1L;
// private final String nodePoolName;
//
// transient NodePool nodePool;
private Node[] heap = new Node[128];
private int size = 0;
private IntKeyOpenHashMap<Node> userstateToNodeMap;
BinaryHeap() {
// this.nodePoolName = nodePoolName;
// this.nodePool = AStar.getNodePool(nodePoolName);
this.userstateToNodeMap = new IntKeyOpenHashMap<Node>(4096);
}
void clear() {
for (int i = 0; i < size; i ++) {
heap[i].heapIdx = -1;
// nodePool.release(heap[i]);
heap[i] = null;
}
size = 0;
userstateToNodeMap.clear();
}
// /**
// * Deserialization support
// */
// private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
// stream.defaultReadObject();
// nodePool = AStar.getNodePool(nodePoolName);
// }
void insert(Node node) {
// Expand if necessary.
if (size == heap.length) {
Node[] newHeap = new Node[size << 1];
System.arraycopy(heap, 0, newHeap, 0, size);
heap = newHeap;
}
// Map userstate
if (userstateToNodeMap.put(node.userState, node) != null) {
throw new IllegalStateException("Binary heap already contains "+node);
}
// Insert at end and bubble up.
heap[size] = node;
node.heapIdx = size;
upHeap(size++);
}
Node peek() {
if (size == 0) {
throw new IllegalStateException("Can't peek - heap is empty");
}
return heap[0];
}
Node pop() {
if (size == 0) {
throw new IllegalStateException("Can't pop - heap is empty");
}
Node popped = heap[0];
heap[0] = heap[--size];
heap[size] = null;
if (size > 0) {
downHeap(0);
}
// Unmap userstate
popped.heapIdx = -1;
if (userstateToNodeMap.remove(popped.userState) == null) {
throw new IllegalStateException("Popped but wasn't in the heap");
}
return popped;
}
void remove(Node node) {
if (size == 0) {
throw new IllegalStateException("Binary heap is empty: attempting to remove node "+node);
}
if (node.heapIdx == -1) {
throw new IllegalStateException("Node "+node+" is not in the binary heap");
}
// This is what node.heapIdx is for.
heap[node.heapIdx] = heap[--size];
heap[size] = null;
if (size > node.heapIdx) {
if (heap[node.heapIdx].f < node.f) {
upHeap(node.heapIdx);
} else {
downHeap(node.heapIdx);
}
}
// Just as a precaution: should make stuff blow up if the node is
// abused.
node.heapIdx = -1;
//nodePool.release(node);
// Unmap userstate
userstateToNodeMap.remove(node.userState);
}
Node getNode(int userstate) {
if (size == 0) {
return null;
}
return userstateToNodeMap.get(userstate);
}
void changeCost(Node node, int newCost) {
float oldCost = node.f;
node.f = newCost;
if (newCost < oldCost) {
upHeap(node.heapIdx);
} else {
downHeap(node.heapIdx);
}
}
int size() {
return size;
}
boolean isEmpty() {
return size == 0;
}
private void upHeap(int idx) {
Node node = heap[idx];
int cost = node.f;
while (idx > 0) {
int parentIdx = idx - 1 >> 1;
Node parent = heap[parentIdx];
if (cost < parent.f) {
heap[idx] = parent;
parent.heapIdx = idx;
idx = parentIdx;
} else {
break;
}
}
heap[idx] = node;
node.heapIdx = idx;
}
private void downHeap(int idx) {
Node node = heap[idx];
int cost = node.f;
while (true) {
int leftIdx = 1 + (idx << 1);
int rightIdx = leftIdx + 1;
if (leftIdx >= size) {
break;
}
// We definitely have a left child.
Node leftNode = heap[leftIdx];
int leftCost = leftNode.f;
// We may have a right child.
Node rightNode;
int rightCost;
if (rightIdx >= size) {
// Only need to compare with left.
rightNode = null;
rightCost = Integer.MAX_VALUE;
} else {
rightNode = heap[rightIdx];
rightCost = rightNode.f;
}
// Find the smallest of the three costs: the corresponding node
// should be the parent.
if (leftCost < rightCost) {
if (leftCost < cost) {
heap[idx] = leftNode;
leftNode.heapIdx = idx;
idx = leftIdx;
} else {
break;
}
} else {
if (rightCost < cost) {
heap[idx] = rightNode;
rightNode.heapIdx = idx;
idx = rightIdx;
} else {
break;
}
}
}
heap[idx] = node;
node.heapIdx = idx;
}
}