The previous and initial iteration at MSD radix sort in Java for parallel arrays.
Changes:
Entryhides both the sort key and satellite datum in private final fields. Henceforth, they are accessed through a pair of getters.sortTopImplis removed and replaced bysortImpl, which now can handle the sign bit as well.compareToofEntryusesLong.compare.
(Demo.java is the same as in the last iteration.)
So, what do you think?
Arrays.java:
package net.coderodde.util;
public class Arrays {
private static final int BITS_PER_BUCKET = 8;
private static final int BUCKETS = 1 << BITS_PER_BUCKET;
private static final int BUCKET_MASK = BUCKETS - 1;
private static final long SIGN_MASK = 1L << 63;
private static final int MERGESORT_THRESHOLD = 4096;
public static final <E> void sort(final Entry<E>[] array,
final int fromIndex,
final int toIndex) {
if (toIndex - fromIndex < 2) {
// Trivially sorted or indices ass-backwards.
return;
}
final Entry<E>[] buffer = array.clone();
sortImpl(array, buffer, 0, fromIndex, toIndex);
}
public static final <E> void sort(final Entry<E>[] array) {
sort(array, 0, array.length);
}
public static final <E extends Comparable<? super E>>
boolean isSorted(final E[] array,
final int fromIndex,
final int toIndex) {
for (int i = fromIndex; i < toIndex - 1; ++i) {
if (array[i].compareTo(array[i + 1]) > 0) {
return false;
}
}
return true;
}
public static final <E extends Comparable<? super E>>
boolean isSorted(final E[] array) {
return isSorted(array, 0, array.length);
}
public static final <E> boolean areEqual(final Entry<E>[]... arrays) {
for (int i = 0; i < arrays.length - 1; ++i) {
if (arrays[i].length != arrays[i + 1].length) {
return false;
}
}
for (int i = 0; i < arrays[0].length; ++i) {
for (int j = 0; j < arrays.length - 1; ++j) {
if (arrays[j][i] != arrays[j + 1][i]) {
return false;
}
}
}
return true;
}
/**
* This static method sorts the entry array by bytes that are not
* most-significant.
*
* @param <E> the type of satellite data in the entry array.
* @param source the source array.
* @param target the target array.
* @param byteIndex the index of the byte to use as the sorting key. 0
* represents the least-significant byte.
* @param fromIndex the least index of the range to sort.
* @param toIndex the index one past the greatest index of the range to
* sort.
*/
private static <E> void sortImpl(final Entry<E>[] source,
final Entry<E>[] target,
final int recursionDepth,
final int fromIndex,
final int toIndex) {
// Try merge sort.
if (toIndex - fromIndex <= MERGESORT_THRESHOLD) {
// If 'even' is true, the sorted ranged ended up in 'source'.
final boolean even = mergesort(source, target, fromIndex, toIndex);
if (even) {
// source contains the sorted bucket.
if ((recursionDepth & 1) == 1) {
// byteIndex = 6, 4, 2, 0.
// source is buffer, copy to target.
System.arraycopy(source,
fromIndex,
target,
fromIndex,
toIndex - fromIndex);
}
} else {
// target contains the sorted bucket.
if ((recursionDepth & 1) == 0) {
// byteIndex = 5, 3, 1.
// target is buffer, copy to source.
System.arraycopy(target,
fromIndex,
source,
fromIndex,
toIndex - fromIndex);
}
}
return;
}
final int[] bucketSizeMap = new int[BUCKETS];
final int[] startIndexMap = new int[BUCKETS];
final int[] processedMap = new int[BUCKETS];
// Compute the size of each bucket.
for (int i = fromIndex; i < toIndex; ++i) {
bucketSizeMap[getBucket(source[i].key(), recursionDepth)]++;
}
// Initialize the start index map.
startIndexMap[0] = fromIndex;
// Compute the start index map in its entirety.
for (int i = 1; i != BUCKETS; ++i) {
startIndexMap[i] = startIndexMap[i - 1] +
bucketSizeMap[i - 1];
}
// Insert the entries from 'source' into their respective 'target'.
for (int i = fromIndex; i < toIndex; ++i) {
final Entry<E> e = source[i];
final int index = getBucket(source[i].key(), recursionDepth);
target[startIndexMap[index] + processedMap[index]++] = e;
}
if (recursionDepth == 7) {
// There is nowhere to recur, return.
return;
}
// Recur to sort each bucket.
for (int i = 0; i != BUCKETS; ++i) {
if (bucketSizeMap[i] != 0) {
sortImpl(target,
source,
recursionDepth + 1,
startIndexMap[i],
startIndexMap[i] + bucketSizeMap[i]);
}
}
}
/**
* Sorts the range <code>[fromIndex, toIndex)</code> between the arrays
* <code>source</code> and <code>target</code>.
*
* @param <E> the type of entries' satellite data.
* @param source the source array; the data to sort is assumed to be in this
* array.
* @param target acts as an auxiliary array.
* @param fromIndex the least component index of the range to sort.
* @param toIndex <code>toIndex - 1</code> is the index of the rightmost
* component in the range to sort.
* @return <code>true</code> if there was an even amount of merge passes,
* which implies that the sorted range ended up in <code>source</code>.
* Otherwise <code>false</code> is returned, and the sorted range ended up
* in the array <code>target</code>.
*/
private static final <E> boolean mergesort(final Entry<E>[] source,
final Entry<E>[] target,
final int fromIndex,
final int toIndex) {
final int RANGE_LENGTH = toIndex - fromIndex;
Entry<E>[] s = source;
Entry<E>[] t = target;
int passes = 0;
for (int width = 1; width < RANGE_LENGTH; width <<= 1) {
++passes;
int c = 0;
for (; c < RANGE_LENGTH / width; c += 2) {
int left = fromIndex + c * width;
int right = left + width;
int i = left;
final int leftBound = right;
final int rightBound = Math.min(toIndex, right + width);
while (left < leftBound && right < rightBound) {
t[i++] = s[right].key() < s[left].key() ?
s[right++] :
s[left++];
}
while (left < leftBound) { t[i++] = s[left++]; }
while (right < rightBound) { t[i++] = s[right++]; }
}
if (c * width < RANGE_LENGTH) {
for (int i = fromIndex + c * width; i < toIndex; ++i) {
t[i] = s[i];
}
}
final Entry<E>[] tmp = s;
s = t;
t = tmp;
}
return (passes & 1) == 0;
}
private static int getPassAmount(int length) {
if (length < 1) {
// Should not get here.
length = 1;
}
return 32 - Integer.numberOfLeadingZeros(length - 1);
}
private static final int getBucket(final long key,
final int recursionDepth) {
final int bitShift = 64 - (recursionDepth + 1) * BITS_PER_BUCKET;
return (int)((key ^ SIGN_MASK) >>> bitShift) & BUCKET_MASK;
}
}
Entry.java:
package net.coderodde.util;
/**
* The wrapper class holding a satellite datum and the key.
*
* @param <E> the type of a satellite datum.
*/
public final class Entry<E> implements Comparable<Entry<E>> {
/**
* The sorting key.
*/
private final long key;
/**
* The satellite data.
*/
private final E satelliteData;
/**
* Constructs a new <code>Entry</code> with key <code>key</code> and
* the satellite datum <code>satelliteData</code>.
*
* @param key the key of this entry.
* @param satelliteData the satellite data associated with the key.
*/
public Entry(final long key, final E satelliteData) {
this.key = key;
this.satelliteData = satelliteData;
}
public long key() {
return key;
}
public E satelliteData() {
return satelliteData;
}
/**
* Compares this <code>Entry</code> with another.
*
* @param o the entry to compare against.
*
* @return a negative value if this entry's key is less than that of
* <code>o</code>, a positive value if this entry's key is greater than that
* of <code>o</code>, or 0 if the two keys are equal.
*/
@Override
public int compareTo(Entry<E> o) {
return Long.compare(key, o.key);
}
}