/**
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.waveprotocol.wave.client.common.util;
import com.google.gwt.core.client.JsArrayNumber;
import java.util.NoSuchElementException;
/**
* An unbounded priority queue based on a priority heap. <a
* href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/PriorityQueue.html">[Sun
* docs]</a>
*
* Specialized version copied from com/google/gwt/emul/java/util/PriorityQueue.java
* so it doesn't require box/unbox.
*
*/
public class NumberPriorityQueue implements org.waveprotocol.wave.model.util.NumberPriorityQueue {
public interface Comparator {
int compare(double a, double b);
}
/**
* A heap held in an array. heap[0] is the root of the heap (the smallest
* element), the subtrees of node i are 2*i+1 (left) and 2*i+2 (right). Node i
* is a leaf node if 2*i>=n. Node i's parent, if i>0, is floor((i-1)/2).
*/
private final JsArrayNumber heap;
private final Comparator comparator;
public NumberPriorityQueue() {
this(new Comparator() {
@Override
public int compare(double a, double b) {
return Double.compare(a, b);
}
});
}
public NumberPriorityQueue(Comparator comparator) {
heap = (JsArrayNumber)JsArrayNumber.createArray();
this.comparator = comparator;
}
@Override
public boolean offer(double e) {
int node = heap.length();
heap.push(e);
while (node > 0) {
int childNode = node;
node = (node - 1) / 2; // get parent of current node
if (comparator.compare(heap.get(node), e) <= 0) {
// parent is smaller, so we have a valid heap
heap.set(childNode, e);
return true;
}
// exchange with parent and try again
heap.set(childNode, heap.get(node));
}
heap.set(node, e);
return true;
}
@Override
public double peek() {
if (heap.length() == 0) {
throw new NoSuchElementException("Empty queue");
}
return heap.get(0);
}
@Override
public double poll() {
if (heap.length() == 0) {
throw new NoSuchElementException("Empty queue");
}
double value = heap.get(0);
if (heap.length() > 1) {
heap.set(0, pop(heap)); // move last element to root
mergeHeaps(0); // make it back into a heap
} else {
pop(heap);
}
return value;
}
private static native double pop(JsArrayNumber arr) /*-{
return arr.pop();
}-*/;
@Override
public int size() {
return heap.length();
}
/**
* Merge two subheaps into a single heap. O(log n) time
*
* PRECONDITION: both children of <code>node</code> are heaps
*
* @param node the parent of the two subtrees to merge
*/
protected void mergeHeaps(int node) {
int n = heap.length();
double value = heap.get(node);
while (node * 2 + 1 < n) {
int childNode = 2 * node + 1; // start with left child
if ((childNode + 1 < n)
&& (comparator.compare(heap.get(childNode + 1), heap.get(childNode)) < 0)) {
childNode++; // right child is smaller, go down that path
}
// if the current element is smaller than the children, stop
if (comparator.compare(value, heap.get(childNode)) <= 0) {
break;
}
heap.set(node, heap.get(childNode));
node = childNode;
}
heap.set(node, value);
}
}