package pt.ist.fenixframework.core.adt.bplustree;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.Arrays;
import pt.ist.fenixframework.core.AbstractDomainObject;
@SuppressWarnings({"unchecked", "rawtypes", "serial"})
public class DoubleArray<T extends AbstractDomainObject> implements Serializable {
// Can this be final? Is it a problem due to deserialization?
// In terms of immutability of the ValueType it does not matter much anyway
// as the slots of the arrays are always mutable
public Comparable[] keys;
public T[] values;
protected Class<T> valuesClazz;
public DoubleArray(Class<T> valuesClazz) {
this.keys = new Comparable[0];
this.values = (T[]) Array.newInstance(valuesClazz, 0);
this.valuesClazz = valuesClazz;
}
public DoubleArray(Class<T> valuesClazz, Comparable key, T value, T lastValue) {
this.keys = new Comparable[2];
this.values = (T[]) Array.newInstance(valuesClazz, 2);
this.keys[0] = key;
this.values[0] = value;
this.keys[1] = BPlusTreeArray.LAST_KEY;
this.values[1] = lastValue;
this.valuesClazz = valuesClazz;
}
public DoubleArray(Class<T> valuesClazz, Comparable[] keys, T[] values) {
this.keys = keys;
this.values = values;
this.valuesClazz = valuesClazz;
}
public int length() {
return keys.length;
}
private <E> int binarySearchForInsertion(E[] array, Comparable key) {
int result = Arrays.binarySearch(array, key, BPlusTreeArray.COMPARATOR_SUPPORTING_LAST_KEY);
if (result < 0) {
result *= -1;
result--;
}
return result;
}
public DoubleArray<T> duplicateAndAddKey(Comparable midKey, T left, T right) {
Comparable[] newKeys = new Comparable[keys.length + 1];
T[] newValues = (T[]) Array.newInstance(valuesClazz, values.length + 1);
int midKeyIndex = binarySearchForInsertion(keys, midKey);
// copy as many items as up to the new insertion spot
System.arraycopy(keys, 0, newKeys, 0, midKeyIndex);
System.arraycopy(values, 0, newValues, 0, midKeyIndex);
newKeys[midKeyIndex] = midKey;
newValues[midKeyIndex] = left;
// the midKey can never be the last index, because of the LAST_KEY
System.arraycopy(keys, midKeyIndex, newKeys, midKeyIndex + 1, keys.length - midKeyIndex);
System.arraycopy(values, midKeyIndex, newValues, midKeyIndex + 1, values.length - midKeyIndex);
newValues[midKeyIndex + 1] = right;
return new DoubleArray<T>(valuesClazz, newKeys, newValues);
}
public T get(Comparable key) {
int index = Arrays.binarySearch(keys, key, BPlusTreeArray.COMPARATOR_SUPPORTING_LAST_KEY);
if (index >= 0) {
return values[index];
} else {
return null;
}
}
public boolean containsKey(Comparable key) {
return get(key) != null;
}
// assumes the deletedKey exists
public DoubleArray<T> replaceKey(Comparable deletedKey, Comparable replacementKey, T subNode) {
int remKeyIndex = binarySearchForInsertion(keys, deletedKey);
int newKeyIndex = binarySearchForInsertion(keys, replacementKey);
int lowerIndex = remKeyIndex < newKeyIndex ? remKeyIndex : newKeyIndex;
int higherIndex = remKeyIndex > newKeyIndex ? remKeyIndex : newKeyIndex;
Comparable[] newKeys = new Comparable[keys.length];
T[] newValues = (T[]) Array.newInstance(valuesClazz, keys.length);
// copy up to (excluding) the lower index changed
// does nothing if the lower index is zero
System.arraycopy(keys, 0, newKeys, 0, lowerIndex);
System.arraycopy(values, 0, newValues, 0, lowerIndex);
if (remKeyIndex == newKeyIndex) {
// both changes take place in the same index
// so copy the right side of the changed index to the new arrays
System.arraycopy(keys, lowerIndex + 1, newKeys, lowerIndex + 1, keys.length - lowerIndex - 1);
System.arraycopy(values, lowerIndex + 1, newValues, lowerIndex + 1, values.length - lowerIndex - 1);
// and modify the index left out for the replacement
newKeys[lowerIndex] = replacementKey;
newValues[lowerIndex] = subNode;
} else {
// copy between the two changed indexes in case they are different
if (remKeyIndex == lowerIndex) {
// the removed key is the lower index, so we skip that copy from the original arrays
// we copy as many as up to the replacement key index (excluding) because it is the higher index
System.arraycopy(keys, lowerIndex + 1, newKeys, lowerIndex, higherIndex - lowerIndex - 1);
System.arraycopy(values, lowerIndex + 1, newValues, lowerIndex, higherIndex - lowerIndex - 1);
// and we insert the replacement key in the higher index
// note that the higher index was calculated in the original array, form which
// an element has been subtracted (the lower index in the original array)
newKeys[higherIndex - 1] = replacementKey;
newValues[higherIndex - 1] = subNode;
// copy the rest of the array
System.arraycopy(keys, higherIndex, newKeys, higherIndex, keys.length - higherIndex);
System.arraycopy(values, higherIndex, newValues, higherIndex, values.length - higherIndex);
} else {
// the replacement key is the lower index, so we save one slot on the new arrays for it
// we copy as many as up to the removed key index (which is the higher index in this case)
System.arraycopy(keys, lowerIndex, newKeys, lowerIndex + 1, higherIndex - lowerIndex);
System.arraycopy(values, lowerIndex, newValues, lowerIndex + 1, higherIndex - lowerIndex);
// and we insert the replacement key in the lower index
newKeys[lowerIndex] = replacementKey;
newValues[lowerIndex] = subNode;
// copy the rest of the array, by skipping the removed index (which is the higher) from the
// original array and taking into account that we also added the replacement key in the
// new array
System.arraycopy(keys, higherIndex + 1, newKeys, higherIndex + 1, keys.length - higherIndex);
System.arraycopy(values, higherIndex + 1, newValues, higherIndex + 1, values.length - higherIndex);
}
}
return new DoubleArray<T>(valuesClazz, newKeys, newValues);
}
public T firstValue() {
return values[0];
}
public Comparable firstKey() {
return keys[0];
}
public T lastValue() {
return values[values.length - 1];
}
// produces a new double array
public DoubleArray<T> mergeWith(Comparable splitKey, DoubleArray<T> left) {
int leftSize = left.length();
int thisSize = this.length();
Comparable[] newKeys = new Comparable[thisSize + leftSize];
T[] newValues = (T[]) Array.newInstance(valuesClazz, thisSize + leftSize);
System.arraycopy(left.keys, 0, newKeys, 0, leftSize);
System.arraycopy(left.values, 0, newValues, 0, leftSize);
// remove the entry for left's LAST_KEY and add the higher left value associated with the split-key
// this boils down to only updating the key, because the value was already copied
newKeys[leftSize - 1] = splitKey;
// merge the remaining sub-nodes
System.arraycopy(this.keys, 0, newKeys, leftSize, thisSize);
System.arraycopy(this.values, 0, newValues, leftSize, thisSize);
// sort both arrays according to the keys
quicksort(newKeys, newValues);
return new DoubleArray<T>(valuesClazz, newKeys, newValues);
}
// produces a new double array
public DoubleArray<T> mergeWith(DoubleArray<T> other) {
int otherSize = other.length();
int thisSize = this.length();
Comparable[] newKeys = new Comparable[thisSize + otherSize];
T[] newValues = (T[]) Array.newInstance(valuesClazz, thisSize + otherSize);
System.arraycopy(other.keys, 0, newKeys, 0, otherSize);
System.arraycopy(other.values, 0, newValues, 0, otherSize);
System.arraycopy(this.keys, 0, newKeys, otherSize, thisSize);
System.arraycopy(this.values, 0, newValues, otherSize, thisSize);
// sort both arrays according to the keys
quicksort(newKeys, newValues);
return new DoubleArray<T>(valuesClazz, newKeys, newValues);
}
// adapted a quicksort algorithm to simultaneously sort both arrays based on the keys' order
public void quicksort(Comparable[] main, T[] index) {
quicksort(main, index, 0, index.length - 1);
}
// quicksort a[left] to a[right]
public void quicksort(Comparable[] a, T[] index, int left, int right) {
if (right <= left) return;
int i = partition(a, index, left, right);
quicksort(a, index, left, i-1);
quicksort(a, index, i+1, right);
}
// partition a[left] to a[right], assumes left < right
private int partition(Comparable[] a, T[] index, int left, int right) {
int i = left - 1;
int j = right;
while (true) {
while (less(a[++i], a[right]))
;
while (less(a[right], a[--j]))
if (j == left) break;
if (i >= j) break;
exch(a, index, i, j);
}
exch(a, index, i, right);
return i;
}
private boolean less(Comparable x, Comparable y) {
if (y == BPlusTreeArray.LAST_KEY) {
return y.compareTo(x) > 0;
}
return x.compareTo(y) < 0;
}
private void exch(Comparable[] a, T[] index, int i, int j) {
Comparable tmp = a[i];
a[i] = a[j];
a[j] = tmp;
T tmpi = index[i];
index[i] = index[j];
index[j] = tmpi;
}
protected class KeyVal {
public final Comparable key;
public final T val;
public KeyVal(Comparable key, T val) {
this.key = key;
this.val = val;
}
}
public KeyVal getSmallestKeyValue() {
return new KeyVal(keys[0], values[0]);
}
public KeyVal getBiggestKeyValue() {
return new KeyVal(keys[keys.length - 1], values[values.length - 1]);
}
public DoubleArray<T> removeSmallestKeyValue() {
Comparable[] newKeys = new Comparable[this.length() - 1];
T[] newValues = (T[]) Array.newInstance(valuesClazz, this.length() - 1);
System.arraycopy(keys, 1, newKeys, 0, keys.length - 1);
System.arraycopy(values, 1, newValues, 0, values.length - 1);
return new DoubleArray<T>(valuesClazz, newKeys, newValues);
}
public DoubleArray<T> removeBiggestKeyValue() {
Comparable[] newKeys = new Comparable[this.length() - 1];
T[] newValues = (T[]) Array.newInstance(valuesClazz, this.length() - 1);
System.arraycopy(keys, 0, newKeys, 0, keys.length - 1);
System.arraycopy(values, 0, newValues, 0, values.length - 1);
return new DoubleArray<T>(valuesClazz, newKeys, newValues);
}
public DoubleArray<T> removeKey(Comparable key) {
Comparable[] newKeys = new Comparable[this.length() - 1];
T[] newValues = (T[]) Array.newInstance(valuesClazz, this.length() - 1);
int indexToRemove = Arrays.binarySearch(keys, key, BPlusTreeArray.COMPARATOR_SUPPORTING_LAST_KEY);
System.arraycopy(keys, 0, newKeys, 0, indexToRemove);
System.arraycopy(values, 0, newValues, 0, indexToRemove);
System.arraycopy(keys, indexToRemove + 1, newKeys, indexToRemove, keys.length - indexToRemove - 1);
System.arraycopy(values, indexToRemove + 1, newValues, indexToRemove, values.length - indexToRemove - 1);
return new DoubleArray<T>(valuesClazz, newKeys, newValues);
}
public DoubleArray<T> addKeyValue(KeyVal keyVal) {
return addKeyValue(keyVal.key, keyVal.val);
}
public DoubleArray<T> addKeyValue(Comparable keyToInsert, T valToInsert) {
Comparable[] newKeys = new Comparable[this.length() + 1];
T[] newValues = (T[]) Array.newInstance(valuesClazz, this.length() + 1);
int indexToInsert = binarySearchForInsertion(keys, keyToInsert);
System.arraycopy(keys, 0, newKeys, 0, indexToInsert);
System.arraycopy(values, 0, newValues, 0, indexToInsert);
newKeys[indexToInsert] = keyToInsert;
newValues[indexToInsert] = valToInsert;
System.arraycopy(keys, indexToInsert, newKeys, indexToInsert + 1, keys.length - indexToInsert);
System.arraycopy(values, indexToInsert, newValues, indexToInsert + 1, values.length - indexToInsert);
return new DoubleArray<T>(valuesClazz, newKeys, newValues);
}
public Comparable lowerKeyThanHighest() {
return keys[keys.length - 2];
}
public Comparable findRightMiddlePosition() {
return keys[BPlusTreeArray.LOWER_BOUND + 1];
}
// Used by Leafs that do not have a LAST_KEY
public DoubleArray<T> leftPart(int splitIndex) {
return leftPart(splitIndex, 0);
}
// Left part up to the "splitIndex" (excluding)
public DoubleArray<T> leftPart(int splitIndex, int extraSlots) {
Comparable[] newKeys = new Comparable[splitIndex + extraSlots];
T[] newValues = (T[]) Array.newInstance(valuesClazz, splitIndex + extraSlots);
System.arraycopy(keys, 0, newKeys, 0, splitIndex);
System.arraycopy(values, 0, newValues, 0, splitIndex);
return new DoubleArray<T>(valuesClazz, newKeys, newValues);
}
// Right part from the "splitIndex" (including)
public DoubleArray<T> rightPart(int splitIndex) {
Comparable[] newKeys = new Comparable[keys.length - splitIndex];
T[] newValues = (T[]) Array.newInstance(valuesClazz, keys.length - splitIndex);
System.arraycopy(keys, splitIndex, newKeys, 0, keys.length - splitIndex);
System.arraycopy(values, splitIndex, newValues, 0, values.length - splitIndex);
return new DoubleArray<T>(valuesClazz, newKeys, newValues);
}
}