package ags.utils.dataStructures;
import java.util.Arrays;
/**
* An implementation of an implicit binary interval heap.
*/
public class IntervalHeap<T> implements MinHeap<T>, MaxHeap<T> {
private static final int defaultCapacity = 64;
private Object[] data;
private double[] keys;
private int capacity;
private int size;
public IntervalHeap() {
this(defaultCapacity);
}
public IntervalHeap(int capacity) {
this.data = new Object[capacity];
this.keys = new double[capacity];
this.capacity = capacity;
this.size = 0;
}
public void offer(double key, T value) {
// If move room is needed, double array size
if (size >= capacity) {
capacity *= 2;
data = Arrays.copyOf(data, capacity);
keys = Arrays.copyOf(keys, capacity);
}
// Insert new value at the end
size++;
data[size-1] = value;
keys[size-1] = key;
siftInsertedValueUp();
}
public void removeMin() {
if (size == 0) {
throw new IllegalStateException();
}
size--;
data[0] = data[size];
keys[0] = keys[size];
data[size] = null;
siftDownMin(0);
}
public void replaceMin(double key, T value) {
if (size == 0) {
throw new IllegalStateException();
}
data[0] = value;
keys[0] = key;
if (size > 1) {
// Swap with pair if necessary
if (keys[1] < key) {
swap(0, 1);
}
siftDownMin(0);
}
}
public void removeMax() {
if (size == 0) {
throw new IllegalStateException();
} else if (size == 1) {
removeMin();
return;
}
size--;
data[1] = data[size];
keys[1] = keys[size];
data[size] = null;
siftDownMax(1);
}
public void replaceMax(double key, T value) {
if (size == 0) {
throw new IllegalStateException();
} else if (size == 1) {
replaceMin(key, value);
return;
}
data[1] = value;
keys[1] = key;
// Swap with pair if necessary
if (key < keys[0]) {
swap(0, 1);
}
siftDownMax(1);
}
@SuppressWarnings("unchecked")
public T getMin() {
if (size == 0) {
throw new IllegalStateException();
}
return (T) data[0];
}
@SuppressWarnings("unchecked")
public T getMax() {
if (size == 0) {
throw new IllegalStateException();
} else if (size == 1) {
return (T) data[0];
}
return (T) data[1];
}
public double getMinKey() {
if (size == 0) {
throw new IllegalStateException();
}
return keys[0];
}
public double getMaxKey() {
if (size == 0) {
throw new IllegalStateException();
} else if (size == 1) {
return keys[0];
}
return keys[1];
}
private int swap(int x, int y) {
Object yData = data[y];
double yDist = keys[y];
data[y] = data[x];
keys[y] = keys[x];
data[x] = yData;
keys[x] = yDist;
return y;
}
/**
* Min-side (u % 2 == 0):
* - leftchild: 2u + 2
* - rightchild: 2u + 4
* - parent: (x/2-1)&~1
*
* Max-side (u % 2 == 1):
* - leftchild: 2u + 1
* - rightchild: 2u + 3
* - parent: (x/2-1)|1
*/
private void siftInsertedValueUp() {
int u = size-1;
if (u == 0) {
// Do nothing if it's the only element!
}
else if (u == 1) {
// If it is the second element, just sort it with it's pair
if (keys[u] < keys[u-1]) { // If less than it's pair
swap(u, u-1); // Swap with it's pair
}
}
else if (u % 2 == 1) {
// Already paired. Ensure pair is ordered right
int p = (u/2-1)|1; // The larger value of the parent pair
if (keys[u] < keys[u-1]) { // If less than it's pair
u = swap(u, u-1); // Swap with it's pair
if (keys[u] < keys[p-1]) { // If smaller than smaller parent pair
// Swap into min-heap side
u = swap(u, p-1);
siftUpMin(u);
}
} else {
if (keys[u] > keys[p]) { // If larger that larger parent pair
// Swap into max-heap side
u = swap(u, p);
siftUpMax(u);
}
}
} else {
// Inserted in the lower-value slot without a partner
int p = (u/2-1)|1; // The larger value of the parent pair
if (keys[u] > keys[p]) { // If larger that larger parent pair
// Swap into max-heap side
u = swap(u, p);
siftUpMax(u);
} else if (keys[u] < keys[p-1]) { // If smaller than smaller parent pair
// Swap into min-heap side
u = swap(u, p-1);
siftUpMin(u);
}
}
}
private void siftUpMin(int c) {
// Min-side parent: (x/2-1)&~1
for (int p = (c/2-1)&~1; p >= 0 && keys[c] < keys[p]; c = p, p = (c/2-1)&~1) {
swap(c, p);
}
}
private void siftUpMax(int c) {
// Max-side parent: (x/2-1)|1
for (int p = (c/2-1)|1; p >= 0 && keys[c] > keys[p]; c = p, p = (c/2-1)|1) {
swap(c, p);
}
}
private void siftDownMin(int p) {
for (int c = p * 2 + 2; c < size; p = c, c = p * 2 + 2) {
if (c + 2 < size && keys[c + 2] < keys[c]) {
c += 2;
}
if (keys[c] < keys[p]) {
swap(p, c);
// Swap with pair if necessary
if (c+1 < size && keys[c+1] < keys[c]) {
swap(c, c+1);
}
} else {
break;
}
}
}
private void siftDownMax(int p) {
for (int c = p * 2 + 1; c <= size; p = c, c = p * 2 + 1) {
if (c == size) {
// If the left child only has half a pair
if (keys[c - 1] > keys[p]) {
swap(p, c - 1);
}
break;
} else if (c + 2 == size) {
// If there is only room for a right child lower pair
if (keys[c + 1] > keys[c]) {
if (keys[c + 1] > keys[p]) {
swap(p, c + 1);
}
break;
}
} else if (c + 2 < size) {
// If there is room for a right child upper pair
if (keys[c + 2] > keys[c]) {
c += 2;
}
}
if (keys[c] > keys[p]) {
swap(p, c);
// Swap with pair if necessary
if (keys[c-1] > keys[c]) {
swap(c, c-1);
}
} else {
break;
}
}
}
public int size() {
return size;
}
public int capacity() {
return capacity;
}
@Override
public String toString() {
java.text.DecimalFormat twoPlaces = new java.text.DecimalFormat("0.00");
StringBuffer str = new StringBuffer(IntervalHeap.class.getName());
str.append(", size: ").append(size()).append(" capacity: ").append(capacity());
int i = 0, p = 2;
while (i < size()) {
int x = 0;
str.append("\t");
while ((i+x) < size() && x < p) {
str.append(twoPlaces.format(keys[i + x])).append(", ");
x++;
}
str.append("\n");
i += x;
p *= 2;
}
return str.toString();
}
private boolean validateHeap() {
// Validate left-right
for (int i = 0; i < size-1; i+= 2) {
if (keys[i] > keys[i+1]) return false;
}
// Validate within parent interval
for (int i = 2; i < size; i++) {
double maxParent = keys[(i/2-1)|1];
double minParent = keys[(i/2-1)&~1];
if (keys[i] > maxParent || keys[i] < minParent) return false;
}
return true;
}
}