blob: 35ed2fe63fc069bcd80e3e1a634687955e436a57 [file] [log] [blame]
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Licensed under the MIT License. Text in LICENSE file.
package com.google.archivepatcher.generator.bsdiff;
import java.io.IOException;
/**
* Taken from
* https://github.com/carrotsearch/jsuffixarrays/blob/master/src/main/java/org/jsuffixarrays/
* DivSufSort.java and refactored to support RandomAccessObject instead of just arrays.
*
* <p>Straightforward reimplementation of the divsufsort algorithm given in: <pre><code>
* Yuta Mori, Short description of improved two-stage suffix sorting
* algorithm, 2005.
* http://homepage3.nifty.com/wpage/software/itssort.txt
* </code></pre>
*
* <p>This implementation is basically a translation of the C version given by Yuta Mori:
* <tt>libdivsufsort-2.0.0, http://code.google.com/p/libdivsufsort/</tt>
*/
public final class DivSuffixSorter implements SuffixSorter {
// TODO(admo): Clean up the code, variable names and documentation of this class
private static final int ALPHABET_SIZE = 256;
private static final int BUCKET_A_SIZE = ALPHABET_SIZE;
private static final int BUCKET_B_SIZE = ALPHABET_SIZE * ALPHABET_SIZE;
private static final int SS_INSERTIONSORT_THRESHOLD = 8;
private static final int SS_BLOCKSIZE = 1024;
private static final int SS_MISORT_STACKSIZE = 16;
private static final int SS_SMERGE_STACKSIZE = 32;
private static final int TR_STACKSIZE = 64;
private static final int TR_INSERTIONSORT_THRESHOLD = 8;
private static final int[] SQQ_TABLE = {
0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, 59, 61, 64, 65, 67, 69, 71, 73, 75, 76,
78, 80, 81, 83, 84, 86, 87, 89, 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, 103, 104, 106, 107,
108, 109, 110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 128,
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 145,
146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, 156, 157, 158, 159, 160, 160, 161,
162, 163, 163, 164, 165, 166, 167, 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 176,
176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189,
189, 190, 191, 192, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 199, 199, 200, 201, 201,
202, 203, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 213,
214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, 221, 222, 222, 223, 224, 224,
225, 225, 226, 226, 227, 227, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 234, 234, 235,
235, 236, 236, 237, 237, 238, 238, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245,
245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254,
255
};
private static final int[] LG_TABLE = {
-1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
};
/* fields */
private final RandomAccessObjectFactory randomAccessObjectFactory;
private RandomAccessObject suffixArray;
private RandomAccessObject input;
public DivSuffixSorter(RandomAccessObjectFactory randomAccessObjectFactory) {
this.randomAccessObjectFactory = randomAccessObjectFactory;
}
@Override
public RandomAccessObject suffixSort(RandomAccessObject input) throws IOException, InterruptedException {
if (4 * (input.length() + 1) >= Integer.MAX_VALUE) {
throw new IllegalArgumentException("Input too large (" + input.length() + " bytes)");
}
int length = (int) input.length();
RandomAccessObject suffixArray = randomAccessObjectFactory.create((length + 1) * 4);
suffixArray.seek(0);
suffixArray.writeInt(length);
this.suffixArray = suffixArray;
// Deal with small cases separately.
if (length == 0) {
return suffixArray;
} else if (length == 1) {
writeSuffixArray(0, 0);
return suffixArray;
}
this.input = input;
int[] bucketA = new int[BUCKET_A_SIZE];
int[] bucketB = new int[BUCKET_B_SIZE];
/* Suffixsort. */
int m = sortTypeBstar(bucketA, bucketB, length);
constructSuffixArray(bucketA, bucketB, length, m);
return suffixArray;
}
/**
* Constructs the suffix array by using the sorted order of type B* suffixes.
*/
private final void constructSuffixArray(int[] bucketA, int[] bucketB, int n, int m)
throws IOException {
int i, j, k; // ptr
int s, c0, c1, c2;
// (_c1)])
if (0 < m) {
/*
* Construct the sorted order of type B suffixes by using the sorted order of
* type B suffixes.
*/
for (c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) {
/* Scan the suffix array from right to left. */
for (i = bucketB[(c1) * ALPHABET_SIZE + (c1 + 1)], j = bucketA[c1 + 1] - 1, k = 0, c2 = -1;
i <= j;
--j) {
if (0 < (s = readSuffixArray(j))) {
writeSuffixArray(j, ~s);
c0 = readInput(--s);
if ((0 < s) && (readInput(s - 1) > c0)) {
s = ~s;
}
if (c0 != c2) {
if (0 <= c2) {
bucketB[(c1) * ALPHABET_SIZE + (c2)] = k;
}
k = bucketB[(c1) * ALPHABET_SIZE + (c2 = c0)];
}
writeSuffixArray(k--, s);
} else {
writeSuffixArray(j, ~s);
}
}
}
}
/*
* Construct the suffix array by using the sorted order of type B suffixes.
*/
k = bucketA[c2 = readInput(n - 1)];
writeSuffixArray(k++, readInput(n - 2) < c2 ? ~(n - 1) : (n - 1));
/* Scan the suffix array from left to right. */
for (i = 0, j = n; i < j; ++i) {
if (0 < (s = readSuffixArray(i))) {
c0 = readInput(--s);
if ((s == 0) || (readInput(s - 1) < c0)) {
s = ~s;
}
if (c0 != c2) {
bucketA[c2] = k;
k = bucketA[c2 = c0];
}
writeSuffixArray(k++, s);
} else {
writeSuffixArray(i, ~s);
}
}
}
private final int sortTypeBstar(int[] bucketA, int[] bucketB, int n)
throws IOException, InterruptedException {
int PAb, ISAb, buf;
int i, j, k, t, m, bufsize;
int c0, c1 = 0;
/*
* Count the number of occurrences of the first one or two characters of each type
* A, B and B suffix. Moreover, store the beginning position of all type B
* suffixes into the array SA.
*/
for (i = n - 1, m = n, c0 = readInput(n - 1); 0 <= i; ) {
/* type A suffix. */
do {
++bucketA[c1 = c0];
} while ((0 <= --i) && ((c0 = readInput(i)) >= c1));
if (0 <= i) {
/* type B suffix. */
++bucketB[(c0) * ALPHABET_SIZE + (c1)];
writeSuffixArray(--m, i);
/* type B suffix. */
for (--i, c1 = c0; (0 <= i) && ((c0 = readInput(i)) <= c1); --i, c1 = c0) {
++bucketB[(c1) * ALPHABET_SIZE + (c0)];
}
}
}
m = n - m;
// note:
// A type B* suffix is lexicographically smaller than a type B suffix
// that
// begins with the same first two characters.
// Calculate the index of 0/end point of each bucket.
for (c0 = 0, i = 0, j = 0; c0 < ALPHABET_SIZE; ++c0) {
t = i + bucketA[c0];
bucketA[c0] = i + j; /* 0 point */
i = t + bucketB[(c0) * ALPHABET_SIZE + (c0)];
for (c1 = c0 + 1; c1 < ALPHABET_SIZE; ++c1) {
j += bucketB[(c0) * ALPHABET_SIZE + (c1)];
bucketB[(c0) * ALPHABET_SIZE + (c1)] = j; // end point
i += bucketB[(c1) * ALPHABET_SIZE + (c0)];
}
}
if (0 < m) {
// Sort the type B* suffixes by their first two characters.
PAb = n - m; // SA
ISAb = m; // SA
for (i = m - 2; 0 <= i; --i) {
t = readSuffixArray(PAb + i);
c0 = readInput(t);
c1 = readInput(t + 1);
writeSuffixArray(--bucketB[(c0) * ALPHABET_SIZE + (c1)], i);
}
t = readSuffixArray(PAb + m - 1);
c0 = readInput(t);
c1 = readInput(t + 1);
writeSuffixArray(--bucketB[(c0) * ALPHABET_SIZE + (c1)], m - 1);
// Sort the type B* substrings using sssort.
buf = m; // SA
bufsize = n - (2 * m);
for (c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) {
if (Thread.interrupted()) {
throw new InterruptedException();
}
for (c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) {
i = bucketB[(c0) * ALPHABET_SIZE + (c1)];
if (1 < (j - i)) {
ssSort(PAb, i, j, buf, bufsize, 2, n, readSuffixArray(i) == (m - 1));
}
}
}
// Compute ranks of type B* substrings.
for (i = m - 1; 0 <= i; --i) {
if (0 <= readSuffixArray(i)) {
j = i;
do {
writeSuffixArray(ISAb + readSuffixArray(i), i);
} while ((0 <= --i) && (0 <= readSuffixArray(i)));
writeSuffixArray(i + 1, i - j);
if (i <= 0) {
break;
}
}
j = i;
do {
writeSuffixArray(ISAb + (writeSuffixArray(i, ~readSuffixArray(i))), j);
} while (readSuffixArray(--i) < 0);
writeSuffixArray(ISAb + readSuffixArray(i), j);
}
// Construct the inverse suffix array of type B* suffixes using
// trsort.
trSort(ISAb, m, 1);
// Set the sorted order of type B* suffixes.
for (i = n - 1, j = m, c0 = readInput(n - 1); 0 <= i; ) {
if (Thread.interrupted()) {
throw new InterruptedException();
}
for (--i, c1 = c0; (0 <= i) && ((c0 = readInput(i)) >= c1); --i, c1 = c0) {}
if (0 <= i) {
t = i;
for (--i, c1 = c0; (0 <= i) && ((c0 = readInput(i)) <= c1); --i, c1 = c0) {}
writeSuffixArray(readSuffixArray(ISAb + --j), ((t == 0) || (1 < (t - i))) ? t : ~t);
}
}
// Calculate the index of 0/end point of each bucket.
bucketB[(ALPHABET_SIZE - 1) * ALPHABET_SIZE + (ALPHABET_SIZE - 1)] = n; // end point
for (c0 = ALPHABET_SIZE - 2, k = m - 1; 0 <= c0; --c0) {
if (Thread.interrupted()) {
throw new InterruptedException();
}
i = bucketA[c0 + 1] - 1;
for (c1 = ALPHABET_SIZE - 1; c0 < c1; --c1) {
t = i - bucketB[(c1) * ALPHABET_SIZE + (c0)];
bucketB[(c1) * ALPHABET_SIZE + (c0)] = i; // end point
// Move all type B* suffixes to the correct position.
for (i = t, j = bucketB[(c0) * ALPHABET_SIZE + (c1)]; j <= k; --i, --k) {
writeSuffixArray(i, readSuffixArray(k));
}
}
bucketB[(c0) * ALPHABET_SIZE + (c0 + 1)] = i - bucketB[(c0) * ALPHABET_SIZE + (c0)] + 1;
bucketB[(c0) * ALPHABET_SIZE + (c0)] = i; // end point
}
}
return m;
}
private final void ssSort(
final int PA, int first, int last, int buf, int bufsize, int depth, int n, boolean lastsuffix)
throws IOException {
int a, b, middle, curbuf; // SA pointer
int j, k, curbufsize, limit;
int i;
if (lastsuffix) {
++first;
}
if ((bufsize < SS_BLOCKSIZE)
&& (bufsize < (last - first))
&& (bufsize < (limit = ssIsqrt(last - first)))) {
if (SS_BLOCKSIZE < limit) {
limit = SS_BLOCKSIZE;
}
buf = middle = last - limit;
bufsize = limit;
} else {
middle = last;
limit = 0;
}
for (a = first, i = 0; SS_BLOCKSIZE < (middle - a); a += SS_BLOCKSIZE, ++i) {
ssMintroSort(PA, a, a + SS_BLOCKSIZE, depth);
curbufsize = last - (a + SS_BLOCKSIZE);
curbuf = a + SS_BLOCKSIZE;
if (curbufsize <= bufsize) {
curbufsize = bufsize;
curbuf = buf;
}
for (b = a, k = SS_BLOCKSIZE, j = i; (j & 1) != 0; b -= k, k <<= 1, j >>= 1) {
ssSwapMerge(PA, b - k, b, b + k, curbuf, curbufsize, depth);
}
}
ssMintroSort(PA, a, middle, depth);
for (k = SS_BLOCKSIZE; i != 0; k <<= 1, i >>= 1) {
if ((i & 1) != 0) {
ssSwapMerge(PA, a - k, a, middle, buf, bufsize, depth);
a -= k;
}
}
if (limit != 0) {
ssMintroSort(PA, middle, last, depth);
ssInplaceMerge(PA, first, middle, last, depth);
}
if (lastsuffix) {
int p1 = readSuffixArray(PA + readSuffixArray(first - 1));
int p11 = n - 2;
for (a = first, i = readSuffixArray(first - 1);
(a < last)
&& ((readSuffixArray(a) < 0)
|| (0 < ssCompare(p1, p11, PA + readSuffixArray(a), depth)));
++a) {
writeSuffixArray(a - 1, readSuffixArray(a));
}
writeSuffixArray(a - 1, i);
}
}
/**
* special version of ssCompare for handling
* <code>ssCompare(T, &(PAi[0]), PA + *a, depth)</code> situation.
*/
private final int ssCompare(int pa, int pb, int p2, int depth) throws IOException {
int U1, U2, U1n, U2n; // pointers to T
for (U1 = depth + pa, U2 = depth + readSuffixArray(p2), U1n = pb + 2,
U2n = readSuffixArray(p2 + 1) + 2;
(U1 < U1n) && (U2 < U2n) && (readInput(U1) == readInput(U2));
++U1, ++U2) {}
return U1 < U1n ? (U2 < U2n ? readInput(U1) - readInput(U2) : 1) : (U2 < U2n ? -1 : 0);
}
private final int ssCompare(int p1, int p2, int depth) throws IOException {
int U1, U2, U1n, U2n; // pointers to T
for (U1 = depth + readSuffixArray(p1), U2 = depth + readSuffixArray(p2),
U1n = readSuffixArray(p1 + 1) + 2, U2n = readSuffixArray(p2 + 1) + 2;
(U1 < U1n) && (U2 < U2n) && (readInput(U1) == readInput(U2));
++U1, ++U2) {}
return U1 < U1n ? (U2 < U2n ? readInput(U1) - readInput(U2) : 1) : (U2 < U2n ? -1 : 0);
}
private final void ssInplaceMerge(int PA, int first, int middle, int last, int depth)
throws IOException {
// PA, middle, first, last are pointers to SA
int p, a, b; // pointer to SA
int len, half;
int q, r;
int x;
for (; ; ) {
if (readSuffixArray(last - 1) < 0) {
x = 1;
p = PA + ~readSuffixArray(last - 1);
} else {
x = 0;
p = PA + readSuffixArray(last - 1);
}
for (a = first, len = middle - first, half = len >> 1, r = -1;
0 < len;
len = half, half >>= 1) {
b = a + half;
q =
ssCompare(
PA + ((0 <= readSuffixArray(b)) ? readSuffixArray(b) : ~readSuffixArray(b)),
p,
depth);
if (q < 0) {
a = b + 1;
half -= (len & 1) ^ 1;
} else {
r = q;
}
}
if (a < middle) {
if (r == 0) {
writeSuffixArray(a, ~readSuffixArray(a));
}
ssRotate(a, middle, last);
last -= middle - a;
middle = a;
if (first == middle) {
break;
}
}
--last;
if (x != 0) {
while (readSuffixArray(--last) < 0) {
// nop
}
}
if (middle == last) {
break;
}
}
}
private final void ssRotate(int first, int middle, int last) throws IOException {
// first, middle, last are pointers in SA
int a, b, t; // pointers in SA
int l, r;
l = middle - first;
r = last - middle;
for (; (0 < l) && (0 < r); ) {
if (l == r) {
ssBlockSwap(first, middle, l);
break;
}
if (l < r) {
a = last - 1;
b = middle - 1;
t = readSuffixArray(a);
do {
writeSuffixArray(a--, readSuffixArray(b));
writeSuffixArray(b--, readSuffixArray(a));
if (b < first) {
writeSuffixArray(a, t);
last = a;
if ((r -= l + 1) <= l) {
break;
}
a -= 1;
b = middle - 1;
t = readSuffixArray(a);
}
} while (true);
} else {
a = first;
b = middle;
t = readSuffixArray(a);
do {
writeSuffixArray(a++, readSuffixArray(b));
writeSuffixArray(b++, readSuffixArray(a));
if (last <= b) {
writeSuffixArray(a, t);
first = a + 1;
if ((l -= r + 1) <= r) {
break;
}
a += 1;
b = middle;
t = readSuffixArray(a);
}
} while (true);
}
}
}
private final void ssBlockSwap(int a, int b, int n) throws IOException {
// a, b -- pointer to SA
int t;
for (; 0 < n; --n, ++a, ++b) {
t = readSuffixArray(a);
writeSuffixArray(a, readSuffixArray(b));
writeSuffixArray(b, t);
}
}
private static final int getIDX(int a) {
return (0 <= (a)) ? (a) : (~(a));
}
private static final int min(int a, int b) {
return a < b ? a : b;
}
/**
* D&C based merge.
*/
private final void ssSwapMerge(
int PA, int first, int middle, int last, int buf, int bufsize, int depth) throws IOException {
// Pa, first, middle, last and buf - pointers in SA array
final int STACK_SIZE = SS_SMERGE_STACKSIZE;
StackElement[] stack = new StackElement[STACK_SIZE];
int l, r, lm, rm; // pointers in SA
int m, len, half;
int ssize;
int check, next;
for (check = 0, ssize = 0; ; ) {
if ((last - middle) <= bufsize) {
if ((first < middle) && (middle < last)) {
ssMergeBackward(PA, first, middle, last, buf, depth);
}
if (((check & 1) != 0)
|| (((check & 2) != 0)
&& (ssCompare(
PA + getIDX(readSuffixArray(first - 1)), PA + readSuffixArray(first), depth)
== 0))) {
writeSuffixArray(first, ~readSuffixArray(first));
}
if (((check & 4) != 0)
&& ((ssCompare(
PA + getIDX(readSuffixArray(last - 1)), PA + readSuffixArray(last), depth)
== 0))) {
writeSuffixArray(last, ~readSuffixArray(last));
}
if (ssize > 0) {
StackElement se = stack[--ssize];
first = se.a;
middle = se.b;
last = se.c;
check = se.d;
} else {
return;
}
continue;
}
if ((middle - first) <= bufsize) {
if (first < middle) {
ssMergeForward(PA, first, middle, last, buf, depth);
}
if (((check & 1) != 0)
|| (((check & 2) != 0)
&& (ssCompare(
PA + getIDX(readSuffixArray(first - 1)), PA + readSuffixArray(first), depth)
== 0))) {
writeSuffixArray(first, ~readSuffixArray(first));
}
if (((check & 4) != 0)
&& ((ssCompare(
PA + getIDX(readSuffixArray(last - 1)), PA + readSuffixArray(last), depth)
== 0))) {
writeSuffixArray(last, ~readSuffixArray(last));
}
if (ssize > 0) {
StackElement se = stack[--ssize];
first = se.a;
middle = se.b;
last = se.c;
check = se.d;
} else {
return;
}
continue;
}
for (m = 0, len = min(middle - first, last - middle), half = len >> 1;
0 < len;
len = half, half >>= 1) {
if (ssCompare(
PA + getIDX(readSuffixArray(middle + m + half)),
PA + getIDX(readSuffixArray(middle - m - half - 1)),
depth)
< 0) {
m += half + 1;
half -= (len & 1) ^ 1;
}
}
if (0 < m) {
lm = middle - m;
rm = middle + m;
ssBlockSwap(lm, middle, m);
l = r = middle;
next = 0;
if (rm < last) {
if (readSuffixArray(rm) < 0) {
writeSuffixArray(rm, ~readSuffixArray(rm));
if (first < lm) {
for (; readSuffixArray(--l) < 0; ) {}
next |= 4;
}
next |= 1;
} else if (first < lm) {
for (; readSuffixArray(r) < 0; ++r) {}
next |= 2;
}
}
if ((l - first) <= (last - r)) {
stack[ssize++] = new StackElement(r, rm, last, (next & 3) | (check & 4));
middle = lm;
last = l;
check = (check & 3) | (next & 4);
} else {
if (((next & 2) != 0) && (r == middle)) {
next ^= 6;
}
stack[ssize++] = new StackElement(first, lm, l, (check & 3) | (next & 4));
first = r;
middle = rm;
check = (next & 3) | (check & 4);
}
} else {
if (ssCompare(PA + getIDX(readSuffixArray(middle - 1)), PA + readSuffixArray(middle), depth)
== 0) {
writeSuffixArray(middle, ~readSuffixArray(middle));
}
if (((check & 1) != 0)
|| (((check & 2) != 0)
&& (ssCompare(
PA + getIDX(readSuffixArray(first - 1)), PA + readSuffixArray(first), depth)
== 0))) {
writeSuffixArray(first, ~readSuffixArray(first));
}
if (((check & 4) != 0)
&& ((ssCompare(
PA + getIDX(readSuffixArray(last - 1)), PA + readSuffixArray(last), depth)
== 0))) {
writeSuffixArray(last, ~readSuffixArray(last));
}
if (ssize > 0) {
StackElement se = stack[--ssize];
first = se.a;
middle = se.b;
last = se.c;
check = se.d;
} else {
return;
}
}
}
}
/**
* Merge-forward with internal buffer.
*/
private final void ssMergeForward(int PA, int first, int middle, int last, int buf, int depth)
throws IOException {
// PA, first, middle, last, buf are pointers to SA
int a, b, c, bufend; // pointers to SA
int t, r;
bufend = buf + (middle - first) - 1;
ssBlockSwap(buf, first, middle - first);
for (t = readSuffixArray(a = first), b = buf, c = middle; ; ) {
r = ssCompare(PA + readSuffixArray(b), PA + readSuffixArray(c), depth);
if (r < 0) {
do {
writeSuffixArray(a++, readSuffixArray(b));
if (bufend <= b) {
writeSuffixArray(bufend, t);
return;
}
writeSuffixArray(b++, readSuffixArray(a));
} while (readSuffixArray(b) < 0);
} else if (r > 0) {
do {
writeSuffixArray(a++, readSuffixArray(c));
writeSuffixArray(c++, readSuffixArray(a));
if (last <= c) {
while (b < bufend) {
writeSuffixArray(a++, readSuffixArray(b));
writeSuffixArray(b++, readSuffixArray(a));
}
writeSuffixArray(a, readSuffixArray(b));
writeSuffixArray(b, t);
return;
}
} while (readSuffixArray(c) < 0);
} else {
writeSuffixArray(c, ~readSuffixArray(c));
do {
writeSuffixArray(a++, readSuffixArray(b));
if (bufend <= b) {
writeSuffixArray(bufend, t);
return;
}
writeSuffixArray(b++, readSuffixArray(a));
} while (readSuffixArray(b) < 0);
do {
writeSuffixArray(a++, readSuffixArray(c));
writeSuffixArray(c++, readSuffixArray(a));
if (last <= c) {
while (b < bufend) {
writeSuffixArray(a++, readSuffixArray(b));
writeSuffixArray(b++, readSuffixArray(a));
}
writeSuffixArray(a, readSuffixArray(b));
writeSuffixArray(b, t);
return;
}
} while (readSuffixArray(c) < 0);
}
}
}
/**
* Merge-backward with internal buffer.
*/
private final void ssMergeBackward(int PA, int first, int middle, int last, int buf, int depth)
throws IOException {
// PA, first, middle, last, buf are pointers in SA
int p1, p2; // pointers in SA
int a, b, c, bufend; // pointers in SA
int t, r, x;
bufend = buf + (last - middle) - 1;
ssBlockSwap(buf, middle, last - middle);
x = 0;
if (readSuffixArray(bufend) < 0) {
p1 = PA + ~readSuffixArray(bufend);
x |= 1;
} else {
p1 = PA + readSuffixArray(bufend);
}
if (readSuffixArray(middle - 1) < 0) {
p2 = PA + ~readSuffixArray(middle - 1);
x |= 2;
} else {
p2 = PA + readSuffixArray(middle - 1);
}
for (t = readSuffixArray(a = last - 1), b = bufend, c = middle - 1; ; ) {
r = ssCompare(p1, p2, depth);
if (0 < r) {
if ((x & 1) != 0) {
do {
writeSuffixArray(a--, readSuffixArray(b));
writeSuffixArray(b--, readSuffixArray(a));
} while (readSuffixArray(b) < 0);
x ^= 1;
}
writeSuffixArray(a--, readSuffixArray(b));
if (b <= buf) {
writeSuffixArray(buf, t);
break;
}
writeSuffixArray(b--, readSuffixArray(a));
if (readSuffixArray(b) < 0) {
p1 = PA + ~readSuffixArray(b);
x |= 1;
} else {
p1 = PA + readSuffixArray(b);
}
} else if (r < 0) {
if ((x & 2) != 0) {
do {
writeSuffixArray(a--, readSuffixArray(c));
writeSuffixArray(c--, readSuffixArray(a));
} while (readSuffixArray(c) < 0);
x ^= 2;
}
writeSuffixArray(a--, readSuffixArray(c));
writeSuffixArray(c--, readSuffixArray(a));
if (c < first) {
while (buf < b) {
writeSuffixArray(a--, readSuffixArray(b));
writeSuffixArray(b--, readSuffixArray(a));
}
writeSuffixArray(a, readSuffixArray(b));
writeSuffixArray(b, t);
break;
}
if (readSuffixArray(c) < 0) {
p2 = PA + ~readSuffixArray(c);
x |= 2;
} else {
p2 = PA + readSuffixArray(c);
}
} else {
if ((x & 1) != 0) {
do {
writeSuffixArray(a--, readSuffixArray(b));
writeSuffixArray(b--, readSuffixArray(a));
} while (readSuffixArray(b) < 0);
x ^= 1;
}
writeSuffixArray(a--, ~readSuffixArray(b));
if (b <= buf) {
writeSuffixArray(buf, t);
break;
}
writeSuffixArray(b--, readSuffixArray(a));
if ((x & 2) != 0) {
do {
writeSuffixArray(a--, readSuffixArray(c));
writeSuffixArray(c--, readSuffixArray(a));
} while (readSuffixArray(c) < 0);
x ^= 2;
}
writeSuffixArray(a--, readSuffixArray(c));
writeSuffixArray(c--, readSuffixArray(a));
if (c < first) {
while (buf < b) {
writeSuffixArray(a--, readSuffixArray(b));
writeSuffixArray(b--, readSuffixArray(a));
}
writeSuffixArray(a, readSuffixArray(b));
writeSuffixArray(b, t);
break;
}
if (readSuffixArray(b) < 0) {
p1 = PA + ~readSuffixArray(b);
x |= 1;
} else {
p1 = PA + readSuffixArray(b);
}
if (readSuffixArray(c) < 0) {
p2 = PA + ~readSuffixArray(c);
x |= 2;
} else {
p2 = PA + readSuffixArray(c);
}
}
}
}
/**
* Insertion sort for small size groups
*/
private final void ssInsertionSort(int PA, int first, int last, int depth) throws IOException {
// PA, first, last are pointers in SA
int i, j; // pointers in SA
int t, r;
for (i = last - 2; first <= i; --i) {
for (t = readSuffixArray(i), j = i + 1;
0 < (r = ssCompare(PA + t, PA + readSuffixArray(j), depth));
) {
do {
writeSuffixArray(j - 1, readSuffixArray(j));
} while ((++j < last) && (readSuffixArray(j) < 0));
if (last <= j) {
break;
}
}
if (r == 0) {
writeSuffixArray(j, ~readSuffixArray(j));
}
writeSuffixArray(j - 1, t);
}
}
private static final int ssIsqrt(int x) {
int y, e;
if (x >= (SS_BLOCKSIZE * SS_BLOCKSIZE)) {
return SS_BLOCKSIZE;
}
e =
((x & 0xffff0000) != 0)
? (((x & 0xff000000) != 0)
? 24 + LG_TABLE[(x >> 24) & 0xff]
: 16 + LG_TABLE[(x >> 16) & 0xff])
: (((x & 0x0000ff00) != 0) ? 8 + LG_TABLE[(x >> 8) & 0xff] : LG_TABLE[(x >> 0) & 0xff]);
if (e >= 16) {
y = SQQ_TABLE[x >> ((e - 6) - (e & 1))] << ((e >> 1) - 7);
if (e >= 24) {
y = (y + 1 + x / y) >> 1;
}
y = (y + 1 + x / y) >> 1;
} else if (e >= 8) {
y = (SQQ_TABLE[x >> ((e - 6) - (e & 1))] >> (7 - (e >> 1))) + 1;
} else {
return SQQ_TABLE[x] >> 4;
}
return (x < (y * y)) ? y - 1 : y;
}
/** Multikey introsort for medium size groups. */
private final void ssMintroSort(int PA, int first, int last, int depth) throws IOException {
final int STACK_SIZE = SS_MISORT_STACKSIZE;
StackElement[] stack = new StackElement[STACK_SIZE];
int Td; // T ptr
int a, b, c, d, e, f; // SA ptr
int s, t;
int ssize;
int limit;
int v, x = 0;
for (ssize = 0, limit = ssIlg(last - first); ; ) {
if ((last - first) <= SS_INSERTIONSORT_THRESHOLD) {
if (1 < (last - first)) {
ssInsertionSort(PA, first, last, depth);
}
if (ssize > 0) {
StackElement se = stack[--ssize];
first = se.a;
last = se.b;
depth = se.c;
limit = se.d;
} else {
return;
}
continue;
}
Td = depth;
if (limit-- == 0) {
ssHeapSort(Td, PA, first, last - first);
}
if (limit < 0) {
for (a = first + 1, v = readInput(Td + readSuffixArray(PA + readSuffixArray(first)));
a < last;
++a) {
if ((x = readInput(Td + readSuffixArray(PA + readSuffixArray(a)))) != v) {
if (1 < (a - first)) {
break;
}
v = x;
first = a;
}
}
if (readInput(Td + readSuffixArray(PA + readSuffixArray(first)) - 1) < v) {
first = ssPartition(PA, first, a, depth);
}
if ((a - first) <= (last - a)) {
if (1 < (a - first)) {
stack[ssize++] = new StackElement(a, last, depth, -1);
last = a;
depth += 1;
limit = ssIlg(a - first);
} else {
first = a;
limit = -1;
}
} else {
if (1 < (last - a)) {
stack[ssize++] = new StackElement(first, a, depth + 1, ssIlg(a - first));
first = a;
limit = -1;
} else {
last = a;
depth += 1;
limit = ssIlg(a - first);
}
}
continue;
}
// choose pivot
a = ssPivot(Td, PA, first, last);
v = readInput(Td + readSuffixArray(PA + readSuffixArray(a)));
swapInSA(first, a);
// partition
for (b = first;
(++b < last) && ((x = readInput(Td + readSuffixArray(PA + readSuffixArray(b)))) == v);
) {}
if (((a = b) < last) && (x < v)) {
for (;
(++b < last) && ((x = readInput(Td + readSuffixArray(PA + readSuffixArray(b)))) <= v);
) {
if (x == v) {
swapInSA(b, a);
++a;
}
}
}
for (c = last;
(b < --c) && ((x = readInput(Td + readSuffixArray(PA + readSuffixArray(c)))) == v);
) {}
if ((b < (d = c)) && (x > v)) {
for (;
(b < --c) && ((x = readInput(Td + readSuffixArray(PA + readSuffixArray(c)))) >= v);
) {
if (x == v) {
swapInSA(c, d);
--d;
}
}
}
for (; b < c; ) {
swapInSA(b, c);
for (;
(++b < c) && ((x = readInput(Td + readSuffixArray(PA + readSuffixArray(b)))) <= v);
) {
if (x == v) {
swapInSA(b, a);
++a;
}
}
for (;
(b < --c) && ((x = readInput(Td + readSuffixArray(PA + readSuffixArray(c)))) >= v);
) {
if (x == v) {
swapInSA(c, d);
--d;
}
}
}
if (a <= d) {
c = b - 1;
if ((s = a - first) > (t = b - a)) {
s = t;
}
for (e = first, f = b - s; 0 < s; --s, ++e, ++f) {
swapInSA(e, f);
}
if ((s = d - c) > (t = last - d - 1)) {
s = t;
}
for (e = b, f = last - s; 0 < s; --s, ++e, ++f) {
swapInSA(e, f);
}
a = first + (b - a);
c = last - (d - c);
b =
(v <= readInput(Td + readSuffixArray(PA + readSuffixArray(a)) - 1))
? a
: ssPartition(PA, a, c, depth);
if ((a - first) <= (last - c)) {
if ((last - c) <= (c - b)) {
stack[ssize++] = new StackElement(b, c, depth + 1, ssIlg(c - b));
stack[ssize++] = new StackElement(c, last, depth, limit);
last = a;
} else if ((a - first) <= (c - b)) {
stack[ssize++] = new StackElement(c, last, depth, limit);
stack[ssize++] = new StackElement(b, c, depth + 1, ssIlg(c - b));
last = a;
} else {
stack[ssize++] = new StackElement(c, last, depth, limit);
stack[ssize++] = new StackElement(first, a, depth, limit);
first = b;
last = c;
depth += 1;
limit = ssIlg(c - b);
}
} else {
if ((a - first) <= (c - b)) {
stack[ssize++] = new StackElement(b, c, depth + 1, ssIlg(c - b));
stack[ssize++] = new StackElement(first, a, depth, limit);
first = c;
} else if ((last - c) <= (c - b)) {
stack[ssize++] = new StackElement(first, a, depth, limit);
stack[ssize++] = new StackElement(b, c, depth + 1, ssIlg(c - b));
first = c;
} else {
stack[ssize++] = new StackElement(first, a, depth, limit);
stack[ssize++] = new StackElement(c, last, depth, limit);
first = b;
last = c;
depth += 1;
limit = ssIlg(c - b);
}
}
} else {
limit += 1;
if (readInput(Td + readSuffixArray(PA + readSuffixArray(first)) - 1) < v) {
first = ssPartition(PA, first, last, depth);
limit = ssIlg(last - first);
}
depth += 1;
}
}
}
/**
* Returns the pivot element.
*/
private final int ssPivot(int Td, int PA, int first, int last) throws IOException {
int middle; // SA pointer
int t = last - first;
middle = first + t / 2;
if (t <= 512) {
if (t <= 32) {
return ssMedian3(Td, PA, first, middle, last - 1);
} else {
t >>= 2;
return ssMedian5(Td, PA, first, first + t, middle, last - 1 - t, last - 1);
}
}
t >>= 3;
first = ssMedian3(Td, PA, first, first + t, first + (t << 1));
middle = ssMedian3(Td, PA, middle - t, middle, middle + t);
last = ssMedian3(Td, PA, last - 1 - (t << 1), last - 1 - t, last - 1);
return ssMedian3(Td, PA, first, middle, last);
}
/**
* Returns the median of five elements
*/
private final int ssMedian5(int Td, int PA, int v1, int v2, int v3, int v4, int v5)
throws IOException {
int t;
if (readInput(Td + readSuffixArray(PA + readSuffixArray(v2)))
> readInput(Td + readSuffixArray(PA + readSuffixArray(v3)))) {
t = v2;
v2 = v3;
v3 = t;
}
if (readInput(Td + readSuffixArray(PA + readSuffixArray(v4)))
> readInput(Td + readSuffixArray(PA + readSuffixArray(v5)))) {
t = v4;
v4 = v5;
v5 = t;
}
if (readInput(Td + readSuffixArray(PA + readSuffixArray(v2)))
> readInput(Td + readSuffixArray(PA + readSuffixArray(v4)))) {
t = v2;
v2 = v4;
v4 = t;
t = v3;
v3 = v5;
v5 = t;
}
if (readInput(Td + readSuffixArray(PA + readSuffixArray(v1)))
> readInput(Td + readSuffixArray(PA + readSuffixArray(v3)))) {
t = v1;
v1 = v3;
v3 = t;
}
if (readInput(Td + readSuffixArray(PA + readSuffixArray(v1)))
> readInput(Td + readSuffixArray(PA + readSuffixArray(v4)))) {
t = v1;
v1 = v4;
v4 = t;
t = v3;
v3 = v5;
v5 = t;
}
if (readInput(Td + readSuffixArray(PA + readSuffixArray(v3)))
> readInput(Td + readSuffixArray(PA + readSuffixArray(v4)))) {
return v4;
}
return v3;
}
/**
* Returns the median of three elements.
*/
private final int ssMedian3(int Td, int PA, int v1, int v2, int v3) throws IOException {
if (readInput(Td + readSuffixArray(PA + readSuffixArray(v1)))
> readInput(Td + readSuffixArray(PA + readSuffixArray(v2)))) {
int t = v1;
v1 = v2;
v2 = t;
}
if (readInput(Td + readSuffixArray(PA + readSuffixArray(v2)))
> readInput(Td + readSuffixArray(PA + readSuffixArray(v3)))) {
if (readInput(Td + readSuffixArray(PA + readSuffixArray(v1)))
> readInput(Td + readSuffixArray(PA + readSuffixArray(v3)))) {
return v1;
} else {
return v3;
}
}
return v2;
}
/**
* Binary partition for substrings.
*/
private final int ssPartition(int PA, int first, int last, int depth) throws IOException {
int a, b; // SA pointer
int t;
for (a = first - 1, b = last; ; ) {
for (;
(++a < b)
&& ((readSuffixArray(PA + readSuffixArray(a)) + depth)
>= (readSuffixArray(PA + readSuffixArray(a) + 1) + 1));
) {
writeSuffixArray(a, ~readSuffixArray(a));
}
for (;
(a < --b)
&& ((readSuffixArray(PA + readSuffixArray(b)) + depth)
< (readSuffixArray(PA + readSuffixArray(b) + 1) + 1));
) {}
if (b <= a) {
break;
}
t = ~readSuffixArray(b);
writeSuffixArray(b, readSuffixArray(a));
writeSuffixArray(a, t);
}
if (first < a) {
writeSuffixArray(first, ~readSuffixArray(first));
}
return a;
}
/**
* Simple top-down heapsort.
*/
private final void ssHeapSort(int Td, int PA, int sa, int size) throws IOException {
int i, m, t;
m = size;
if ((size % 2) == 0) {
m--;
if (readInput(Td + readSuffixArray(PA + readSuffixArray(sa + (m / 2))))
< readInput(Td + readSuffixArray(PA + readSuffixArray(sa + m)))) {
swapInSA(sa + m, sa + (m / 2));
}
}
for (i = m / 2 - 1; 0 <= i; --i) {
ssFixDown(Td, PA, sa, i, m);
}
if ((size % 2) == 0) {
swapInSA(sa, sa + m);
ssFixDown(Td, PA, sa, 0, m);
}
for (i = m - 1; 0 < i; --i) {
t = readSuffixArray(sa);
writeSuffixArray(sa, readSuffixArray(sa + i));
ssFixDown(Td, PA, sa, 0, i);
writeSuffixArray(sa + i, t);
}
}
private final void ssFixDown(int Td, int PA, int sa, int i, int size) throws IOException {
int j, k;
int v;
int c, d, e;
for (v = readSuffixArray(sa + i), c = readInput(Td + readSuffixArray(PA + v));
(j = 2 * i + 1) < size;
writeSuffixArray(sa + i, readSuffixArray(sa + k)), i = k) {
d = readInput(Td + readSuffixArray(PA + readSuffixArray(sa + (k = j++))));
if (d < (e = readInput(Td + readSuffixArray(PA + readSuffixArray(sa + j))))) {
k = j;
d = e;
}
if (d <= c) {
break;
}
}
writeSuffixArray(i + sa, v);
}
private static final int ssIlg(int n) {
return ((n & 0xff00) != 0) ? 8 + LG_TABLE[(n >> 8) & 0xff] : LG_TABLE[(n >> 0) & 0xff];
}
private final void swapInSA(int a, int b) throws IOException {
int tmp = readSuffixArray(a);
writeSuffixArray(a, readSuffixArray(b));
writeSuffixArray(b, tmp);
}
/** Tandem repeat sort */
private final void trSort(int ISA, int n, int depth) throws IOException, InterruptedException {
TRBudget budget = new TRBudget(trIlg(n) * 2 / 3, n);
int ISAd;
int first, last; // SA pointers
int t, skip, unsorted;
for (ISAd = ISA + depth; -n < readSuffixArray(0); ISAd += ISAd - ISA) {
if (Thread.interrupted()) {
throw new InterruptedException();
}
first = 0;
skip = 0;
unsorted = 0;
do {
if ((t = readSuffixArray(first)) < 0) {
first -= t;
skip += t;
} else {
if (skip != 0) {
writeSuffixArray(first + skip, skip);
skip = 0;
}
last = readSuffixArray(ISA + t) + 1;
if (1 < (last - first)) {
budget.count = 0;
trIntroSort(ISA, ISAd, first, last, budget);
if (budget.count != 0) {
unsorted += budget.count;
} else {
skip = first - last;
}
} else if ((last - first) == 1) {
skip = -1;
}
first = last;
}
} while (first < n);
if (skip != 0) {
writeSuffixArray(first + skip, skip);
}
if (unsorted == 0) {
break;
}
}
}
private final TRPartitionResult trPartition(
int ISAd, int first, int middle, int last, int v) throws IOException {
int a, b, c, d, e, f; // ptr
int t, s, x = 0;
for (b = middle - 1;
(++b < last) && ((x = readSuffixArray(ISAd + readSuffixArray(b))) == v);
) {}
if (((a = b) < last) && (x < v)) {
for (; (++b < last) && ((x = readSuffixArray(ISAd + readSuffixArray(b))) <= v); ) {
if (x == v) {
swapInSA(a, b);
++a;
}
}
}
for (c = last; (b < --c) && ((x = readSuffixArray(ISAd + readSuffixArray(c))) == v); ) {}
if ((b < (d = c)) && (x > v)) {
for (; (b < --c) && ((x = readSuffixArray(ISAd + readSuffixArray(c))) >= v); ) {
if (x == v) {
swapInSA(c, d);
--d;
}
}
}
for (; b < c; ) {
swapInSA(c, b);
for (; (++b < c) && ((x = readSuffixArray(ISAd + readSuffixArray(b))) <= v); ) {
if (x == v) {
swapInSA(a, b);
++a;
}
}
for (; (b < --c) && ((x = readSuffixArray(ISAd + readSuffixArray(c))) >= v); ) {
if (x == v) {
swapInSA(c, d);
--d;
}
}
}
if (a <= d) {
c = b - 1;
if ((s = a - first) > (t = b - a)) {
s = t;
}
for (e = first, f = b - s; 0 < s; --s, ++e, ++f) {
swapInSA(e, f);
}
if ((s = d - c) > (t = last - d - 1)) {
s = t;
}
for (e = b, f = last - s; 0 < s; --s, ++e, ++f) {
swapInSA(e, f);
}
first += (b - a);
last -= (d - c);
}
return new TRPartitionResult(first, last);
}
private final void trIntroSort(int ISA, int ISAd, int first, int last, TRBudget budget)
throws IOException {
final int STACK_SIZE = TR_STACKSIZE;
StackElement[] stack = new StackElement[STACK_SIZE];
int a = 0, b = 0, c; // pointers
int v, x = 0;
int incr = ISAd - ISA;
int limit, next;
int ssize, trlink = -1;
for (ssize = 0, limit = trIlg(last - first); ; ) {
if (limit < 0) {
if (limit == -1) {
/* tandem repeat partition */
TRPartitionResult res = trPartition(ISAd - incr, first, first, last, last - 1);
a = res.a;
b = res.b;
/* update ranks */
if (a < last) {
for (c = first, v = a - 1; c < a; ++c) {
writeSuffixArray(ISA + readSuffixArray(c), v);
}
}
if (b < last) {
for (c = a, v = b - 1; c < b; ++c) {
writeSuffixArray(ISA + readSuffixArray(c), v);
}
}
/* push */
if (1 < (b - a)) {
stack[ssize++] = new StackElement(0, a, b, 0, 0);
stack[ssize++] = new StackElement(ISAd - incr, first, last, -2, trlink);
trlink = ssize - 2;
}
if ((a - first) <= (last - b)) {
if (1 < (a - first)) {
stack[ssize++] = new StackElement(ISAd, b, last, trIlg(last - b), trlink);
last = a;
limit = trIlg(a - first);
} else if (1 < (last - b)) {
first = b;
limit = trIlg(last - b);
} else {
if (ssize > 0) {
StackElement se = stack[--ssize];
ISAd = se.a;
first = se.b;
last = se.c;
limit = se.d;
trlink = se.e;
} else {
return;
}
}
} else {
if (1 < (last - b)) {
stack[ssize++] = new StackElement(ISAd, first, a, trIlg(a - first), trlink);
first = b;
limit = trIlg(last - b);
} else if (1 < (a - first)) {
last = a;
limit = trIlg(a - first);
} else {
if (ssize > 0) {
StackElement se = stack[--ssize];
ISAd = se.a;
first = se.b;
last = se.c;
limit = se.d;
trlink = se.e;
} else {
return;
}
}
}
} else if (limit == -2) {
/* tandem repeat copy */
StackElement se = stack[--ssize];
a = se.b;
b = se.c;
if (stack[ssize].d == 0) {
trCopy(ISA, first, a, b, last, ISAd - ISA);
} else {
if (0 <= trlink) {
stack[trlink].d = -1;
}
trPartialCopy(ISA, first, a, b, last, ISAd - ISA);
}
if (ssize > 0) {
se = stack[--ssize];
ISAd = se.a;
first = se.b;
last = se.c;
limit = se.d;
trlink = se.e;
} else {
return;
}
} else {
/* sorted partition */
if (0 <= readSuffixArray(first)) {
a = first;
do {
writeSuffixArray(ISA + readSuffixArray(a), a);
} while ((++a < last) && (0 <= readSuffixArray(a)));
first = a;
}
if (first < last) {
a = first;
do {
writeSuffixArray(a, ~readSuffixArray(a));
} while (readSuffixArray(++a) < 0);
next =
(readSuffixArray(ISA + readSuffixArray(a))
!= readSuffixArray(ISAd + readSuffixArray(a)))
? trIlg(a - first + 1)
: -1;
if (++a < last) {
for (b = first, v = a - 1; b < a; ++b) {
writeSuffixArray(ISA + readSuffixArray(b), v);
}
}
/* push */
if (budget.check(a - first) != 0) {
if ((a - first) <= (last - a)) {
stack[ssize++] = new StackElement(ISAd, a, last, -3, trlink);
ISAd += incr;
last = a;
limit = next;
} else {
if (1 < (last - a)) {
stack[ssize++] = new StackElement(ISAd + incr, first, a, next, trlink);
first = a;
limit = -3;
} else {
ISAd += incr;
last = a;
limit = next;
}
}
} else {
if (0 <= trlink) {
stack[trlink].d = -1;
}
if (1 < (last - a)) {
first = a;
limit = -3;
} else {
if (ssize > 0) {
StackElement se = stack[--ssize];
ISAd = se.a;
first = se.b;
last = se.c;
limit = se.d;
trlink = se.e;
} else {
return;
}
}
}
} else {
if (ssize > 0) {
StackElement se = stack[--ssize];
ISAd = se.a;
first = se.b;
last = se.c;
limit = se.d;
trlink = se.e;
} else {
return;
}
}
}
continue;
}
if ((last - first) <= TR_INSERTIONSORT_THRESHOLD) {
trInsertionSort(ISAd, first, last);
limit = -3;
continue;
}
if (limit-- == 0) {
trHeapSort(ISAd, first, last - first);
for (a = last - 1; first < a; a = b) {
for (x = readSuffixArray(ISAd + readSuffixArray(a)), b = a - 1;
(first <= b) && (readSuffixArray(ISAd + readSuffixArray(b)) == x);
--b) {
writeSuffixArray(b, ~readSuffixArray(b));
}
}
limit = -3;
continue;
}
// choose pivot
a = trPivot(ISAd, first, last);
swapInSA(first, a);
v = readSuffixArray(ISAd + readSuffixArray(first));
// partition
TRPartitionResult res = trPartition(ISAd, first, first + 1, last, v);
a = res.a;
b = res.b;
if ((last - first) != (b - a)) {
next = (readSuffixArray(ISA + readSuffixArray(a)) != v) ? trIlg(b - a) : -1;
/* update ranks */
for (c = first, v = a - 1; c < a; ++c) {
writeSuffixArray(ISA + readSuffixArray(c), v);
}
if (b < last) {
for (c = a, v = b - 1; c < b; ++c) {
writeSuffixArray(ISA + readSuffixArray(c), v);
}
}
/* push */
if ((1 < (b - a)) && ((budget.check(b - a) != 0))) {
if ((a - first) <= (last - b)) {
if ((last - b) <= (b - a)) {
if (1 < (a - first)) {
stack[ssize++] = new StackElement(ISAd + incr, a, b, next, trlink);
stack[ssize++] = new StackElement(ISAd, b, last, limit, trlink);
last = a;
} else if (1 < (last - b)) {
stack[ssize++] = new StackElement(ISAd + incr, a, b, next, trlink);
first = b;
} else {
ISAd += incr;
first = a;
last = b;
limit = next;
}
} else if ((a - first) <= (b - a)) {
if (1 < (a - first)) {
stack[ssize++] = new StackElement(ISAd, b, last, limit, trlink);
stack[ssize++] = new StackElement(ISAd + incr, a, b, next, trlink);
last = a;
} else {
stack[ssize++] = new StackElement(ISAd, b, last, limit, trlink);
ISAd += incr;
first = a;
last = b;
limit = next;
}
} else {
stack[ssize++] = new StackElement(ISAd, b, last, limit, trlink);
stack[ssize++] = new StackElement(ISAd, first, a, limit, trlink);
ISAd += incr;
first = a;
last = b;
limit = next;
}
} else {
if ((a - first) <= (b - a)) {
if (1 < (last - b)) {
stack[ssize++] = new StackElement(ISAd + incr, a, b, next, trlink);
stack[ssize++] = new StackElement(ISAd, first, a, limit, trlink);
first = b;
} else if (1 < (a - first)) {
stack[ssize++] = new StackElement(ISAd + incr, a, b, next, trlink);
last = a;
} else {
ISAd += incr;
first = a;
last = b;
limit = next;
}
} else if ((last - b) <= (b - a)) {
if (1 < (last - b)) {
stack[ssize++] = new StackElement(ISAd, first, a, limit, trlink);
stack[ssize++] = new StackElement(ISAd + incr, a, b, next, trlink);
first = b;
} else {
stack[ssize++] = new StackElement(ISAd, first, a, limit, trlink);
ISAd += incr;
first = a;
last = b;
limit = next;
}
} else {
stack[ssize++] = new StackElement(ISAd, first, a, limit, trlink);
stack[ssize++] = new StackElement(ISAd, b, last, limit, trlink);
ISAd += incr;
first = a;
last = b;
limit = next;
}
}
} else {
if ((1 < (b - a)) && (0 <= trlink)) {
stack[trlink].d = -1;
}
if ((a - first) <= (last - b)) {
if (1 < (a - first)) {
stack[ssize++] = new StackElement(ISAd, b, last, limit, trlink);
last = a;
} else if (1 < (last - b)) {
first = b;
} else {
if (ssize > 0) {
StackElement se = stack[--ssize];
ISAd = se.a;
first = se.b;
last = se.c;
limit = se.d;
trlink = se.e;
} else {
return;
}
}
} else {
if (1 < (last - b)) {
stack[ssize++] = new StackElement(ISAd, first, a, limit, trlink);
first = b;
} else if (1 < (a - first)) {
last = a;
} else {
if (ssize > 0) {
StackElement se = stack[--ssize];
ISAd = se.a;
first = se.b;
last = se.c;
limit = se.d;
trlink = se.e;
} else {
return;
}
}
}
}
} else {
if (budget.check(last - first) != 0) {
limit = trIlg(last - first);
ISAd += incr;
} else {
if (0 <= trlink) {
stack[trlink].d = -1;
}
if (ssize > 0) {
StackElement se = stack[--ssize];
ISAd = se.a;
first = se.b;
last = se.c;
limit = se.d;
trlink = se.e;
} else {
return;
}
}
}
}
}
/**
* Returns the pivot element.
*/
private final int trPivot(int ISAd, int first, int last) throws IOException {
int middle;
int t;
t = last - first;
middle = first + t / 2;
if (t <= 512) {
if (t <= 32) {
return trMedian3(ISAd, first, middle, last - 1);
} else {
t >>= 2;
return trMedian5(ISAd, first, first + t, middle, last - 1 - t, last - 1);
}
}
t >>= 3;
first = trMedian3(ISAd, first, first + t, first + (t << 1));
middle = trMedian3(ISAd, middle - t, middle, middle + t);
last = trMedian3(ISAd, last - 1 - (t << 1), last - 1 - t, last - 1);
return trMedian3(ISAd, first, middle, last);
}
/**
* Returns the median of five elements.
*/
private final int trMedian5(int ISAd, int v1, int v2, int v3, int v4, int v5) throws IOException {
int t;
if (readSuffixArray(ISAd + readSuffixArray(v2)) > readSuffixArray(ISAd + readSuffixArray(v3))) {
t = v2;
v2 = v3;
v3 = t;
}
if (readSuffixArray(ISAd + readSuffixArray(v4)) > readSuffixArray(ISAd + readSuffixArray(v5))) {
t = v4;
v4 = v5;
v5 = t;
}
if (readSuffixArray(ISAd + readSuffixArray(v2)) > readSuffixArray(ISAd + readSuffixArray(v4))) {
t = v2;
v2 = v4;
v4 = t;
t = v3;
v3 = v5;
v5 = t;
}
if (readSuffixArray(ISAd + readSuffixArray(v1)) > readSuffixArray(ISAd + readSuffixArray(v3))) {
t = v1;
v1 = v3;
v3 = t;
}
if (readSuffixArray(ISAd + readSuffixArray(v1)) > readSuffixArray(ISAd + readSuffixArray(v4))) {
t = v1;
v1 = v4;
v4 = t;
t = v3;
v3 = v5;
v5 = t;
}
if (readSuffixArray(ISAd + readSuffixArray(v3)) > readSuffixArray(ISAd + readSuffixArray(v4))) {
return v4;
}
return v3;
}
/**
* Returns the median of three elements.
*/
private final int trMedian3(int ISAd, int v1, int v2, int v3) throws IOException {
if (readSuffixArray(ISAd + readSuffixArray(v1)) > readSuffixArray(ISAd + readSuffixArray(v2))) {
int t = v1;
v1 = v2;
v2 = t;
}
if (readSuffixArray(ISAd + readSuffixArray(v2)) > readSuffixArray(ISAd + readSuffixArray(v3))) {
if (readSuffixArray(ISAd + readSuffixArray(v1))
> readSuffixArray(ISAd + readSuffixArray(v3))) {
return v1;
} else {
return v3;
}
}
return v2;
}
private final void trHeapSort(int ISAd, int sa, int size) throws IOException {
int i, m, t;
m = size;
if ((size % 2) == 0) {
m--;
if (readSuffixArray(ISAd + readSuffixArray(sa + m / 2))
< readSuffixArray(ISAd + readSuffixArray(sa + m))) {
swapInSA(sa + m, sa + m / 2);
}
}
for (i = m / 2 - 1; 0 <= i; --i) {
trFixDown(ISAd, sa, i, m);
}
if ((size % 2) == 0) {
swapInSA(sa, sa + m);
trFixDown(ISAd, sa, 0, m);
}
for (i = m - 1; 0 < i; --i) {
t = readSuffixArray(sa);
writeSuffixArray(sa, readSuffixArray(sa + i));
trFixDown(ISAd, sa, 0, i);
writeSuffixArray(sa + i, t);
}
}
private final void trFixDown(int ISAd, int sa, int i, int size) throws IOException {
int j, k;
int v;
int c, d, e;
for (v = readSuffixArray(sa + i), c = readSuffixArray(ISAd + v);
(j = 2 * i + 1) < size;
writeSuffixArray(sa + i, readSuffixArray(sa + k)), i = k) {
d = readSuffixArray(ISAd + readSuffixArray(sa + (k = j++)));
if (d < (e = readSuffixArray(ISAd + readSuffixArray(sa + j)))) {
k = j;
d = e;
}
if (d <= c) {
break;
}
}
writeSuffixArray(sa + i, v);
}
private final void trInsertionSort(int ISAd, int first, int last) throws IOException {
int a, b; // SA ptr
int t, r;
for (a = first + 1; a < last; ++a) {
for (t = readSuffixArray(a), b = a - 1;
0 > (r = readSuffixArray(ISAd + t) - readSuffixArray(ISAd + readSuffixArray(b)));
) {
do {
writeSuffixArray(b + 1, readSuffixArray(b));
} while ((first <= --b) && (readSuffixArray(b) < 0));
if (b < first) {
break;
}
}
if (r == 0) {
writeSuffixArray(b, ~readSuffixArray(b));
}
writeSuffixArray(b + 1, t);
}
}
private final void trPartialCopy(int ISA, int first, int a, int b, int last, int depth)
throws IOException {
int c, d, e; // ptr
int s, v;
int rank, lastrank, newrank = -1;
v = b - 1;
lastrank = -1;
for (c = first, d = a - 1; c <= d; ++c) {
if ((0 <= (s = readSuffixArray(c) - depth)) && (readSuffixArray(ISA + s) == v)) {
writeSuffixArray(++d, s);
rank = readSuffixArray(ISA + s + depth);
if (lastrank != rank) {
lastrank = rank;
newrank = d;
}
writeSuffixArray(ISA + s, newrank);
}
}
lastrank = -1;
for (e = d; first <= e; --e) {
rank = readSuffixArray(ISA + readSuffixArray(e));
if (lastrank != rank) {
lastrank = rank;
newrank = e;
}
if (newrank != rank) {
writeSuffixArray(ISA + readSuffixArray(e), newrank);
}
}
lastrank = -1;
for (c = last - 1, e = d + 1, d = b; e < d; --c) {
if ((0 <= (s = readSuffixArray(c) - depth)) && (readSuffixArray(ISA + s) == v)) {
writeSuffixArray(--d, s);
rank = readSuffixArray(ISA + s + depth);
if (lastrank != rank) {
lastrank = rank;
newrank = d;
}
writeSuffixArray(ISA + s, newrank);
}
}
}
/**
* Sort suffixes of middle partition by using sorted order of suffixes of left and right
* partition.
*/
private final void trCopy(int ISA, int first, int a, int b, int last, int depth)
throws IOException {
int c, d, e; // ptr
int s, v;
v = b - 1;
for (c = first, d = a - 1; c <= d; ++c) {
s = readSuffixArray(c) - depth;
if ((0 <= s) && (readSuffixArray(ISA + s) == v)) {
writeSuffixArray(++d, s);
writeSuffixArray(ISA + s, d);
}
}
for (c = last - 1, e = d + 1, d = b; e < d; --c) {
s = readSuffixArray(c) - depth;
if ((0 <= s) && (readSuffixArray(ISA + s) == v)) {
writeSuffixArray(--d, s);
writeSuffixArray(ISA + s, d);
}
}
}
private static final int trIlg(int n) {
return ((n & 0xffff0000) != 0)
? (((n & 0xff000000) != 0)
? 24 + LG_TABLE[(n >> 24) & 0xff]
: 16 + LG_TABLE[(n >> 16) & 0xff])
: (((n & 0x0000ff00) != 0) ? 8 + LG_TABLE[(n >> 8) & 0xff] : LG_TABLE[(n >> 0) & 0xff]);
}
private static final class StackElement {
final int a, b, c, e;
int d;
StackElement(int a, int b, int c, int d, int e) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.e = e;
}
StackElement(int a, int b, int c, int d) {
this(a, b, c, d, 0);
}
}
private static final class TRBudget {
int chance;
int remain;
int incval;
int count;
private TRBudget(int chance, int incval) {
this.chance = chance;
this.remain = incval;
this.incval = incval;
}
private int check(int size) {
if (size <= this.remain) {
this.remain -= size;
return 1;
}
if (this.chance == 0) {
this.count += size;
return 0;
}
this.remain += this.incval - size;
this.chance -= 1;
return 1;
}
}
private static final class TRPartitionResult {
final int a;
final int b;
public TRPartitionResult(int a, int b) {
this.a = a;
this.b = b;
}
}
private int readInput(long pos) throws IOException {
input.seek(pos);
return input.readUnsignedByte();
}
private int readSuffixArray(long pos) throws IOException {
/*
* This is an ugly hack because the imported code omits the first entry in the suffix array
* (which is always the length of the array) and shifts everything by one. So we do the
* correction here.
*/
suffixArray.seekToIntAligned(pos + 1);
return suffixArray.readInt();
}
private int writeSuffixArray(long pos, int write) throws IOException {
/*
* This is an ugly hack because the imported code omits the first entry in the suffix array
* (which is always the length of the array) and shifts everything by one. So we do the
* correction here.
*/
suffixArray.seekToIntAligned(pos + 1);
suffixArray.writeInt(write);
return write;
}
}