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