1 /*
2  * Copyright 2015 Goldman Sachs.
3  * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 only, as
8  * published by the Free Software Foundation.
9  *
10  * This code is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13  * version 2 for more details (a copy is included in the LICENSE file that
14  * accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License version
17  * 2 along with this work; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19  *
20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21  * or visit www.oracle.com if you need additional information or have any
22  * questions.
23  */
24 /*
25  * @test
26  * @bug 8154049
27  * @summary Tests the sorting of a large array of sorted primitive values,
28  *          predominently for cases where the array is nearly sorted. This tests
29  *          code that detects patterns in the array to determine if it is nearly
30  *          sorted and if so employs and optimizes merge sort rather than a
31  *          Dual-Pivot QuickSort.
32  *
33  * @run testng SortingNearlySortedPrimitive
34  */
35 
36 package test.java.util.Arrays;
37 
38 import org.testng.annotations.DataProvider;
39 import org.testng.annotations.Test;
40 
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.List;
44 import java.util.StringJoiner;
45 import java.util.function.IntFunction;
46 import java.util.stream.IntStream;
47 import java.util.stream.Stream;
48 
49 public class SortingNearlySortedPrimitive {
50 
51     static final int BASE = 3;
52     static final int WIDTH = 4;
53     // Should be > DualPivotQuicksort.QUICKSORT_THRESHOLD
54     static final int PAD = 300;
55 
createCombinations()56     Stream<int[]> createCombinations() {
57         // Create all combinations for the BASE value and double the WIDTH
58         // elements
59         // This is create various combinations of ascending, descending and
60         // equal runs to exercise the nearly sorted code paths
61         return IntStream.range(0, (int) Math.pow(BASE, 2 * WIDTH)).
62                 mapToObj(this::createArray);
63     }
64 
65     // Create an array which at either end is filled with -ve and +ve elements
66     // according to the base value and padded with zeros in between
createArray(int v)67     int[] createArray(int v) {
68         int[] a = new int[WIDTH + PAD + WIDTH];
69 
70         // Fill head of array
71         for (int j = 0; j < WIDTH; j++) {
72             a[j] = (v % BASE) - (BASE / 2);
73             v /= BASE;
74         }
75         // Fill tail of array
76         for (int j = 0; j < WIDTH; j++) {
77             a[WIDTH + PAD + j] = (v % BASE) - (BASE / 2);
78             v /= BASE;
79         }
80         return a;
81     }
82 
83     @Test
testCombination()84     public void testCombination() {
85         createCombinations().forEach(a -> {
86             try {
87                 // Clone source array to ensure it is not modified
88                 this.sortAndAssert(a.clone());
89                 this.sortAndAssert(floatCopyFromInt(a));
90                 this.sortAndAssert(doubleCopyFromInt(a));
91                 this.sortAndAssert(longCopyFromInt(a));
92                 this.sortAndAssert(shortCopyFromInt(a));
93                 this.sortAndAssert(charCopyFromInt(a));
94             } catch (AssertionError sae) {
95                 AssertionError ae = new AssertionError("Sort failed for " + arrayToString(a));
96                 ae.addSuppressed(sae);
97                 throw ae;
98             }
99         });
100     }
101 
arrayToString(int[] a)102     String arrayToString(int[] a) {
103         int[] l = Arrays.copyOfRange(a, 0, WIDTH + 2);
104         int[] r = Arrays.copyOfRange(a, a.length - (WIDTH + 2), a.length);
105         StringJoiner sj = new StringJoiner(",", "[", "]");
106         for (int i : l) {
107             sj.add(Integer.toString(i));
108         }
109         sj.add("...");
110         for (int i : r) {
111             sj.add(Integer.toString(i));
112         }
113         return sj.toString();
114     }
115 
116 
117     @DataProvider(name = "shapes")
createShapes()118     public Object[][] createShapes() {
119         Stream<List<Object>> baseCases = Stream.of(
120                 List.of("hiZeroLowTest", (IntFunction<int[]>) this::hiZeroLowData),
121                 List.of("endLessThanTest", (IntFunction<int[]>) this::endLessThanData),
122                 List.of("highFlatLowTest", (IntFunction<int[]>) this::highFlatLowData),
123                 List.of("identicalTest", (IntFunction<int[]>) this::identicalData),
124                 List.of("sortedReversedSortedTest", (IntFunction<int[]>) this::sortedReversedSortedData),
125                 List.of("pairFlipTest", (IntFunction<int[]>) this::pairFlipData),
126                 List.of("zeroHiTest", (IntFunction<int[]>) this::zeroHiData)
127         );
128 
129         // Ensure the following inequality holds for certain sizes
130         // DualPivotQuicksort.QUICKSORT_THRESHOLD <= size - 1
131         //   < DualPivotQuicksort.COUNTING_SORT_THRESHOLD_FOR_SHORT_OR_CHAR
132         // This guarantees that code paths are taken for checking nearly sorted
133         // arrays for all primitive types
134         List<Integer> sizes = List.of(100, 1_000, 10_000, 1_000_000);
135         return baseCases.
136                 flatMap(l -> sizes.stream().map(s -> append(l, s))).
137                 toArray(Object[][]::new);
138     }
139 
append(List<Object> l, Object value)140     Object[] append(List<Object> l, Object value) {
141         List<Object> nl = new ArrayList<>(l);
142         nl.add(value);
143         return nl.toArray();
144     }
145 
146     @Test(dataProvider = "shapes")
testShapes(String testName, IntFunction<int[]> dataMethod, int size)147     public void testShapes(String testName, IntFunction<int[]> dataMethod, int size) {
148         int[] intSourceArray = dataMethod.apply(size);
149 
150         // Clone source array to ensure it is not modified
151         this.sortAndAssert(intSourceArray.clone());
152         this.sortAndAssert(floatCopyFromInt(intSourceArray));
153         this.sortAndAssert(doubleCopyFromInt(intSourceArray));
154         this.sortAndAssert(longCopyFromInt(intSourceArray));
155         this.sortAndAssert(shortCopyFromInt(intSourceArray));
156         this.sortAndAssert(charCopyFromInt(intSourceArray));
157     }
158 
floatCopyFromInt(int[] src)159     private float[] floatCopyFromInt(int[] src) {
160         float[] result = new float[src.length];
161         for (int i = 0; i < result.length; i++) {
162             result[i] = src[i];
163         }
164         return result;
165     }
166 
doubleCopyFromInt(int[] src)167     private double[] doubleCopyFromInt(int[] src) {
168         double[] result = new double[src.length];
169         for (int i = 0; i < result.length; i++) {
170             result[i] = src[i];
171         }
172         return result;
173     }
174 
longCopyFromInt(int[] src)175     private long[] longCopyFromInt(int[] src) {
176         long[] result = new long[src.length];
177         for (int i = 0; i < result.length; i++) {
178             result[i] = src[i];
179         }
180         return result;
181     }
182 
shortCopyFromInt(int[] src)183     private short[] shortCopyFromInt(int[] src) {
184         short[] result = new short[src.length];
185         for (int i = 0; i < result.length; i++) {
186             result[i] = (short) src[i];
187         }
188         return result;
189     }
190 
charCopyFromInt(int[] src)191     private char[] charCopyFromInt(int[] src) {
192         char[] result = new char[src.length];
193         for (int i = 0; i < result.length; i++) {
194             result[i] = (char) src[i];
195         }
196         return result;
197     }
198 
sortAndAssert(int[] array)199     private void sortAndAssert(int[] array) {
200         Arrays.sort(array);
201         for (int i = 1; i < array.length; i++) {
202             if (array[i] < array[i - 1]) {
203                 throw new AssertionError("not sorted");
204             }
205         }
206     }
207 
sortAndAssert(char[] array)208     private void sortAndAssert(char[] array) {
209         Arrays.sort(array);
210         for (int i = 1; i < array.length; i++) {
211             if (array[i] < array[i - 1]) {
212                 throw new AssertionError("not sorted");
213             }
214         }
215     }
216 
sortAndAssert(short[] array)217     private void sortAndAssert(short[] array) {
218         Arrays.sort(array);
219         for (int i = 1; i < array.length; i++) {
220             if (array[i] < array[i - 1]) {
221                 throw new AssertionError("not sorted");
222             }
223         }
224     }
225 
sortAndAssert(double[] array)226     private void sortAndAssert(double[] array) {
227         Arrays.sort(array);
228         for (int i = 1; i < array.length; i++) {
229             if (array[i] < array[i - 1]) {
230                 throw new AssertionError("not sorted");
231             }
232         }
233     }
234 
sortAndAssert(float[] array)235     private void sortAndAssert(float[] array) {
236         Arrays.sort(array);
237         for (int i = 1; i < array.length; i++) {
238             if (array[i] < array[i - 1]) {
239                 throw new AssertionError("not sorted");
240             }
241         }
242     }
243 
sortAndAssert(long[] array)244     private void sortAndAssert(long[] array) {
245         Arrays.sort(array);
246         for (int i = 1; i < array.length; i++) {
247             if (array[i] < array[i - 1]) {
248                 throw new AssertionError("not sorted");
249             }
250         }
251     }
252 
zeroHiData(int size)253     private int[] zeroHiData(int size) {
254         int[] array = new int[size];
255 
256         int threeQuarters = (int) (size * 0.75);
257         for (int i = 0; i < threeQuarters; i++) {
258             array[i] = 0;
259         }
260         int k = 1;
261         for (int i = threeQuarters; i < size; i++) {
262             array[i] = k;
263             k++;
264         }
265 
266         return array;
267     }
268 
hiZeroLowData(int size)269     private int[] hiZeroLowData(int size) {
270         int[] array = new int[size];
271 
272         int oneThird = size / 3;
273         for (int i = 0; i < oneThird; i++) {
274             array[i] = i;
275         }
276         int twoThirds = oneThird * 2;
277         for (int i = oneThird; i < twoThirds; i++) {
278             array[i] = 0;
279         }
280         for (int i = twoThirds; i < size; i++) {
281             array[i] = oneThird - i + twoThirds;
282         }
283         return array;
284     }
285 
highFlatLowData(int size)286     private int[] highFlatLowData(int size) {
287         int[] array = new int[size];
288 
289         int oneThird = size / 3;
290         for (int i = 0; i < oneThird; i++) {
291             array[i] = i;
292         }
293         int twoThirds = oneThird * 2;
294         int constant = oneThird - 1;
295         for (int i = oneThird; i < twoThirds; i++) {
296             array[i] = constant;
297         }
298         for (int i = twoThirds; i < size; i++) {
299             array[i] = constant - i + twoThirds;
300         }
301 
302         return array;
303     }
304 
identicalData(int size)305     private int[] identicalData(int size) {
306         int[] array = new int[size];
307         int listNumber = 24;
308 
309         for (int i = 0; i < size; i++) {
310             array[i] = listNumber;
311         }
312 
313         return array;
314     }
315 
endLessThanData(int size)316     private int[] endLessThanData(int size) {
317         int[] array = new int[size];
318 
319         for (int i = 0; i < size - 1; i++) {
320             array[i] = 3;
321         }
322         array[size - 1] = 1;
323 
324         return array;
325     }
326 
sortedReversedSortedData(int size)327     private int[] sortedReversedSortedData(int size) {
328         int[] array = new int[size];
329 
330         for (int i = 0; i < size / 2; i++) {
331             array[i] = i;
332         }
333         int num = 0;
334         for (int i = size / 2; i < size; i++) {
335             array[i] = size - num;
336             num++;
337         }
338 
339         return array;
340     }
341 
pairFlipData(int size)342     private int[] pairFlipData(int size) {
343         int[] array = new int[size];
344 
345         for (int i = 0; i < size; i++) {
346             array[i] = i;
347         }
348         for (int i = 0; i < size; i += 2) {
349             int temp = array[i];
350             array[i] = array[i + 1];
351             array[i + 1] = temp;
352         }
353 
354         return array;
355     }
356 }
357