├── .gitignore ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── pom.xml └── src ├── main └── fmpp │ ├── Primitive.fmpp │ └── templates │ └── net │ └── mintern │ └── primitive │ ├── Comparators.java.ft │ ├── DualPivotQuicksorts.java.ft │ ├── Primitive.java.ft │ ├── TimSorts.java.ft │ └── comparators │ └── package-info.java.ft └── test └── java └── net └── mintern └── primitive └── PrimitiveTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | target/ 4 | 5 | # Eclipse 6 | .settings/ 7 | .project 8 | .classpath 9 | 10 | # NetBeans 11 | nb-configuration.xml 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 2.0 (2024-07-28) 2 | 3 | #### Added 4 | - `naturalOrder()` and `reverseOrder()` to comparators (#11) 5 | 6 | #### Changed 7 | - Discontinued support for Java 6 and 7 8 | 9 | ### 1.3 (2016-09-30) 10 | 11 | #### Added 12 | - Binary search (credit to Dmitry Cherniachenko) 13 | 14 | ### 1.2.3 (2016-04-28) 15 | 16 | #### Fixed 17 | - [JDK9 DualPivotQuicksort "optimization" is buggy](https://github.com/mintern-java/primitive/issues/6) 18 | (upstream: ) 19 | 20 | #### Reverted 21 | - Reverts "Performance improvement" introduced by 1.2.2 since it was buggy. 22 | 23 | ### 1.2.2 (2016-04-13) 24 | 25 | #### Fixed 26 | - During a non-stable sort, if two different items compared the same, it was 27 | possible for one item to be replaced by a copy of the other. 28 | 29 | #### Performance improvement 30 | - Incorporates . 31 | 32 | ### 1.2.1 (2015-03-19) 33 | 34 | #### Fixed 35 | - Source packaging 36 | 37 | ### 1.2 (2015-03-07) 38 | 39 | #### Fixed 40 | - [TimSort bug](http://envisage-project.eu/proving-android-java-and-python-sorting-algorithm-is-broken-and-how-to-fix-it) 41 | - Stable sort is now the default, as indicated in the documentation. This bug 42 | was introduced in 1.1. 43 | 44 | ### 1.1 (2014-11-25) 45 | 46 | #### Added 47 | - Unstable sorting 48 | 49 | ### 1.0 (2014-11-22) 50 | 51 | - Initial revision 52 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | This project is Copyright 2016, Brandon Mintern. All rights reserved. 2 | All source files are licensed under GPLv2+CE (see below). 3 | 4 | DualPivotQuicksorts.java.ft is also Copyright 2009, 2015, Oracle and/or its 5 | affiliates. 6 | 7 | TimSorts.java.ft is also Copyright 2009, 2013, Oracle and/or its affiliates, 8 | and Copyright 2009 Google Inc. 9 | 10 | All files are licensed under the GNU General Public License, version 2, with 11 | the Classpath Exception (the "License"); you may not use this software except 12 | in compliance with the License. You may obtain a copy of the License at 13 | 14 | http://openjdk.java.net/legal/gplv2+ce.html 15 | 16 | Unless required by applicable law or agreed to in writing, software 17 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 18 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 19 | License for the specific language governing permissions and limitations under 20 | the License. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Java Primitive 2 | 3 | Provides utility methods for functionality related to primitive types. 4 | This currently includes sorting and searching based on custom comparators. 5 | 6 | To include the library in your project (Java 8+ supported), add the following 7 | to your POM: 8 | 9 | ```xml 10 | 11 | ... 12 | 13 | ... 14 | 15 | net.mintern 16 | primitive 17 | 2.0 18 | 19 | ... 20 | 21 | ... 22 | 23 | ``` 24 | 25 | - [Sort arrays using primitive comparators](#sort-arrays-using-primitive-comparators) 26 | - [Binary search using primitive comparators](#binary-search-using-primitive-comparators) 27 | - [Sample usage](#sample-usage) 28 | - [Contributing](#contributing) 29 | - [Building](#building) 30 | - [Where are all the source files?](#where-are-all-the-source-files) 31 | - [Deploy new release](#deploy-new-release) 32 | - [Related projects](#related-projects) 33 | 34 | ### Sort arrays using primitive comparators 35 | 36 | The `Primitive.sort(...)` methods allow primitive arrays to be sorted using 37 | custom comparators (defined in `net.mintern.primitive.comparators`). This 38 | library provides both stable and unstable sorting algorithms. When the stable 39 | algorithm is used, it keeps two values in the same relative position when the 40 | comparator returns `0` for those two values. 41 | 42 | The stable algorithm is based on 43 | [TimSort](http://en.wikipedia.org/wiki/Timsort), the sorting algorithm 44 | originally developed for use in Python. The implementation in this library is 45 | a shameless copy-paste-edit of the JDK 8 version of Joshua Bloch's 46 | `java.util.TimSort` implementation. 47 | 48 | The unstable algorithm is based on Java's default primitives `Arrays.sort` 49 | implementation, a [Dual-Pivot 50 | Quicksort](http://en.wikipedia.org/wiki/Quicksort#Variants). For some inputs, 51 | it may be up to twice as fast as stable sorting. 52 | 53 | ### Binary search using primitive comparators 54 | 55 | After sorting an array using a provided comparator, binary search can be 56 | performed using that same comparator. The implementation is another 57 | copy-paste-edit of the JDK 8 implementation. 58 | 59 | ### Sample usage 60 | 61 | ```java 62 | int[] arr = ...; 63 | 64 | // Sort by absolute values 65 | Primitive.sort(arr, (i1, i2) -> Math.abs(i1) - Math.abs(i2)); 66 | 67 | // Sort in reverse order 68 | IntComparator revCmp = (i1, i2) -> Integer.compare(i2, i1); 69 | Primitive.sort(arr, revCmp); 70 | 71 | // Binary search in reverse order (now that it's sorted) 72 | int n = ...; 73 | Primitive.binarySearch(arr, n, revCmp); 74 | ``` 75 | 76 | ### Contributing 77 | 78 | I will happily accept Pull Requests. If you have any questions, ask away. 79 | Please keep changes to a minimum; do not make gratuitous style changes. 80 | 81 | #### Building 82 | 83 | In the root directory, run `mvn install`. That will build everything. 84 | 85 | #### Where are all the source files? 86 | 87 | For this project, I used an ancient-but-solid (by modern standards) template 88 | language called [FreeMarker](http://freemarker.org) to provide templates that 89 | generate the source. A handy [FreeMarker PreProcessor 90 | (FMPP)](http://fmpp.sourceforge.net/index.html)—in conjunction with an 91 | [FMPP Maven plugin](https://code.google.com/p/freemarkerpp-maven-plugin/) that 92 | I came across on [Stack 93 | Overflow](http://stackoverflow.com/a/3925944/1237044)—turns those 94 | templates into the Java source files. 95 | 96 | The template configuration and source is in 97 | [`src/main/fmpp`](https://github.com/mintern-java/primitive/tree/master/src/main/fmpp). 98 | 99 | #### Deploy new release 100 | 101 | mvn clean deploy -P release 102 | 103 | ### Related projects 104 | 105 | All of my Java libraries are available in the 106 | [mintern-java](https://github.com/mintern-java) organization. 107 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | net.mintern 8 | primitive 9 | 2.0 10 | Primitive 11 | Utility methods for primitive types 12 | http://mintern.net/primitive 13 | 14 | 15 | UTF-8 16 | 17 | 18 | 19 | 20 | GNU General Public License, version 2, with the Classpath Exception 21 | http://openjdk.java.net/legal/gplv2+ce.html 22 | 23 | 24 | 25 | 26 | 27 | Brandon Mintern 28 | brandon@mintern.net 29 | Mintern Java Projects 30 | https://github.com/mintern-java 31 | 32 | 33 | 34 | 35 | scm:git:git@github.com:mintern-java/primitive.git 36 | scm:git:git@github.com:mintern-java/primitive.git 37 | git@github.com:mintern-java/primitive.git 38 | 39 | 40 | 41 | 42 | ossrh 43 | https://oss.sonatype.org/content/repositories/snapshots 44 | 45 | 46 | 47 | 48 | 49 | org.junit.jupiter 50 | junit-jupiter-api 51 | 5.10.3 52 | test 53 | 54 | 55 | 56 | 57 | 58 | 59 | com.googlecode.fmpp-maven-plugin 60 | fmpp-maven-plugin 61 | 1.0 62 | 63 | src/main/fmpp/Primitive.fmpp 64 | src/main/fmpp/templates 65 | 66 | 67 | 68 | generate-sources 69 | 70 | generate 71 | 72 | 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-compiler-plugin 78 | 3.13.0 79 | 80 | 1.8 81 | 1.8 82 | -Xlint:unchecked 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | release 91 | 92 | 93 | 94 | org.apache.maven.plugins 95 | maven-source-plugin 96 | 3.3.1 97 | 98 | 99 | attach-sources 100 | 101 | jar-no-fork 102 | 103 | 104 | 105 | 106 | 107 | org.apache.maven.plugins 108 | maven-javadoc-plugin 109 | 3.8.0 110 | 111 | 112 | attach-javadocs 113 | 114 | jar 115 | 116 | 117 | target/generated-sources 118 | 119 | 120 | 121 | 122 | 123 | org.apache.maven.plugins 124 | maven-gpg-plugin 125 | 3.2.4 126 | 127 | 128 | sign-artifacts 129 | verify 130 | 131 | sign 132 | 133 | 134 | 135 | 136 | 137 | org.sonatype.plugins 138 | nexus-staging-maven-plugin 139 | 1.7.0 140 | true 141 | 142 | ossrh 143 | https://oss.sonatype.org/ 144 | true 145 | 146 | 147 | 148 | 149 | 150 | 151 | coverage 152 | 153 | 154 | 155 | org.jacoco 156 | jacoco-maven-plugin 157 | 0.8.12 158 | 159 | 160 | 161 | prepare-agent 162 | 163 | 164 | 165 | report 166 | prepare-package 167 | 168 | report 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /src/main/fmpp/Primitive.fmpp: -------------------------------------------------------------------------------- 1 | ## Copyright 2024 Brandon Mintern . 2 | ## License: GPLv2+CE 3 | 4 | removeExtensions: ft 5 | data: { 6 | PRIMITIVES: [boolean, byte, char, double, float, int, long, short] 7 | BOXED: [Boolean, Byte, Character, Double, Float, Integer, Long, Short] 8 | PACKAGE: net.mintern.primitive 9 | } 10 | -------------------------------------------------------------------------------- /src/main/fmpp/templates/net/mintern/primitive/Comparators.java.ft: -------------------------------------------------------------------------------- 1 | <@pp.dropOutputFile /> 2 | <#list PRIMITIVES as type> 3 | <#assign Type = type?capitalize> 4 | <#assign Boxed = BOXED[type?index]> 5 | <#assign v = type[0]> 6 | <@pp.nestOutputFile name = "comparators/${Type}Comparator.java"> 7 | /* 8 | * Copyright 2024 Brandon Mintern . 9 | * License: GPLv2+CE 10 | */ 11 | package ${PACKAGE}.comparators; 12 | 13 | /** 14 | * A comparison function that imposes total ordering on {@code ${type}} values. 15 | */ 16 | @FunctionalInterface 17 | public interface ${Type}Comparator { 18 | 19 | /** 20 | * Compares {@code ${v}1} and {@code ${v}2}. Returns a negative value to indicate 21 | * that {@code ${v}1 < ${v}2}, 0 to indicate that {@code ${v}1 == ${v}2}, and a positive 22 | * value to indicate that {@code ${v}1 > ${v}2}. 23 | *

24 | * Implementations of this method must maintain the following invariants: 25 | *

    26 | *
  • {@code s(compare(x, y)) == -s(compare(y, x))} 27 | *
  • {@code s(compare(x, y)) == s(compare(y, z))} → 28 | * {@code s(compare(x, y)) == s(compare(x, z))} (transitivity) 29 | *
  • {@code compare(x, y) == 0} → 30 | * {@code s(compare(x, z)) == s(compare(y, z))} ∀ {@code z} 31 | *
32 | *

33 | * where {@code s(x)} is defined as follows: 34 | *

    35 | *
  • {@code x < 0}: -1 36 | *
  • {@code x == 0}: 0 37 | *
  • {@code x > 0}: 1 38 | *
39 | <#if type = "float" || type = "double"> 40 | *

Important Note

