├── .classpath
├── .gitignore
├── .project
├── .settings
└── org.eclipse.jdt.core.prefs
├── src
└── javagrailsort
│ ├── SortType.java
│ ├── Tester.java
│ └── GrailSort.java
├── LICENSE
└── README.md
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 |
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Grail Sort for Java
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
5 | org.eclipse.jdt.core.compiler.compliance=1.8
6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
11 | org.eclipse.jdt.core.compiler.source=1.8
12 |
--------------------------------------------------------------------------------
/src/javagrailsort/SortType.java:
--------------------------------------------------------------------------------
1 | package javagrailsort;
2 |
3 | import java.util.Comparator;
4 |
5 | class SortType {
6 | public int key;
7 | public int value;
8 |
9 | public SortType() {
10 | this.key = 0;
11 | this.value = 0;
12 | }
13 |
14 | public SortType(int key, int value) {
15 | this.key = key;
16 | this.value = value;
17 | }
18 | }
19 |
20 | class SortComparator implements Comparator {
21 | @Override
22 | public int compare(SortType a, SortType b) {
23 | if (a.key < b.key) return -1;
24 | else if (a.key > b.key) return 1;
25 | else return 0;
26 | }
27 | }
28 |
29 | class GrailState {
30 | private int leftOverLen;
31 | private int leftOverFrag;
32 |
33 | public GrailState(int len, int frag) {
34 | this.leftOverLen = len;
35 | this.leftOverFrag = frag;
36 | }
37 |
38 | public int getLeftOverLen() {
39 | return leftOverLen;
40 | }
41 |
42 | public int getLeftOverFrag() {
43 | return leftOverFrag;
44 | }
45 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Andrey Astrelin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Grail-Sorting-for-Java
2 | Refactoring of Grail Sort by Andrey Astrelin (https://github.com/Mrrl/GrailSort/blob/master/GrailSort.h) from C to Java.
3 |
4 | GrailSort is a variant of Block Merge Sort (https://en.wikipedia.org/wiki/Block_sort), a stable, in-place, worst-case O(n log n) implementation of merge sort. The algorithm is adaptive, but not to the degree of TimSort. It is similar to Mike McFadden's WikiSort (https://github.com/BonzaiThePenguin/WikiSort), yet mainly differs by swapping blocks and their tags in parallel before merging, and shifting the position of an internal buffer used for locally merging/appending portions of an array.
5 |
6 | Like WikiSort, extra memory can be allocated to an external buffer, potentially bypassing the need for an internal buffer and giving GrailSort a slight boost in speed. This implementation includes three options: 1) sorting without an external buffer -- O(1) space complexity, 2) sorting with a static buffer of 512 items -- O(512) space complexity, and 3) sorting with a dynamic buffer scaled to the square root of the input array's length -- O(sqrt(n)) space complexity.
7 |
8 | EDIT: The results for GrailSort's runtime have been removed. I just discovered they were heavily skewed because the random number generator was not working properly. Should have compared Mr. Astrelin's results to mine, anyways. Oh dear. Will update this when I get the chance.
9 |
10 | EDIT2: GrailSort's Insertion Sort is now a Binary Insertion Sort with a few tricks up its sleeves, making it asymptotically optimal. Because of that, I boosted the "small array" cutoff up to 32.
11 |
12 | EDIT3: The previous trick with recursion ended up changing the space complexity (thanks for pointing this out, Morwenn!), and that was not
13 | my goal. grailFindKeys is once again a bottleneck for this algorithm. Hopefully some day I'll figure out how to properly optimize it. For now, keeping the changes made to Insertion Sort as that is for sure optimal now.
14 |
--------------------------------------------------------------------------------
/src/javagrailsort/Tester.java:
--------------------------------------------------------------------------------
1 | package javagrailsort;
2 |
3 | import java.text.DecimalFormat;
4 | import java.text.DecimalFormatSymbols;
5 | import java.text.NumberFormat;
6 | import java.util.Arrays;
7 | import java.util.Locale;
8 |
9 | import javagrailsort.SortType;
10 | import javagrailsort.SortComparator;
11 |
12 | public class Tester {
13 | private DecimalFormat formatter;
14 | private DecimalFormatSymbols symbols;
15 |
16 | private SortComparator test;
17 |
18 | private int seed;
19 |
20 | private double newArrayFinish;
21 | private double generateArrayFinish;
22 | private double noBufferFinish;
23 | private double staticBufferFinish;
24 | private double dynamicBufferFinish;
25 |
26 | private double noBuffAverage;
27 | private double statAverage;
28 | private double dynAverage;
29 |
30 | public Tester() {
31 | this.seed = 100000001;
32 |
33 | this.test = new SortComparator();
34 |
35 | this.formatter = (DecimalFormat) NumberFormat.getInstance(Locale.US);
36 | this.symbols = this.formatter.getDecimalFormatSymbols();
37 |
38 | this.symbols.setGroupingSeparator(',');
39 | this.formatter.setDecimalFormatSymbols(this.symbols);
40 | }
41 |
42 | /******** Tests *********/
43 |
44 | private int randomNumber(int k){
45 | this.seed = (this.seed * 1234565) + 1;
46 | return ((this.seed & 0x7fffffff) * k) >>> 31;
47 | }
48 |
49 |
50 | private void generateArray(SortType[] arr, int[] keyCenter, int Len, int NKey) {
51 | for(int i = 0; i < NKey; i++) keyCenter[i] = 0;
52 |
53 | for(int i = 0; i < Len; i++) {
54 | if(NKey != 0) {
55 | int key = randomNumber(NKey);
56 | arr[i].key = key;
57 | arr[i].value = keyCenter[key]++;
58 | } else {
59 | arr[i].key = randomNumber(1000000000);
60 | arr[i].value = 0;
61 | }
62 | }
63 | }
64 |
65 | private boolean testArray(SortType[] arr, int Len) {
66 | for(int i = 1; i < Len; i++) {
67 | int dk = this.test.compare(arr[i - 1], arr[i]);
68 | if(dk > 0) return false;
69 | if(dk == 0 && arr[i - 1].value > arr[i].value) return false;
70 | }
71 | return true;
72 | }
73 |
74 | public static void main(String[] args) {
75 | GrailSort GrailSorter = new GrailSort();
76 | Tester GrailTest = new Tester();
77 |
78 | int NMax = 10000000;
79 | int NMaxKey = ((int) (2*Math.sqrt(NMax))) - 1;
80 |
81 | SortType[] arr = new SortType[NMax];
82 | long timeStart = System.nanoTime();
83 | for(int i = 0; i < arr.length; i++) {
84 | arr[i] = new SortType();
85 | }
86 | long timeFinish = System.nanoTime();
87 | System.out.println("Finished allocating memory for array");
88 | GrailTest.newArrayFinish = (timeFinish - timeStart) / 1e+6;
89 | int[] keys = new int[NMaxKey];
90 |
91 | int sortRuns = 20;
92 |
93 | for(int j = 0; j < sortRuns; j++) {
94 | timeStart = System.nanoTime();
95 | GrailTest.generateArray(arr, keys, NMax, NMaxKey);
96 | timeFinish = System.nanoTime();
97 | System.out.println("Finished generating array");
98 |
99 | GrailTest.generateArrayFinish = (timeFinish - timeStart) / 1e+6;
100 | SortType[] staticBufferArray = Arrays.copyOf(arr, arr.length);
101 | SortType[] dynamicBufferArray = Arrays.copyOf(arr, arr.length);
102 |
103 | timeStart = System.nanoTime();
104 | GrailSorter.grailSortWithoutBuffer(arr);
105 | timeFinish = System.nanoTime();
106 | System.out.println("Finished Grail Sort w/o buffer");
107 | GrailTest.noBufferFinish = (timeFinish - timeStart) / 1e+6;
108 |
109 | timeStart = System.nanoTime();
110 | GrailSorter.grailSortWithBuffer(staticBufferArray);
111 | timeFinish = System.nanoTime();
112 | System.out.println("Finished Grail Sort w/ static buffer");
113 | GrailTest.staticBufferFinish = (timeFinish - timeStart) / 1e+6;
114 |
115 | timeStart = System.nanoTime();
116 | GrailSorter.grailSortWithDynBuffer(dynamicBufferArray);
117 | System.out.println("Finished Grail Sort w/ dynamic buffer");
118 | timeFinish = System.nanoTime();
119 | GrailTest.dynamicBufferFinish = (timeFinish - timeStart) / 1e+6;
120 |
121 | System.out.println(" ");
122 | System.out.println("New array of length " + GrailTest.formatter.format(NMax) + " in " + GrailTest.formatter.format(GrailTest.newArrayFinish) + " milliseconds.");
123 | System.out.println("Generated array of length " + GrailTest.formatter.format(NMax) + " in " + GrailTest.formatter.format(GrailTest.generateArrayFinish) + " milliseconds.");
124 |
125 | if(!GrailTest.testArray(arr, arr.length)) {
126 | System.out.println("Grail Sort without buffers DID NOT sort successfully.");
127 | System.exit(1);
128 | }
129 | else System.out.println("Grail Sorting " + GrailTest.formatter.format(NMax) + " numbers without buffers sorted successfully in " + GrailTest.formatter.format(GrailTest.noBufferFinish) + " milliseconds.");
130 |
131 | if(!GrailTest.testArray(staticBufferArray, staticBufferArray.length)) {
132 | System.out.println("Grail Sort with static buffer DID NOT sort successfully.");
133 | System.exit(1);
134 | }
135 | else System.out.println("Grail Sorting " + GrailTest.formatter.format(NMax) + " numbers with static buffer sorted successfully in " + GrailTest.formatter.format(GrailTest.staticBufferFinish) + " milliseconds.");
136 |
137 | if(!GrailTest.testArray(dynamicBufferArray, dynamicBufferArray.length)) {
138 | System.out.println("Grail Sort with dynamic buffer DID NOT sort successfully.");
139 | System.exit(1);
140 | }
141 | else System.out.println("Grail Sorting " + GrailTest.formatter.format(NMax) + " numbers with dynamic buffer sorted successfully in " + GrailTest.formatter.format(GrailTest.dynamicBufferFinish) + " milliseconds.");
142 |
143 | GrailTest.noBuffAverage += GrailTest.noBufferFinish;
144 | GrailTest.statAverage += GrailTest.staticBufferFinish;
145 | GrailTest.dynAverage += GrailTest.dynamicBufferFinish;
146 |
147 | if(NMaxKey == ((int) (2*Math.sqrt(NMax))) - 1) {
148 | NMaxKey = ((int) (Math.sqrt(NMax))) - 1;
149 | }
150 | else {
151 | NMaxKey = ((int) (2*Math.sqrt(NMax))) - 1;
152 | }
153 |
154 | System.out.println(" ");
155 | System.out.println("Test " + (j + 1) + " complete.");
156 | System.out.println(" ");
157 | }
158 |
159 | System.out.println(" ");
160 | System.out.println("Average time in ms without buffer: " + GrailTest.noBuffAverage / sortRuns);
161 | System.out.println("Average time in ms with static buffer: " + GrailTest.statAverage / sortRuns);
162 | System.out.println("Average time in ms with dynamic buffer: " + GrailTest.dynAverage / sortRuns);
163 | }
164 | }
--------------------------------------------------------------------------------
/src/javagrailsort/GrailSort.java:
--------------------------------------------------------------------------------
1 | package javagrailsort;
2 |
3 | /********* Grail sorting *********************************/
4 | /* */
5 | /* (c) 2013 by Andrey Astrelin */
6 | /* Refactored by MusicTheorist */
7 | /* */
8 | /* Stable sorting that works in O(N*log(N)) worst time */
9 | /* and uses O(1) extra memory */
10 | /* */
11 | /* Define SortType / SortComparator */
12 | /* and then call GrailSort() function */
13 | /* */
14 | /* For sorting w/ fixed external buffer (512 items) */
15 | /* use GrailSortWithBuffer() */
16 | /* */
17 | /* For sorting w/ dynamic external buffer (sqrt(length)) */
18 | /* use GrailSortWithDynBuffer() */
19 | /* */
20 | /*********************************************************/
21 |
22 | final public class GrailSort {
23 |
24 | final private int grailStaticBufferLen = 512;
25 |
26 | private SortComparator grail;
27 |
28 | private static void grailSwap(SortType[] arr, int a, int b) {
29 | SortType temp = arr[a];
30 | arr[a] = arr[b];
31 | arr[b] = temp;
32 | }
33 |
34 | private static void grailMultiSwap(SortType[] arr, int a, int b, int swapsLeft) {
35 | while(swapsLeft != 0) {
36 | grailSwap(arr, a++, b++);
37 | swapsLeft--;
38 | }
39 | }
40 |
41 | private static void grailRotate(SortType[] array, int pos, int lenA, int lenB) {
42 | while(lenA != 0 && lenB != 0) {
43 | if(lenA <= lenB) {
44 | grailMultiSwap(array, pos, pos + lenA, lenA);
45 | pos += lenA;
46 | lenB -= lenA;
47 | }
48 | else {
49 | grailMultiSwap(array, pos + (lenA - lenB), pos + lenA, lenB);
50 | lenA -= lenB;
51 | }
52 | }
53 | }
54 |
55 | // Thanks to https://jeffreystedfast.blogspot.com/2007/02/binary-insertion-sort.html for
56 | // a great reference on InsertSort optimizations!!
57 |
58 | @SuppressWarnings("fallthrough")
59 | private void grailInsertSort(SortType[] arr, int pos, int len) {
60 | for(int i = 1; i < len; i++) {
61 | int insertPos = grailBinSearch(arr, pos, pos + i, pos + i, false);
62 |
63 | if(insertPos < i) {
64 | SortType item = arr[pos + i];
65 |
66 | // Used TimSort's Binary Insert as a reference here.
67 | int shifts = (pos + i) - insertPos;
68 | switch(shifts) {
69 | case 2: arr[insertPos + 2] = arr[insertPos + 1];
70 | case 1: arr[insertPos + 1] = arr[insertPos];
71 | break;
72 | default: System.arraycopy(arr, insertPos, arr, insertPos + 1, shifts);
73 | }
74 | arr[insertPos] = item;
75 | }
76 | }
77 | }
78 |
79 | //boolean argument determines direction
80 | private int grailBinSearch(SortType[] arr, int pos, int len, int keyPos, boolean isLeft) {
81 | int left = -1, right = len;
82 |
83 | while(left < right - 1) {
84 | int mid = left + ((right - left) / 2);
85 |
86 | if(isLeft) {
87 | if(this.grail.compare(arr[pos + mid], arr[keyPos]) >= 0) {
88 | right = mid;
89 | }
90 | else left = mid;
91 | }
92 | else {
93 | if(this.grail.compare(arr[pos + mid], arr[keyPos]) > 0) {
94 | right = mid;
95 | }
96 | else left = mid;
97 | }
98 | }
99 | return right;
100 | }
101 |
102 | // cost: 2 * len + numKeys^2 / 2
103 | private int grailGetKeys(SortType[] arr, int pos, int len, int numKeys) {
104 | int dist = 1;
105 | int foundKeys = 1, firstKey = 0; // first key is always here
106 |
107 | while(dist < len && foundKeys < numKeys) {
108 | //Binary Search left
109 | int loc = grailBinSearch(arr, pos + firstKey, foundKeys, pos + dist, true);
110 |
111 | if(loc == foundKeys || this.grail.compare(arr[pos + dist], arr[pos + (firstKey + loc)]) != 0) {
112 | grailRotate(arr, pos + firstKey, foundKeys, dist - (firstKey + foundKeys));
113 |
114 | firstKey = dist - foundKeys;
115 |
116 | grailRotate(arr, pos + (firstKey + loc), foundKeys - loc, 1);
117 | foundKeys++;
118 | }
119 | dist++;
120 | }
121 | grailRotate(arr, pos, firstKey, foundKeys);
122 |
123 | return foundKeys;
124 | }
125 |
126 | // cost: min(len1, len2)^2 + max(len1, len2)
127 | private void grailMergeWithoutBuffer(SortType[] arr, int pos, int len1, int len2) {
128 | if(len1 < len2) {
129 | while(len1 != 0) {
130 | //Binary Search left
131 | int loc = grailBinSearch(arr, pos + len1, len2, pos, true);
132 |
133 | if(loc != 0) {
134 | grailRotate(arr, pos, len1, loc);
135 |
136 | pos += loc;
137 | len2 -= loc;
138 | }
139 |
140 | if(len2 == 0) break;
141 |
142 | do {
143 | pos++;
144 | len1--;
145 | } while(len1 != 0 && this.grail.compare(arr[pos], arr[pos + len1]) <= 0);
146 | }
147 | }
148 | else {
149 | while(len2 != 0) {
150 | //Binary Search right
151 | int loc = grailBinSearch(arr, pos, len1, pos + (len1 + len2 - 1), false);
152 |
153 | if(loc != len1) {
154 | grailRotate(arr, pos + loc, len1 - loc, len2);
155 | len1 = loc;
156 | }
157 |
158 | if(len1 == 0) break;
159 |
160 | do {
161 | len2--;
162 | } while(len2 != 0 && this.grail.compare(arr[pos + len1 - 1], arr[pos + len1 + len2 - 1]) <= 0);
163 | }
164 | }
165 | }
166 |
167 | // arr - starting array. arr[0 - blockLen..-1] - buffer (if havebuf).
168 | // blockLen - length of regular blocks. First blockCount blocks are stable sorted by 1st elements and key-coded
169 | // keysPos - arrays of keys, in same order as blocks. keysPos < midkey means stream A
170 | // aBlockCount are regular blocks from stream A.
171 | // lastLen is length of last (irregular) block from stream B, that should go before aBlockCount blocks.
172 | // lastLen = 0 requires aBlockCount = 0 (no irregular blocks). lastLen > 0, aBlockCount = 0 is possible.
173 | private void grailMergeBuffersLeft(SortType[] arr, int keysPos, int midkey, int pos, int blockCount, int blockLen,
174 | boolean havebuf, int aBlockCount, int lastLen) {
175 |
176 | if(blockCount == 0) {
177 | int aBlocksLen = aBlockCount * blockLen;
178 |
179 | if(havebuf) grailMergeLeft(arr, pos, aBlocksLen, lastLen, 0 - blockLen);
180 | else grailMergeWithoutBuffer(arr, pos, aBlocksLen, lastLen);
181 |
182 | return;
183 | }
184 |
185 | int leftOverLen, processIndex;
186 | leftOverLen = processIndex = blockLen;
187 |
188 | int leftOverFrag = this.grail.compare(arr[keysPos], arr[midkey]) < 0 ? 0 : 1;
189 | int restToProcess;
190 |
191 | for(int keyIndex = 1; keyIndex < blockCount; keyIndex++, processIndex += blockLen) {
192 | restToProcess = processIndex - leftOverLen;
193 | int nextFrag = this.grail.compare(arr[keysPos + keyIndex], arr[midkey]) < 0 ? 0 : 1;
194 |
195 | if(nextFrag == leftOverFrag) {
196 | if(havebuf) grailMultiSwap(arr, pos + restToProcess - blockLen, pos + restToProcess, leftOverLen);
197 |
198 | restToProcess = processIndex;
199 | leftOverLen = blockLen;
200 | }
201 | else {
202 | if(havebuf) {
203 | GrailState results = grailSmartMergeWithBuffer(arr, pos + restToProcess, leftOverLen, leftOverFrag, blockLen);
204 |
205 | leftOverLen = results.getLeftOverLen();
206 | leftOverFrag = results.getLeftOverFrag();
207 | }
208 | else {
209 | GrailState results = grailSmartMergeWithoutBuffer(arr, pos + restToProcess, leftOverLen, leftOverFrag, blockLen);
210 |
211 | leftOverLen = results.getLeftOverLen();
212 | leftOverFrag = results.getLeftOverFrag();
213 | }
214 | }
215 | }
216 |
217 | restToProcess = processIndex - leftOverLen;
218 |
219 | if(lastLen != 0) {
220 | if(leftOverFrag != 0) {
221 | if(havebuf) grailMultiSwap(arr, pos + restToProcess - blockLen, pos + restToProcess, leftOverLen);
222 |
223 | restToProcess = processIndex;
224 | leftOverLen = blockLen * aBlockCount;
225 | leftOverFrag = 0;
226 | }
227 | else leftOverLen += blockLen * aBlockCount;
228 |
229 | if(havebuf) grailMergeLeft(arr, pos + restToProcess, leftOverLen, lastLen, 0 - blockLen);
230 | else grailMergeWithoutBuffer(arr, pos + restToProcess, leftOverLen, lastLen);
231 | }
232 | else {
233 | if(havebuf) grailMultiSwap(arr, pos + restToProcess, pos + (restToProcess - blockLen), leftOverLen);
234 | }
235 | }
236 |
237 | // arr[dist..-1] - buffer, arr[0, leftLen - 1] ++ arr[leftLen, leftLen + rightLen - 1]
238 | // -> arr[dist, dist + leftLen + rightLen - 1]
239 | private void grailMergeLeft(SortType[] arr, int pos, int leftLen, int rightLen, int dist) {
240 | int left = 0, right = leftLen;
241 |
242 | rightLen += leftLen;
243 |
244 | while(right < rightLen) {
245 | if(left == leftLen || this.grail.compare(arr[pos + left], arr[pos + right]) > 0) {
246 | grailSwap(arr, pos + (dist++), pos + (right++));
247 | }
248 | else grailSwap(arr, pos + (dist++), pos + (left++));
249 | }
250 | if(dist != left) grailMultiSwap(arr, pos + dist, pos + left, leftLen - left);
251 | }
252 | private void grailMergeRight(SortType[] arr, int pos, int leftLen, int rightLen, int dist) {
253 | int mergedPos = leftLen + rightLen + dist - 1;
254 | int right = leftLen + rightLen - 1, left = leftLen - 1;
255 |
256 | while(left >= 0) {
257 | if(right < leftLen || this.grail.compare(arr[pos + left], arr[pos + right]) > 0) {
258 | grailSwap(arr, pos + (mergedPos--), pos + (left--));
259 | }
260 | else grailSwap(arr, pos + (mergedPos--), pos + (right--));
261 | }
262 | if(right != mergedPos) {
263 | while(right >= leftLen) grailSwap(arr, pos + (mergedPos--), pos + (right--));
264 | }
265 | }
266 |
267 | //returns the leftover length, then the leftover fragment
268 | private GrailState grailSmartMergeWithoutBuffer(SortType[] arr, int pos, int leftOverLen, int leftOverFrag, int regBlockLen) {
269 | if(regBlockLen == 0) return new GrailState(leftOverLen, leftOverFrag);
270 |
271 | int len1 = leftOverLen, len2 = regBlockLen;
272 | int typeFrag = 1 - leftOverFrag; //1 if inverted
273 |
274 | if(len1 != 0 && this.grail.compare(arr[pos + (len1 - 1)], arr[pos + len1]) - typeFrag >= 0) {
275 | while(len1 != 0) {
276 | int foundLen;
277 |
278 | //Binary search left, else search right
279 | if (typeFrag != 0) foundLen = grailBinSearch(arr, pos + len1, len2, pos, true);
280 | else foundLen = grailBinSearch(arr, pos + len1, len2, pos, false);
281 |
282 | if(foundLen != 0) {
283 | grailRotate(arr, pos, len1, foundLen);
284 |
285 | pos += foundLen;
286 | len2 -= foundLen;
287 | }
288 |
289 | if(len2 == 0) return new GrailState(len1, leftOverFrag);
290 |
291 | do {
292 | pos++;
293 | len1--;
294 | } while(len1 != 0 && this.grail.compare(arr[pos], arr[pos + len1]) - typeFrag < 0);
295 | }
296 | }
297 | return new GrailState(len2, typeFrag);
298 | }
299 |
300 | //returns the leftover length, then the leftover fragment
301 | private GrailState grailSmartMergeWithBuffer(SortType[] arr, int pos, int leftOverLen, int leftOverFrag, int blockLen) {
302 | int dist = 0 - blockLen;
303 | int left = 0, right = leftOverLen;
304 | int leftEnd = right, rightEnd = right + blockLen;
305 | int typeFrag = 1 - leftOverFrag; // 1 if inverted
306 |
307 | while(left < leftEnd && right < rightEnd) {
308 | if(this.grail.compare(arr[pos + left], arr[pos + right]) - typeFrag < 0) {
309 | grailSwap(arr, pos + (dist++), pos + (left++));
310 | }
311 | else grailSwap(arr, pos + (dist++), pos + (right++));
312 | }
313 |
314 | int length, fragment = leftOverFrag;
315 |
316 | if(left < leftEnd) {
317 | length = leftEnd - left;
318 |
319 | while(left < leftEnd) grailSwap(arr, pos + (--leftEnd), pos + (--rightEnd));
320 | }
321 | else {
322 | length = rightEnd - right;
323 | fragment = typeFrag;
324 | }
325 | return new GrailState(length, fragment);
326 | }
327 |
328 |
329 | /***** Sort With Extra Buffer *****/
330 |
331 | //returns the leftover length, then the leftover fragment
332 | private GrailState grailSmartMergeWithXBuf(SortType[] arr, int pos, int leftOverLen, int leftOverFrag, int blockLen) {
333 | int dist = 0 - blockLen;
334 | int left = 0, right = leftOverLen;
335 | int leftEnd = right, rightEnd = right + blockLen;
336 | int typeFrag = 1 - leftOverFrag; // 1 if inverted
337 |
338 | while(left < leftEnd && right < rightEnd) {
339 | if(this.grail.compare(arr[pos + left], arr[pos + right]) - typeFrag < 0) {
340 | arr[pos + (dist++)] = arr[pos + (left++)];
341 | }
342 | else arr[pos + (dist++)] = arr[pos + (right++)];
343 | }
344 |
345 | int length, fragment = leftOverFrag;
346 |
347 | if(left < leftEnd) {
348 | length = leftEnd - left;
349 |
350 | while(left < leftEnd) arr[pos + (--rightEnd)] = arr[pos + (--leftEnd)];
351 | }
352 | else {
353 | length = rightEnd - right;
354 | fragment = typeFrag;
355 | }
356 | return new GrailState(length, fragment);
357 | }
358 |
359 | // arr[dist..-1] - free, arr[0, leftEnd - 1] ++ arr[leftEnd, leftEnd + rightEnd - 1]
360 | // -> arr[dist, dist + leftEnd + rightEnd - 1]
361 | private void grailMergeLeftWithXBuf(SortType[] arr, int pos, int leftEnd, int rightEnd, int dist) {
362 | int left = 0, right = leftEnd;
363 | rightEnd += leftEnd;
364 |
365 | while(right < rightEnd) {
366 | if(left == leftEnd || this.grail.compare(arr[pos + left], arr[pos + right]) > 0) {
367 | arr[pos + (dist++)] = arr[pos + (right++)];
368 | }
369 | else arr[pos + (dist++)] = arr[pos + (left++)];
370 | }
371 | if(dist != left) {
372 | while(left < leftEnd) arr[pos + (dist++)] = arr[pos + (left++)];
373 | }
374 | }
375 |
376 | // arr - starting array. arr[0 - regBlockLen..-1] - buffer (if havebuf).
377 | // regBlockLen - length of regular blocks. First blockCount blocks are stable sorted by 1st elements and key-coded
378 | // keysPos - where keys are in array, in same order as blocks. keysPos < midkey means stream A
379 | // aBlockCount are regular blocks from stream A.
380 | // lastLen is length of last (irregular) block from stream B, that should go before aBlockCount blocks.
381 | // lastLen = 0 requires aBlockCount = 0 (no irregular blocks). lastLen > 0, aBlockCount = 0 is possible.
382 | private void grailMergeBuffersLeftWithXBuf(SortType[] arr, int keysPos, int midkey, int pos, int blockCount,
383 | int regBlockLen, int aBlockCount, int lastLen) {
384 |
385 | if(blockCount == 0) {
386 | int aBlocksLen = aBlockCount * regBlockLen;
387 |
388 | grailMergeLeftWithXBuf(arr, pos, aBlocksLen, lastLen, 0 - regBlockLen);
389 | return;
390 | }
391 |
392 | int leftOverLen, processIndex;
393 | leftOverLen = processIndex = regBlockLen;
394 |
395 | int leftOverFrag = this.grail.compare(arr[keysPos], arr[midkey]) < 0 ? 0 : 1;
396 | int restToProcess;
397 |
398 | for(int keyIndex = 1; keyIndex < blockCount; keyIndex++, processIndex += regBlockLen) {
399 | restToProcess = processIndex - leftOverLen;
400 | int nextFrag = this.grail.compare(arr[keysPos + keyIndex], arr[midkey]) < 0 ? 0 : 1;
401 |
402 | if(nextFrag == leftOverFrag) {
403 | System.arraycopy(arr, pos + restToProcess, arr, pos + restToProcess - regBlockLen, leftOverLen);
404 |
405 | restToProcess = processIndex;
406 | leftOverLen = regBlockLen;
407 | }
408 | else {
409 | GrailState results = grailSmartMergeWithXBuf(arr, pos + restToProcess, leftOverLen, leftOverFrag, regBlockLen);
410 |
411 | leftOverLen = results.getLeftOverLen();
412 | leftOverFrag = results.getLeftOverFrag();
413 | }
414 | }
415 | restToProcess = processIndex - leftOverLen;
416 |
417 | if(lastLen != 0) {
418 | if(leftOverFrag != 0) {
419 | System.arraycopy(arr, pos + restToProcess, arr, pos + restToProcess - regBlockLen, leftOverLen);
420 |
421 | restToProcess = processIndex;
422 | leftOverLen = regBlockLen * aBlockCount;
423 | leftOverFrag = 0;
424 | }
425 | else leftOverLen += regBlockLen * aBlockCount;
426 |
427 | grailMergeLeftWithXBuf(arr, pos + restToProcess, leftOverLen, lastLen, 0 - regBlockLen);
428 | }
429 | else {
430 | System.arraycopy(arr, pos + restToProcess, arr, pos + restToProcess - regBlockLen, leftOverLen);
431 | }
432 | }
433 |
434 | /***** End Sort With Extra Buffer *****/
435 |
436 | // build blocks of length buildLen
437 | // input: [-buildLen, -1] elements are buffer
438 | // output: first buildLen elements are buffer, blocks 2 * buildLen and last subblock sorted
439 | private void grailBuildBlocks(SortType[] arr, int pos, int len, int buildLen, SortType[] extbuf, int bufferPos, int extBufLen) {
440 | int buildBuf = buildLen < extBufLen ? buildLen : extBufLen;
441 |
442 | while((buildBuf & (buildBuf - 1)) != 0) {
443 | buildBuf &= buildBuf - 1; // max power or 2 - just in case
444 | }
445 |
446 | int extraDist, part;
447 |
448 | if(buildBuf != 0) {
449 | System.arraycopy(arr, pos - buildBuf, extbuf, bufferPos, buildBuf);
450 |
451 | for(int dist = 1; dist < len; dist += 2) {
452 | extraDist = 0;
453 | if(this.grail.compare(arr[pos + (dist - 1)], arr[pos + dist]) > 0) extraDist = 1;
454 |
455 | arr[pos + dist - 3] = arr[pos + dist - 1 + extraDist];
456 | arr[pos + dist - 2] = arr[pos + dist - extraDist];
457 | }
458 | if(len % 2 != 0) arr[pos + len - 3] = arr[pos + len - 1];
459 |
460 | pos -= 2;
461 |
462 | for(part = 2; part < buildBuf; part *= 2) {
463 | int left = 0, right = len - 2 * part;
464 |
465 | while(left <= right) {
466 | grailMergeLeftWithXBuf(arr, pos + left, part, part, 0 - part);
467 | left += 2 * part;
468 | }
469 |
470 | int rest = len - left;
471 |
472 | if(rest > part) grailMergeLeftWithXBuf(arr, pos + left, part, rest - part, 0 - part);
473 | else {
474 | while(left < len) arr[pos + left - part] = arr[pos + left++];
475 | }
476 | pos -= part;
477 | }
478 | System.arraycopy(extbuf, bufferPos, arr, pos + len, buildBuf);
479 | }
480 | else {
481 | for(int dist = 1; dist < len; dist += 2) {
482 | extraDist = 0;
483 | if(this.grail.compare(arr[pos + (dist - 1)], arr[pos + dist]) > 0) extraDist = 1;
484 |
485 | grailSwap(arr, pos + (dist - 3), pos + (dist - 1 + extraDist));
486 | grailSwap(arr, pos + (dist - 2), pos + (dist - extraDist));
487 | }
488 |
489 | if(len % 2 != 0) grailSwap(arr, pos + (len - 1), pos + (len - 3));
490 |
491 | pos -= 2;
492 | part = 2;
493 | }
494 |
495 | while(part < buildLen) {
496 | int left = 0, right = len - 2 * part;
497 |
498 | while(left <= right) {
499 | grailMergeLeft(arr, pos + left, part, part, 0 - part);
500 | left += 2 * part;
501 | }
502 |
503 | int rest = len - left;
504 |
505 | if(rest > part) {
506 | grailMergeLeft(arr, pos + left, part, rest - part, 0 - part);
507 | }
508 | else grailRotate(arr, pos + left - part, part, rest);
509 |
510 | pos -= part;
511 | part *= 2;
512 | }
513 |
514 | int restToBuild = len % (2 * buildLen);
515 | int leftOverPos = len - restToBuild;
516 |
517 | if(restToBuild <= buildLen) grailRotate(arr, pos + leftOverPos, restToBuild, buildLen);
518 | else grailMergeRight(arr, pos + leftOverPos, buildLen, restToBuild - buildLen, buildLen);
519 |
520 | while(leftOverPos > 0) {
521 | leftOverPos -= 2 * buildLen;
522 | grailMergeRight(arr, pos + leftOverPos, buildLen, buildLen, buildLen);
523 | }
524 | }
525 |
526 | // keys are on the left of arr. Blocks of length buildLen combined. We'll combine them into pairs
527 | // buildLen and keys are powers of 2. (2 * buildLen / regBlockLen) keys are guaranteed
528 | private void grailCombineBlocks(SortType[] arr, int keyPos, int pos, int len, int buildLen, int regBlockLen,
529 | boolean havebuf, SortType[] buffer, int bufferPos) {
530 |
531 | int combineLen = len / (2 * buildLen);
532 | int leftOver = len % (2 * buildLen);
533 |
534 | if(leftOver <= buildLen) {
535 | len -= leftOver;
536 | leftOver = 0;
537 | }
538 |
539 | if(buffer != null) System.arraycopy(arr, pos - regBlockLen, buffer, bufferPos, regBlockLen);
540 |
541 | for(int i = 0; i <= combineLen; i++) {
542 | if(i == combineLen && leftOver == 0) break;
543 |
544 | int blockPos = pos + i * 2 * buildLen;
545 | int blockCount = (i == combineLen ? leftOver : 2 * buildLen) / regBlockLen;
546 |
547 | grailInsertSort(arr, keyPos, blockCount + (i == combineLen ? 1 : 0));
548 |
549 | int midkey = buildLen / regBlockLen;
550 |
551 | for(int index = 1; index < blockCount; index++) {
552 | int leftIndex = index - 1;
553 |
554 | for(int rightIndex = index; rightIndex < blockCount; rightIndex++) {
555 | int rightComp = this.grail.compare(arr[blockPos + leftIndex * regBlockLen], arr[blockPos + rightIndex * regBlockLen]);
556 |
557 | if(rightComp > 0 || (rightComp == 0 && this.grail.compare(arr[keyPos + leftIndex], arr[keyPos + rightIndex]) > 0)) {
558 | leftIndex = rightIndex;
559 | }
560 | }
561 |
562 | if(leftIndex != index - 1) {
563 | grailMultiSwap(arr, blockPos + (index - 1) * regBlockLen, blockPos + leftIndex * regBlockLen, regBlockLen);
564 | grailSwap(arr, keyPos + (index - 1), keyPos + leftIndex);
565 |
566 | if(midkey == index - 1 || midkey == leftIndex) {
567 | midkey ^= (index - 1) ^ leftIndex;
568 | }
569 | }
570 | }
571 |
572 | int aBlockCount, lastLen;
573 | aBlockCount = lastLen = 0;
574 | if(i == combineLen) lastLen = leftOver % regBlockLen;
575 |
576 | if(lastLen != 0) {
577 | while(aBlockCount < blockCount && this.grail.compare(arr[blockPos + blockCount * regBlockLen],
578 | arr[blockPos + (blockCount - aBlockCount - 1) * regBlockLen]) < 0) {
579 |
580 | aBlockCount++;
581 | }
582 | }
583 |
584 | if(buffer != null) {
585 | grailMergeBuffersLeftWithXBuf(arr, keyPos, keyPos + midkey, blockPos, blockCount - aBlockCount,
586 | regBlockLen, aBlockCount, lastLen);
587 | }
588 | else grailMergeBuffersLeft(arr, keyPos, keyPos + midkey, blockPos, blockCount - aBlockCount,
589 | regBlockLen, havebuf, aBlockCount, lastLen);
590 | }
591 | if(buffer != null) {
592 | for(int i = len - 1; i >= 0; i--) arr[pos + i] = arr[pos + i - regBlockLen];
593 |
594 | System.arraycopy(buffer, bufferPos, arr, pos - regBlockLen, regBlockLen);
595 | }
596 | else if(havebuf) {
597 | while(--len >= 0) grailSwap(arr, pos + len, pos + len - regBlockLen);
598 | }
599 | }
600 |
601 | private void grailLazyStableSort(SortType[] arr, int pos, int len) {
602 | for(int dist = 1; dist < len; dist += 2) {
603 | if(this.grail.compare(arr[pos + dist - 1], arr[pos + dist]) > 0) {
604 | grailSwap(arr, pos + (dist - 1), pos + dist);
605 | }
606 | }
607 |
608 | for(int part = 2; part < len; part *= 2) {
609 | int left = 0, right = len - 2 * part;
610 |
611 | while(left <= right) {
612 | grailMergeWithoutBuffer(arr, pos + left, part, part);
613 | left += 2 * part;
614 | }
615 |
616 | int rest = len - left;
617 |
618 | if(rest > part) grailMergeWithoutBuffer(arr, pos + left, part, rest - part);
619 | }
620 | }
621 |
622 | private void grailCommonSort(SortType[] arr, int pos, int len, SortType[] buffer, int bufferPos, int bufferLen) {
623 | if(len <= 32) {
624 | grailInsertSort(arr, pos, len);
625 | return;
626 | }
627 |
628 | int blockLen = (int) Math.sqrt(len);
629 | int numKeys = ((len - 1) / blockLen) + 1;
630 | int keyLength = numKeys + blockLen;
631 | int keysFound = grailGetKeys(arr, pos, len, keyLength);
632 |
633 | boolean bufferEnabled = true;
634 |
635 | if(keysFound < keyLength) {
636 | if(keysFound < 4) {
637 | grailLazyStableSort(arr, pos, len);
638 | return;
639 | }
640 |
641 | numKeys = blockLen;
642 | while(numKeys > keysFound) numKeys /= 2;
643 |
644 | bufferEnabled = false;
645 | blockLen = 0;
646 | }
647 |
648 | int dist = blockLen + numKeys;
649 | int buildLen = bufferEnabled ? blockLen : numKeys;
650 |
651 | if(bufferEnabled) {
652 | grailBuildBlocks(arr, pos + dist, len - dist, buildLen, buffer, bufferPos, bufferLen);
653 | }
654 | else {
655 | grailBuildBlocks(arr, pos + dist, len - dist, buildLen, null, bufferPos, 0);
656 | }
657 |
658 | // 2 * buildLen are built
659 | while(len - dist > (buildLen *= 2)) {
660 | int regBlockLen = blockLen;
661 | boolean buildBufEnabled = bufferEnabled;
662 |
663 | if(!bufferEnabled) {
664 | if(numKeys > 4 && numKeys / 8 * numKeys >= buildLen) {
665 | regBlockLen = numKeys / 2;
666 | buildBufEnabled = true;
667 | }
668 | else {
669 | int calcKeys = 1;
670 | int i = buildLen * keysFound / 2;
671 |
672 | while(calcKeys < numKeys && i != 0) {
673 | calcKeys *= 2;
674 | i /= 8;
675 | }
676 | regBlockLen = (2 * buildLen) / calcKeys;
677 | }
678 | }
679 | grailCombineBlocks(arr, pos, pos + dist, len - dist, buildLen, regBlockLen, buildBufEnabled,
680 | buildBufEnabled && regBlockLen <= bufferLen ? buffer : null, bufferPos);
681 | }
682 |
683 | grailInsertSort(arr, pos, dist);
684 | grailMergeWithoutBuffer(arr, pos, dist, len - dist);
685 | }
686 |
687 | public void grailSortWithoutBuffer(SortType[] arr) {
688 | this.grail = new SortComparator();
689 | grailCommonSort(arr, 0, arr.length, null, 0, 0);
690 | }
691 | public void grailSortWithoutBuffer(SortType[] arr, SortComparator cmp) {
692 | this.grail = cmp;
693 | grailCommonSort(arr, 0, arr.length, null, 0, 0);
694 | }
695 |
696 | public void grailSortWithBuffer(SortType[] arr) {
697 | this.grail = new SortComparator();
698 | SortType[] ExtBuf = new SortType[this.grailStaticBufferLen];
699 | grailCommonSort(arr, 0, arr.length, ExtBuf, 0, this.grailStaticBufferLen);
700 | }
701 | public void grailSortWithBuffer(SortType[] arr, SortComparator cmp) {
702 | this.grail = cmp;
703 | SortType[] ExtBuf = new SortType[this.grailStaticBufferLen];
704 | grailCommonSort(arr, 0, arr.length, ExtBuf, 0, this.grailStaticBufferLen);
705 | }
706 |
707 | public void grailSortWithDynBuffer(SortType[] arr) {
708 | this.grail = new SortComparator();
709 | int tempLen = 1;
710 | while(tempLen * tempLen < arr.length) tempLen *= 2;
711 | SortType[] ExtBuf = new SortType[tempLen];
712 | grailCommonSort(arr, 0, arr.length, ExtBuf, 0, tempLen);
713 | }
714 | public void grailSortWithDynBuffer(SortType[] arr, SortComparator cmp) {
715 | this.grail = cmp;
716 | int tempLen = 1;
717 | while(tempLen * tempLen < arr.length) tempLen *= 2;
718 | SortType[] ExtBuf = new SortType[tempLen];
719 | grailCommonSort(arr, 0, arr.length, ExtBuf, 0, tempLen);
720 | }
721 | }
--------------------------------------------------------------------------------