package edu.stanford.nlp.util;
import java.io.Serializable;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/**
* A priority queue based on a binary heap. This implementation trades
* flexibility for speed: while it is up to 2x faster than {@link
* BinaryHeapPriorityQueue} and nearly as fast as {@link
* java.util.PriorityQueue}, it does not support removing or changing the
* priority of an element. Also, while {@link #getPriority(Object key)
* getPriority(Object key)} is supported, performance will be linear, not
* constant.
*
* @author Dan Klein, Bill MacCartney
*/
public class FixedPrioritiesPriorityQueue<E>
extends AbstractSet<E>
implements PriorityQueue<E>, Iterator<E>, Serializable, Cloneable {
private static final long serialVersionUID = 1L;
private int size;
private int capacity;
private List<E> elements;
private double[] priorities;
// constructors ----------------------------------------------------------
public FixedPrioritiesPriorityQueue() {
this(15);
}
public FixedPrioritiesPriorityQueue(int capacity) {
int legalCapacity = 0;
while (legalCapacity < capacity) {
legalCapacity = 2 * legalCapacity + 1;
}
grow(legalCapacity);
}
// iterator methods ------------------------------------------------------
/**
* Returns true if the priority queue is non-empty
*/
public boolean hasNext() {
return ! isEmpty();
}
/**
* Returns the element in the queue with highest priority, and pops it from
* the queue.
*/
public E next() throws NoSuchElementException {
return removeFirst();
}
/**
* Not supported -- next() already removes the head of the queue.
*/
public void remove() {
throw new UnsupportedOperationException();
}
// PriorityQueue methods -------------------------------------------------
/**
* Adds a key to the queue with the given priority. If the key is already in
* the queue, it will be added an additional time, NOT promoted/demoted.
*
*/
public boolean add(E key, double priority) {
if (size == capacity) {
grow(2 * capacity + 1);
}
elements.add(key);
priorities[size] = priority;
heapifyUp(size);
size++;
return true;
}
/**
* Not supported in this implementation.
*/
public boolean changePriority(E key, double priority) {
throw new UnsupportedOperationException();
}
/**
* Returns the highest-priority element without removing it from the
* queue.
*/
public E getFirst() {
if (size() > 0)
return elements.get(0);
throw new NoSuchElementException();
}
/**
* Note that this method will be linear (not constant) time in this
* implementation! Better not to use it.
*/
public double getPriority(Object key) {
for (int i = 0, sz = elements.size(); i < sz; i++) {
if (elements.get(i).equals(key)) {
return priorities[i];
}
}
throw new NoSuchElementException();
}
/**
* Gets the priority of the highest-priority element of the queue.
*/
public double getPriority() {
// check empty other way around
if (size() > 0)
return priorities[0];
throw new NoSuchElementException();
}
/**
* Not supported in this implementation.
*/
public boolean relaxPriority(E key, double priority) {
throw new UnsupportedOperationException();
}
/**
* Returns the highest-priority element and removes it from the queue.
*/
public E removeFirst() throws NoSuchElementException {
E first = getFirst();
swap(0, size - 1);
size--;
elements.remove(size);
heapifyDown(0);
return first;
}
public List<E> toSortedList() {
// initialize with size
List<E> list = new ArrayList<>();
while (hasNext()) {
list.add(next());
}
return list;
}
// Set methods -----------------------------------------------------------
/**
* Number of elements in the queue.
*/
@Override
public int size() {
return size;
}
@Override
public void clear() {
size = 0;
grow(15);
}
@Override
public Iterator<E> iterator() {
return Collections.unmodifiableCollection(toSortedList()).iterator();
}
// -----------------------------------------------------------------------
private void grow(int newCapacity) {
List<E> newElements = new ArrayList<>(newCapacity);
double[] newPriorities = new double[newCapacity];
if (size > 0) {
newElements.addAll(elements);
System.arraycopy(priorities, 0, newPriorities, 0, priorities.length);
}
elements = newElements;
priorities = newPriorities;
capacity = newCapacity;
}
private static int parent(int loc) {
return (loc - 1) / 2;
}
private static int leftChild(int loc) {
return 2 * loc + 1;
}
private static int rightChild(int loc) {
return 2 * loc + 2;
}
private void heapifyUp(int loc) {
if (loc == 0) return;
int parent = parent(loc);
if (priorities[loc] > priorities[parent]) {
swap(loc, parent);
heapifyUp(parent);
}
}
private void heapifyDown(int loc) {
int max = loc;
int leftChild = leftChild(loc);
if (leftChild < size()) {
double priority = priorities[loc];
double leftChildPriority = priorities[leftChild];
if (leftChildPriority > priority)
max = leftChild;
int rightChild = rightChild(loc);
if (rightChild < size()) {
double rightChildPriority = priorities[rightChild(loc)];
if (rightChildPriority > priority && rightChildPriority > leftChildPriority)
max = rightChild;
}
}
if (max == loc)
return;
swap(loc, max);
heapifyDown(max);
}
private void swap(int loc1, int loc2) {
double tempPriority = priorities[loc1];
E tempElement = elements.get(loc1);
priorities[loc1] = priorities[loc2];
elements.set(loc1, elements.get(loc2));
priorities[loc2] = tempPriority;
elements.set(loc2, tempElement);
}
// -----------------------------------------------------------------------
/**
* Returns a representation of the queue in decreasing priority order.
*/
@Override
public String toString() {
return toString(size(), null);
}
/** {@inheritDoc} */
public String toString(int maxKeysToPrint) {
return toString(maxKeysToPrint, "%.3f");
}
/**
* Returns a representation of the queue in decreasing priority order,
* displaying at most maxKeysToPrint elements.
*
*/
public String toString(int maxKeysToPrint, String dblFmt) {
if (maxKeysToPrint <= 0) maxKeysToPrint = Integer.MAX_VALUE;
FixedPrioritiesPriorityQueue<E> pq = clone();
StringBuilder sb = new StringBuilder("[");
int numKeysPrinted = 0;
while (numKeysPrinted < maxKeysToPrint && pq.hasNext()) {
double priority = pq.getPriority();
E element = pq.next();
sb.append(element);
sb.append('=');
if (dblFmt == null) {
sb.append(priority);
} else {
sb.append(String.format(dblFmt, priority));
}
if (numKeysPrinted < size() - 1)
sb.append(", ");
numKeysPrinted++;
}
if (numKeysPrinted < size()) {
sb.append("...");
}
sb.append("]");
return sb.toString();
}
/**
* Returns a clone of this priority queue. Modifications to one will not
* affect modifications to the other.
*/
@Override
public final FixedPrioritiesPriorityQueue<E> clone() {
FixedPrioritiesPriorityQueue<E> clonePQ;
try {
clonePQ = ErasureUtils.uncheckedCast(super.clone());
} catch (CloneNotSupportedException cnse) {
throw new AssertionError("Should be able to clone.");
}
clonePQ.elements = new ArrayList<>(capacity);
clonePQ.priorities = new double[capacity];
if (size() > 0) {
clonePQ.elements.addAll(elements);
System.arraycopy(priorities, 0, clonePQ.priorities, 0, size());
}
return clonePQ;
}
}