41 | * A floating-point compare implementation must be careful to maintain 42 | * total ordering, in particular when comparing -0.0, 0.0, @{code NaN}, 43 | * {@link ${Type}#NEGATIVE_INFINITY}, and {@link ${Type}#POSITIVE_INFINITY} 44 | * values. See {@link ${Type}#compareTo(${Type})} for a valid example. 45 | 46 | * 47 | * @param ${v}1 the first ${type} to compare 48 | * @param ${v}2 the second ${type} to compare 49 | * @return a negative value, 0, or a positive value to indicate that 50 | * {@code ${v}1} is less than, equal to, or greater than {@code ${v}2}, 51 | * respectively 52 | */ 53 | int compare(${type} ${v}1, ${type} ${v}2); 54 | 55 | /** 56 | * @return a comparator that compares {@code ${type}} values in natural order, 57 | * as defined by {@link ${Boxed}#compare(${type}, ${type})}. 58 | */ 59 | static ${Type}Comparator naturalOrder() { 60 | return ${Boxed}::compare; 61 | } 62 | 63 | /** 64 | * @return a comparator that compares {@code ${type}} values in the reverse of 65 | * {@link #naturalOrder()}. 66 | */ 67 | static ${Type}Comparator reverseOrder() { 68 | return (${v}1, ${v}2) -> ${Boxed}.compare(${v}2, ${v}1); 69 | } 70 | } 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/main/fmpp/templates/net/mintern/primitive/DualPivotQuicksorts.java.ft: -------------------------------------------------------------------------------- 1 | <@pp.dropOutputFile /> 2 | <#list PRIMITIVES[1..] as type> 3 | <#assign Type = type?capitalize> 4 | <#assign Sort = Type + "DualPivotQuicksort"> 5 | <#assign Cmp = Type + "Comparator"> 6 | <@pp.nestOutputFile name = "${Sort}.java"> 7 | /* 8 | * Copyright 2016, Brandon Mintern. All Rights Reserved. 9 | * Copyright 2016, Dmitry Cherniachenko. All Rights Reserved. 10 | * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. 11 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 12 | * 13 | * This code is free software; you can redistribute it and/or modify it 14 | * under the terms of the GNU General Public License version 2 only, as 15 | * published by the Free Software Foundation. Oracle designates this 16 | * particular file as subject to the "Classpath" exception as provided 17 | * by Oracle in the LICENSE file that accompanied this code. 18 | * 19 | * This code is distributed in the hope that it will be useful, but WITHOUT 20 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 21 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 22 | * version 2 for more details (a copy is included in the LICENSE file that 23 | * accompanied this code). 24 | * 25 | * You should have received a copy of the GNU General Public License version 26 | * 2 along with this work; if not, write to the Free Software Foundation, 27 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 28 | * 29 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 30 | * or visit www.oracle.com if you need additional information or have any 31 | * questions. 32 | */ 33 | package net.mintern.primitive; 34 | 35 | import net.mintern.primitive.comparators.${Cmp}; 36 | 37 | /** 38 | * This class implements the Dual-Pivot Quicksort algorithm by 39 | * Vladimir Yaroslavskiy, Jon Bentley, and Josh Bloch. The algorithm 40 | * offers O(n log(n)) performance on many data sets that cause other 41 | * quicksorts to degrade to quadratic performance, and is typically 42 | * faster than traditional (one-pivot) Quicksort implementations. 43 | * 44 | * All exposed methods are package-private, designed to be invoked 45 | * from public methods (in class Arrays) after performing any 46 | * necessary array bounds checks and expanding parameters into the 47 | * required forms. 48 | * 49 | * @author Vladimir Yaroslavskiy 50 | * @author Jon Bentley 51 | * @author Josh Bloch 52 | * 53 | * @version 2011.02.11 m765.827.12i:5\7pm 54 | * @since 1.7 55 | */ 56 | public final class ${Sort} { 57 | 58 | /** 59 | * Prevents instantiation. 60 | */ 61 | private ${Sort}() {} 62 | 63 | /* 64 | * Tuning parameters. 65 | */ 66 | 67 | /** 68 | * The maximum number of runs in merge sort. 69 | */ 70 | private static final int MAX_RUN_COUNT = 67; 71 | 72 | /** 73 | * The maximum length of run in merge sort. 74 | */ 75 | private static final int MAX_RUN_LENGTH = 33; 76 | 77 | /** 78 | * If the length of an array to be sorted is less than this 79 | * constant, Quicksort is used in preference to merge sort. 80 | */ 81 | private static final int QUICKSORT_THRESHOLD = 286; 82 | 83 | /** 84 | * If the length of an array to be sorted is less than this 85 | * constant, insertion sort is used in preference to Quicksort. 86 | */ 87 | private static final int INSERTION_SORT_THRESHOLD = 47; 88 | 89 | /** 90 | * Sorts the specified range of the array using the given 91 | * workspace array slice if possible for merging 92 | * 93 | * @param a the array to be sorted 94 | * @param left the index of the first element, inclusive, to be sorted 95 | * @param right the index of the last element, inclusive, to be sorted 96 | * @param work a workspace array (slice) 97 | * @param workBase origin of usable space in work array 98 | * @param workLen usable size of work array 99 | */ 100 | static void sort(${type}[] a, int left, int right, ${Cmp} c, 101 | ${type}[] work, int workBase, int workLen) { 102 | // Use Quicksort on small arrays 103 | if (right - left < QUICKSORT_THRESHOLD) { 104 | sort(a, left, right, true, c); 105 | return; 106 | } 107 | 108 | /* 109 | * Index run[i] is the start of i-th run 110 | * (ascending or descending sequence). 111 | */ 112 | int[] run = new int[MAX_RUN_COUNT + 1]; 113 | int count = 0; run[0] = left; 114 | 115 | // Check if the array is nearly sorted 116 | for (int k = left; k < right; run[count] = k) { 117 | int cmp = c.compare(a[k], a[k + 1]); 118 | if (cmp < 0) { // ascending 119 | while (++k <= right && c.compare(a[k - 1], a[k]) <= 0); 120 | } else if (cmp > 0) { // descending 121 | while (++k <= right && c.compare(a[k - 1], a[k]) >= 0); 122 | for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) { 123 | ${type} t = a[lo]; a[lo] = a[hi]; a[hi] = t; 124 | } 125 | } else { // equal 126 | for (int m = MAX_RUN_LENGTH; ++k <= right && c.compare(a[k - 1], a[k]) == 0; ) { 127 | if (--m == 0) { 128 | sort(a, left, right, true, c); 129 | return; 130 | } 131 | } 132 | } 133 | 134 | /* 135 | * The array is not highly structured, 136 | * use Quicksort instead of merge sort. 137 | */ 138 | if (++count == MAX_RUN_COUNT) { 139 | sort(a, left, right, true, c); 140 | return; 141 | } 142 | } 143 | 144 | // Check special cases 145 | // Implementation note: variable "right" is increased by 1. 146 | if (run[count] == right++) { // The last run contains one element 147 | run[++count] = right; 148 | } else if (count == 1) { // The array is already sorted 149 | return; 150 | } 151 | 152 | // Determine alternation base for merge 153 | byte odd = 0; 154 | for (int n = 1; (n <<= 1) < count; odd ^= 1); 155 | 156 | // Use or create temporary array b for merging 157 | ${type}[] b; // temp array; alternates with a 158 | int ao, bo; // array offsets from 'left' 159 | int blen = right - left; // space needed for b 160 | if (work == null || workLen < blen || workBase + blen > work.length) { 161 | work = new ${type}[blen]; 162 | workBase = 0; 163 | } 164 | if (odd == 0) { 165 | System.arraycopy(a, left, work, workBase, blen); 166 | b = a; 167 | bo = 0; 168 | a = work; 169 | ao = workBase - left; 170 | } else { 171 | b = work; 172 | ao = 0; 173 | bo = workBase - left; 174 | } 175 | 176 | // Merging 177 | for (int last; count > 1; count = last) { 178 | for (int k = (last = 0) + 2; k <= count; k += 2) { 179 | int hi = run[k], mi = run[k - 1]; 180 | for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) { 181 | if (q >= hi || p < mi && c.compare(a[p + ao], a[q + ao]) <= 0) { 182 | b[i + bo] = a[p++ + ao]; 183 | } else { 184 | b[i + bo] = a[q++ + ao]; 185 | } 186 | } 187 | run[++last] = hi; 188 | } 189 | if ((count & 1) != 0) { 190 | for (int i = right, lo = run[count - 1]; --i >= lo; 191 | b[i + bo] = a[i + ao] 192 | ); 193 | run[++last] = right; 194 | } 195 | ${type}[] t = a; a = b; b = t; 196 | int o = ao; ao = bo; bo = o; 197 | } 198 | } 199 | 200 | /** 201 | * Sorts the specified range of the array by Dual-Pivot Quicksort. 202 | * 203 | * @param a the array to be sorted 204 | * @param left the index of the first element, inclusive, to be sorted 205 | * @param right the index of the last element, inclusive, to be sorted 206 | * @param leftmost indicates if this part is the leftmost in the range 207 | */ 208 | private static void sort(${type}[] a, int left, int right, boolean leftmost, ${Cmp} c) { 209 | int length = right - left + 1; 210 | 211 | // Use insertion sort on tiny arrays 212 | if (length < INSERTION_SORT_THRESHOLD) { 213 | if (leftmost) { 214 | /* 215 | * Traditional (without sentinel) insertion sort, 216 | * optimized for server VM, is used in case of 217 | * the leftmost part. 218 | */ 219 | for (int i = left, j = i; i < right; j = ++i) { 220 | ${type} ai = a[i + 1]; 221 | while (c.compare(ai, a[j]) < 0) { 222 | a[j + 1] = a[j]; 223 | if (j-- == left) { 224 | break; 225 | } 226 | } 227 | a[j + 1] = ai; 228 | } 229 | } else { 230 | /* 231 | * Skip the longest ascending sequence. 232 | */ 233 | do { 234 | if (left >= right) { 235 | return; 236 | } 237 | } while (c.compare(a[++left], a[left - 1]) >= 0); 238 | 239 | /* 240 | * Every element from adjoining part plays the role 241 | * of sentinel, therefore this allows us to avoid the 242 | * left range check on each iteration. Moreover, we use 243 | * the more optimized algorithm, so called pair insertion 244 | * sort, which is faster (in the context of Quicksort) 245 | * than traditional implementation of insertion sort. 246 | */ 247 | for (int k = left; ++left <= right; k = ++left) { 248 | ${type} a1 = a[k], a2 = a[left]; 249 | 250 | if (c.compare(a1, a2) < 0) { 251 | a2 = a1; a1 = a[left]; 252 | } 253 | while (c.compare(a1, a[--k]) < 0) { 254 | a[k + 2] = a[k]; 255 | } 256 | a[++k + 1] = a1; 257 | 258 | while (c.compare(a2, a[--k]) < 0) { 259 | a[k + 1] = a[k]; 260 | } 261 | a[k + 1] = a2; 262 | } 263 | ${type} last = a[right]; 264 | 265 | while (c.compare(last, a[--right]) < 0) { 266 | a[right + 1] = a[right]; 267 | } 268 | a[right + 1] = last; 269 | } 270 | return; 271 | } 272 | 273 | // Inexpensive approximation of length / 7 274 | int seventh = (length >> 3) + (length >> 6) + 1; 275 | 276 | /* 277 | * Sort five evenly spaced elements around (and including) the 278 | * center element in the range. These elements will be used for 279 | * pivot selection as described below. The choice for spacing 280 | * these elements was empirically determined to work well on 281 | * a wide variety of inputs. 282 | */ 283 | int e3 = (left + right) >>> 1; // The midpoint 284 | int e2 = e3 - seventh; 285 | int e1 = e2 - seventh; 286 | int e4 = e3 + seventh; 287 | int e5 = e4 + seventh; 288 | 289 | // Sort these elements using insertion sort 290 | if (c.compare(a[e2], a[e1]) < 0) { ${type} t = a[e2]; a[e2] = a[e1]; a[e1] = t; } 291 | 292 | if (c.compare(a[e3], a[e2]) < 0) { ${type} t = a[e3]; a[e3] = a[e2]; a[e2] = t; 293 | if (c.compare(t, a[e1]) < 0) { a[e2] = a[e1]; a[e1] = t; } 294 | } 295 | if (c.compare(a[e4], a[e3]) < 0) { ${type} t = a[e4]; a[e4] = a[e3]; a[e3] = t; 296 | if (c.compare(t, a[e2]) < 0) { a[e3] = a[e2]; a[e2] = t; 297 | if (c.compare(t, a[e1]) < 0) { a[e2] = a[e1]; a[e1] = t; } 298 | } 299 | } 300 | if (c.compare(a[e5], a[e4]) < 0) { ${type} t = a[e5]; a[e5] = a[e4]; a[e4] = t; 301 | if (c.compare(t, a[e3]) < 0) { a[e4] = a[e3]; a[e3] = t; 302 | if (c.compare(t, a[e2]) < 0) { a[e3] = a[e2]; a[e2] = t; 303 | if (c.compare(t, a[e1]) < 0) { a[e2] = a[e1]; a[e1] = t; } 304 | } 305 | } 306 | } 307 | 308 | // Pointers 309 | int less = left; // The index of the first element of center part 310 | int great = right; // The index before the first element of right part 311 | 312 | if (c.compare(a[e1], a[e2]) != 0 && c.compare(a[e2], a[e3]) != 0 313 | && c.compare(a[e3], a[e4]) != 0 && c.compare(a[e4], a[e5]) != 0) { 314 | /* 315 | * Use the second and fourth of the five sorted elements as pivots. 316 | * These values are inexpensive approximations of the first and 317 | * second terciles of the array. Note that pivot1 <= pivot2. 318 | */ 319 | ${type} pivot1 = a[e2]; 320 | ${type} pivot2 = a[e4]; 321 | 322 | /* 323 | * The first and the last elements to be sorted are moved to the 324 | * locations formerly occupied by the pivots. When partitioning 325 | * is complete, the pivots are swapped back into their final 326 | * positions, and excluded from subsequent sorting. 327 | */ 328 | a[e2] = a[left]; 329 | a[e4] = a[right]; 330 | 331 | /* 332 | * Skip elements, which are less or greater than pivot values. 333 | */ 334 | while (c.compare(a[++less], pivot1) < 0); 335 | while (c.compare(a[--great], pivot2) > 0); 336 | 337 | /* 338 | * Partitioning: 339 | * 340 | * left part center part right part 341 | * +--------------------------------------------------------------+ 342 | * | < pivot1 | pivot1 <= && <= pivot2 | ? | > pivot2 | 343 | * +--------------------------------------------------------------+ 344 | * ^ ^ ^ 345 | * | | | 346 | * less k great 347 | * 348 | * Invariants: 349 | * 350 | * all in (left, less) < pivot1 351 | * pivot1 <= all in [less, k) <= pivot2 352 | * all in (great, right) > pivot2 353 | * 354 | * Pointer k is the first index of ?-part. 355 | */ 356 | outer: 357 | for (int k = less - 1; ++k <= great; ) { 358 | ${type} ak = a[k]; 359 | if (c.compare(ak, pivot1) < 0) { // Move a[k] to left part 360 | a[k] = a[less]; 361 | /* 362 | * Here and below we use "a[i] = b; i++;" instead 363 | * of "a[i++] = b;" due to performance issue. 364 | */ 365 | a[less] = ak; 366 | ++less; 367 | } else if (c.compare(ak, pivot2) > 0) { // Move a[k] to right part 368 | while (c.compare(a[great], pivot2) > 0) { 369 | if (great-- == k) { 370 | break outer; 371 | } 372 | } 373 | if (c.compare(a[great], pivot1) < 0) { // a[great] <= pivot2 374 | a[k] = a[less]; 375 | a[less] = a[great]; 376 | ++less; 377 | } else { // pivot1 <= a[great] <= pivot2 378 | a[k] = a[great]; 379 | } 380 | /* 381 | * Here and below we use "a[i] = b; i--;" instead 382 | * of "a[i--] = b;" due to performance issue. 383 | */ 384 | a[great] = ak; 385 | --great; 386 | } 387 | } 388 | 389 | // Swap pivots into their final positions 390 | a[left] = a[less - 1]; a[less - 1] = pivot1; 391 | a[right] = a[great + 1]; a[great + 1] = pivot2; 392 | 393 | // Sort left and right parts recursively, excluding known pivots 394 | sort(a, left, less - 2, leftmost, c); 395 | sort(a, great + 2, right, false, c); 396 | 397 | /* 398 | * If center part is too large (comprises > 4/7 of the array), 399 | * swap internal pivot values to ends. 400 | */ 401 | if (less < e1 && e5 < great) { 402 | /* 403 | * Skip elements, which are equal to pivot values. 404 | */ 405 | while (c.compare(a[less], pivot1) == 0) { 406 | ++less; 407 | } 408 | 409 | while (c.compare(a[great], pivot2) == 0) { 410 | --great; 411 | } 412 | 413 | /* 414 | * Partitioning: 415 | * 416 | * left part center part right part 417 | * +----------------------------------------------------------+ 418 | * | == pivot1 | pivot1 < && < pivot2 | ? | == pivot2 | 419 | * +----------------------------------------------------------+ 420 | * ^ ^ ^ 421 | * | | | 422 | * less k great 423 | * 424 | * Invariants: 425 | * 426 | * all in (*, less) == pivot1 427 | * pivot1 < all in [less, k) < pivot2 428 | * all in (great, *) == pivot2 429 | * 430 | * Pointer k is the first index of ?-part. 431 | */ 432 | outer: 433 | for (int k = less - 1; ++k <= great; ) { 434 | ${type} ak = a[k]; 435 | if (c.compare(ak, pivot1) == 0) { // Move a[k] to left part 436 | a[k] = a[less]; 437 | a[less] = ak; 438 | ++less; 439 | } else if (c.compare(ak, pivot2) == 0) { // Move a[k] to right part 440 | while (c.compare(a[great], pivot2) == 0) { 441 | if (great-- == k) { 442 | break outer; 443 | } 444 | } 445 | if (c.compare(a[great], pivot1) == 0) { // a[great] < pivot2 446 | a[k] = a[less]; 447 | /* 448 | * Even though a[great] compares equal to pivot1, 449 | * the assignment a[less] = pivot1 may be 450 | * incorrect since a[great] and pivot1 may be 451 | * different values. 452 | */ 453 | a[less] = a[great]; 454 | ++less; 455 | } else { // pivot1 < a[great] < pivot2 456 | a[k] = a[great]; 457 | } 458 | a[great] = ak; 459 | --great; 460 | } 461 | } 462 | } 463 | 464 | // Sort center part recursively 465 | sort(a, less, great, false, c); 466 | 467 | } else { // Partitioning with one pivot 468 | /* 469 | * Use the third of the five sorted elements as pivot. 470 | * This value is inexpensive approximation of the median. 471 | */ 472 | ${type} pivot = a[e3]; 473 | 474 | /* 475 | * Partitioning degenerates to the traditional 3-way 476 | * (or "Dutch National Flag") schema: 477 | * 478 | * left part center part right part 479 | * +-------------------------------------------------+ 480 | * | < pivot | == pivot | ? | > pivot | 481 | * +-------------------------------------------------+ 482 | * ^ ^ ^ 483 | * | | | 484 | * less k great 485 | * 486 | * Invariants: 487 | * 488 | * all in (left, less) < pivot 489 | * all in [less, k) == pivot 490 | * all in (great, right) > pivot 491 | * 492 | * Pointer k is the first index of ?-part. 493 | */ 494 | for (int k = less; k <= great; ++k) { 495 | int cmp = c.compare(a[k], pivot); 496 | if (cmp == 0) { 497 | continue; 498 | } 499 | ${type} ak = a[k]; 500 | if (cmp < 0) { // Move a[k] to left part 501 | a[k] = a[less]; 502 | a[less] = ak; 503 | ++less; 504 | } else { // a[k] > pivot - Move a[k] to right part 505 | while (c.compare(a[great], pivot) > 0) { 506 | --great; 507 | } 508 | if (c.compare(a[great], pivot) < 0) { // a[great] <= pivot 509 | a[k] = a[less]; 510 | a[less] = a[great]; 511 | ++less; 512 | } else { // a[great] == pivot 513 | /* 514 | * Even though a[great] compares equal to pivot, the 515 | * assignment a[k] = pivot may be incorrect since 516 | * a[great] and pivot may be different values. 517 | */ 518 | a[k] = a[great]; 519 | } 520 | a[great] = ak; 521 | --great; 522 | } 523 | } 524 | 525 | /* 526 | * Sort left and right parts recursively. 527 | * All elements from center part are equal 528 | * and, therefore, already sorted. 529 | */ 530 | sort(a, left, less - 1, leftmost, c); 531 | sort(a, great + 1, right, false, c); 532 | } 533 | } 534 | } 535 | 536 | 537 | -------------------------------------------------------------------------------- /src/main/fmpp/templates/net/mintern/primitive/Primitive.java.ft: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Brandon Mintern . All rights reserved. 3 | * Copyright 2016, Dmitry Cherniachenko . All rights reserved. 4 | * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. 5 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 | * 7 | * This code is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU General Public License version 2 only, as 9 | * published by the Free Software Foundation. Oracle designates this 10 | * particular file as subject to the "Classpath" exception as provided 11 | * by Oracle in the LICENSE file that accompanied this code. 12 | * 13 | * This code is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 | * version 2 for more details (a copy is included in the LICENSE file that 17 | * accompanied this code). 18 | * 19 | * You should have received a copy of the GNU General Public License version 20 | * 2 along with this work; if not, write to the Free Software Foundation, 21 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 | * 23 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 | * or visit www.oracle.com if you need additional information or have any 25 | * questions. 26 | */ 27 | package ${PACKAGE}; 28 | 29 | import java.util.Arrays; 30 | <#list PRIMITIVES as p> 31 | import ${PACKAGE}.comparators.${p?capitalize}Comparator; 32 | 33 | 34 | /** 35 | * A utility class that provides comparator-based sorting methods for all 36 | * primitive arrays. It also provides non-comparator sort methods for 37 | * {@code boolean} arrays since {@link Arrays} does not. 38 | */ 39 | public final class Primitive { 40 | 41 | /** 42 | * Sorts the given array so that all the {@code false} values are at the 43 | * beginning. 44 | * 45 | * @param a the array to sort 46 | * @throws NullPointerException if {@code a == null} 47 | */ 48 | public static void sort(boolean[] a) { 49 | sort(a, 0, a.length); 50 | } 51 | 52 | /** 53 | * Sorts the indicated portion of the given array so that all the 54 | * {@code false} values are at the beginning. 55 | * 56 | * @param a the array to sort 57 | * @param fromIndex the index (inclusive) marking the beginning of the array 58 | * portion 59 | * @param toIndex the index (exclusive) marking the end of the array portion 60 | * @throws NullPointerException if {@code a == null} 61 | * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or 62 | * {@code toIndex > a.length} 63 | * @throws IllegalArgumentException if {@code fromIndex > toIndex} 64 | */ 65 | public static void sort(boolean[] a, int fromIndex, int toIndex) { 66 | checkBounds(a.length, fromIndex, toIndex); 67 | while (fromIndex < toIndex && !a[fromIndex]) { 68 | fromIndex++; 69 | } 70 | if (fromIndex == toIndex) { 71 | // all false values 72 | return; 73 | } 74 | int nextFalse = fromIndex; 75 | for (int i = fromIndex + 1; i < toIndex; i++) { 76 | if (!a[i]) { 77 | a[nextFalse] = false; 78 | a[i] = true; 79 | nextFalse++; 80 | } 81 | } 82 | } 83 | <#list PRIMITIVES as type> 84 | <#assign Type = type?capitalize> 85 | <#assign Cmp = Type + "Comparator"> 86 | 87 | /** 88 | * Sorts the given array by the given comparator. The sorting algorithm used 89 | * is a stable sort, so two items that compare to 0 will be kept in the same 90 | * order when the sort is complete. 91 | <#if type != "boolean"> 92 | *

93 | * For uses that don't care about a stable sort, especially when the data 94 | * has no underlying patterns (that is, completely unsorted, random data), 95 | * the non-stable version of this method may be up to twice as fast. Use 96 | * {@link #sort(${type}[], ${Cmp}, boolean)}, with {@code stable} set to 97 | * {@code false}. 98 | 99 | * 100 | * @param a the array to sort 101 | * @param c the comparator to use for sorting the array, or {@code null} for 102 | * natural ordering 103 | * @throws NullPointerException if {@code a == null} 104 | * @throws IllegalArgumentException if sorting finds that {@code c} violates 105 | * the {@link ${Cmp}} contract 106 | */ 107 | public static void sort(${type}[] a, ${Cmp} c) { 108 | sort(a, 0, a.length, c); 109 | } 110 | 111 | /** 112 | * Sorts the indicated portion of the given array by the given comparator. 113 | * The sorting algorithm used is a stable sort, so two items that compare to 114 | * 0 will be kept in the same order when the sort is complete. 115 | <#if type != "boolean"> 116 | *

117 | * For uses that don't care about a stable sort, especially when the data 118 | * has no underlying patterns (that is, completely unsorted, random data), 119 | * the non-stable version of this method may be up to twice as fast. Use 120 | * {@link #sort(${type}[], int, int, ${Cmp}, boolean)}, with {@code stable} 121 | * set to {@code false}. 122 | 123 | * 124 | * @param a the array to sort 125 | * @param fromIndex the index (inclusive) marking the beginning of the array 126 | * portion 127 | * @param toIndex the index (exclusive) marking the end of the array portion 128 | * @param c the comparator to use for sorting the array, or {@code null} for 129 | * natural ordering 130 | * @throws NullPointerException if {@code a == null} 131 | * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or 132 | * {@code toIndex > a.length} 133 | * @throws IllegalArgumentException if {@code fromIndex > toIndex} or 134 | * sorting finds that {@code c} violates the {@link ${Cmp}} 135 | * contract 136 | */ 137 | public static void sort(${type}[] a, int fromIndex, int toIndex, 138 | ${Cmp} c) { 139 | <#if type = "boolean"> 140 | if (c == null) { 141 | sort(a, fromIndex, toIndex); 142 | } else { 143 | checkBounds(a.length, fromIndex, toIndex); 144 | BooleanTimSort.sort(a, fromIndex, toIndex, c, null, 0, 0); 145 | } 146 | <#else> 147 | sort(a, fromIndex, toIndex, c, true); 148 | 149 | } 150 | <#if type != "boolean"> 151 | 152 | /** 153 | * Sorts the given array by the given comparator. When {@code stable} is 154 | * {@code true}, the sorting algorithm will result in a stable sort, so two 155 | * items that compare to 0 will be kept in the same order when the sort is 156 | * complete. When {@code false}, no such guarantees are made, but the sort 157 | * may be up to twice as fast, especially for unpatterned data. 158 | * 159 | * @param a the array to sort 160 | * @param c the comparator to use for sorting the array, or {@code null} for 161 | * natural ordering 162 | * @param stable whether to use a slower, but stable, sorting algorithm 163 | * @throws NullPointerException if {@code a == null} 164 | * @throws IllegalArgumentException if sorting finds that {@code c} violates 165 | * the {@link ${Cmp}} contract 166 | */ 167 | public static void sort(${type}[] a, ${Cmp} c, boolean stable) { 168 | sort(a, 0, a.length, c, stable); 169 | } 170 | 171 | /** 172 | * Sorts the indicated portion of the given array by the given comparator. 173 | * When {@code stable} is {@code true}, the sorting algorithm will result in 174 | * a stable sort, so two items that compare to 0 will be kept in the same 175 | * order when the sort is complete. When {@code false}, no such guarantees 176 | * are made, but the sort may be up to twice as fast, especially for 177 | * unpatterned data. 178 | * 179 | * @param a the array to sort 180 | * @param fromIndex the index (inclusive) marking the beginning of the array 181 | * portion 182 | * @param toIndex the index (exclusive) marking the end of the array portion 183 | * @param c the comparator to use for sorting the array, or {@code null} for 184 | * natural ordering 185 | * @param stable whether to use a slower, but stable, sorting algorithm 186 | * @throws NullPointerException if {@code a == null} 187 | * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or 188 | * {@code toIndex > a.length} 189 | * @throws IllegalArgumentException if {@code fromIndex > toIndex} or 190 | * sorting finds that {@code c} violates the {@link ${Cmp}} 191 | * contract 192 | */ 193 | public static void sort(${type}[] a, int fromIndex, int toIndex, 194 | ${Cmp} c, boolean stable) { 195 | if (c == null) { 196 | Arrays.sort(a, fromIndex, toIndex); 197 | } else { 198 | checkBounds(a.length, fromIndex, toIndex); 199 | if (stable) { 200 | ${Type}TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0); 201 | } else { 202 | ${Type}DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, c, null, 0, 0); 203 | } 204 | } 205 | } 206 | 207 | 208 | /** 209 | * Searches the specified array for the specified {@code key} using the binary 210 | * search algorithm. The array must be sorted into ascending order 211 | * according to the specified comparator (as by the 212 | * {@link #sort(${type}[], ${Cmp}) sort(${type}[], ${Cmp})} 213 | * method) prior to making this call. If it is 214 | * not sorted, the results are undefined. 215 | * If the array contains multiple 216 | * elements equal to the specified object, there is no guarantee which one 217 | * will be found. 218 | * 219 | * @param a the array to be searched 220 | * @param key the value to be searched for 221 | * @param c the comparator by which the array is ordered. A 222 | * {@code null} value indicates that the elements' 223 | * natural ordering should be used. 224 | * @return index of the search key, if it is contained in the array; 225 | * otherwise, {@code (-(insertion point) - 1)}. The 226 | * insertion point is defined as the point at which the 227 | * key would be inserted into the array: the index of the first 228 | * element greater than the key, or {@code a.length} if all 229 | * elements in the array are less than the specified key. Note 230 | * that this guarantees that the return value will be >= 0 if 231 | * and only if the key is found. 232 | */ 233 | public static int binarySearch(${type}[] a, ${type} key, ${Cmp} c) { 234 | return binarySearch(a, 0, a.length, key, c); 235 | } 236 | 237 | /** 238 | * Searches a range of 239 | * the specified array for the specified {@code key} using the binary 240 | * search algorithm. 241 | * The array must be sorted into ascending order 242 | * according to the specified comparator (as by the 243 | * {@link #sort(${type}[], int, int, ${Cmp}) 244 | * sort(${type}[], int, int, ${Cmp})} 245 | * method) prior to making this call. 246 | * If it is not sorted, the results are undefined. 247 | * If the range contains multiple elements equal to the specified object, 248 | * there is no guarantee which one will be found. 249 | * 250 | * @param a the array to be searched 251 | * @param fromIndex the index of the first element (inclusive) to be 252 | * searched 253 | * @param toIndex the index of the last element (exclusive) to be searched 254 | * @param key the value to be searched for 255 | * @param c the comparator by which the array is ordered. A 256 | * {@code null} value indicates that the elements' 257 | * natural ordering should be used. 258 | * @return index of the search key, if it is contained in the array 259 | * within the specified range; 260 | * otherwise, {@code (-(insertion point) - 1)}. The 261 | * insertion point is defined as the point at which the 262 | * key would be inserted into the array: the index of the first 263 | * element greater than the key, or {@code toIndex} if all 264 | * elements in the range are less than the specified key. Note 265 | * that this guarantees that the return value will be >= 266 | * {@code fromIndex} if and only if the key is found. 267 | */ 268 | public static int binarySearch(${type}[] a, int fromIndex, int toIndex, ${type} key, 269 | ${Cmp} c) { 270 | if (c == null) { 271 | return Arrays.binarySearch(a, fromIndex, toIndex, key); 272 | } 273 | checkBounds(a.length, fromIndex, toIndex); 274 | 275 | int low = fromIndex; 276 | int high = toIndex - 1; 277 | 278 | while (low <= high) { 279 | int mid = (low + high) >>> 1; 280 | int cmp = c.compare(a[mid], key); 281 | if (cmp < 0) 282 | low = mid + 1; 283 | else if (cmp > 0) 284 | high = mid - 1; 285 | else 286 | return mid; // key found 287 | } 288 | return -(low + 1); // key not found. 289 | } 290 | 291 | 292 | 293 | private static void checkBounds(int len, int fromIndex, int toIndex) { 294 | if (fromIndex < 0) { 295 | throw new ArrayIndexOutOfBoundsException("fromIndex < 0"); 296 | } 297 | if (toIndex > len) { 298 | throw new ArrayIndexOutOfBoundsException("toIndex > a.length"); 299 | } 300 | if (fromIndex > toIndex) { 301 | throw new IllegalArgumentException("fromIndex > toIndex"); 302 | } 303 | } 304 | 305 | private Primitive(){} 306 | } 307 | -------------------------------------------------------------------------------- /src/main/fmpp/templates/net/mintern/primitive/TimSorts.java.ft: -------------------------------------------------------------------------------- 1 | <@pp.dropOutputFile /> 2 | <#list PRIMITIVES as type> 3 | <#assign Type = type?capitalize> 4 | <#assign Sort = Type + "TimSort"> 5 | <#assign Cmp = Type + "Comparator"> 6 | <@pp.nestOutputFile name = "${Sort}.java"> 7 | /* 8 | * Copyright 2016, Brandon Mintern. All Rights Reserved. 9 | * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. 10 | * Copyright 2009 Google Inc. All Rights Reserved. 11 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 12 | * 13 | * This code is free software; you can redistribute it and/or modify it 14 | * under the terms of the GNU General Public License version 2 only, as 15 | * published by the Free Software Foundation. Oracle designates this 16 | * particular file as subject to the "Classpath" exception as provided 17 | * by Oracle in the LICENSE file that accompanied this code. 18 | * 19 | * This code is distributed in the hope that it will be useful, but WITHOUT 20 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 21 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 22 | * version 2 for more details (a copy is included in the LICENSE file that 23 | * accompanied this code). 24 | * 25 | * You should have received a copy of the GNU General Public License version 26 | * 2 along with this work; if not, write to the Free Software Foundation, 27 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 28 | * 29 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 30 | * or visit www.oracle.com if you need additional information or have any 31 | * questions. 32 | */ 33 | package ${PACKAGE}; 34 | 35 | import ${PACKAGE}.comparators.${Cmp}; 36 | 37 | /** 38 | * This class provides a specialization of {@link java.util.TimSort} for ${type} 39 | * arrays. This code is shamelessly copied, with Object references changed to 40 | * ${type} instead. Everything else (even some now inaccurate comments) is left 41 | * as Josh Bloch originally wrote it. 42 | * 43 | * @author Brandon Mintern 44 | * 45 | * A stable, adaptive, iterative mergesort that requires far fewer than 46 | * n lg(n) comparisons when running on partially sorted arrays, while 47 | * offering performance comparable to a traditional mergesort when run 48 | * on random arrays. Like all proper mergesorts, this sort is stable and 49 | * runs O(n log n) time (worst case). In the worst case, this sort requires 50 | * temporary storage space for n/2 object references; in the best case, 51 | * it requires only a small constant amount of space. 52 | * 53 | * This implementation was adapted from Tim Peters's list sort for 54 | * Python, which is described in detail here: 55 | * 56 | * http://svn.python.org/projects/python/trunk/Objects/listsort.txt 57 | * 58 | * Tim's C code may be found here: 59 | * 60 | * http://svn.python.org/projects/python/trunk/Objects/listobject.c 61 | * 62 | * The underlying techniques are described in this paper (and may have 63 | * even earlier origins): 64 | * 65 | * "Optimistic Sorting and Information Theoretic Complexity" 66 | * Peter McIlroy 67 | * SODA (Fourth Annual ACM-SIAM Symposium on Discrete Algorithms), 68 | * pp 467-474, Austin, Texas, 25-27 January 1993. 69 | * 70 | * While the API to this class consists solely of static methods, it is 71 | * (privately) instantiable; a TimSort instance holds the state of an ongoing 72 | * sort, assuming the input array is large enough to warrant the full-blown 73 | * TimSort. Small arrays are sorted in place, using a binary insertion sort. 74 | * 75 | * @author Josh Bloch 76 | */ 77 | class ${Sort} { 78 | /** 79 | * This is the minimum sized sequence that will be merged. Shorter 80 | * sequences will be lengthened by calling binarySort. If the entire 81 | * array is less than this length, no merges will be performed. 82 | * 83 | * This constant should be a power of two. It was 64 in Tim Peter's C 84 | * implementation, but 32 was empirically determined to work better in 85 | * this implementation. In the unlikely event that you set this constant 86 | * to be a number that's not a power of two, you'll need to change the 87 | * {@link #minRunLength} computation. 88 | * 89 | * If you decrease this constant, you must change the stackLen 90 | * computation in the TimSort constructor, or you risk an 91 | * ArrayOutOfBounds exception. See listsort.txt for a discussion 92 | * of the minimum stack length required as a function of the length 93 | * of the array being sorted and the minimum merge sequence length. 94 | */ 95 | private static final int MIN_MERGE = 32; 96 | 97 | /** 98 | * The array being sorted. 99 | */ 100 | private final ${type}[] a; 101 | 102 | /** 103 | * The comparator for this sort. 104 | */ 105 | private final ${Cmp} c; 106 | 107 | /** 108 | * When we get into galloping mode, we stay there until both runs win less 109 | * often than MIN_GALLOP consecutive times. 110 | */ 111 | private static final int MIN_GALLOP = 7; 112 | 113 | /** 114 | * This controls when we get *into* galloping mode. It is initialized 115 | * to MIN_GALLOP. The mergeLo and mergeHi methods nudge it higher for 116 | * random data, and lower for highly structured data. 117 | */ 118 | private int minGallop = MIN_GALLOP; 119 | 120 | /** 121 | * Maximum initial size of tmp array, which is used for merging. The array 122 | * can grow to accommodate demand. 123 | * 124 | * Unlike Tim's original C version, we do not allocate this much storage 125 | * when sorting smaller arrays. This change was required for performance. 126 | */ 127 | private static final int INITIAL_TMP_STORAGE_LENGTH = 256; 128 | 129 | /** 130 | * Temp storage for merges. A workspace array may optionally be 131 | * provided in constructor, and if so will be used as long as it 132 | * is big enough. 133 | */ 134 | private ${type}[] tmp; 135 | private int tmpBase; // base of tmp array slice 136 | private int tmpLen; // length of tmp array slice 137 | 138 | /** 139 | * A stack of pending runs yet to be merged. Run i starts at 140 | * address base[i] and extends for len[i] elements. It's always 141 | * true (so long as the indices are in bounds) that: 142 | * 143 | * runBase[i] + runLen[i] == runBase[i + 1] 144 | * 145 | * so we could cut the storage for this, but it's a minor amount, 146 | * and keeping all the info explicit simplifies the code. 147 | */ 148 | private int stackSize = 0; // Number of pending runs on stack 149 | private final int[] runBase; 150 | private final int[] runLen; 151 | 152 | /** 153 | * Creates a TimSort instance to maintain the state of an ongoing sort. 154 | * 155 | * @param a the array to be sorted 156 | * @param c the comparator to determine the order of the sort 157 | * @param work a workspace array (slice) 158 | * @param workBase origin of usable space in work array 159 | * @param workLen usable size of work array 160 | */ 161 | private ${Sort}(${type}[] a, ${Cmp} c, ${type}[] work, int workBase, int workLen) { 162 | this.a = a; 163 | this.c = c; 164 | 165 | // Allocate temp storage (which may be increased later if necessary) 166 | int len = a.length; 167 | int tlen = (len < 2 * INITIAL_TMP_STORAGE_LENGTH) ? 168 | len >>> 1 : INITIAL_TMP_STORAGE_LENGTH; 169 | if (work == null || workLen < tlen || workBase + tlen > work.length) { 170 | ${type}[] newArray = new ${type}[tlen]; 171 | tmp = newArray; 172 | tmpBase = 0; 173 | tmpLen = tlen; 174 | } 175 | else { 176 | tmp = work; 177 | tmpBase = workBase; 178 | tmpLen = workLen; 179 | } 180 | 181 | /* 182 | * Allocate runs-to-be-merged stack (which cannot be expanded). The 183 | * stack length requirements are described in listsort.txt. The C 184 | * version always uses the same stack length (85), but this was 185 | * measured to be too expensive when sorting "mid-sized" arrays (e.g., 186 | * 100 elements) in Java. Therefore, we use smaller (but sufficiently 187 | * large) stack lengths for smaller arrays. The "magic numbers" in the 188 | * computation below must be changed if MIN_MERGE is decreased. See 189 | * the MIN_MERGE declaration above for more information. 190 | */ 191 | int stackLen = (len < 120 ? 5 : 192 | len < 1542 ? 10 : 193 | len < 119151 ? 24 : 49); 194 | runBase = new int[stackLen]; 195 | runLen = new int[stackLen]; 196 | } 197 | 198 | /* 199 | * The next method (package private and static) constitutes the 200 | * entire API of this class. 201 | */ 202 | 203 | /** 204 | * Sorts the given range, using the given workspace array slice 205 | * for temp storage when possible. This method is designed to be 206 | * invoked from public methods (in class Arrays) after performing 207 | * any necessary array bounds checks and expanding parameters into 208 | * the required forms. 209 | * 210 | * @param a the array to be sorted 211 | * @param lo the index of the first element, inclusive, to be sorted 212 | * @param hi the index of the last element, exclusive, to be sorted 213 | * @param c the comparator to use 214 | * @param work a workspace array (slice) 215 | * @param workBase origin of usable space in work array 216 | * @param workLen usable size of work array 217 | * @since 1.8 218 | */ 219 | static void sort(${type}[] a, int lo, int hi, ${Cmp} c, 220 | ${type}[] work, int workBase, int workLen) { 221 | assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length; 222 | 223 | int nRemaining = hi - lo; 224 | if (nRemaining < 2) 225 | return; // Arrays of size 0 and 1 are always sorted 226 | 227 | // If array is small, do a "mini-TimSort" with no merges 228 | if (nRemaining < MIN_MERGE) { 229 | int initRunLen = countRunAndMakeAscending(a, lo, hi, c); 230 | binarySort(a, lo, hi, lo + initRunLen, c); 231 | return; 232 | } 233 | 234 | /** 235 | * March over the array once, left to right, finding natural runs, 236 | * extending short natural runs to minRun elements, and merging runs 237 | * to maintain stack invariant. 238 | */ 239 | ${Sort} ts = new ${Sort}(a, c, work, workBase, workLen); 240 | int minRun = minRunLength(nRemaining); 241 | do { 242 | // Identify next run 243 | int runLen = countRunAndMakeAscending(a, lo, hi, c); 244 | 245 | // If run is short, extend to min(minRun, nRemaining) 246 | if (runLen < minRun) { 247 | int force = nRemaining <= minRun ? nRemaining : minRun; 248 | binarySort(a, lo, lo + force, lo + runLen, c); 249 | runLen = force; 250 | } 251 | 252 | // Push run onto pending-run stack, and maybe merge 253 | ts.pushRun(lo, runLen); 254 | ts.mergeCollapse(); 255 | 256 | // Advance to find next run 257 | lo += runLen; 258 | nRemaining -= runLen; 259 | } while (nRemaining != 0); 260 | 261 | // Merge all remaining runs to complete sort 262 | assert lo == hi; 263 | ts.mergeForceCollapse(); 264 | assert ts.stackSize == 1; 265 | } 266 | 267 | /** 268 | * Sorts the specified portion of the specified array using a binary 269 | * insertion sort. This is the best method for sorting small numbers 270 | * of elements. It requires O(n log n) compares, but O(n^2) data 271 | * movement (worst case). 272 | * 273 | * If the initial part of the specified range is already sorted, 274 | * this method can take advantage of it: the method assumes that the 275 | * elements from index {@code lo}, inclusive, to {@code start}, 276 | * exclusive are already sorted. 277 | * 278 | * @param a the array in which a range is to be sorted 279 | * @param lo the index of the first element in the range to be sorted 280 | * @param hi the index after the last element in the range to be sorted 281 | * @param start the index of the first element in the range that is 282 | * not already known to be sorted ({@code lo <= start <= hi}) 283 | * @param c comparator to used for the sort 284 | */ 285 | @SuppressWarnings("fallthrough") 286 | private static void binarySort(${type}[] a, int lo, int hi, int start, 287 | ${Cmp} c) { 288 | assert lo <= start && start <= hi; 289 | if (start == lo) 290 | start++; 291 | for ( ; start < hi; start++) { 292 | ${type} pivot = a[start]; 293 | 294 | // Set left (and right) to the index where a[start] (pivot) belongs 295 | int left = lo; 296 | int right = start; 297 | assert left <= right; 298 | /* 299 | * Invariants: 300 | * pivot >= all in [lo, left). 301 | * pivot < all in [right, start). 302 | */ 303 | while (left < right) { 304 | int mid = (left + right) >>> 1; 305 | if (c.compare(pivot, a[mid]) < 0) 306 | right = mid; 307 | else 308 | left = mid + 1; 309 | } 310 | assert left == right; 311 | 312 | /* 313 | * The invariants still hold: pivot >= all in [lo, left) and 314 | * pivot < all in [left, start), so pivot belongs at left. Note 315 | * that if there are elements equal to pivot, left points to the 316 | * first slot after them -- that's why this sort is stable. 317 | * Slide elements over to make room for pivot. 318 | */ 319 | int n = start - left; // The number of elements to move 320 | // Switch is just an optimization for arraycopy in default case 321 | switch (n) { 322 | case 2: a[left + 2] = a[left + 1]; 323 | case 1: a[left + 1] = a[left]; 324 | break; 325 | default: System.arraycopy(a, left, a, left + 1, n); 326 | } 327 | a[left] = pivot; 328 | } 329 | } 330 | 331 | /** 332 | * Returns the length of the run beginning at the specified position in 333 | * the specified array and reverses the run if it is descending (ensuring 334 | * that the run will always be ascending when the method returns). 335 | * 336 | * A run is the longest ascending sequence with: 337 | * 338 | * a[lo] <= a[lo + 1] <= a[lo + 2] <= ... 339 | * 340 | * or the longest descending sequence with: 341 | * 342 | * a[lo] > a[lo + 1] > a[lo + 2] > ... 343 | * 344 | * For its intended use in a stable mergesort, the strictness of the 345 | * definition of "descending" is needed so that the call can safely 346 | * reverse a descending sequence without violating stability. 347 | * 348 | * @param a the array in which a run is to be counted and possibly reversed 349 | * @param lo index of the first element in the run 350 | * @param hi index after the last element that may be contained in the run. 351 | It is required that {@code lo < hi}. 352 | * @param c the comparator to used for the sort 353 | * @return the length of the run beginning at the specified position in 354 | * the specified array 355 | */ 356 | private static int countRunAndMakeAscending(${type}[] a, int lo, int hi, 357 | ${Cmp} c) { 358 | assert lo < hi; 359 | int runHi = lo + 1; 360 | if (runHi == hi) 361 | return 1; 362 | 363 | // Find end of run, and reverse range if descending 364 | if (c.compare(a[runHi++], a[lo]) < 0) { // Descending 365 | while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0) 366 | runHi++; 367 | reverseRange(a, lo, runHi); 368 | } else { // Ascending 369 | while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0) 370 | runHi++; 371 | } 372 | 373 | return runHi - lo; 374 | } 375 | 376 | /** 377 | * Reverse the specified range of the specified array. 378 | * 379 | * @param a the array in which a range is to be reversed 380 | * @param lo the index of the first element in the range to be reversed 381 | * @param hi the index after the last element in the range to be reversed 382 | */ 383 | private static void reverseRange(${type}[] a, int lo, int hi) { 384 | hi--; 385 | while (lo < hi) { 386 | ${type} t = a[lo]; 387 | a[lo++] = a[hi]; 388 | a[hi--] = t; 389 | } 390 | } 391 | 392 | /** 393 | * Returns the minimum acceptable run length for an array of the specified 394 | * length. Natural runs shorter than this will be extended with 395 | * {@link #binarySort}. 396 | * 397 | * Roughly speaking, the computation is: 398 | * 399 | * If n < MIN_MERGE, return n (it's too small to bother with fancy stuff). 400 | * Else if n is an exact power of 2, return MIN_MERGE/2. 401 | * Else return an int k, MIN_MERGE/2 <= k <= MIN_MERGE, such that n/k 402 | * is close to, but strictly less than, an exact power of 2. 403 | * 404 | * For the rationale, see listsort.txt. 405 | * 406 | * @param n the length of the array to be sorted 407 | * @return the length of the minimum run to be merged 408 | */ 409 | private static int minRunLength(int n) { 410 | assert n >= 0; 411 | int r = 0; // Becomes 1 if any 1 bits are shifted off 412 | while (n >= MIN_MERGE) { 413 | r |= (n & 1); 414 | n >>= 1; 415 | } 416 | return n + r; 417 | } 418 | 419 | /** 420 | * Pushes the specified run onto the pending-run stack. 421 | * 422 | * @param runBase index of the first element in the run 423 | * @param runLen the number of elements in the run 424 | */ 425 | private void pushRun(int runBase, int runLen) { 426 | this.runBase[stackSize] = runBase; 427 | this.runLen[stackSize] = runLen; 428 | stackSize++; 429 | } 430 | 431 | /** 432 | * Examines the stack of runs waiting to be merged and merges adjacent runs 433 | * until the stack invariants are reestablished: 434 | * 435 | * 1. runLen[i - 3] > runLen[i - 2] + runLen[i - 1] 436 | * 2. runLen[i - 2] > runLen[i - 1] 437 | * 438 | * This method is called each time a new run is pushed onto the stack, 439 | * so the invariants are guaranteed to hold for i < stackSize upon 440 | * entry to the method. 441 | */ 442 | private void mergeCollapse() { 443 | while (stackSize > 1) { 444 | int n = stackSize - 2; 445 | if (n > 0 && runLen[n-1] <= runLen[n] + runLen[n+1]) { 446 | if (runLen[n - 1] < runLen[n + 1]) 447 | n--; 448 | mergeAt(n); 449 | } else if (runLen[n] <= runLen[n + 1]) { 450 | mergeAt(n); 451 | } else { 452 | break; // Invariant is established 453 | } 454 | } 455 | } 456 | 457 | /** 458 | * Merges all runs on the stack until only one remains. This method is 459 | * called once, to complete the sort. 460 | */ 461 | private void mergeForceCollapse() { 462 | while (stackSize > 1) { 463 | int n = stackSize - 2; 464 | if (n > 0 && runLen[n - 1] < runLen[n + 1]) 465 | n--; 466 | mergeAt(n); 467 | } 468 | } 469 | 470 | /** 471 | * Merges the two runs at stack indices i and i+1. Run i must be 472 | * the penultimate or antepenultimate run on the stack. In other words, 473 | * i must be equal to stackSize-2 or stackSize-3. 474 | * 475 | * @param i stack index of the first of the two runs to merge 476 | */ 477 | private void mergeAt(int i) { 478 | assert stackSize >= 2; 479 | assert i >= 0; 480 | assert i == stackSize - 2 || i == stackSize - 3; 481 | 482 | int base1 = runBase[i]; 483 | int len1 = runLen[i]; 484 | int base2 = runBase[i + 1]; 485 | int len2 = runLen[i + 1]; 486 | assert len1 > 0 && len2 > 0; 487 | assert base1 + len1 == base2; 488 | 489 | /* 490 | * Record the length of the combined runs; if i is the 3rd-last 491 | * run now, also slide over the last run (which isn't involved 492 | * in this merge). The current run (i+1) goes away in any case. 493 | */ 494 | runLen[i] = len1 + len2; 495 | if (i == stackSize - 3) { 496 | runBase[i + 1] = runBase[i + 2]; 497 | runLen[i + 1] = runLen[i + 2]; 498 | } 499 | stackSize--; 500 | 501 | /* 502 | * Find where the first element of run2 goes in run1. Prior elements 503 | * in run1 can be ignored (because they're already in place). 504 | */ 505 | int k = gallopRight(a[base2], a, base1, len1, 0, c); 506 | assert k >= 0; 507 | base1 += k; 508 | len1 -= k; 509 | if (len1 == 0) 510 | return; 511 | 512 | /* 513 | * Find where the last element of run1 goes in run2. Subsequent elements 514 | * in run2 can be ignored (because they're already in place). 515 | */ 516 | len2 = gallopLeft(a[base1 + len1 - 1], a, base2, len2, len2 - 1, c); 517 | assert len2 >= 0; 518 | if (len2 == 0) 519 | return; 520 | 521 | // Merge remaining runs, using tmp array with min(len1, len2) elements 522 | if (len1 <= len2) 523 | mergeLo(base1, len1, base2, len2); 524 | else 525 | mergeHi(base1, len1, base2, len2); 526 | } 527 | 528 | /** 529 | * Locates the position at which to insert the specified key into the 530 | * specified sorted range; if the range contains an element equal to key, 531 | * returns the index of the leftmost equal element. 532 | * 533 | * @param key the key whose insertion point to search for 534 | * @param a the array in which to search 535 | * @param base the index of the first element in the range 536 | * @param len the length of the range; must be > 0 537 | * @param hint the index at which to begin the search, 0 <= hint < n. 538 | * The closer hint is to the result, the faster this method will run. 539 | * @param c the comparator used to order the range, and to search 540 | * @return the int k, 0 <= k <= n such that a[b + k - 1] < key <= a[b + k], 541 | * pretending that a[b - 1] is minus infinity and a[b + n] is infinity. 542 | * In other words, key belongs at index b + k; or in other words, 543 | * the first k elements of a should precede key, and the last n - k 544 | * should follow it. 545 | */ 546 | private static int gallopLeft(${type} key, ${type}[] a, int base, int len, int hint, 547 | ${Cmp} c) { 548 | assert len > 0 && hint >= 0 && hint < len; 549 | int lastOfs = 0; 550 | int ofs = 1; 551 | if (c.compare(key, a[base + hint]) > 0) { 552 | // Gallop right until a[base+hint+lastOfs] < key <= a[base+hint+ofs] 553 | int maxOfs = len - hint; 554 | while (ofs < maxOfs && c.compare(key, a[base + hint + ofs]) > 0) { 555 | lastOfs = ofs; 556 | ofs = (ofs << 1) + 1; 557 | if (ofs <= 0) // int overflow 558 | ofs = maxOfs; 559 | } 560 | if (ofs > maxOfs) 561 | ofs = maxOfs; 562 | 563 | // Make offsets relative to base 564 | lastOfs += hint; 565 | ofs += hint; 566 | } else { // key <= a[base + hint] 567 | // Gallop left until a[base+hint-ofs] < key <= a[base+hint-lastOfs] 568 | final int maxOfs = hint + 1; 569 | while (ofs < maxOfs && c.compare(key, a[base + hint - ofs]) <= 0) { 570 | lastOfs = ofs; 571 | ofs = (ofs << 1) + 1; 572 | if (ofs <= 0) // int overflow 573 | ofs = maxOfs; 574 | } 575 | if (ofs > maxOfs) 576 | ofs = maxOfs; 577 | 578 | // Make offsets relative to base 579 | int tmp = lastOfs; 580 | lastOfs = hint - ofs; 581 | ofs = hint - tmp; 582 | } 583 | assert -1 <= lastOfs && lastOfs < ofs && ofs <= len; 584 | 585 | /* 586 | * Now a[base+lastOfs] < key <= a[base+ofs], so key belongs somewhere 587 | * to the right of lastOfs but no farther right than ofs. Do a binary 588 | * search, with invariant a[base + lastOfs - 1] < key <= a[base + ofs]. 589 | */ 590 | lastOfs++; 591 | while (lastOfs < ofs) { 592 | int m = lastOfs + ((ofs - lastOfs) >>> 1); 593 | 594 | if (c.compare(key, a[base + m]) > 0) 595 | lastOfs = m + 1; // a[base + m] < key 596 | else 597 | ofs = m; // key <= a[base + m] 598 | } 599 | assert lastOfs == ofs; // so a[base + ofs - 1] < key <= a[base + ofs] 600 | return ofs; 601 | } 602 | 603 | /** 604 | * Like gallopLeft, except that if the range contains an element equal to 605 | * key, gallopRight returns the index after the rightmost equal element. 606 | * 607 | * @param key the key whose insertion point to search for 608 | * @param a the array in which to search 609 | * @param base the index of the first element in the range 610 | * @param len the length of the range; must be > 0 611 | * @param hint the index at which to begin the search, 0 <= hint < n. 612 | * The closer hint is to the result, the faster this method will run. 613 | * @param c the comparator used to order the range, and to search 614 | * @return the int k, 0 <= k <= n such that a[b + k - 1] <= key < a[b + k] 615 | */ 616 | private static int gallopRight(${type} key, ${type}[] a, int base, int len, 617 | int hint, ${Cmp} c) { 618 | assert len > 0 && hint >= 0 && hint < len; 619 | 620 | int ofs = 1; 621 | int lastOfs = 0; 622 | if (c.compare(key, a[base + hint]) < 0) { 623 | // Gallop left until a[b+hint - ofs] <= key < a[b+hint - lastOfs] 624 | int maxOfs = hint + 1; 625 | while (ofs < maxOfs && c.compare(key, a[base + hint - ofs]) < 0) { 626 | lastOfs = ofs; 627 | ofs = (ofs << 1) + 1; 628 | if (ofs <= 0) // int overflow 629 | ofs = maxOfs; 630 | } 631 | if (ofs > maxOfs) 632 | ofs = maxOfs; 633 | 634 | // Make offsets relative to b 635 | int tmp = lastOfs; 636 | lastOfs = hint - ofs; 637 | ofs = hint - tmp; 638 | } else { // a[b + hint] <= key 639 | // Gallop right until a[b+hint + lastOfs] <= key < a[b+hint + ofs] 640 | int maxOfs = len - hint; 641 | while (ofs < maxOfs && c.compare(key, a[base + hint + ofs]) >= 0) { 642 | lastOfs = ofs; 643 | ofs = (ofs << 1) + 1; 644 | if (ofs <= 0) // int overflow 645 | ofs = maxOfs; 646 | } 647 | if (ofs > maxOfs) 648 | ofs = maxOfs; 649 | 650 | // Make offsets relative to b 651 | lastOfs += hint; 652 | ofs += hint; 653 | } 654 | assert -1 <= lastOfs && lastOfs < ofs && ofs <= len; 655 | 656 | /* 657 | * Now a[b + lastOfs] <= key < a[b + ofs], so key belongs somewhere to 658 | * the right of lastOfs but no farther right than ofs. Do a binary 659 | * search, with invariant a[b + lastOfs - 1] <= key < a[b + ofs]. 660 | */ 661 | lastOfs++; 662 | while (lastOfs < ofs) { 663 | int m = lastOfs + ((ofs - lastOfs) >>> 1); 664 | 665 | if (c.compare(key, a[base + m]) < 0) 666 | ofs = m; // key < a[b + m] 667 | else 668 | lastOfs = m + 1; // a[b + m] <= key 669 | } 670 | assert lastOfs == ofs; // so a[b + ofs - 1] <= key < a[b + ofs] 671 | return ofs; 672 | } 673 | 674 | /** 675 | * Merges two adjacent runs in place, in a stable fashion. The first 676 | * element of the first run must be greater than the first element of the 677 | * second run (a[base1] > a[base2]), and the last element of the first run 678 | * (a[base1 + len1-1]) must be greater than all elements of the second run. 679 | * 680 | * For performance, this method should be called only when len1 <= len2; 681 | * its twin, mergeHi should be called if len1 >= len2. (Either method 682 | * may be called if len1 == len2.) 683 | * 684 | * @param base1 index of first element in first run to be merged 685 | * @param len1 length of first run to be merged (must be > 0) 686 | * @param base2 index of first element in second run to be merged 687 | * (must be aBase + aLen) 688 | * @param len2 length of second run to be merged (must be > 0) 689 | */ 690 | private void mergeLo(int base1, int len1, int base2, int len2) { 691 | assert len1 > 0 && len2 > 0 && base1 + len1 == base2; 692 | 693 | // Copy first run into temp array 694 | ${type}[] a = this.a; // For performance 695 | ${type}[] tmp = ensureCapacity(len1); 696 | int cursor1 = tmpBase; // Indexes into tmp array 697 | int cursor2 = base2; // Indexes int a 698 | int dest = base1; // Indexes int a 699 | System.arraycopy(a, base1, tmp, cursor1, len1); 700 | 701 | // Move first element of second run and deal with degenerate cases 702 | a[dest++] = a[cursor2++]; 703 | if (--len2 == 0) { 704 | System.arraycopy(tmp, cursor1, a, dest, len1); 705 | return; 706 | } 707 | if (len1 == 1) { 708 | System.arraycopy(a, cursor2, a, dest, len2); 709 | a[dest + len2] = tmp[cursor1]; // Last elt of run 1 to end of merge 710 | return; 711 | } 712 | 713 | ${Cmp} c = this.c; // Use local variable for performance 714 | int minGallop = this.minGallop; // " " " " " 715 | outer: 716 | while (true) { 717 | int count1 = 0; // Number of times in a row that first run won 718 | int count2 = 0; // Number of times in a row that second run won 719 | 720 | /* 721 | * Do the straightforward thing until (if ever) one run starts 722 | * winning consistently. 723 | */ 724 | do { 725 | assert len1 > 1 && len2 > 0; 726 | if (c.compare(a[cursor2], tmp[cursor1]) < 0) { 727 | a[dest++] = a[cursor2++]; 728 | count2++; 729 | count1 = 0; 730 | if (--len2 == 0) 731 | break outer; 732 | } else { 733 | a[dest++] = tmp[cursor1++]; 734 | count1++; 735 | count2 = 0; 736 | if (--len1 == 1) 737 | break outer; 738 | } 739 | } while ((count1 | count2) < minGallop); 740 | 741 | /* 742 | * One run is winning so consistently that galloping may be a 743 | * huge win. So try that, and continue galloping until (if ever) 744 | * neither run appears to be winning consistently anymore. 745 | */ 746 | do { 747 | assert len1 > 1 && len2 > 0; 748 | count1 = gallopRight(a[cursor2], tmp, cursor1, len1, 0, c); 749 | if (count1 != 0) { 750 | System.arraycopy(tmp, cursor1, a, dest, count1); 751 | dest += count1; 752 | cursor1 += count1; 753 | len1 -= count1; 754 | if (len1 <= 1) // len1 == 1 || len1 == 0 755 | break outer; 756 | } 757 | a[dest++] = a[cursor2++]; 758 | if (--len2 == 0) 759 | break outer; 760 | 761 | count2 = gallopLeft(tmp[cursor1], a, cursor2, len2, 0, c); 762 | if (count2 != 0) { 763 | System.arraycopy(a, cursor2, a, dest, count2); 764 | dest += count2; 765 | cursor2 += count2; 766 | len2 -= count2; 767 | if (len2 == 0) 768 | break outer; 769 | } 770 | a[dest++] = tmp[cursor1++]; 771 | if (--len1 == 1) 772 | break outer; 773 | minGallop--; 774 | } while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP); 775 | if (minGallop < 0) 776 | minGallop = 0; 777 | minGallop += 2; // Penalize for leaving gallop mode 778 | } // End of "outer" loop 779 | this.minGallop = minGallop < 1 ? 1 : minGallop; // Write back to field 780 | 781 | if (len1 == 1) { 782 | assert len2 > 0; 783 | System.arraycopy(a, cursor2, a, dest, len2); 784 | a[dest + len2] = tmp[cursor1]; // Last elt of run 1 to end of merge 785 | } else if (len1 == 0) { 786 | throw new IllegalArgumentException( 787 | "Comparison method violates its general contract!"); 788 | } else { 789 | assert len2 == 0; 790 | assert len1 > 1; 791 | System.arraycopy(tmp, cursor1, a, dest, len1); 792 | } 793 | } 794 | 795 | /** 796 | * Like mergeLo, except that this method should be called only if 797 | * len1 >= len2; mergeLo should be called if len1 <= len2. (Either method 798 | * may be called if len1 == len2.) 799 | * 800 | * @param base1 index of first element in first run to be merged 801 | * @param len1 length of first run to be merged (must be > 0) 802 | * @param base2 index of first element in second run to be merged 803 | * (must be aBase + aLen) 804 | * @param len2 length of second run to be merged (must be > 0) 805 | */ 806 | private void mergeHi(int base1, int len1, int base2, int len2) { 807 | assert len1 > 0 && len2 > 0 && base1 + len1 == base2; 808 | 809 | // Copy second run into temp array 810 | ${type}[] a = this.a; // For performance 811 | ${type}[] tmp = ensureCapacity(len2); 812 | int tmpBase = this.tmpBase; 813 | System.arraycopy(a, base2, tmp, tmpBase, len2); 814 | 815 | int cursor1 = base1 + len1 - 1; // Indexes into a 816 | int cursor2 = tmpBase + len2 - 1; // Indexes into tmp array 817 | int dest = base2 + len2 - 1; // Indexes into a 818 | 819 | // Move last element of first run and deal with degenerate cases 820 | a[dest--] = a[cursor1--]; 821 | if (--len1 == 0) { 822 | System.arraycopy(tmp, tmpBase, a, dest - (len2 - 1), len2); 823 | return; 824 | } 825 | if (len2 == 1) { 826 | dest -= len1; 827 | cursor1 -= len1; 828 | System.arraycopy(a, cursor1 + 1, a, dest + 1, len1); 829 | a[dest] = tmp[cursor2]; 830 | return; 831 | } 832 | 833 | ${Cmp} c = this.c; // Use local variable for performance 834 | int minGallop = this.minGallop; // " " " " " 835 | outer: 836 | while (true) { 837 | int count1 = 0; // Number of times in a row that first run won 838 | int count2 = 0; // Number of times in a row that second run won 839 | 840 | /* 841 | * Do the straightforward thing until (if ever) one run 842 | * appears to win consistently. 843 | */ 844 | do { 845 | assert len1 > 0 && len2 > 1; 846 | if (c.compare(tmp[cursor2], a[cursor1]) < 0) { 847 | a[dest--] = a[cursor1--]; 848 | count1++; 849 | count2 = 0; 850 | if (--len1 == 0) 851 | break outer; 852 | } else { 853 | a[dest--] = tmp[cursor2--]; 854 | count2++; 855 | count1 = 0; 856 | if (--len2 == 1) 857 | break outer; 858 | } 859 | } while ((count1 | count2) < minGallop); 860 | 861 | /* 862 | * One run is winning so consistently that galloping may be a 863 | * huge win. So try that, and continue galloping until (if ever) 864 | * neither run appears to be winning consistently anymore. 865 | */ 866 | do { 867 | assert len1 > 0 && len2 > 1; 868 | count1 = len1 - gallopRight(tmp[cursor2], a, base1, len1, len1 - 1, c); 869 | if (count1 != 0) { 870 | dest -= count1; 871 | cursor1 -= count1; 872 | len1 -= count1; 873 | System.arraycopy(a, cursor1 + 1, a, dest + 1, count1); 874 | if (len1 == 0) 875 | break outer; 876 | } 877 | a[dest--] = tmp[cursor2--]; 878 | if (--len2 == 1) 879 | break outer; 880 | 881 | count2 = len2 - gallopLeft(a[cursor1], tmp, tmpBase, len2, len2 - 1, c); 882 | if (count2 != 0) { 883 | dest -= count2; 884 | cursor2 -= count2; 885 | len2 -= count2; 886 | System.arraycopy(tmp, cursor2 + 1, a, dest + 1, count2); 887 | if (len2 <= 1) // len2 == 1 || len2 == 0 888 | break outer; 889 | } 890 | a[dest--] = a[cursor1--]; 891 | if (--len1 == 0) 892 | break outer; 893 | minGallop--; 894 | } while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP); 895 | if (minGallop < 0) 896 | minGallop = 0; 897 | minGallop += 2; // Penalize for leaving gallop mode 898 | } // End of "outer" loop 899 | this.minGallop = minGallop < 1 ? 1 : minGallop; // Write back to field 900 | 901 | if (len2 == 1) { 902 | assert len1 > 0; 903 | dest -= len1; 904 | cursor1 -= len1; 905 | System.arraycopy(a, cursor1 + 1, a, dest + 1, len1); 906 | a[dest] = tmp[cursor2]; // Move first elt of run2 to front of merge 907 | } else if (len2 == 0) { 908 | throw new IllegalArgumentException( 909 | "Comparison method violates its general contract!"); 910 | } else { 911 | assert len1 == 0; 912 | assert len2 > 0; 913 | System.arraycopy(tmp, tmpBase, a, dest - (len2 - 1), len2); 914 | } 915 | } 916 | 917 | /** 918 | * Ensures that the external array tmp has at least the specified 919 | * number of elements, increasing its size if necessary. The size 920 | * increases exponentially to ensure amortized linear time complexity. 921 | * 922 | * @param minCapacity the minimum required capacity of the tmp array 923 | * @return tmp, whether or not it grew 924 | */ 925 | private ${type}[] ensureCapacity(int minCapacity) { 926 | if (tmpLen < minCapacity) { 927 | // Compute smallest power of 2 > minCapacity 928 | int newSize = minCapacity; 929 | newSize |= newSize >> 1; 930 | newSize |= newSize >> 2; 931 | newSize |= newSize >> 4; 932 | newSize |= newSize >> 8; 933 | newSize |= newSize >> 16; 934 | newSize++; 935 | 936 | if (newSize < 0) // Not bloody likely! 937 | newSize = minCapacity; 938 | else 939 | newSize = Math.min(newSize, a.length >>> 1); 940 | 941 | ${type}[] newArray = new ${type}[newSize]; 942 | tmp = newArray; 943 | tmpLen = newSize; 944 | tmpBase = 0; 945 | } 946 | return tmp; 947 | } 948 | } 949 | 950 | 951 | -------------------------------------------------------------------------------- /src/main/fmpp/templates/net/mintern/primitive/comparators/package-info.java.ft: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Brandon Mintern . 3 | * License: GPLv2+CE 4 | */ 5 | 6 | /** 7 | * Provides comparators for primitive values. 8 | */ 9 | package ${PACKAGE}.comparators; 10 | -------------------------------------------------------------------------------- /src/test/java/net/mintern/primitive/PrimitiveTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Brandon Mintern . 3 | * DPQS9_REGRESSION Copyright 2016 Everlaw . 4 | * Binary search Copyright 2016 Dmitry Cherniachenko . 5 | * License: GPLv2+CE 6 | */ 7 | package net.mintern.primitive; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | import static org.junit.jupiter.api.Assertions.assertFalse; 12 | import static org.junit.jupiter.api.Assertions.assertTrue; 13 | 14 | import java.util.Arrays; 15 | import java.util.Random; 16 | 17 | import net.mintern.primitive.comparators.ByteComparator; 18 | import net.mintern.primitive.comparators.CharComparator; 19 | import net.mintern.primitive.comparators.DoubleComparator; 20 | import net.mintern.primitive.comparators.FloatComparator; 21 | import net.mintern.primitive.comparators.IntComparator; 22 | import net.mintern.primitive.comparators.LongComparator; 23 | import net.mintern.primitive.comparators.ShortComparator; 24 | 25 | import org.junit.jupiter.api.Test; 26 | 27 | public class PrimitiveTest { 28 | 29 | final Random rng = new Random(1234567890); 30 | 31 | final int[] ARRAY_SIZES_TO_TEST = { 0, 1, 2, 4, 8, 20, 100, 300, 5000, 50000 }; 32 | 33 | final int REPEAT_WITH_RANDOMNESS = 5; 34 | 35 | final String[] names = { 36 | "empty", 37 | "short", 38 | "short reversed", 39 | "all byte values", 40 | "all byte values reversed", 41 | "999 0s", 42 | "999 random values", 43 | "one item" 44 | }; 45 | 46 | final byte[][] bytes = { 47 | {}, 48 | { -128, -100, -78, -55, -24, -1, 3, 6, 25, 84, 97, 99, 100, 127 }, 49 | { 127, 100, 99, 97, 84, 25, 6, 3, -1, -24, -55, -78, -100, -128 }, 50 | new byte[256], 51 | new byte[256], 52 | new byte[999], 53 | new byte[999], 54 | { 42 } 55 | }; 56 | 57 | { 58 | // bytes[3]: all byte values 59 | byte b = Byte.MIN_VALUE; 60 | for (int i = 0; i < bytes[3].length; i++) { 61 | bytes[3][i] = b++; 62 | } 63 | // bytes[4]: all byte values reversed 64 | for (int i = 0; i < bytes[3].length; i++) { 65 | bytes[4][i] = bytes[3][256 - i - 1]; 66 | } 67 | // bytes[5]: all 0s 68 | Arrays.fill(bytes[5], (byte) 0); 69 | // bytes[6]: random values 70 | rng.nextBytes(bytes[6]); 71 | } 72 | 73 | @Test 74 | public void testBooleanSort() { 75 | boolean[] a = new boolean[999]; 76 | int falseCount = 0; 77 | for (int i = 0; i < 999; i++) { 78 | if (!(a[i] = bytes[6][i] % 2 == 0)) { 79 | falseCount++; 80 | } 81 | } 82 | Primitive.sort(a); 83 | for (int i = 0; i < falseCount; i++) { 84 | assertFalse(a[i]); 85 | } 86 | for (int i = falseCount; i < 999; i++) { 87 | assertTrue(a[i]); 88 | } 89 | } 90 | 91 | @Test 92 | public void testBytesNoBoundsNullComparator() { 93 | for (int i = 0; i < bytes.length; i++) { 94 | for (boolean stable: new boolean[]{ false, true }) { 95 | String name = names[i] + (stable ? ", stable" : ""); 96 | byte[] a = Arrays.copyOf(bytes[i], bytes[i].length); 97 | Primitive.sort(a, null, stable); 98 | for (int j = 1; j < a.length; j++) { 99 | assertTrue(a[j - 1] <= a[j], name); 100 | } 101 | } 102 | } 103 | } 104 | 105 | @Test 106 | public void testBytesNoBoundsEverythingEqualComparator() { 107 | for (int i = 0; i < bytes.length; i++) { 108 | String name = names[i]; 109 | byte[] a = Arrays.copyOf(bytes[i], bytes[i].length); 110 | Primitive.sort(a, new ByteComparator() { 111 | @Override 112 | public int compare(byte b1, byte b2) { 113 | return 0; 114 | } 115 | }); 116 | assertArrayEquals(bytes[i], a, name); 117 | } 118 | } 119 | 120 | @Test 121 | public void testBytesNoBoundsNaturalComparator() { 122 | for (int i = 0; i < bytes.length; i++) { 123 | for (boolean stable: new boolean[]{ false, true }) { 124 | String name = names[i] + (stable ? ", stable" : ""); 125 | byte[] a = Arrays.copyOf(bytes[i], bytes[i].length); 126 | Primitive.sort(a, new ByteComparator() { 127 | @Override 128 | public int compare(byte b1, byte b2) { 129 | return b1 < b2 ? -1 : b1 == b2 ? 0 : 1; 130 | } 131 | }); 132 | for (int j = 1; j < a.length; j++) { 133 | assertTrue(a[j - 1] <= a[j], name); 134 | } 135 | } 136 | } 137 | } 138 | 139 | @Test 140 | public void testSortCharArray() { 141 | for (int r = 0; r < REPEAT_WITH_RANDOMNESS; r++) { 142 | for (int size : ARRAY_SIZES_TO_TEST) { 143 | char[] array = new char[size]; 144 | for (int i = 0; i < array.length; i++) { 145 | array[i] = (char) rng.nextInt(Character.MAX_CODE_POINT); 146 | } 147 | for (boolean stable: new boolean[]{ false, true }) { 148 | char[] actual = array.clone(); 149 | Primitive.sort(actual, new ReverseCharComparator(), stable); 150 | 151 | char[] expected = array.clone(); 152 | Arrays.sort(expected); 153 | reverse(expected); 154 | 155 | assertArrayEquals(actual, expected); 156 | } 157 | } 158 | } 159 | } 160 | 161 | @Test 162 | public void testSortShortArray() { 163 | for (int r = 0; r < REPEAT_WITH_RANDOMNESS; r++) { 164 | for (int size : ARRAY_SIZES_TO_TEST) { 165 | short[] array = new short[size]; 166 | for (int i = 0; i < array.length; i++) { 167 | array[i] = (short) rng.nextInt(Short.MAX_VALUE); 168 | if (rng.nextBoolean()) { 169 | array[i] *= -1; 170 | } 171 | } 172 | for (boolean stable: new boolean[]{ false, true }) { 173 | short[] actual = array.clone(); 174 | Primitive.sort(actual, new ReverseShortComparator(), stable); 175 | 176 | short[] expected = array.clone(); 177 | Arrays.sort(expected); 178 | reverse(expected); 179 | 180 | assertArrayEquals(actual, expected); 181 | } 182 | } 183 | } 184 | } 185 | 186 | @Test 187 | public void testSortIntArray() { 188 | for (int r = 0; r < REPEAT_WITH_RANDOMNESS; r++) { 189 | for (int size : ARRAY_SIZES_TO_TEST) { 190 | int[] array = new int[size]; 191 | for (int i = 0; i < array.length; i++) { 192 | array[i] = rng.nextInt(); 193 | if (rng.nextBoolean()) { 194 | array[i] *= -1; 195 | } 196 | } 197 | for (boolean stable: new boolean[]{ false, true }) { 198 | int[] actual = array.clone(); 199 | Primitive.sort(actual, new ReverseIntComparator(), stable); 200 | 201 | int[] expected = array.clone(); 202 | Arrays.sort(expected); 203 | reverse(expected); 204 | 205 | assertArrayEquals(actual, expected); 206 | } 207 | } 208 | } 209 | } 210 | 211 | @Test 212 | public void testSortLongArray() { 213 | for (int r = 0; r < REPEAT_WITH_RANDOMNESS; r++) { 214 | for (int size : ARRAY_SIZES_TO_TEST) { 215 | long[] array = new long[size]; 216 | for (int i = 0; i < array.length; i++) { 217 | array[i] = rng.nextLong(); 218 | if (rng.nextBoolean()) { 219 | array[i] *= -1; 220 | } 221 | } 222 | for (boolean stable: new boolean[]{ false, true }) { 223 | long[] actual = array.clone(); 224 | Primitive.sort(actual, new ReverseLongComparator(), stable); 225 | 226 | long[] expected = array.clone(); 227 | Arrays.sort(expected); 228 | reverse(expected); 229 | 230 | assertArrayEquals(actual, expected); 231 | } 232 | } 233 | } 234 | } 235 | 236 | @Test 237 | public void testSortFloatArray() { 238 | for (int r = 0; r < REPEAT_WITH_RANDOMNESS; r++) { 239 | for (int size : ARRAY_SIZES_TO_TEST) { 240 | float[] array = new float[size]; 241 | for (int i = 0; i < array.length; i++) { 242 | array[i] = rng.nextFloat(); 243 | } 244 | for (boolean stable: new boolean[]{ false, true }) { 245 | float[] actual = array.clone(); 246 | Primitive.sort(actual, new ReverseFloatComparator(), stable); 247 | 248 | float[] expected = array.clone(); 249 | Arrays.sort(expected); 250 | reverse(expected); 251 | 252 | assertArrayEquals(actual, expected, 0.0f); 253 | } 254 | } 255 | } 256 | } 257 | 258 | @Test 259 | public void testSortDoubleArray() { 260 | for (int r = 0; r < REPEAT_WITH_RANDOMNESS; r++) { 261 | for (int size : ARRAY_SIZES_TO_TEST) { 262 | double[] array = new double[size]; 263 | for (int i = 0; i < array.length; i++) { 264 | array[i] = rng.nextDouble(); 265 | } 266 | for (boolean stable: new boolean[]{ false, true }) { 267 | double[] actual = array.clone(); 268 | Primitive.sort(actual, new ReverseDoubleComparator(), stable); 269 | 270 | double[] expected = array.clone(); 271 | Arrays.sort(expected); 272 | reverse(expected); 273 | 274 | assertTrue(Arrays.equals(actual, expected)); 275 | } 276 | } 277 | } 278 | } 279 | 280 | /** 281 | * This data Copyright 2016, Everlaw, licensed under GPLv2+CE. All rights 282 | * reserved. Licensed per agreement by CEO Ajeet Shankar in a 284 | * GitHub Issue. 285 | */ 286 | final int[] DPQS9_REGRESSION = { 287 | 999,267,269,356,443,999,100,101,102,103,104,105,106,107,108,109, 288 | 110,111,999,114,115,116,117,118,119,120,121,122,123,124,125,126, 289 | 127,128,130,131,132,129,133,134,135,136,137,138,139,140,141,142, 290 | 143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158, 291 | 159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174, 292 | 175,176,178,180,181,182,183,184,999,218,221,222,224,225,999,112, 293 | 113,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199, 294 | 200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,999, 295 | 227,229,231,233,235,237,244,245,246,249,263,265,999,223,999,228, 296 | 234,266,999,357,359,360,361,362,358,363,364,365,366,367,368,369, 297 | 370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385, 298 | 386,387,388,389,390,391,392,393,394,396,395,397,398,399,400,401, 299 | 402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417, 300 | 418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433, 301 | 434,436,435,437,438,439,440,441,442,999,217,230,232,264,999,268, 302 | 999,219,220,250,251,252,253,254,255,256,257,258,259,260,261,262, 303 | 999,179,999,236,999,216,999,226,238,239,240,241,242,999,270,271, 304 | 272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287, 305 | 288,289,291,290,292,293,294,295,296,297,298,299,300,301,303,302, 306 | 304,305,306,307,308,309,310,311,312,313,314,316,315,318,317,319, 307 | 320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,336, 308 | 335,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351, 309 | 352,353,354,355,177,999,243,999,444,445,446,447,448,449,450,452, 310 | 453,454,451,455,456,457,458,459,460,461,462,463,464,465,466,467, 311 | 468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483, 312 | 484,485,486,487,488,489,490,491,492,493,495,494,496,497,498,499, 313 | 500,501,502,999,215,999,248,247,999,999,999,999,999,999,999,999 314 | }; 315 | 316 | @Test 317 | public void testDualPivotQuicksort9Regression() { 318 | int[] copy = Arrays.copyOf(DPQS9_REGRESSION, DPQS9_REGRESSION.length); 319 | Primitive.sort(copy, new IntComparator() { 320 | @Override 321 | public int compare(int i1, int i2) { 322 | return i1 < i2 ? -1 : i1 == i2 ? 0 : 1; 323 | } 324 | }, false); 325 | int[] expected = Arrays.copyOf(DPQS9_REGRESSION, DPQS9_REGRESSION.length); 326 | Arrays.sort(expected); 327 | assertArrayEquals(expected, copy); 328 | } 329 | 330 | @Test 331 | public void testBinarySearchNaturalOrder() { 332 | int[] data = { 1, 2, 3, 4, 5 }; 333 | IntComparator naturalComparator = new IntComparator() { 334 | @Override 335 | public int compare(int i1, int i2) { 336 | return i1 < i2 ? -1 : i1 == i2 ? 0 : 1; 337 | } 338 | }; 339 | IntComparator naturalOrder = IntComparator.naturalOrder(); 340 | 341 | assertEquals(0, Primitive.binarySearch(data, 1, naturalComparator)); 342 | assertEquals(0, Primitive.binarySearch(data, 1, naturalOrder)); 343 | assertEquals(3, Primitive.binarySearch(data, 4, naturalComparator)); 344 | assertEquals(3, Primitive.binarySearch(data, 4, naturalOrder)); 345 | assertEquals(4, Primitive.binarySearch(data, 5, naturalComparator)); 346 | assertEquals(4, Primitive.binarySearch(data, 5, naturalOrder)); 347 | assertEquals(-1, Primitive.binarySearch(data, 0, naturalComparator)); 348 | assertEquals(-1, Primitive.binarySearch(data, 0, naturalOrder)); 349 | assertEquals(-6, Primitive.binarySearch(data, 6, naturalComparator)); 350 | assertEquals(-6, Primitive.binarySearch(data, 6, naturalOrder)); 351 | } 352 | 353 | @Test 354 | public void testBinarySearchReversedOrder() { 355 | int[] data = { 6, 5, 4, 3, 2, 1 }; 356 | IntComparator reverseComparator = new ReverseIntComparator(); 357 | IntComparator reverseOrder = IntComparator.reverseOrder(); 358 | 359 | assertEquals(5, Primitive.binarySearch(data, 1, reverseComparator)); 360 | assertEquals(5, Primitive.binarySearch(data, 1, reverseOrder)); 361 | assertEquals(2, Primitive.binarySearch(data, 4, reverseComparator)); 362 | assertEquals(2, Primitive.binarySearch(data, 4, reverseOrder)); 363 | assertEquals(0, Primitive.binarySearch(data, 6, reverseComparator)); 364 | assertEquals(0, Primitive.binarySearch(data, 6, reverseOrder)); 365 | assertEquals(-7, Primitive.binarySearch(data, 0, reverseComparator)); 366 | assertEquals(-7, Primitive.binarySearch(data, 0, reverseOrder)); 367 | assertEquals(-1, Primitive.binarySearch(data, 7, reverseComparator)); 368 | assertEquals(-1, Primitive.binarySearch(data, 7, reverseOrder)); 369 | } 370 | 371 | private static final class ReverseShortComparator implements ShortComparator { 372 | @Override 373 | public final int compare(short d1, short d2) { 374 | return d1 < d2 ? 1 : d1 == d2 ? 0 : -1; 375 | } 376 | } 377 | 378 | private static final class ReverseCharComparator implements CharComparator { 379 | @Override 380 | public final int compare(char d1, char d2) { 381 | return d1 < d2 ? 1 : d1 == d2 ? 0 : -1; 382 | } 383 | } 384 | 385 | private static final class ReverseIntComparator implements IntComparator { 386 | @Override 387 | public final int compare(int d1, int d2) { 388 | return d1 < d2 ? 1 : d1 == d2 ? 0 : -1; 389 | } 390 | } 391 | 392 | private static final class ReverseLongComparator implements LongComparator { 393 | @Override 394 | public final int compare(long d1, long d2) { 395 | return d1 < d2 ? 1 : d1 == d2 ? 0 : -1; 396 | } 397 | } 398 | 399 | private static final class ReverseFloatComparator implements FloatComparator { 400 | 401 | @Override 402 | public final int compare(float d1, float d2) { 403 | return Float.compare(d2, d1); 404 | } 405 | 406 | } 407 | 408 | private static final class ReverseDoubleComparator implements DoubleComparator { 409 | 410 | @Override 411 | public final int compare(double d1, double d2) { 412 | return Double.compare(d2, d1); 413 | } 414 | 415 | } 416 | 417 | private void reverse(int[] array) { 418 | for (int i = 0; i < array.length / 2; i++) { 419 | // swap the elements 420 | int temp = array[i]; 421 | array[i] = array[array.length - (i + 1)]; 422 | array[array.length - (i + 1)] = temp; 423 | } 424 | } 425 | 426 | private void reverse(char[] array) { 427 | for (int i = 0; i < array.length / 2; i++) { 428 | // swap the elements 429 | char temp = array[i]; 430 | array[i] = array[array.length - (i + 1)]; 431 | array[array.length - (i + 1)] = temp; 432 | } 433 | } 434 | 435 | private void reverse(short[] array) { 436 | for (int i = 0; i < array.length / 2; i++) { 437 | // swap the elements 438 | short temp = array[i]; 439 | array[i] = array[array.length - (i + 1)]; 440 | array[array.length - (i + 1)] = temp; 441 | } 442 | } 443 | 444 | private void reverse(long[] array) { 445 | for (int i = 0; i < array.length / 2; i++) { 446 | // swap the elements 447 | long temp = array[i]; 448 | array[i] = array[array.length - (i + 1)]; 449 | array[array.length - (i + 1)] = temp; 450 | } 451 | } 452 | 453 | private void reverse(float[] array) { 454 | for (int i = 0; i < array.length / 2; i++) { 455 | // swap the elements 456 | float temp = array[i]; 457 | array[i] = array[array.length - (i + 1)]; 458 | array[array.length - (i + 1)] = temp; 459 | } 460 | } 461 | 462 | private void reverse(double[] array) { 463 | for (int i = 0; i < array.length / 2; i++) { 464 | // swap the elements 465 | double temp = array[i]; 466 | array[i] = array[array.length - (i + 1)]; 467 | array[array.length - (i + 1)] = temp; 468 | } 469 | } 470 | } 471 | --------------------------------------------------------------------------------