From 1650885e56af92b28655e9d180c7102f62d90558 Mon Sep 17 00:00:00 2001 From: chegar Date: Thu, 27 Dec 2012 21:55:24 +0000 Subject: [PATCH] 8003981: Support Parallel Array Sorting - JEP 103 Reviewed-by: chegar, forax, dholmes, dl Contributed-by: david.holmes@oracle.com, dl@cs.oswego.edu, chris.hegarty@oracle.com --- make/java/java/FILES_java.gmk | 1 + src/share/classes/java/util/Arrays.java | 630 ++++- .../java/util/ArraysParallelSortHelpers.java | 1223 ++++++++++ test/java/util/Arrays/ParallelSorting.java | 2067 +++++++++++++++++ 4 files changed, 3916 insertions(+), 5 deletions(-) create mode 100644 src/share/classes/java/util/ArraysParallelSortHelpers.java create mode 100644 test/java/util/Arrays/ParallelSorting.java diff --git a/make/java/java/FILES_java.gmk b/make/java/java/FILES_java.gmk index b9dad85d5..f6affbf98 100644 --- a/make/java/java/FILES_java.gmk +++ b/make/java/java/FILES_java.gmk @@ -294,6 +294,7 @@ JAVA_JAVA_java = \ java/util/IdentityHashMap.java \ java/util/EnumMap.java \ java/util/Arrays.java \ + java/util/ArraysParallelSortHelpers.java \ java/util/DualPivotQuicksort.java \ java/util/TimSort.java \ java/util/ComparableTimSort.java \ diff --git a/src/share/classes/java/util/Arrays.java b/src/share/classes/java/util/Arrays.java index c4dde9bbe..ee7f48f35 100644 --- a/src/share/classes/java/util/Arrays.java +++ b/src/share/classes/java/util/Arrays.java @@ -26,6 +26,7 @@ package java.util; import java.lang.reflect.*; +import static java.util.ArraysParallelSortHelpers.*; /** * This class contains various methods for manipulating arrays (such as @@ -54,6 +55,13 @@ import java.lang.reflect.*; */ public class Arrays { + /** + * The minimum array length below which the sorting algorithm will not + * further partition the sorting task. + */ + // reasonable default so that we don't overcreate tasks + private static final int MIN_ARRAY_SORT_GRAN = 256; + // Suppresses default constructor, ensuring non-instantiability. private Arrays() {} @@ -787,6 +795,613 @@ public class Arrays { } } + /* + * Parallel sorting of primitive type arrays. + */ + + /** + * Sorts the specified array into ascending numerical order. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(byte[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * + * @since 1.8 + */ + public static void parallelSort(byte[] a) { + parallelSort(a, 0, a.length); + } + + /** + * Sorts the specified range of the array into ascending order. The range + * to be sorted extends from the index {@code fromIndex}, inclusive, to + * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex}, + * the range to be sorted is empty. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(byte[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * @param fromIndex the index of the first element, inclusive, to be sorted + * @param toIndex the index of the last element, exclusive, to be sorted + * + * @throws IllegalArgumentException if {@code fromIndex > toIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code fromIndex < 0} or {@code toIndex > a.length} + * + * @since 1.8 + */ + public static void parallelSort(byte[] a, int fromIndex, int toIndex) { + rangeCheck(a.length, fromIndex, toIndex); + int nelements = toIndex - fromIndex; + int gran = getSplitThreshold(nelements); + FJByte.Sorter task = new FJByte.Sorter(a, new byte[a.length], fromIndex, + nelements, gran); + task.invoke(); + } + + /** + * Sorts the specified array into ascending numerical order. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(char[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * + * @since 1.8 + */ + public static void parallelSort(char[] a) { + parallelSort(a, 0, a.length); + } + + /** + * Sorts the specified range of the array into ascending order. The range + * to be sorted extends from the index {@code fromIndex}, inclusive, to + * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex}, + * the range to be sorted is empty. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(char[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * @param fromIndex the index of the first element, inclusive, to be sorted + * @param toIndex the index of the last element, exclusive, to be sorted + * + * @throws IllegalArgumentException if {@code fromIndex > toIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code fromIndex < 0} or {@code toIndex > a.length} + * + * @since 1.8 + */ + public static void parallelSort(char[] a, int fromIndex, int toIndex) { + rangeCheck(a.length, fromIndex, toIndex); + int nelements = toIndex - fromIndex; + int gran = getSplitThreshold(nelements); + FJChar.Sorter task = new FJChar.Sorter(a, new char[a.length], fromIndex, + nelements, gran); + task.invoke(); + } + + /** + * Sorts the specified array into ascending numerical order. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(short[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * + * @since 1.8 + */ + public static void parallelSort(short[] a) { + parallelSort(a, 0, a.length); + } + + /** + * Sorts the specified range of the array into ascending order. The range + * to be sorted extends from the index {@code fromIndex}, inclusive, to + * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex}, + * the range to be sorted is empty. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(short[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * @param fromIndex the index of the first element, inclusive, to be sorted + * @param toIndex the index of the last element, exclusive, to be sorted + * + * @throws IllegalArgumentException if {@code fromIndex > toIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code fromIndex < 0} or {@code toIndex > a.length} + * + * @since 1.8 + */ + public static void parallelSort(short[] a, int fromIndex, int toIndex) { + rangeCheck(a.length, fromIndex, toIndex); + int nelements = toIndex - fromIndex; + int gran = getSplitThreshold(nelements); + FJShort.Sorter task = new FJShort.Sorter(a, new short[a.length], fromIndex, + nelements, gran); + task.invoke(); + } + + /** + * Sorts the specified array into ascending numerical order. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(int[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * + * @since 1.8 + */ + public static void parallelSort(int[] a) { + parallelSort(a, 0, a.length); + } + + /** + * Sorts the specified range of the array into ascending order. The range + * to be sorted extends from the index {@code fromIndex}, inclusive, to + * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex}, + * the range to be sorted is empty. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(int[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * @param fromIndex the index of the first element, inclusive, to be sorted + * @param toIndex the index of the last element, exclusive, to be sorted + * + * @throws IllegalArgumentException if {@code fromIndex > toIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code fromIndex < 0} or {@code toIndex > a.length} + * + * @since 1.8 + */ + public static void parallelSort(int[] a, int fromIndex, int toIndex) { + rangeCheck(a.length, fromIndex, toIndex); + int nelements = toIndex - fromIndex; + int gran = getSplitThreshold(nelements); + FJInt.Sorter task = new FJInt.Sorter(a, new int[a.length], fromIndex, + nelements, gran); + task.invoke(); + } + + /** + * Sorts the specified array into ascending numerical order. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(long[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * + * @since 1.8 + */ + public static void parallelSort(long[] a) { + parallelSort(a, 0, a.length); + } + + /** + * Sorts the specified range of the array into ascending order. The range + * to be sorted extends from the index {@code fromIndex}, inclusive, to + * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex}, + * the range to be sorted is empty. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(long[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * @param fromIndex the index of the first element, inclusive, to be sorted + * @param toIndex the index of the last element, exclusive, to be sorted + * + * @throws IllegalArgumentException if {@code fromIndex > toIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code fromIndex < 0} or {@code toIndex > a.length} + * + * @since 1.8 + */ + public static void parallelSort(long[] a, int fromIndex, int toIndex) { + rangeCheck(a.length, fromIndex, toIndex); + int nelements = toIndex - fromIndex; + int gran = getSplitThreshold(nelements); + FJLong.Sorter task = new FJLong.Sorter(a, new long[a.length], fromIndex, + nelements, gran); + task.invoke(); + } + + /** + * Sorts the specified array into ascending numerical order. + * + *

The {@code <} relation does not provide a total order on all float + * values: {@code -0.0f == 0.0f} is {@code true} and a {@code Float.NaN} + * value compares neither less than, greater than, nor equal to any value, + * even itself. This method uses the total order imposed by the method + * {@link Float#compareTo}: {@code -0.0f} is treated as less than value + * {@code 0.0f} and {@code Float.NaN} is considered greater than any + * other value and all {@code Float.NaN} values are considered equal. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(float[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * + * @since 1.8 + */ + public static void parallelSort(float[] a) { + parallelSort(a, 0, a.length); + } + + /** + * Sorts the specified range of the array into ascending order. The range + * to be sorted extends from the index {@code fromIndex}, inclusive, to + * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex}, + * the range to be sorted is empty. + * + *

The {@code <} relation does not provide a total order on all float + * values: {@code -0.0f == 0.0f} is {@code true} and a {@code Float.NaN} + * value compares neither less than, greater than, nor equal to any value, + * even itself. This method uses the total order imposed by the method + * {@link Float#compareTo}: {@code -0.0f} is treated as less than value + * {@code 0.0f} and {@code Float.NaN} is considered greater than any + * other value and all {@code Float.NaN} values are considered equal. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(float[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * @param fromIndex the index of the first element, inclusive, to be sorted + * @param toIndex the index of the last element, exclusive, to be sorted + * + * @throws IllegalArgumentException if {@code fromIndex > toIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code fromIndex < 0} or {@code toIndex > a.length} + * + * @since 1.8 + */ + public static void parallelSort(float[] a, int fromIndex, int toIndex) { + rangeCheck(a.length, fromIndex, toIndex); + int nelements = toIndex - fromIndex; + int gran = getSplitThreshold(nelements); + FJFloat.Sorter task = new FJFloat.Sorter(a, new float[a.length], fromIndex, + nelements, gran); + task.invoke(); + } + + /** + * Sorts the specified array into ascending numerical order. + * + *

The {@code <} relation does not provide a total order on all double + * values: {@code -0.0d == 0.0d} is {@code true} and a {@code Double.NaN} + * value compares neither less than, greater than, nor equal to any value, + * even itself. This method uses the total order imposed by the method + * {@link Double#compareTo}: {@code -0.0d} is treated as less than value + * {@code 0.0d} and {@code Double.NaN} is considered greater than any + * other value and all {@code Double.NaN} values are considered equal. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(double[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * + * @since 1.8 + */ + public static void parallelSort(double[] a) { + parallelSort(a, 0, a.length); + } + + /** + * Sorts the specified range of the array into ascending order. The range + * to be sorted extends from the index {@code fromIndex}, inclusive, to + * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex}, + * the range to be sorted is empty. + * + *

The {@code <} relation does not provide a total order on all double + * values: {@code -0.0d == 0.0d} is {@code true} and a {@code Double.NaN} + * value compares neither less than, greater than, nor equal to any value, + * even itself. This method uses the total order imposed by the method + * {@link Double#compareTo}: {@code -0.0d} is treated as less than value + * {@code 0.0d} and {@code Double.NaN} is considered greater than any + * other value and all {@code Double.NaN} values are considered equal. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(double[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * @param fromIndex the index of the first element, inclusive, to be sorted + * @param toIndex the index of the last element, exclusive, to be sorted + * + * @throws IllegalArgumentException if {@code fromIndex > toIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code fromIndex < 0} or {@code toIndex > a.length} + * + * @since 1.8 + */ + public static void parallelSort(double[] a, int fromIndex, int toIndex) { + rangeCheck(a.length, fromIndex, toIndex); + int nelements = toIndex - fromIndex; + int gran = getSplitThreshold(nelements); + FJDouble.Sorter task = new FJDouble.Sorter(a, new double[a.length], + fromIndex, nelements, gran); + task.invoke(); + } + + /* + * Parallel sorting of complex type arrays. + */ + + /** + * Sorts the specified array of objects into ascending order, according + * to the {@linkplain Comparable natural ordering} of its elements. + * All elements in the array must implement the {@link Comparable} + * interface. Furthermore, all elements in the array must be + * mutually comparable (that is, {@code e1.compareTo(e2)} must + * not throw a {@code ClassCastException} for any elements {@code e1} + * and {@code e2} in the array). + * + *

This sort is not guaranteed to be stable: equal elements + * may be reordered as a result of the sort. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(Object[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * + * @throws ClassCastException if the array contains elements that are not + * mutually comparable (for example, strings and integers) + * @throws IllegalArgumentException (optional) if the natural + * ordering of the array elements is found to violate the + * {@link Comparable} contract + * + * @since 1.8 + */ + public static > void parallelSort(T[] a) { + parallelSort(a, 0, a.length); + } + + /** + * Sorts the specified range of the specified array of objects into + * ascending order, according to the + * {@linkplain Comparable natural ordering} of its + * elements. The range to be sorted extends from index + * {@code fromIndex}, inclusive, to index {@code toIndex}, exclusive. + * (If {@code fromIndex==toIndex}, the range to be sorted is empty.) All + * elements in this range must implement the {@link Comparable} + * interface. Furthermore, all elements in this range must be mutually + * comparable (that is, {@code e1.compareTo(e2)} must not throw a + * {@code ClassCastException} for any elements {@code e1} and + * {@code e2} in the array). + * + *

This sort is not guaranteed to be stable: equal elements + * may be reordered as a result of the sort. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(Object[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * @param fromIndex the index of the first element (inclusive) to be + * sorted + * @param toIndex the index of the last element (exclusive) to be sorted + * @throws IllegalArgumentException if {@code fromIndex > toIndex} or + * (optional) if the natural ordering of the array elements is + * found to violate the {@link Comparable} contract + * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or + * {@code toIndex > a.length} + * @throws ClassCastException if the array contains elements that are + * not mutually comparable (for example, strings and + * integers). + * + * @since 1.8 + */ + public static > + void parallelSort(T[] a, int fromIndex, int toIndex) { + rangeCheck(a.length, fromIndex, toIndex); + int nelements = toIndex - fromIndex; + Class tc = a.getClass().getComponentType(); + @SuppressWarnings("unchecked") + T[] workspace = (T[])Array.newInstance(tc, a.length); + int gran = getSplitThreshold(nelements); + FJComparable.Sorter task = new FJComparable.Sorter<>(a, workspace, + fromIndex, + nelements, gran); + task.invoke(); + } + + /** + * Sorts the specified array of objects according to the order induced by + * the specified comparator. All elements in the array must be + * mutually comparable by the specified comparator (that is, + * {@code c.compare(e1, e2)} must not throw a {@code ClassCastException} + * for any elements {@code e1} and {@code e2} in the array). + * + *

This sort is not guaranteed to be stable: equal elements + * may be reordered as a result of the sort. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(Object[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * @param c the comparator to determine the order of the array. A + * {@code null} value indicates that the elements' + * {@linkplain Comparable natural ordering} should be used. + * @throws ClassCastException if the array contains elements that are + * not mutually comparable using the specified comparator + * @throws IllegalArgumentException (optional) if the comparator is + * found to violate the {@link java.util.Comparator} contract + * + * @since 1.8 + */ + public static void parallelSort(T[] a, Comparator c) { + parallelSort(a, 0, a.length, c); + } + + /** + * Sorts the specified range of the specified array of objects according + * to the order induced by the specified comparator. The range to be + * sorted extends from index {@code fromIndex}, inclusive, to index + * {@code toIndex}, exclusive. (If {@code fromIndex==toIndex}, the + * range to be sorted is empty.) All elements in the range must be + * mutually comparable by the specified comparator (that is, + * {@code c.compare(e1, e2)} must not throw a {@code ClassCastException} + * for any elements {@code e1} and {@code e2} in the range). + * + *

This sort is not guaranteed to be stable: equal elements + * may be reordered as a result of the sort. + * + *

Implementation note: The sorting algorithm is a parallel sort-merge + * that breaks the array into sub-arrays that are themselves sorted and then + * merged. When the sub-array length reaches a minimum granularity, the + * sub-array is sorted using the appropriate {@link Arrays#sort(Object[]) + * Arrays.sort} method. The algorithm requires a working space equal to the + * size of the original array. The {@link + * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is + * used to execute any parallel tasks. + * + * @param a the array to be sorted + * @param fromIndex the index of the first element (inclusive) to be + * sorted + * @param toIndex the index of the last element (exclusive) to be sorted + * @param c the comparator to determine the order of the array. A + * {@code null} value indicates that the elements' + * {@linkplain Comparable natural ordering} should be used. + * @throws IllegalArgumentException if {@code fromIndex > toIndex} or + * (optional) if the natural ordering of the array elements is + * found to violate the {@link Comparable} contract + * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or + * {@code toIndex > a.length} + * @throws ClassCastException if the array contains elements that are + * not mutually comparable (for example, strings and + * integers). + * + * @since 1.8 + */ + public static void parallelSort(T[] a, int fromIndex, int toIndex, + Comparator c) { + rangeCheck(a.length, fromIndex, toIndex); + int nelements = toIndex - fromIndex; + Class tc = a.getClass().getComponentType(); + @SuppressWarnings("unchecked") + T[] workspace = (T[])Array.newInstance(tc, a.length); + int gran = getSplitThreshold(nelements); + FJComparator.Sorter task = new FJComparator.Sorter<>(a, workspace, + fromIndex, + nelements, gran, c); + task.invoke(); + } + + /** + * Returns the size threshold for splitting into subtasks. + * By default, uses about 8 times as many tasks as threads + * + * @param n number of elements in the array to be processed + */ + private static int getSplitThreshold(int n) { + int p = java.util.concurrent.ForkJoinPool.getCommonPoolParallelism(); + int t = (p > 1) ? (1 + n / (p << 3)) : n; + return t < MIN_ARRAY_SORT_GRAN ? MIN_ARRAY_SORT_GRAN : t; + } + /** * Checks that {@code fromIndex} and {@code toIndex} are in * the range and throws an appropriate exception, if they aren't. @@ -1480,9 +2095,9 @@ public class Arrays { while (low <= high) { int mid = (low + high) >>> 1; @SuppressWarnings("rawtypes") - Comparable midVal = (Comparable)a[mid]; + Comparable midVal = (Comparable)a[mid]; @SuppressWarnings("unchecked") - int cmp = midVal.compareTo(key); + int cmp = midVal.compareTo(key); if (cmp < 0) low = mid + 1; @@ -2847,19 +3462,20 @@ public class Arrays { private final E[] a; ArrayList(E[] array) { - if (array==null) - throw new NullPointerException(); - a = array; + a = Objects.requireNonNull(array); } + @Override public int size() { return a.length; } + @Override public Object[] toArray() { return a.clone(); } + @Override @SuppressWarnings("unchecked") public T[] toArray(T[] a) { int size = size(); @@ -2872,16 +3488,19 @@ public class Arrays { return a; } + @Override public E get(int index) { return a[index]; } + @Override public E set(int index, E element) { E oldValue = a[index]; a[index] = element; return oldValue; } + @Override public int indexOf(Object o) { if (o==null) { for (int i=0; i Cilk: + * Basic algorithm: + * if array size is small, just use a sequential quicksort (via Arrays.sort) + * Otherwise: + * 1. Break array in half. + * 2. For each half, + * a. break the half in half (i.e., quarters), + * b. sort the quarters + * c. merge them together + * 3. merge together the two halves. + * + * One reason for splitting in quarters is that this guarantees + * that the final sort is in the main array, not the workspace + * array. (workspace and main swap roles on each subsort step.) + * Leaf-level sorts use a Sequential quicksort, that in turn uses + * insertion sort if under threshold. Otherwise it uses median of + * three to pick pivot, and loops rather than recurses along left + * path. + * + * + * Merger classes perform merging for Sorter. If big enough, splits Left + * partition in half; finds the greatest point in Right partition + * less than the beginning of the second half of Left via binary + * search; and then, in parallel, merges left half of Left with + * elements of Right up to split point, and merges right half of + * Left with elements of R past split point. At leaf, it just + * sequentially merges. This is all messy to code; sadly we need + * distinct versions for each type. + * + */ +/*package*/ class ArraysParallelSortHelpers { + + // RFE: we should only need a working array as large as the subarray + // to be sorted, but the logic assumes that indices in the two + // arrays always line-up + + /** byte support class */ + static final class FJByte { + static final class Sorter extends RecursiveAction { + static final long serialVersionUID = 749471161188027634L; + final byte[] a; // array to be sorted. + final byte[] w; // workspace for merge + final int origin; // origin of the part of array we deal with + final int n; // Number of elements in (sub)arrays. + final int gran; // split control + + Sorter(byte[] a, byte[] w, int origin, int n, int gran) { + this.a = a; + this.w = w; + this.origin = origin; + this.n = n; + this.gran = gran; + } + + public void compute() { + final int l = origin; + final int g = gran; + final int n = this.n; + final byte[] a = this.a; + final byte[] w = this.w; + if (n > g) { + int h = n >>> 1; // half + int q = n >>> 2; // lower quarter index + int u = h + q; // upper quarter + FJSubSorter ls = new FJSubSorter(new Sorter(a, w, l, q, g), + new Sorter(a, w, l+q, h-q, g), + new Merger(a, w, l, q, + l+q, h-q, l, g, null)); + FJSubSorter rs = new FJSubSorter(new Sorter(a, w, l+h, q, g), + new Sorter(a, w, l+u, n-u, g), + new Merger(a, w, l+h, q, + l+u, n-u, l+h, g, null)); + rs.fork(); + ls.compute(); + if (rs.tryUnfork()) rs.compute(); else rs.join(); + new Merger(w, a, l, h, + l+h, n-h, l, g, null).compute(); + } else { + DualPivotQuicksort.sort(a, l, l+n-1); //skip rangeCheck + } + } + } + + static final class Merger extends RecursiveAction { + static final long serialVersionUID = -9090258248781844470L; + final byte[] a; + final byte[] w; + final int lo; + final int ln; + final int ro; + final int rn; + final int wo; + final int gran; + final Merger next; + + Merger(byte[] a, byte[] w, int lo, int ln, int ro, int rn, int wo, + int gran, Merger next) { + this.a = a; + this.w = w; + this.lo = lo; + this.ln = ln; + this.ro = ro; + this.rn = rn; + this.wo = wo; + this.gran = gran; + this.next = next; + } + + public void compute() { + final byte[] a = this.a; + final byte[] w = this.w; + Merger rights = null; + int nleft = ln; + int nright = rn; + while (nleft > gran) { + int lh = nleft >>> 1; + int splitIndex = lo + lh; + byte split = a[splitIndex]; + int rl = 0; + int rh = nright; + while (rl < rh) { + int mid = (rl + rh) >>> 1; + if (split <= a[ro + mid]) + rh = mid; + else + rl = mid + 1; + } + (rights = new Merger(a, w, splitIndex, nleft-lh, ro+rh, + nright-rh, wo+lh+rh, gran, rights)).fork(); + nleft = lh; + nright = rh; + } + + int l = lo; + int lFence = l + nleft; + int r = ro; + int rFence = r + nright; + int k = wo; + while (l < lFence && r < rFence) { + byte al = a[l]; + byte ar = a[r]; + byte t; + if (al <= ar) {++l; t=al;} else {++r; t = ar;} + w[k++] = t; + } + while (l < lFence) + w[k++] = a[l++]; + while (r < rFence) + w[k++] = a[r++]; + while (rights != null) { + if (rights.tryUnfork()) + rights.compute(); + else + rights.join(); + rights = rights.next; + } + } + } + } // FJByte + + /** char support class */ + static final class FJChar { + static final class Sorter extends RecursiveAction { + static final long serialVersionUID = 8723376019074596641L; + final char[] a; // array to be sorted. + final char[] w; // workspace for merge + final int origin; // origin of the part of array we deal with + final int n; // Number of elements in (sub)arrays. + final int gran; // split control + + Sorter(char[] a, char[] w, int origin, int n, int gran) { + this.a = a; + this.w = w; + this.origin = origin; + this.n = n; + this.gran = gran; + } + + public void compute() { + final int l = origin; + final int g = gran; + final int n = this.n; + final char[] a = this.a; + final char[] w = this.w; + if (n > g) { + int h = n >>> 1; // half + int q = n >>> 2; // lower quarter index + int u = h + q; // upper quarter + FJSubSorter ls = new FJSubSorter(new Sorter(a, w, l, q, g), + new Sorter(a, w, l+q, h-q, g), + new Merger(a, w, l, q, + l+q, h-q, l, g, null)); + FJSubSorter rs = new FJSubSorter(new Sorter(a, w, l + h, q, g), + new Sorter(a, w, l+u, n-u, g), + new Merger(a, w, l+h, q, + l+u, n-u, l+h, g, null)); + rs.fork(); + ls.compute(); + if (rs.tryUnfork()) rs.compute(); else rs.join(); + new Merger(w, a, l, h, l + h, n - h, l, g, null).compute(); + } else { + DualPivotQuicksort.sort(a, l, l+n-1); // skip rangeCheck + } + } + } + + static final class Merger extends RecursiveAction { + static final long serialVersionUID = -1383975444621698926L; + final char[] a; + final char[] w; + final int lo; + final int ln; + final int ro; + final int rn; + final int wo; + final int gran; + final Merger next; + + Merger(char[] a, char[] w, int lo, int ln, int ro, int rn, int wo, + int gran, Merger next) { + this.a = a; + this.w = w; + this.lo = lo; + this.ln = ln; + this.ro = ro; + this.rn = rn; + this.wo = wo; + this.gran = gran; + this.next = next; + } + + public void compute() { + final char[] a = this.a; + final char[] w = this.w; + Merger rights = null; + int nleft = ln; + int nright = rn; + while (nleft > gran) { + int lh = nleft >>> 1; + int splitIndex = lo + lh; + char split = a[splitIndex]; + int rl = 0; + int rh = nright; + while (rl < rh) { + int mid = (rl + rh) >>> 1; + if (split <= a[ro + mid]) + rh = mid; + else + rl = mid + 1; + } + (rights = new Merger(a, w, splitIndex, nleft-lh, ro+rh, + nright-rh, wo+lh+rh, gran, rights)).fork(); + nleft = lh; + nright = rh; + } + + int l = lo; + int lFence = l + nleft; + int r = ro; + int rFence = r + nright; + int k = wo; + while (l < lFence && r < rFence) { + char al = a[l]; + char ar = a[r]; + char t; + if (al <= ar) {++l; t=al;} else {++r; t = ar;} + w[k++] = t; + } + while (l < lFence) + w[k++] = a[l++]; + while (r < rFence) + w[k++] = a[r++]; + while (rights != null) { + if (rights.tryUnfork()) + rights.compute(); + else + rights.join(); + rights = rights.next; + } + } + } + } // FJChar + + /** short support class */ + static final class FJShort { + static final class Sorter extends RecursiveAction { + static final long serialVersionUID = -7886754793730583084L; + final short[] a; // array to be sorted. + final short[] w; // workspace for merge + final int origin; // origin of the part of array we deal with + final int n; // Number of elements in (sub)arrays. + final int gran; // split control + + Sorter(short[] a, short[] w, int origin, int n, int gran) { + this.a = a; + this.w = w; + this.origin = origin; + this.n = n; + this.gran = gran; + } + + public void compute() { + final int l = origin; + final int g = gran; + final int n = this.n; + final short[] a = this.a; + final short[] w = this.w; + if (n > g) { + int h = n >>> 1; // half + int q = n >>> 2; // lower quarter index + int u = h + q; // upper quarter + FJSubSorter ls = new FJSubSorter(new Sorter(a, w, l, q, g), + new Sorter(a, w, l+q, h-q, g), + new Merger(a, w, l, q, + l+q, h-q, l, g, null)); + FJSubSorter rs = new FJSubSorter(new Sorter(a, w, l + h, q, g), + new Sorter(a, w, l+u, n-u, g), + new Merger(a, w, l+h, q, + l+u, n-u, l+h, g, null)); + rs.fork(); + ls.compute(); + if (rs.tryUnfork()) rs.compute(); else rs.join(); + new Merger(w, a, l, h, l + h, n - h, l, g, null).compute(); + } else { + DualPivotQuicksort.sort(a, l, l+n-1); // skip rangeCheck + } + } + } + + static final class Merger extends RecursiveAction { + static final long serialVersionUID = 3895749408536700048L; + final short[] a; + final short[] w; + final int lo; + final int ln; + final int ro; + final int rn; + final int wo; + final int gran; + final Merger next; + + Merger(short[] a, short[] w, int lo, int ln, int ro, int rn, int wo, + int gran, Merger next) { + this.a = a; + this.w = w; + this.lo = lo; + this.ln = ln; + this.ro = ro; + this.rn = rn; + this.wo = wo; + this.gran = gran; + this.next = next; + } + + public void compute() { + final short[] a = this.a; + final short[] w = this.w; + Merger rights = null; + int nleft = ln; + int nright = rn; + while (nleft > gran) { + int lh = nleft >>> 1; + int splitIndex = lo + lh; + short split = a[splitIndex]; + int rl = 0; + int rh = nright; + while (rl < rh) { + int mid = (rl + rh) >>> 1; + if (split <= a[ro + mid]) + rh = mid; + else + rl = mid + 1; + } + (rights = new Merger(a, w, splitIndex, nleft-lh, ro+rh, + nright-rh, wo+lh+rh, gran, rights)).fork(); + nleft = lh; + nright = rh; + } + + int l = lo; + int lFence = l + nleft; + int r = ro; + int rFence = r + nright; + int k = wo; + while (l < lFence && r < rFence) { + short al = a[l]; + short ar = a[r]; + short t; + if (al <= ar) {++l; t=al;} else {++r; t = ar;} + w[k++] = t; + } + while (l < lFence) + w[k++] = a[l++]; + while (r < rFence) + w[k++] = a[r++]; + while (rights != null) { + if (rights.tryUnfork()) + rights.compute(); + else + rights.join(); + rights = rights.next; + } + } + } + } // FJShort + + /** int support class */ + static final class FJInt { + static final class Sorter extends RecursiveAction { + static final long serialVersionUID = 4263311808957292729L; + final int[] a; // array to be sorted. + final int[] w; // workspace for merge + final int origin; // origin of the part of array we deal with + final int n; // Number of elements in (sub)arrays. + final int gran; // split control + + Sorter(int[] a, int[] w, int origin, int n, int gran) { + this.a = a; + this.w = w; + this.origin = origin; + this.n = n; + this.gran = gran; + } + + public void compute() { + final int l = origin; + final int g = gran; + final int n = this.n; + final int[] a = this.a; + final int[] w = this.w; + if (n > g) { + int h = n >>> 1; // half + int q = n >>> 2; // lower quarter index + int u = h + q; // upper quarter + FJSubSorter ls = new FJSubSorter(new Sorter(a, w, l, q, g), + new Sorter(a, w, l+q, h-q, g), + new Merger(a, w, l, q, + l+q, h-q, l, g, null)); + FJSubSorter rs = new FJSubSorter(new Sorter(a, w, l + h, q, g), + new Sorter(a, w, l+u, n-u, g), + new Merger(a, w, l+h, q, + l+u, n-u, l+h, g, null)); + rs.fork(); + ls.compute(); + if (rs.tryUnfork()) rs.compute(); else rs.join(); + new Merger(w, a, l, h, l + h, n - h, l, g, null).compute(); + } else { + DualPivotQuicksort.sort(a, l, l+n-1); // skip rangeCheck + } + } + } + + static final class Merger extends RecursiveAction { + static final long serialVersionUID = -8727507284219982792L; + final int[] a; + final int[] w; + final int lo; + final int ln; + final int ro; + final int rn; + final int wo; + final int gran; + final Merger next; + + Merger(int[] a, int[] w, int lo, int ln, int ro, int rn, int wo, + int gran, Merger next) { + this.a = a; + this.w = w; + this.lo = lo; + this.ln = ln; + this.ro = ro; + this.rn = rn; + this.wo = wo; + this.gran = gran; + this.next = next; + } + + public void compute() { + final int[] a = this.a; + final int[] w = this.w; + Merger rights = null; + int nleft = ln; + int nright = rn; + while (nleft > gran) { + int lh = nleft >>> 1; + int splitIndex = lo + lh; + int split = a[splitIndex]; + int rl = 0; + int rh = nright; + while (rl < rh) { + int mid = (rl + rh) >>> 1; + if (split <= a[ro + mid]) + rh = mid; + else + rl = mid + 1; + } + (rights = new Merger(a, w, splitIndex, nleft-lh, ro+rh, + nright-rh, wo+lh+rh, gran, rights)).fork(); + nleft = lh; + nright = rh; + } + + int l = lo; + int lFence = l + nleft; + int r = ro; + int rFence = r + nright; + int k = wo; + while (l < lFence && r < rFence) { + int al = a[l]; + int ar = a[r]; + int t; + if (al <= ar) {++l; t=al;} else {++r; t = ar;} + w[k++] = t; + } + while (l < lFence) + w[k++] = a[l++]; + while (r < rFence) + w[k++] = a[r++]; + while (rights != null) { + if (rights.tryUnfork()) + rights.compute(); + else + rights.join(); + rights = rights.next; + } + } + } + } // FJInt + + /** long support class */ + static final class FJLong { + static final class Sorter extends RecursiveAction { + static final long serialVersionUID = 6553695007444392455L; + final long[] a; // array to be sorted. + final long[] w; // workspace for merge + final int origin; // origin of the part of array we deal with + final int n; // Number of elements in (sub)arrays. + final int gran; // split control + + Sorter(long[] a, long[] w, int origin, int n, int gran) { + this.a = a; + this.w = w; + this.origin = origin; + this.n = n; + this.gran = gran; + } + + public void compute() { + final int l = origin; + final int g = gran; + final int n = this.n; + final long[] a = this.a; + final long[] w = this.w; + if (n > g) { + int h = n >>> 1; // half + int q = n >>> 2; // lower quarter index + int u = h + q; // upper quarter + FJSubSorter ls = new FJSubSorter(new Sorter(a, w, l, q, g), + new Sorter(a, w, l+q, h-q, g), + new Merger(a, w, l, q, + l+q, h-q, l, g, null)); + FJSubSorter rs = new FJSubSorter(new Sorter(a, w, l + h, q, g), + new Sorter(a, w, l+u, n-u, g), + new Merger(a, w, l+h, q, + l+u, n-u, l+h, g, null)); + rs.fork(); + ls.compute(); + if (rs.tryUnfork()) rs.compute(); else rs.join(); + new Merger(w, a, l, h, l + h, n - h, l, g, null).compute(); + } else { + DualPivotQuicksort.sort(a, l, l+n-1); // skip rangeCheck + } + } + } + + static final class Merger extends RecursiveAction { + static final long serialVersionUID = 8843567516333283861L; + final long[] a; + final long[] w; + final int lo; + final int ln; + final int ro; + final int rn; + final int wo; + final int gran; + final Merger next; + + Merger(long[] a, long[] w, int lo, int ln, int ro, int rn, int wo, + int gran, Merger next) { + this.a = a; + this.w = w; + this.lo = lo; + this.ln = ln; + this.ro = ro; + this.rn = rn; + this.wo = wo; + this.gran = gran; + this.next = next; + } + + public void compute() { + final long[] a = this.a; + final long[] w = this.w; + Merger rights = null; + int nleft = ln; + int nright = rn; + while (nleft > gran) { + int lh = nleft >>> 1; + int splitIndex = lo + lh; + long split = a[splitIndex]; + int rl = 0; + int rh = nright; + while (rl < rh) { + int mid = (rl + rh) >>> 1; + if (split <= a[ro + mid]) + rh = mid; + else + rl = mid + 1; + } + (rights = new Merger(a, w, splitIndex, nleft-lh, ro+rh, + nright-rh, wo+lh+rh, gran, rights)).fork(); + nleft = lh; + nright = rh; + } + + int l = lo; + int lFence = l + nleft; + int r = ro; + int rFence = r + nright; + int k = wo; + while (l < lFence && r < rFence) { + long al = a[l]; + long ar = a[r]; + long t; + if (al <= ar) {++l; t=al;} else {++r; t = ar;} + w[k++] = t; + } + while (l < lFence) + w[k++] = a[l++]; + while (r < rFence) + w[k++] = a[r++]; + while (rights != null) { + if (rights.tryUnfork()) + rights.compute(); + else + rights.join(); + rights = rights.next; + } + } + } + } // FJLong + + /** float support class */ + static final class FJFloat { + static final class Sorter extends RecursiveAction { + static final long serialVersionUID = 1602600178202763377L; + final float[] a; // array to be sorted. + final float[] w; // workspace for merge + final int origin; // origin of the part of array we deal with + final int n; // Number of elements in (sub)arrays. + final int gran; // split control + + Sorter(float[] a, float[] w, int origin, int n, int gran) { + this.a = a; + this.w = w; + this.origin = origin; + this.n = n; + this.gran = gran; + } + + public void compute() { + final int l = origin; + final int g = gran; + final int n = this.n; + final float[] a = this.a; + final float[] w = this.w; + if (n > g) { + int h = n >>> 1; // half + int q = n >>> 2; // lower quarter index + int u = h + q; // upper quarter + FJSubSorter ls = new FJSubSorter(new Sorter(a, w, l, q, g), + new Sorter(a, w, l+q, h-q, g), + new Merger(a, w, l, q, + l+q, h-q, l, g, null)); + FJSubSorter rs = new FJSubSorter(new Sorter(a, w, l + h, q, g), + new Sorter(a, w, l+u, n-u, g), + new Merger(a, w, l+h, q, + l+u, n-u, l+h, g, null)); + rs.fork(); + ls.compute(); + if (rs.tryUnfork()) rs.compute(); else rs.join(); + new Merger(w, a, l, h, l + h, n - h, l, g, null).compute(); + } else { + DualPivotQuicksort.sort(a, l, l+n-1); // skip rangeCheck + } + } + } + + static final class Merger extends RecursiveAction { + static final long serialVersionUID = 1518176433845397426L; + final float[] a; + final float[] w; + final int lo; + final int ln; + final int ro; + final int rn; + final int wo; + final int gran; + final Merger next; + + Merger(float[] a, float[] w, int lo, int ln, int ro, int rn, int wo, + int gran, Merger next) { + this.a = a; + this.w = w; + this.lo = lo; + this.ln = ln; + this.ro = ro; + this.rn = rn; + this.wo = wo; + this.gran = gran; + this.next = next; + } + + public void compute() { + final float[] a = this.a; + final float[] w = this.w; + Merger rights = null; + int nleft = ln; + int nright = rn; + while (nleft > gran) { + int lh = nleft >>> 1; + int splitIndex = lo + lh; + float split = a[splitIndex]; + int rl = 0; + int rh = nright; + while (rl < rh) { + int mid = (rl + rh) >>> 1; + if (Float.compare(split, a[ro+mid]) <= 0) + rh = mid; + else + rl = mid + 1; + } + (rights = new Merger(a, w, splitIndex, nleft-lh, ro+rh, + nright-rh, wo+lh+rh, gran, rights)).fork(); + nleft = lh; + nright = rh; + } + + int l = lo; + int lFence = l + nleft; + int r = ro; + int rFence = r + nright; + int k = wo; + while (l < lFence && r < rFence) { + float al = a[l]; + float ar = a[r]; + float t; + if (Float.compare(al, ar) <= 0) { + ++l; + t = al; + } else { + ++r; + t = ar; + } + w[k++] = t; + } + while (l < lFence) + w[k++] = a[l++]; + while (r < rFence) + w[k++] = a[r++]; + while (rights != null) { + if (rights.tryUnfork()) + rights.compute(); + else + rights.join(); + rights = rights.next; + } + } + } + } // FJFloat + + /** double support class */ + static final class FJDouble { + static final class Sorter extends RecursiveAction { + static final long serialVersionUID = 2446542900576103244L; + final double[] a; // array to be sorted. + final double[] w; // workspace for merge + final int origin; // origin of the part of array we deal with + final int n; // Number of elements in (sub)arrays. + final int gran; // split control + + Sorter(double[] a, double[] w, int origin, int n, int gran) { + this.a = a; + this.w = w; + this.origin = origin; + this.n = n; + this.gran = gran; + } + + public void compute() { + final int l = origin; + final int g = gran; + final int n = this.n; + final double[] a = this.a; + final double[] w = this.w; + if (n > g) { + int h = n >>> 1; // half + int q = n >>> 2; // lower quarter index + int u = h + q; // upper quarter + FJSubSorter ls = new FJSubSorter(new Sorter(a, w, l, q, g), + new Sorter(a, w, l+q, h-q, g), + new Merger(a, w, l, q, + l+q, h-q, l, g, null)); + FJSubSorter rs = new FJSubSorter(new Sorter(a, w, l + h, q, g), + new Sorter(a, w, l+u, n-u, g), + new Merger(a, w, l+h, q, + l+u, n-u, l+h, g, null)); + rs.fork(); + ls.compute(); + if (rs.tryUnfork()) rs.compute(); else rs.join(); + new Merger(w, a, l, h, l + h, n - h, l, g, null).compute(); + } else { + DualPivotQuicksort.sort(a, l, l+n-1); // skip rangeCheck + } + } + } + + static final class Merger extends RecursiveAction { + static final long serialVersionUID = 8076242187166127592L; + final double[] a; + final double[] w; + final int lo; + final int ln; + final int ro; + final int rn; + final int wo; + final int gran; + final Merger next; + + Merger(double[] a, double[] w, int lo, int ln, int ro, int rn, int wo, + int gran, Merger next) { + this.a = a; + this.w = w; + this.lo = lo; + this.ln = ln; + this.ro = ro; + this.rn = rn; + this.wo = wo; + this.gran = gran; + this.next = next; + } + + public void compute() { + final double[] a = this.a; + final double[] w = this.w; + Merger rights = null; + int nleft = ln; + int nright = rn; + while (nleft > gran) { + int lh = nleft >>> 1; + int splitIndex = lo + lh; + double split = a[splitIndex]; + int rl = 0; + int rh = nright; + while (rl < rh) { + int mid = (rl + rh) >>> 1; + if (Double.compare(split, a[ro+mid]) <= 0) + rh = mid; + else + rl = mid + 1; + } + (rights = new Merger(a, w, splitIndex, nleft-lh, ro+rh, + nright-rh, wo+lh+rh, gran, rights)).fork(); + nleft = lh; + nright = rh; + } + + int l = lo; + int lFence = l + nleft; + int r = ro; + int rFence = r + nright; + int k = wo; + while (l < lFence && r < rFence) { + double al = a[l]; + double ar = a[r]; + double t; + if (Double.compare(al, ar) <= 0) { + ++l; + t = al; + } else { + ++r; + t = ar; + } + w[k++] = t; + } + while (l < lFence) + w[k++] = a[l++]; + while (r < rFence) + w[k++] = a[r++]; + while (rights != null) { + if (rights.tryUnfork()) + rights.compute(); + else + rights.join(); + rights = rights.next; + } + } + } + } // FJDouble + + /** Comparable support class */ + static final class FJComparable { + static final class Sorter> extends RecursiveAction { + static final long serialVersionUID = -1024003289463302522L; + final T[] a; + final T[] w; + final int origin; + final int n; + final int gran; + + Sorter(T[] a, T[] w, int origin, int n, int gran) { + this.a = a; + this.w = w; + this.origin = origin; + this.n = n; + this.gran = gran; + } + + public void compute() { + final int l = origin; + final int g = gran; + final int n = this.n; + final T[] a = this.a; + final T[] w = this.w; + if (n > g) { + int h = n >>> 1; + int q = n >>> 2; + int u = h + q; + FJSubSorter ls = new FJSubSorter(new Sorter<>(a, w, l, q, g), + new Sorter<>(a, w, l+q, h-q, g), + new Merger<>(a, w, l, q, + l+q, h-q, l, g, null)); + FJSubSorter rs = new FJSubSorter(new Sorter<>(a, w, l+h, q, g), + new Sorter<>(a, w, l+u, n-u, g), + new Merger<>(a, w, l+h, q, + l+u, n-u, l+h, g, null)); + rs.fork(); + ls.compute(); + if (rs.tryUnfork()) rs.compute(); else rs.join(); + new Merger<>(w, a, l, h, l + h, n - h, l, g, null).compute(); + } else { + Arrays.sort(a, l, l+n); + } + } + } + + static final class Merger> extends RecursiveAction { + static final long serialVersionUID = -3989771675258379302L; + final T[] a; + final T[] w; + final int lo; + final int ln; + final int ro; + final int rn; + final int wo; + final int gran; + final Merger next; + + Merger(T[] a, T[] w, int lo, int ln, int ro, int rn, int wo, + int gran, Merger next) { + this.a = a; + this.w = w; + this.lo = lo; + this.ln = ln; + this.ro = ro; + this.rn = rn; + this.wo = wo; + this.gran = gran; + this.next = next; + } + + public void compute() { + final T[] a = this.a; + final T[] w = this.w; + Merger rights = null; + int nleft = ln; + int nright = rn; + while (nleft > gran) { + int lh = nleft >>> 1; + int splitIndex = lo + lh; + T split = a[splitIndex]; + int rl = 0; + int rh = nright; + while (rl < rh) { + int mid = (rl + rh) >>> 1; + if (split.compareTo(a[ro + mid]) <= 0) + rh = mid; + else + rl = mid + 1; + } + (rights = new Merger<>(a, w, splitIndex, nleft-lh, ro+rh, + nright-rh, wo+lh+rh, gran, rights)).fork(); + nleft = lh; + nright = rh; + } + + int l = lo; + int lFence = l + nleft; + int r = ro; + int rFence = r + nright; + int k = wo; + while (l < lFence && r < rFence) { + T al = a[l]; + T ar = a[r]; + T t; + if (al.compareTo(ar) <= 0) {++l; t=al;} else {++r; t=ar; } + w[k++] = t; + } + while (l < lFence) + w[k++] = a[l++]; + while (r < rFence) + w[k++] = a[r++]; + while (rights != null) { + if (rights.tryUnfork()) + rights.compute(); + else + rights.join(); + rights = rights.next; + } + } + } + } // FJComparable + + /** Object + Comparator support class */ + static final class FJComparator { + static final class Sorter extends RecursiveAction { + static final long serialVersionUID = 9191600840025808581L; + final T[] a; // array to be sorted. + final T[] w; // workspace for merge + final int origin; // origin of the part of array we deal with + final int n; // Number of elements in (sub)arrays. + final int gran; // split control + final Comparator cmp; // Comparator to use + + Sorter(T[] a, T[] w, int origin, int n, int gran, Comparator cmp) { + this.a = a; + this.w = w; + this.origin = origin; + this.n = n; + this.cmp = cmp; + this.gran = gran; + } + + public void compute() { + final int l = origin; + final int g = gran; + final int n = this.n; + final T[] a = this.a; + final T[] w = this.w; + if (n > g) { + int h = n >>> 1; // half + int q = n >>> 2; // lower quarter index + int u = h + q; // upper quarter + FJSubSorter ls = new FJSubSorter(new Sorter<>(a, w, l, q, g, cmp), + new Sorter<>(a, w, l+q, h-q, g, cmp), + new Merger<>(a, w, l, q, + l+q, h-q, l, g, null, cmp)); + FJSubSorter rs = new FJSubSorter(new Sorter<>(a, w, l + h, q, g, cmp), + new Sorter<>(a, w, l+u, n-u, g, cmp), + new Merger<>(a, w, l+h, q, + l+u, n-u, l+h, g, null, cmp)); + rs.fork(); + ls.compute(); + if (rs.tryUnfork()) rs.compute(); else rs.join(); + new Merger<>(w, a, l, h, l + h, n - h, l, g, null, cmp).compute(); + } else { + Arrays.sort(a, l, l+n, cmp); + } + } + } + + static final class Merger extends RecursiveAction { + static final long serialVersionUID = -2679539040379156203L; + final T[] a; + final T[] w; + final int lo; + final int ln; + final int ro; + final int rn; + final int wo; + final int gran; + final Merger next; + final Comparator cmp; + + Merger(T[] a, T[] w, int lo, int ln, int ro, int rn, int wo, + int gran, Merger next, Comparator cmp) { + this.a = a; + this.w = w; + this.lo = lo; + this.ln = ln; + this.ro = ro; + this.rn = rn; + this.wo = wo; + this.gran = gran; + this.next = next; + this.cmp = cmp; + } + + public void compute() { + final T[] a = this.a; + final T[] w = this.w; + Merger rights = null; + int nleft = ln; + int nright = rn; + while (nleft > gran) { + int lh = nleft >>> 1; + int splitIndex = lo + lh; + T split = a[splitIndex]; + int rl = 0; + int rh = nright; + while (rl < rh) { + int mid = (rl + rh) >>> 1; + if (cmp.compare(split, a[ro+mid]) <= 0) + rh = mid; + else + rl = mid + 1; + } + (rights = new Merger<>(a, w, splitIndex, nleft-lh, ro+rh, + nright-rh, wo+lh+rh, gran, rights, cmp)).fork(); + nleft = lh; + nright = rh; + } + + int l = lo; + int lFence = l + nleft; + int r = ro; + int rFence = r + nright; + int k = wo; + while (l < lFence && r < rFence) { + T al = a[l]; + T ar = a[r]; + T t; + if (cmp.compare(al, ar) <= 0) { + ++l; + t = al; + } else { + ++r; + t = ar; + } + w[k++] = t; + } + while (l < lFence) + w[k++] = a[l++]; + while (r < rFence) + w[k++] = a[r++]; + while (rights != null) { + if (rights.tryUnfork()) + rights.compute(); + else + rights.join(); + rights = rights.next; + } + } + } + } // FJComparator + + /** Utility class to sort half a partitioned array */ + private static final class FJSubSorter extends RecursiveAction { + static final long serialVersionUID = 9159249695527935512L; + final RecursiveAction left; + final RecursiveAction right; + final RecursiveAction merger; + + FJSubSorter(RecursiveAction left, RecursiveAction right, + RecursiveAction merger) { + this.left = left; + this.right = right; + this.merger = merger; + } + + public void compute() { + right.fork(); + left.invoke(); + right.join(); + merger.invoke(); + } + } +} diff --git a/test/java/util/Arrays/ParallelSorting.java b/test/java/util/Arrays/ParallelSorting.java new file mode 100644 index 000000000..4fc92199f --- /dev/null +++ b/test/java/util/Arrays/ParallelSorting.java @@ -0,0 +1,2067 @@ +/* + * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* Adapted from test/java/util/Arrays/Sorting.java + * + * Where that test checks Arrays.sort against manual quicksort routines, + * this test checks parallelSort against either Arrays.sort or manual + * quicksort routines. + */ + +/* + * @test + * @bug 8003981 + * @run main ParallelSorting -shortrun + * @summary Exercise Arrays.parallelSort (adapted from test Sorting) + * + * @author Vladimir Yaroslavskiy + * @author Jon Bentley + * @author Josh Bloch + */ + +import java.util.Arrays; +import java.util.Random; +import java.io.PrintStream; +import java.util.Comparator; + +public class ParallelSorting { + private static final PrintStream out = System.out; + private static final PrintStream err = System.err; + + // Array lengths used in a long run (default) + private static final int[] LONG_RUN_LENGTHS = { + 1, 2, 3, 5, 8, 13, 21, 34, 55, 100, 1000, 10000, 100000, 1000000 }; + + // Array lengths used in a short run + private static final int[] SHORT_RUN_LENGTHS = { + 1, 2, 3, 21, 55, 1000, 10000 }; + + // Random initial values used in a long run (default) + private static final long[] LONG_RUN_RANDOMS = { 666, 0xC0FFEE, 999 }; + + // Random initial values used in a short run + private static final long[] SHORT_RUN_RANDOMS = { 666 }; + + public static void main(String[] args) { + boolean shortRun = args.length > 0 && args[0].equals("-shortrun"); + long start = System.currentTimeMillis(); + + if (shortRun) { + testAndCheck(SHORT_RUN_LENGTHS, SHORT_RUN_RANDOMS); + } else { + testAndCheck(LONG_RUN_LENGTHS, LONG_RUN_RANDOMS); + } + long end = System.currentTimeMillis(); + + out.format("PASSED in %d sec.\n", Math.round((end - start) / 1E3)); + } + + private static void testAndCheck(int[] lengths, long[] randoms) { + testEmptyAndNullIntArray(); + testEmptyAndNullLongArray(); + testEmptyAndNullShortArray(); + testEmptyAndNullCharArray(); + testEmptyAndNullByteArray(); + testEmptyAndNullFloatArray(); + testEmptyAndNullDoubleArray(); + + for (int length : lengths) { + testMergeSort(length); + testAndCheckRange(length); + testAndCheckSubArray(length); + } + for (long seed : randoms) { + for (int length : lengths) { + testAndCheckWithInsertionSort(length, new MyRandom(seed)); + testAndCheckWithCheckSum(length, new MyRandom(seed)); + testAndCheckWithScrambling(length, new MyRandom(seed)); + testAndCheckFloat(length, new MyRandom(seed)); + testAndCheckDouble(length, new MyRandom(seed)); + testStable(length, new MyRandom(seed)); + } + } + } + + private static void testEmptyAndNullIntArray() { + ourDescription = "Check empty and null array"; + Arrays.parallelSort(new int[]{}); + Arrays.parallelSort(new int[]{}, 0, 0); + + try { + Arrays.parallelSort((int[]) null); + } catch (NullPointerException expected) { + try { + Arrays.parallelSort((int[]) null, 0, 0); + } catch (NullPointerException expected2) { + return; + } + failed("Arrays.parallelSort(int[],fromIndex,toIndex) shouldn't " + + "catch null array"); + } + failed("Arrays.parallelSort(int[]) shouldn't catch null array"); + } + + private static void testEmptyAndNullLongArray() { + ourDescription = "Check empty and null array"; + Arrays.parallelSort(new long[]{}); + Arrays.parallelSort(new long[]{}, 0, 0); + + try { + Arrays.parallelSort((long[]) null); + } catch (NullPointerException expected) { + try { + Arrays.parallelSort((long[]) null, 0, 0); + } catch (NullPointerException expected2) { + return; + } + failed("Arrays.parallelSort(long[],fromIndex,toIndex) shouldn't " + + "catch null array"); + } + failed("Arrays.parallelSort(long[]) shouldn't catch null array"); + } + + private static void testEmptyAndNullShortArray() { + ourDescription = "Check empty and null array"; + Arrays.parallelSort(new short[]{}); + Arrays.parallelSort(new short[]{}, 0, 0); + + try { + Arrays.parallelSort((short[]) null); + } catch (NullPointerException expected) { + try { + Arrays.parallelSort((short[]) null, 0, 0); + } catch (NullPointerException expected2) { + return; + } + failed("Arrays.parallelSort(short[],fromIndex,toIndex) shouldn't " + + "catch null array"); + } + failed("Arrays.parallelSort(short[]) shouldn't catch null array"); + } + + private static void testEmptyAndNullCharArray() { + ourDescription = "Check empty and null array"; + Arrays.parallelSort(new char[]{}); + Arrays.parallelSort(new char[]{}, 0, 0); + + try { + Arrays.parallelSort((char[]) null); + } catch (NullPointerException expected) { + try { + Arrays.parallelSort((char[]) null, 0, 0); + } catch (NullPointerException expected2) { + return; + } + failed("Arrays.parallelSort(char[],fromIndex,toIndex) shouldn't " + + "catch null array"); + } + failed("Arrays.parallelSort(char[]) shouldn't catch null array"); + } + + private static void testEmptyAndNullByteArray() { + ourDescription = "Check empty and null array"; + Arrays.parallelSort(new byte[]{}); + Arrays.parallelSort(new byte[]{}, 0, 0); + + try { + Arrays.parallelSort((byte[]) null); + } catch (NullPointerException expected) { + try { + Arrays.parallelSort((byte[]) null, 0, 0); + } catch (NullPointerException expected2) { + return; + } + failed("Arrays.parallelSort(byte[],fromIndex,toIndex) shouldn't " + + "catch null array"); + } + failed("Arrays.parallelSort(byte[]) shouldn't catch null array"); + } + + private static void testEmptyAndNullFloatArray() { + ourDescription = "Check empty and null array"; + Arrays.parallelSort(new float[]{}); + Arrays.parallelSort(new float[]{}, 0, 0); + + try { + Arrays.parallelSort((float[]) null); + } catch (NullPointerException expected) { + try { + Arrays.parallelSort((float[]) null, 0, 0); + } catch (NullPointerException expected2) { + return; + } + failed("Arrays.parallelSort(float[],fromIndex,toIndex) shouldn't " + + "catch null array"); + } + failed("Arrays.parallelSort(float[]) shouldn't catch null array"); + } + + private static void testEmptyAndNullDoubleArray() { + ourDescription = "Check empty and null array"; + Arrays.parallelSort(new double[]{}); + Arrays.parallelSort(new double[]{}, 0, 0); + + try { + Arrays.parallelSort((double[]) null); + } catch (NullPointerException expected) { + try { + Arrays.parallelSort((double[]) null, 0, 0); + } catch (NullPointerException expected2) { + return; + } + failed("Arrays.parallelSort(double[],fromIndex,toIndex) shouldn't " + + "catch null array"); + } + failed("Arrays.parallelSort(double[]) shouldn't catch null array"); + } + + private static void testAndCheckSubArray(int length) { + ourDescription = "Check sorting of subarray"; + int[] golden = new int[length]; + boolean newLine = false; + + for (int m = 1; m < length / 2; m *= 2) { + newLine = true; + int fromIndex = m; + int toIndex = length - m; + + prepareSubArray(golden, fromIndex, toIndex, m); + int[] test = golden.clone(); + + for (TypeConverter converter : TypeConverter.values()) { + out.println("Test 'subarray': " + converter + + " length = " + length + ", m = " + m); + Object convertedGolden = converter.convert(golden); + Object convertedTest = converter.convert(test); + sortSubArray(convertedTest, fromIndex, toIndex); + checkSubArray(convertedTest, fromIndex, toIndex, m); + } + } + if (newLine) { + out.println(); + } + } + + private static void testAndCheckRange(int length) { + ourDescription = "Check range check"; + int[] golden = new int[length]; + + for (int m = 1; m < 2 * length; m *= 2) { + for (int i = 1; i <= length; i++) { + golden[i - 1] = i % m + m % i; + } + for (TypeConverter converter : TypeConverter.values()) { + out.println("Test 'range': " + converter + + ", length = " + length + ", m = " + m); + Object convertedGolden = converter.convert(golden); + checkRange(convertedGolden, m); + } + } + out.println(); + } + + private static void testStable(int length, MyRandom random) { + ourDescription = "Check if sorting is stable"; + Pair[] a = build(length, random); + + out.println("Test 'stable': " + "random = " + random.getSeed() + + ", length = " + length); + Arrays.parallelSort(a); + checkSorted(a); + checkStable(a); + out.println(); + + a = build(length, random); + + out.println("Test 'stable' comparator: " + "random = " + random.getSeed() + + ", length = " + length); + Arrays.parallelSort(a, pairCmp); + checkSorted(a); + checkStable(a); + out.println(); + + } + + private static void checkSorted(Pair[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i].getKey() > a[i + 1].getKey()) { + failedSort(i, "" + a[i].getKey(), "" + a[i + 1].getKey()); + } + } + } + + private static void checkStable(Pair[] a) { + for (int i = 0; i < a.length / 4; ) { + int key1 = a[i].getKey(); + int value1 = a[i++].getValue(); + int key2 = a[i].getKey(); + int value2 = a[i++].getValue(); + int key3 = a[i].getKey(); + int value3 = a[i++].getValue(); + int key4 = a[i].getKey(); + int value4 = a[i++].getValue(); + + if (!(key1 == key2 && key2 == key3 && key3 == key4)) { + failed("On position " + i + " keys are different " + + key1 + ", " + key2 + ", " + key3 + ", " + key4); + } + if (!(value1 < value2 && value2 < value3 && value3 < value4)) { + failed("Sorting is not stable at position " + i + + ". Second values have been changed: " + value1 + ", " + + value2 + ", " + value3 + ", " + value4); + } + } + } + + private static Pair[] build(int length, Random random) { + Pair[] a = new Pair[length * 4]; + + for (int i = 0; i < a.length; ) { + int key = random.nextInt(); + a[i++] = new Pair(key, 1); + a[i++] = new Pair(key, 2); + a[i++] = new Pair(key, 3); + a[i++] = new Pair(key, 4); + } + return a; + } + + private static Comparator pairCmp = new Comparator() { + public int compare(Pair p1, Pair p2) { + return p1.compareTo(p2); + } + }; + + private static final class Pair implements Comparable { + Pair(int key, int value) { + myKey = key; + myValue = value; + } + + int getKey() { + return myKey; + } + + int getValue() { + return myValue; + } + + public int compareTo(Pair pair) { + if (myKey < pair.myKey) { + return -1; + } + if (myKey > pair.myKey) { + return 1; + } + return 0; + } + + @Override + public String toString() { + return "(" + myKey + ", " + myValue + ")"; + } + + private int myKey; + private int myValue; + } + + + private static void testAndCheckWithInsertionSort(int length, MyRandom random) { + if (length > 1000) { + return; + } + ourDescription = "Check sorting with insertion sort"; + int[] golden = new int[length]; + + for (int m = 1; m < 2 * length; m *= 2) { + for (UnsortedBuilder builder : UnsortedBuilder.values()) { + builder.build(golden, m, random); + int[] test = golden.clone(); + + for (TypeConverter converter : TypeConverter.values()) { + out.println("Test 'insertion sort': " + converter + + " " + builder + "random = " + random.getSeed() + + ", length = " + length + ", m = " + m); + Object convertedGolden = converter.convert(golden); + Object convertedTest1 = converter.convert(test); + Object convertedTest2 = converter.convert(test); + sort(convertedTest1); + sortByInsertionSort(convertedTest2); + compare(convertedTest1, convertedTest2); + } + } + } + out.println(); + } + + private static void testMergeSort(int length) { + if (length < 1000) { + return; + } + ourDescription = "Check merge sorting"; + int[] golden = new int[length]; + int period = 67; // java.util.DualPivotQuicksort.MAX_RUN_COUNT + + for (int m = period - 2; m <= period + 2; m++) { + for (MergeBuilder builder : MergeBuilder.values()) { + builder.build(golden, m); + int[] test = golden.clone(); + + for (TypeConverter converter : TypeConverter.values()) { + out.println("Test 'merge sort': " + converter + " " + + builder + "length = " + length + ", m = " + m); + Object convertedGolden = converter.convert(golden); + sort(convertedGolden); + checkSorted(convertedGolden); + } + } + } + out.println(); + } + + private static void testAndCheckWithCheckSum(int length, MyRandom random) { + ourDescription = "Check sorting with check sum"; + int[] golden = new int[length]; + + for (int m = 1; m < 2 * length; m *= 2) { + for (UnsortedBuilder builder : UnsortedBuilder.values()) { + builder.build(golden, m, random); + int[] test = golden.clone(); + + for (TypeConverter converter : TypeConverter.values()) { + out.println("Test 'check sum': " + converter + + " " + builder + "random = " + random.getSeed() + + ", length = " + length + ", m = " + m); + Object convertedGolden = converter.convert(golden); + Object convertedTest = converter.convert(test); + sort(convertedTest); + checkWithCheckSum(convertedTest, convertedGolden); + } + } + } + out.println(); + } + + private static void testAndCheckWithScrambling(int length, MyRandom random) { + ourDescription = "Check sorting with scrambling"; + int[] golden = new int[length]; + + for (int m = 1; m <= 7; m++) { + if (m > length) { + break; + } + for (SortedBuilder builder : SortedBuilder.values()) { + builder.build(golden, m); + int[] test = golden.clone(); + scramble(test, random); + + for (TypeConverter converter : TypeConverter.values()) { + out.println("Test 'scrambling': " + converter + + " " + builder + "random = " + random.getSeed() + + ", length = " + length + ", m = " + m); + Object convertedGolden = converter.convert(golden); + Object convertedTest = converter.convert(test); + sort(convertedTest); + compare(convertedTest, convertedGolden); + } + } + } + out.println(); + } + + private static void testAndCheckFloat(int length, MyRandom random) { + ourDescription = "Check float sorting"; + float[] golden = new float[length]; + final int MAX = 10; + boolean newLine = false; + + for (int a = 0; a <= MAX; a++) { + for (int g = 0; g <= MAX; g++) { + for (int z = 0; z <= MAX; z++) { + for (int n = 0; n <= MAX; n++) { + for (int p = 0; p <= MAX; p++) { + if (a + g + z + n + p > length) { + continue; + } + if (a + g + z + n + p < length) { + continue; + } + for (FloatBuilder builder : FloatBuilder.values()) { + out.println("Test 'float': random = " + random.getSeed() + + ", length = " + length + ", a = " + a + ", g = " + + g + ", z = " + z + ", n = " + n + ", p = " + p); + builder.build(golden, a, g, z, n, p, random); + float[] test = golden.clone(); + scramble(test, random); + sort(test); + compare(test, golden, a, n, g); + } + newLine = true; + } + } + } + } + } + if (newLine) { + out.println(); + } + } + + private static void testAndCheckDouble(int length, MyRandom random) { + ourDescription = "Check double sorting"; + double[] golden = new double[length]; + final int MAX = 10; + boolean newLine = false; + + for (int a = 0; a <= MAX; a++) { + for (int g = 0; g <= MAX; g++) { + for (int z = 0; z <= MAX; z++) { + for (int n = 0; n <= MAX; n++) { + for (int p = 0; p <= MAX; p++) { + if (a + g + z + n + p > length) { + continue; + } + if (a + g + z + n + p < length) { + continue; + } + for (DoubleBuilder builder : DoubleBuilder.values()) { + out.println("Test 'double': random = " + random.getSeed() + + ", length = " + length + ", a = " + a + ", g = " + + g + ", z = " + z + ", n = " + n + ", p = " + p); + builder.build(golden, a, g, z, n, p, random); + double[] test = golden.clone(); + scramble(test, random); + sort(test); + compare(test, golden, a, n, g); + } + newLine = true; + } + } + } + } + } + if (newLine) { + out.println(); + } + } + + private static void prepareSubArray(int[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + a[i] = 0xDEDA; + } + int middle = (fromIndex + toIndex) >>> 1; + int k = 0; + + for (int i = fromIndex; i < middle; i++) { + a[i] = k++; + } + for (int i = middle; i < toIndex; i++) { + a[i] = k--; + } + for (int i = toIndex; i < a.length; i++) { + a[i] = 0xBABA; + } + } + + private static void scramble(int[] a, Random random) { + for (int i = 0; i < a.length * 7; i++) { + swap(a, random.nextInt(a.length), random.nextInt(a.length)); + } + } + + private static void scramble(float[] a, Random random) { + for (int i = 0; i < a.length * 7; i++) { + swap(a, random.nextInt(a.length), random.nextInt(a.length)); + } + } + + private static void scramble(double[] a, Random random) { + for (int i = 0; i < a.length * 7; i++) { + swap(a, random.nextInt(a.length), random.nextInt(a.length)); + } + } + + private static void swap(int[] a, int i, int j) { + int t = a[i]; + a[i] = a[j]; + a[j] = t; + } + + private static void swap(float[] a, int i, int j) { + float t = a[i]; + a[i] = a[j]; + a[j] = t; + } + + private static void swap(double[] a, int i, int j) { + double t = a[i]; + a[i] = a[j]; + a[j] = t; + } + + private static enum TypeConverter { + INT { + Object convert(int[] a) { + return a.clone(); + } + }, + LONG { + Object convert(int[] a) { + long[] b = new long[a.length]; + + for (int i = 0; i < a.length; i++) { + b[i] = (long) a[i]; + } + return b; + } + }, + BYTE { + Object convert(int[] a) { + byte[] b = new byte[a.length]; + + for (int i = 0; i < a.length; i++) { + b[i] = (byte) a[i]; + } + return b; + } + }, + SHORT { + Object convert(int[] a) { + short[] b = new short[a.length]; + + for (int i = 0; i < a.length; i++) { + b[i] = (short) a[i]; + } + return b; + } + }, + CHAR { + Object convert(int[] a) { + char[] b = new char[a.length]; + + for (int i = 0; i < a.length; i++) { + b[i] = (char) a[i]; + } + return b; + } + }, + FLOAT { + Object convert(int[] a) { + float[] b = new float[a.length]; + + for (int i = 0; i < a.length; i++) { + b[i] = (float) a[i]; + } + return b; + } + }, + DOUBLE { + Object convert(int[] a) { + double[] b = new double[a.length]; + + for (int i = 0; i < a.length; i++) { + b[i] = (double) a[i]; + } + return b; + } + }, + INTEGER { + Object convert(int[] a) { + Integer[] b = new Integer[a.length]; + + for (int i = 0; i < a.length; i++) { + b[i] = new Integer(a[i]); + } + return b; + } + }; + + abstract Object convert(int[] a); + + @Override public String toString() { + String name = name(); + + for (int i = name.length(); i < 9; i++) { + name += " "; + } + return name; + } + } + + private static enum FloatBuilder { + SIMPLE { + void build(float[] x, int a, int g, int z, int n, int p, Random random) { + int fromIndex = 0; + float negativeValue = -random.nextFloat(); + float positiveValue = random.nextFloat(); + + writeValue(x, negativeValue, fromIndex, n); + fromIndex += n; + + writeValue(x, -0.0f, fromIndex, g); + fromIndex += g; + + writeValue(x, 0.0f, fromIndex, z); + fromIndex += z; + + writeValue(x, positiveValue, fromIndex, p); + fromIndex += p; + + writeValue(x, Float.NaN, fromIndex, a); + } + }; + + abstract void build(float[] x, int a, int g, int z, int n, int p, Random random); + } + + private static enum DoubleBuilder { + SIMPLE { + void build(double[] x, int a, int g, int z, int n, int p, Random random) { + int fromIndex = 0; + double negativeValue = -random.nextFloat(); + double positiveValue = random.nextFloat(); + + writeValue(x, negativeValue, fromIndex, n); + fromIndex += n; + + writeValue(x, -0.0d, fromIndex, g); + fromIndex += g; + + writeValue(x, 0.0d, fromIndex, z); + fromIndex += z; + + writeValue(x, positiveValue, fromIndex, p); + fromIndex += p; + + writeValue(x, Double.NaN, fromIndex, a); + } + }; + + abstract void build(double[] x, int a, int g, int z, int n, int p, Random random); + } + + private static void writeValue(float[] a, float value, int fromIndex, int count) { + for (int i = fromIndex; i < fromIndex + count; i++) { + a[i] = value; + } + } + + private static void compare(float[] a, float[] b, int numNaN, int numNeg, int numNegZero) { + for (int i = a.length - numNaN; i < a.length; i++) { + if (a[i] == a[i]) { + failed("On position " + i + " must be NaN instead of " + a[i]); + } + } + final int NEGATIVE_ZERO = Float.floatToIntBits(-0.0f); + + for (int i = numNeg; i < numNeg + numNegZero; i++) { + if (NEGATIVE_ZERO != Float.floatToIntBits(a[i])) { + failed("On position " + i + " must be -0.0 instead of " + a[i]); + } + } + for (int i = 0; i < a.length - numNaN; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void writeValue(double[] a, double value, int fromIndex, int count) { + for (int i = fromIndex; i < fromIndex + count; i++) { + a[i] = value; + } + } + + private static void compare(double[] a, double[] b, int numNaN, int numNeg, int numNegZero) { + for (int i = a.length - numNaN; i < a.length; i++) { + if (a[i] == a[i]) { + failed("On position " + i + " must be NaN instead of " + a[i]); + } + } + final long NEGATIVE_ZERO = Double.doubleToLongBits(-0.0d); + + for (int i = numNeg; i < numNeg + numNegZero; i++) { + if (NEGATIVE_ZERO != Double.doubleToLongBits(a[i])) { + failed("On position " + i + " must be -0.0 instead of " + a[i]); + } + } + for (int i = 0; i < a.length - numNaN; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static enum SortedBuilder { + REPEATED { + void build(int[] a, int m) { + int period = a.length / m; + int i = 0; + int k = 0; + + while (true) { + for (int t = 1; t <= period; t++) { + if (i >= a.length) { + return; + } + a[i++] = k; + } + if (i >= a.length) { + return; + } + k++; + } + } + }, + ORGAN_PIPES { + void build(int[] a, int m) { + int i = 0; + int k = m; + + while (true) { + for (int t = 1; t <= m; t++) { + if (i >= a.length) { + return; + } + a[i++] = k; + } + } + } + }; + + abstract void build(int[] a, int m); + + @Override public String toString() { + String name = name(); + + for (int i = name.length(); i < 12; i++) { + name += " "; + } + return name; + } + } + + private static enum MergeBuilder { + ASCENDING { + void build(int[] a, int m) { + int period = a.length / m; + int v = 1, i = 0; + + for (int k = 0; k < m; k++) { + v = 1; + for (int p = 0; p < period; p++) { + a[i++] = v++; + } + } + for (int j = i; j < a.length - 1; j++) { + a[j] = v++; + } + a[a.length - 1] = 0; + } + }, + DESCENDING { + void build(int[] a, int m) { + int period = a.length / m; + int v = -1, i = 0; + + for (int k = 0; k < m; k++) { + v = -1; + for (int p = 0; p < period; p++) { + a[i++] = v--; + } + } + for (int j = i; j < a.length - 1; j++) { + a[j] = v--; + } + a[a.length - 1] = 0; + } + }; + + abstract void build(int[] a, int m); + + @Override public String toString() { + String name = name(); + + for (int i = name.length(); i < 12; i++) { + name += " "; + } + return name; + } + } + + private static enum UnsortedBuilder { + RANDOM { + void build(int[] a, int m, Random random) { + for (int i = 0; i < a.length; i++) { + a[i] = random.nextInt(); + } + } + }, + ASCENDING { + void build(int[] a, int m, Random random) { + for (int i = 0; i < a.length; i++) { + a[i] = m + i; + } + } + }, + DESCENDING { + void build(int[] a, int m, Random random) { + for (int i = 0; i < a.length; i++) { + a[i] = a.length - m - i; + } + } + }, + ALL_EQUAL { + void build(int[] a, int m, Random random) { + for (int i = 0; i < a.length; i++) { + a[i] = m; + } + } + }, + SAW { + void build(int[] a, int m, Random random) { + int incCount = 1; + int decCount = a.length; + int i = 0; + int period = m--; + + while (true) { + for (int k = 1; k <= period; k++) { + if (i >= a.length) { + return; + } + a[i++] = incCount++; + } + period += m; + + for (int k = 1; k <= period; k++) { + if (i >= a.length) { + return; + } + a[i++] = decCount--; + } + period += m; + } + } + }, + REPEATED { + void build(int[] a, int m, Random random) { + for (int i = 0; i < a.length; i++) { + a[i] = i % m; + } + } + }, + DUPLICATED { + void build(int[] a, int m, Random random) { + for (int i = 0; i < a.length; i++) { + a[i] = random.nextInt(m); + } + } + }, + ORGAN_PIPES { + void build(int[] a, int m, Random random) { + int middle = a.length / (m + 1); + + for (int i = 0; i < middle; i++) { + a[i] = i; + } + for (int i = middle; i < a.length; i++) { + a[i] = a.length - i - 1; + } + } + }, + STAGGER { + void build(int[] a, int m, Random random) { + for (int i = 0; i < a.length; i++) { + a[i] = (i * m + i) % a.length; + } + } + }, + PLATEAU { + void build(int[] a, int m, Random random) { + for (int i = 0; i < a.length; i++) { + a[i] = Math.min(i, m); + } + } + }, + SHUFFLE { + void build(int[] a, int m, Random random) { + int x = 0, y = 0; + for (int i = 0; i < a.length; i++) { + a[i] = random.nextBoolean() ? (x += 2) : (y += 2); + } + } + }; + + abstract void build(int[] a, int m, Random random); + + @Override public String toString() { + String name = name(); + + for (int i = name.length(); i < 12; i++) { + name += " "; + } + return name; + } + } + + private static void checkWithCheckSum(Object test, Object golden) { + checkSorted(test); + checkCheckSum(test, golden); + } + + private static void failed(String message) { + err.format("\n*** TEST FAILED - %s.\n\n%s.\n\n", ourDescription, message); + throw new RuntimeException("Test failed - see log file for details"); + } + + private static void failedSort(int index, String value1, String value2) { + failed("Array is not sorted at " + index + "-th position: " + + value1 + " and " + value2); + } + + private static void failedCompare(int index, String value1, String value2) { + failed("On position " + index + " must be " + value2 + " instead of " + value1); + } + + private static void compare(Object test, Object golden) { + if (test instanceof int[]) { + compare((int[]) test, (int[]) golden); + } else if (test instanceof long[]) { + compare((long[]) test, (long[]) golden); + } else if (test instanceof short[]) { + compare((short[]) test, (short[]) golden); + } else if (test instanceof byte[]) { + compare((byte[]) test, (byte[]) golden); + } else if (test instanceof char[]) { + compare((char[]) test, (char[]) golden); + } else if (test instanceof float[]) { + compare((float[]) test, (float[]) golden); + } else if (test instanceof double[]) { + compare((double[]) test, (double[]) golden); + } else if (test instanceof Integer[]) { + compare((Integer[]) test, (Integer[]) golden); + } else { + failed("Unknow type of array: " + test + " of class " + + test.getClass().getName()); + } + } + + private static void compare(int[] a, int[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void compare(long[] a, long[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void compare(short[] a, short[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void compare(byte[] a, byte[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void compare(char[] a, char[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void compare(float[] a, float[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void compare(double[] a, double[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void compare(Integer[] a, Integer[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i].compareTo(b[i]) != 0) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void checkSorted(Object object) { + if (object instanceof int[]) { + checkSorted((int[]) object); + } else if (object instanceof long[]) { + checkSorted((long[]) object); + } else if (object instanceof short[]) { + checkSorted((short[]) object); + } else if (object instanceof byte[]) { + checkSorted((byte[]) object); + } else if (object instanceof char[]) { + checkSorted((char[]) object); + } else if (object instanceof float[]) { + checkSorted((float[]) object); + } else if (object instanceof double[]) { + checkSorted((double[]) object); + } else if (object instanceof Integer[]) { + checkSorted((Integer[]) object); + } else { + failed("Unknow type of array: " + object + " of class " + + object.getClass().getName()); + } + } + + private static void checkSorted(int[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + } + + private static void checkSorted(long[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + } + + private static void checkSorted(short[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + } + + private static void checkSorted(byte[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + } + + private static void checkSorted(char[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + } + + private static void checkSorted(float[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + } + + private static void checkSorted(double[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + } + + private static void checkSorted(Integer[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i].intValue() > a[i + 1].intValue()) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + } + + private static void checkCheckSum(Object test, Object golden) { + if (checkSumXor(test) != checkSumXor(golden)) { + failed("Original and sorted arrays are not identical [xor]"); + } + if (checkSumPlus(test) != checkSumPlus(golden)) { + failed("Original and sorted arrays are not identical [plus]"); + } + } + + private static int checkSumXor(Object object) { + if (object instanceof int[]) { + return checkSumXor((int[]) object); + } else if (object instanceof long[]) { + return checkSumXor((long[]) object); + } else if (object instanceof short[]) { + return checkSumXor((short[]) object); + } else if (object instanceof byte[]) { + return checkSumXor((byte[]) object); + } else if (object instanceof char[]) { + return checkSumXor((char[]) object); + } else if (object instanceof float[]) { + return checkSumXor((float[]) object); + } else if (object instanceof double[]) { + return checkSumXor((double[]) object); + } else if (object instanceof Integer[]) { + return checkSumXor((Integer[]) object); + } else { + failed("Unknow type of array: " + object + " of class " + + object.getClass().getName()); + return -1; + } + } + + private static int checkSumXor(Integer[] a) { + int checkSum = 0; + + for (Integer e : a) { + checkSum ^= e.intValue(); + } + return checkSum; + } + + private static int checkSumXor(int[] a) { + int checkSum = 0; + + for (int e : a) { + checkSum ^= e; + } + return checkSum; + } + + private static int checkSumXor(long[] a) { + long checkSum = 0; + + for (long e : a) { + checkSum ^= e; + } + return (int) checkSum; + } + + private static int checkSumXor(short[] a) { + short checkSum = 0; + + for (short e : a) { + checkSum ^= e; + } + return (int) checkSum; + } + + private static int checkSumXor(byte[] a) { + byte checkSum = 0; + + for (byte e : a) { + checkSum ^= e; + } + return (int) checkSum; + } + + private static int checkSumXor(char[] a) { + char checkSum = 0; + + for (char e : a) { + checkSum ^= e; + } + return (int) checkSum; + } + + private static int checkSumXor(float[] a) { + int checkSum = 0; + + for (float e : a) { + checkSum ^= (int) e; + } + return checkSum; + } + + private static int checkSumXor(double[] a) { + int checkSum = 0; + + for (double e : a) { + checkSum ^= (int) e; + } + return checkSum; + } + + private static int checkSumPlus(Object object) { + if (object instanceof int[]) { + return checkSumPlus((int[]) object); + } else if (object instanceof long[]) { + return checkSumPlus((long[]) object); + } else if (object instanceof short[]) { + return checkSumPlus((short[]) object); + } else if (object instanceof byte[]) { + return checkSumPlus((byte[]) object); + } else if (object instanceof char[]) { + return checkSumPlus((char[]) object); + } else if (object instanceof float[]) { + return checkSumPlus((float[]) object); + } else if (object instanceof double[]) { + return checkSumPlus((double[]) object); + } else if (object instanceof Integer[]) { + return checkSumPlus((Integer[]) object); + } else { + failed("Unknow type of array: " + object + " of class " + + object.getClass().getName()); + return -1; + } + } + + private static int checkSumPlus(int[] a) { + int checkSum = 0; + + for (int e : a) { + checkSum += e; + } + return checkSum; + } + + private static int checkSumPlus(long[] a) { + long checkSum = 0; + + for (long e : a) { + checkSum += e; + } + return (int) checkSum; + } + + private static int checkSumPlus(short[] a) { + short checkSum = 0; + + for (short e : a) { + checkSum += e; + } + return (int) checkSum; + } + + private static int checkSumPlus(byte[] a) { + byte checkSum = 0; + + for (byte e : a) { + checkSum += e; + } + return (int) checkSum; + } + + private static int checkSumPlus(char[] a) { + char checkSum = 0; + + for (char e : a) { + checkSum += e; + } + return (int) checkSum; + } + + private static int checkSumPlus(float[] a) { + int checkSum = 0; + + for (float e : a) { + checkSum += (int) e; + } + return checkSum; + } + + private static int checkSumPlus(double[] a) { + int checkSum = 0; + + for (double e : a) { + checkSum += (int) e; + } + return checkSum; + } + + private static int checkSumPlus(Integer[] a) { + int checkSum = 0; + + for (Integer e : a) { + checkSum += e.intValue(); + } + return checkSum; + } + + private static void sortByInsertionSort(Object object) { + if (object instanceof int[]) { + sortByInsertionSort((int[]) object); + } else if (object instanceof long[]) { + sortByInsertionSort((long[]) object); + } else if (object instanceof short[]) { + sortByInsertionSort((short[]) object); + } else if (object instanceof byte[]) { + sortByInsertionSort((byte[]) object); + } else if (object instanceof char[]) { + sortByInsertionSort((char[]) object); + } else if (object instanceof float[]) { + sortByInsertionSort((float[]) object); + } else if (object instanceof double[]) { + sortByInsertionSort((double[]) object); + } else if (object instanceof Integer[]) { + sortByInsertionSort((Integer[]) object); + } else { + failed("Unknow type of array: " + object + " of class " + + object.getClass().getName()); + } + } + + private static void sortByInsertionSort(int[] a) { + for (int j, i = 1; i < a.length; i++) { + int ai = a[i]; + for (j = i - 1; j >= 0 && ai < a[j]; j--) { + a[j + 1] = a[j]; + } + a[j + 1] = ai; + } + } + + private static void sortByInsertionSort(long[] a) { + for (int j, i = 1; i < a.length; i++) { + long ai = a[i]; + for (j = i - 1; j >= 0 && ai < a[j]; j--) { + a[j + 1] = a[j]; + } + a[j + 1] = ai; + } + } + + private static void sortByInsertionSort(short[] a) { + for (int j, i = 1; i < a.length; i++) { + short ai = a[i]; + for (j = i - 1; j >= 0 && ai < a[j]; j--) { + a[j + 1] = a[j]; + } + a[j + 1] = ai; + } + } + + private static void sortByInsertionSort(byte[] a) { + for (int j, i = 1; i < a.length; i++) { + byte ai = a[i]; + for (j = i - 1; j >= 0 && ai < a[j]; j--) { + a[j + 1] = a[j]; + } + a[j + 1] = ai; + } + } + + private static void sortByInsertionSort(char[] a) { + for (int j, i = 1; i < a.length; i++) { + char ai = a[i]; + for (j = i - 1; j >= 0 && ai < a[j]; j--) { + a[j + 1] = a[j]; + } + a[j + 1] = ai; + } + } + + private static void sortByInsertionSort(float[] a) { + for (int j, i = 1; i < a.length; i++) { + float ai = a[i]; + for (j = i - 1; j >= 0 && ai < a[j]; j--) { + a[j + 1] = a[j]; + } + a[j + 1] = ai; + } + } + + private static void sortByInsertionSort(double[] a) { + for (int j, i = 1; i < a.length; i++) { + double ai = a[i]; + for (j = i - 1; j >= 0 && ai < a[j]; j--) { + a[j + 1] = a[j]; + } + a[j + 1] = ai; + } + } + + private static void sortByInsertionSort(Integer[] a) { + for (int j, i = 1; i < a.length; i++) { + Integer ai = a[i]; + for (j = i - 1; j >= 0 && ai < a[j]; j--) { + a[j + 1] = a[j]; + } + a[j + 1] = ai; + } + } + + private static void sort(Object object) { + if (object instanceof int[]) { + Arrays.parallelSort((int[]) object); + } else if (object instanceof long[]) { + Arrays.parallelSort((long[]) object); + } else if (object instanceof short[]) { + Arrays.parallelSort((short[]) object); + } else if (object instanceof byte[]) { + Arrays.parallelSort((byte[]) object); + } else if (object instanceof char[]) { + Arrays.parallelSort((char[]) object); + } else if (object instanceof float[]) { + Arrays.parallelSort((float[]) object); + } else if (object instanceof double[]) { + Arrays.parallelSort((double[]) object); + } else if (object instanceof Integer[]) { + Arrays.parallelSort((Integer[]) object); + } else { + failed("Unknow type of array: " + object + " of class " + + object.getClass().getName()); + } + } + + private static void sortSubArray(Object object, int fromIndex, int toIndex) { + if (object instanceof int[]) { + Arrays.parallelSort((int[]) object, fromIndex, toIndex); + } else if (object instanceof long[]) { + Arrays.parallelSort((long[]) object, fromIndex, toIndex); + } else if (object instanceof short[]) { + Arrays.parallelSort((short[]) object, fromIndex, toIndex); + } else if (object instanceof byte[]) { + Arrays.parallelSort((byte[]) object, fromIndex, toIndex); + } else if (object instanceof char[]) { + Arrays.parallelSort((char[]) object, fromIndex, toIndex); + } else if (object instanceof float[]) { + Arrays.parallelSort((float[]) object, fromIndex, toIndex); + } else if (object instanceof double[]) { + Arrays.parallelSort((double[]) object, fromIndex, toIndex); + } else if (object instanceof Integer[]) { + Arrays.parallelSort((Integer[]) object, fromIndex, toIndex); + } else { + failed("Unknow type of array: " + object + " of class " + + object.getClass().getName()); + } + } + + private static void checkSubArray(Object object, int fromIndex, int toIndex, int m) { + if (object instanceof int[]) { + checkSubArray((int[]) object, fromIndex, toIndex, m); + } else if (object instanceof long[]) { + checkSubArray((long[]) object, fromIndex, toIndex, m); + } else if (object instanceof short[]) { + checkSubArray((short[]) object, fromIndex, toIndex, m); + } else if (object instanceof byte[]) { + checkSubArray((byte[]) object, fromIndex, toIndex, m); + } else if (object instanceof char[]) { + checkSubArray((char[]) object, fromIndex, toIndex, m); + } else if (object instanceof float[]) { + checkSubArray((float[]) object, fromIndex, toIndex, m); + } else if (object instanceof double[]) { + checkSubArray((double[]) object, fromIndex, toIndex, m); + } else if (object instanceof Integer[]) { + checkSubArray((Integer[]) object, fromIndex, toIndex, m); + } else { + failed("Unknow type of array: " + object + " of class " + + object.getClass().getName()); + } + } + + private static void checkSubArray(Integer[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + if (a[i].intValue() != 0xDEDA) { + failed("Range sort changes left element on position " + i + + ": " + a[i] + ", must be " + 0xDEDA); + } + } + + for (int i = fromIndex; i < toIndex - 1; i++) { + if (a[i].intValue() > a[i + 1].intValue()) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + + for (int i = toIndex; i < a.length; i++) { + if (a[i].intValue() != 0xBABA) { + failed("Range sort changes right element on position " + i + + ": " + a[i] + ", must be " + 0xBABA); + } + } + } + + private static void checkSubArray(int[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + if (a[i] != 0xDEDA) { + failed("Range sort changes left element on position " + i + + ": " + a[i] + ", must be " + 0xDEDA); + } + } + + for (int i = fromIndex; i < toIndex - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + + for (int i = toIndex; i < a.length; i++) { + if (a[i] != 0xBABA) { + failed("Range sort changes right element on position " + i + + ": " + a[i] + ", must be " + 0xBABA); + } + } + } + + private static void checkSubArray(byte[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + if (a[i] != (byte) 0xDEDA) { + failed("Range sort changes left element on position " + i + + ": " + a[i] + ", must be " + 0xDEDA); + } + } + + for (int i = fromIndex; i < toIndex - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + + for (int i = toIndex; i < a.length; i++) { + if (a[i] != (byte) 0xBABA) { + failed("Range sort changes right element on position " + i + + ": " + a[i] + ", must be " + 0xBABA); + } + } + } + + private static void checkSubArray(long[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + if (a[i] != (long) 0xDEDA) { + failed("Range sort changes left element on position " + i + + ": " + a[i] + ", must be " + 0xDEDA); + } + } + + for (int i = fromIndex; i < toIndex - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + + for (int i = toIndex; i < a.length; i++) { + if (a[i] != (long) 0xBABA) { + failed("Range sort changes right element on position " + i + + ": " + a[i] + ", must be " + 0xBABA); + } + } + } + + private static void checkSubArray(char[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + if (a[i] != (char) 0xDEDA) { + failed("Range sort changes left element on position " + i + + ": " + a[i] + ", must be " + 0xDEDA); + } + } + + for (int i = fromIndex; i < toIndex - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + + for (int i = toIndex; i < a.length; i++) { + if (a[i] != (char) 0xBABA) { + failed("Range sort changes right element on position " + i + + ": " + a[i] + ", must be " + 0xBABA); + } + } + } + + private static void checkSubArray(short[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + if (a[i] != (short) 0xDEDA) { + failed("Range sort changes left element on position " + i + + ": " + a[i] + ", must be " + 0xDEDA); + } + } + + for (int i = fromIndex; i < toIndex - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + + for (int i = toIndex; i < a.length; i++) { + if (a[i] != (short) 0xBABA) { + failed("Range sort changes right element on position " + i + + ": " + a[i] + ", must be " + 0xBABA); + } + } + } + + private static void checkSubArray(float[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + if (a[i] != (float) 0xDEDA) { + failed("Range sort changes left element on position " + i + + ": " + a[i] + ", must be " + 0xDEDA); + } + } + + for (int i = fromIndex; i < toIndex - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + + for (int i = toIndex; i < a.length; i++) { + if (a[i] != (float) 0xBABA) { + failed("Range sort changes right element on position " + i + + ": " + a[i] + ", must be " + 0xBABA); + } + } + } + + private static void checkSubArray(double[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + if (a[i] != (double) 0xDEDA) { + failed("Range sort changes left element on position " + i + + ": " + a[i] + ", must be " + 0xDEDA); + } + } + + for (int i = fromIndex; i < toIndex - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + + for (int i = toIndex; i < a.length; i++) { + if (a[i] != (double) 0xBABA) { + failed("Range sort changes right element on position " + i + + ": " + a[i] + ", must be " + 0xBABA); + } + } + } + + private static void checkRange(Object object, int m) { + if (object instanceof int[]) { + checkRange((int[]) object, m); + } else if (object instanceof long[]) { + checkRange((long[]) object, m); + } else if (object instanceof short[]) { + checkRange((short[]) object, m); + } else if (object instanceof byte[]) { + checkRange((byte[]) object, m); + } else if (object instanceof char[]) { + checkRange((char[]) object, m); + } else if (object instanceof float[]) { + checkRange((float[]) object, m); + } else if (object instanceof double[]) { + checkRange((double[]) object, m); + } else if (object instanceof Integer[]) { + checkRange((Integer[]) object, m); + } else { + failed("Unknow type of array: " + object + " of class " + + object.getClass().getName()); + } + } + + private static void checkRange(Integer[] a, int m) { + try { + Arrays.parallelSort(a, m + 1, m); + + failed("ParallelSort does not throw IllegalArgumentException " + + " as expected: fromIndex = " + (m + 1) + + " toIndex = " + m); + } + catch (IllegalArgumentException iae) { + try { + Arrays.parallelSort(a, -m, a.length); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: fromIndex = " + (-m)); + } + catch (ArrayIndexOutOfBoundsException aoe) { + try { + Arrays.parallelSort(a, 0, a.length + m); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: toIndex = " + (a.length + m)); + } + catch (ArrayIndexOutOfBoundsException aie) { + return; + } + } + } + } + + private static void checkRange(int[] a, int m) { + try { + Arrays.parallelSort(a, m + 1, m); + + failed("ParallelSort does not throw IllegalArgumentException " + + " as expected: fromIndex = " + (m + 1) + + " toIndex = " + m); + } + catch (IllegalArgumentException iae) { + try { + Arrays.parallelSort(a, -m, a.length); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: fromIndex = " + (-m)); + } + catch (ArrayIndexOutOfBoundsException aoe) { + try { + Arrays.parallelSort(a, 0, a.length + m); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: toIndex = " + (a.length + m)); + } + catch (ArrayIndexOutOfBoundsException aie) { + return; + } + } + } + } + + private static void checkRange(long[] a, int m) { + try { + Arrays.parallelSort(a, m + 1, m); + + failed("ParallelSort does not throw IllegalArgumentException " + + " as expected: fromIndex = " + (m + 1) + + " toIndex = " + m); + } + catch (IllegalArgumentException iae) { + try { + Arrays.parallelSort(a, -m, a.length); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: fromIndex = " + (-m)); + } + catch (ArrayIndexOutOfBoundsException aoe) { + try { + Arrays.parallelSort(a, 0, a.length + m); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: toIndex = " + (a.length + m)); + } + catch (ArrayIndexOutOfBoundsException aie) { + return; + } + } + } + } + + private static void checkRange(byte[] a, int m) { + try { + Arrays.parallelSort(a, m + 1, m); + + failed("ParallelSort does not throw IllegalArgumentException " + + " as expected: fromIndex = " + (m + 1) + + " toIndex = " + m); + } + catch (IllegalArgumentException iae) { + try { + Arrays.parallelSort(a, -m, a.length); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: fromIndex = " + (-m)); + } + catch (ArrayIndexOutOfBoundsException aoe) { + try { + Arrays.parallelSort(a, 0, a.length + m); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: toIndex = " + (a.length + m)); + } + catch (ArrayIndexOutOfBoundsException aie) { + return; + } + } + } + } + + private static void checkRange(short[] a, int m) { + try { + Arrays.parallelSort(a, m + 1, m); + + failed("ParallelSort does not throw IllegalArgumentException " + + " as expected: fromIndex = " + (m + 1) + + " toIndex = " + m); + } + catch (IllegalArgumentException iae) { + try { + Arrays.parallelSort(a, -m, a.length); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: fromIndex = " + (-m)); + } + catch (ArrayIndexOutOfBoundsException aoe) { + try { + Arrays.parallelSort(a, 0, a.length + m); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: toIndex = " + (a.length + m)); + } + catch (ArrayIndexOutOfBoundsException aie) { + return; + } + } + } + } + + private static void checkRange(char[] a, int m) { + try { + Arrays.parallelSort(a, m + 1, m); + + failed("ParallelSort does not throw IllegalArgumentException " + + " as expected: fromIndex = " + (m + 1) + + " toIndex = " + m); + } + catch (IllegalArgumentException iae) { + try { + Arrays.parallelSort(a, -m, a.length); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: fromIndex = " + (-m)); + } + catch (ArrayIndexOutOfBoundsException aoe) { + try { + Arrays.parallelSort(a, 0, a.length + m); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: toIndex = " + (a.length + m)); + } + catch (ArrayIndexOutOfBoundsException aie) { + return; + } + } + } + } + + private static void checkRange(float[] a, int m) { + try { + Arrays.parallelSort(a, m + 1, m); + + failed("ParallelSort does not throw IllegalArgumentException " + + " as expected: fromIndex = " + (m + 1) + + " toIndex = " + m); + } + catch (IllegalArgumentException iae) { + try { + Arrays.parallelSort(a, -m, a.length); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: fromIndex = " + (-m)); + } + catch (ArrayIndexOutOfBoundsException aoe) { + try { + Arrays.parallelSort(a, 0, a.length + m); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: toIndex = " + (a.length + m)); + } + catch (ArrayIndexOutOfBoundsException aie) { + return; + } + } + } + } + + private static void checkRange(double[] a, int m) { + try { + Arrays.parallelSort(a, m + 1, m); + + failed("ParallelSort does not throw IllegalArgumentException " + + " as expected: fromIndex = " + (m + 1) + + " toIndex = " + m); + } + catch (IllegalArgumentException iae) { + try { + Arrays.parallelSort(a, -m, a.length); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: fromIndex = " + (-m)); + } + catch (ArrayIndexOutOfBoundsException aoe) { + try { + Arrays.parallelSort(a, 0, a.length + m); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: toIndex = " + (a.length + m)); + } + catch (ArrayIndexOutOfBoundsException aie) { + return; + } + } + } + } + + private static void outArray(Object[] a) { + for (int i = 0; i < a.length; i++) { + out.print(a[i] + " "); + } + out.println(); + } + + private static void outArray(int[] a) { + for (int i = 0; i < a.length; i++) { + out.print(a[i] + " "); + } + out.println(); + } + + private static void outArray(float[] a) { + for (int i = 0; i < a.length; i++) { + out.print(a[i] + " "); + } + out.println(); + } + + private static void outArray(double[] a) { + for (int i = 0; i < a.length; i++) { + out.print(a[i] + " "); + } + out.println(); + } + + private static class MyRandom extends Random { + MyRandom(long seed) { + super(seed); + mySeed = seed; + } + + long getSeed() { + return mySeed; + } + + private long mySeed; + } + + private static String ourDescription; +} -- GitLab