package io.netty.handler.codec.compression;
final class Bzip2DivSufSort {
private static final int STACK_SIZE = 64;
private static final int BUCKET_A_SIZE = 256;
private static final int BUCKET_B_SIZE = 65536;
private static final int SS_BLOCKSIZE = 1024;
private static final int INSERTIONSORT_THRESHOLD = 8;
private static final int[] LOG_2_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
};
private final int[] SA;
private final byte[] T;
private final int n;
Bzip2DivSufSort(final byte[] block, final int[] bwtBlock, final int blockLength) {
T = block;
SA = bwtBlock;
n = blockLength;
}
private static void swapElements(final int[] array1, final int idx1, final int[] array2, final int idx2) {
final int temp = array1[idx1];
array1[idx1] = array2[idx2];
array2[idx2] = temp;
}
private int ssCompare(final int p1, final int p2, final int depth) {
final int[] SA = this.SA;
final byte[] T = this.T;
final int U1n = SA[p1 + 1] + 2;
final int U2n = SA[p2 + 1] + 2;
int U1 = depth + SA[p1];
int U2 = depth + SA[p2];
while (U1 < U1n && U2 < U2n && T[U1] == T[U2]) {
++U1;
++U2;
}
return U1 < U1n ?
U2 < U2n ? (T[U1] & 0xff) - (T[U2] & 0xff) : 1
: U2 < U2n ? -1 : 0;
}
private int ssCompareLast(int pa, int p1, int p2, int depth, int size) {
final int[] SA = this.SA;
final byte[] T = this.T;
int U1 = depth + SA[p1];
int U2 = depth + SA[p2];
int U1n = size;
int U2n = SA[p2 + 1] + 2;
while (U1 < U1n && U2 < U2n && T[U1] == T[U2]) {
++U1;
++U2;
}
if (U1 < U1n) {
return U2 < U2n ? (T[U1] & 0xff) - (T[U2] & 0xff) : 1;
}
if (U2 == U2n) {
return 1;
}
U1 %= size;
U1n = SA[pa] + 2;
while (U1 < U1n && U2 < U2n && T[U1] == T[U2]) {
++U1;
++U2;
}
return U1 < U1n ?
U2 < U2n ? (T[U1] & 0xff) - (T[U2] & 0xff) : 1
: U2 < U2n ? -1 : 0;
}
private void ssInsertionSort(int pa, int first, int last, int depth) {
final int[] SA = this.SA;
int i, j;
int t;
int r;
for (i = last - 2; first <= i; --i) {
for (t = SA[i], j = i + 1; 0 < (r = ssCompare(pa + t, pa + SA[j], depth));) {
do {
SA[j - 1] = SA[j];
} while (++j < last && SA[j] < 0);
if (last <= j) {
break;
}
}
if (r == 0) {
SA[j] = ~SA[j];
}
SA[j - 1] = t;
}
}
private void ssFixdown(int td, int pa, int sa, int i, int size) {
final int[] SA = this.SA;
final byte[] T = this.T;
int j, k;
int v;
int c, d, e;
for (v = SA[sa + i], c = T[td + SA[pa + v]] & 0xff; (j = 2 * i + 1) < size; SA[sa + i] = SA[sa + k], i = k) {
d = T[td + SA[pa + SA[sa + (k = j++)]]] & 0xff;
if (d < (e = T[td + SA[pa + SA[sa + j]]] & 0xff)) {
k = j;
d = e;
}
if (d <= c) {
break;
}
}
SA[sa + i] = v;
}
private void ssHeapSort(int td, int pa, int sa, int size) {
final int[] SA = this.SA;
final byte[] T = this.T;
int i, m;
int t;
m = size;
if (size % 2 == 0) {
m--;
if ((T[td + SA[pa + SA[sa + m / 2]]] & 0xff) < (T[td + SA[pa + SA[sa + m]]] & 0xff)) {
swapElements(SA, sa + m, SA, sa + m / 2);
}
}
for (i = m / 2 - 1; 0 <= i; --i) {
ssFixdown(td, pa, sa, i, m);
}
if (size % 2 == 0) {
swapElements(SA, sa, SA, sa + m);
ssFixdown(td, pa, sa, 0, m);
}
for (i = m - 1; 0 < i; --i) {
t = SA[sa];
SA[sa] = SA[sa + i];
ssFixdown(td, pa, sa, 0, i);
SA[sa + i] = t;
}
}
private int ssMedian3(final int td, final int pa, int v1, int v2, int v3) {
final int[] SA = this.SA;
final byte[] T = this.T;
int T_v1 = T[td + SA[pa + SA[v1]]] & 0xff;
int T_v2 = T[td + SA[pa + SA[v2]]] & 0xff;
int T_v3 = T[td + SA[pa + SA[v3]]] & 0xff;
if (T_v1 > T_v2) {
final int temp = v1;
v1 = v2;
v2 = temp;
final int T_vtemp = T_v1;
T_v1 = T_v2;
T_v2 = T_vtemp;
}
if (T_v2 > T_v3) {
if (T_v1 > T_v3) {
return v1;
}
return v3;
}
return v2;
}
private int ssMedian5(final int td, final int pa, int v1, int v2, int v3, int v4, int v5) {
final int[] SA = this.SA;
final byte[] T = this.T;
int T_v1 = T[td + SA[pa + SA[v1]]] & 0xff;
int T_v2 = T[td + SA[pa + SA[v2]]] & 0xff;
int T_v3 = T[td + SA[pa + SA[v3]]] & 0xff;
int T_v4 = T[td + SA[pa + SA[v4]]] & 0xff;
int T_v5 = T[td + SA[pa + SA[v5]]] & 0xff;
int temp;
int T_vtemp;
if (T_v2 > T_v3) {
temp = v2;
v2 = v3;
v3 = temp;
T_vtemp = T_v2;
T_v2 = T_v3;
T_v3 = T_vtemp;
}
if (T_v4 > T_v5) {
temp = v4;
v4 = v5;
v5 = temp;
T_vtemp = T_v4;
T_v4 = T_v5;
T_v5 = T_vtemp;
}
if (T_v2 > T_v4) {
temp = v2;
v4 = temp;
T_vtemp = T_v2;
T_v4 = T_vtemp;
temp = v3;
v3 = v5;
v5 = temp;
T_vtemp = T_v3;
T_v3 = T_v5;
T_v5 = T_vtemp;
}
if (T_v1 > T_v3) {
temp = v1;
v1 = v3;
v3 = temp;
T_vtemp = T_v1;
T_v1 = T_v3;
T_v3 = T_vtemp;
}
if (T_v1 > T_v4) {
temp = v1;
v4 = temp;
T_vtemp = T_v1;
T_v4 = T_vtemp;
v3 = v5;
T_v3 = T_v5;
}
if (T_v3 > T_v4) {
return v4;
}
return v3;
}
private int ssPivot(final int td, final int pa, final int first, final int last) {
int middle;
int t;
t = last - first;
middle = first + t / 2;
if (t <= 512) {
if (t <= 32) {
return ssMedian3(td, pa, first, middle, last - 1);
}
t >>= 2;
return ssMedian5(td, pa, first, first + t, middle, last - 1 - t, last - 1);
}
t >>= 3;
return ssMedian3(
td, pa,
ssMedian3(td, pa, first, first + t, first + (t << 1)),
ssMedian3(td, pa, middle - t, middle, middle + t),
ssMedian3(td, pa, last - 1 - (t << 1), last - 1 - t, last - 1)
);
}
private static int ssLog(final int n) {
return (n & 0xff00) != 0 ?
8 + LOG_2_TABLE[n >> 8 & 0xff]
: LOG_2_TABLE[n & 0xff];
}
private int ssSubstringPartition(final int pa, final int first, final int last, final int depth) {
final int[] SA = this.SA;
int a, b;
int t;
for (a = first - 1, b = last;;) {
while (++a < b && (SA[pa + SA[a]] + depth >= SA[pa + SA[a] + 1] + 1)) {
SA[a] = ~SA[a];
}
--b;
while (a < b && (SA[pa + SA[b]] + depth < SA[pa + SA[b] + 1] + 1)) {
--b;
}
if (b <= a) {
break;
}
t = ~SA[b];
SA[b] = SA[a];
SA[a] = t;
}
if (first < a) {
SA[first] = ~SA[first];
}
return a;
}
private static class StackEntry {
final int a;
final int b;
final int c;
final int d;
StackEntry(final int a, final int b, final int c, final int d) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
}
private void ssMultiKeyIntroSort(final int pa, int first, int last, int depth) {
final int[] SA = this.SA;
final byte[] T = this.T;
final StackEntry[] stack = new StackEntry[STACK_SIZE];
int Td;
int a, b, c, d, e, f;
int s, t;
int ssize;
int limit;
int v, x = 0;
for (ssize = 0, limit = ssLog(last - first);;) {
if (last - first <= INSERTIONSORT_THRESHOLD) {
if (1 < last - first) {
ssInsertionSort(pa, first, last, depth);
}
if (ssize == 0) {
return;
}
StackEntry entry = stack[--ssize];
first = entry.a;
last = entry.b;
depth = entry.c;
limit = entry.d;
continue;
}
Td = depth;
if (limit-- == 0) {
ssHeapSort(Td, pa, first, last - first);
}
if (limit < 0) {
for (a = first + 1, v = T[Td + SA[pa + SA[first]]] & 0xff; a < last; ++a) {
if ((x = T[Td + SA[pa + SA[a]]] & 0xff) != v) {
if (1 < a - first) {
break;
}
v = x;
first = a;
}
}
if ((T[Td + SA[pa + SA[first]] - 1] & 0xff) < v) {
first = ssSubstringPartition(pa, first, a, depth);
}
if (a - first <= last - a) {
if (1 < a - first) {
stack[ssize++] = new StackEntry(a, last, depth, -1);
last = a;
depth += 1;
limit = ssLog(a - first);
} else {
first = a;
limit = -1;
}
} else {
if (1 < last - a) {
stack[ssize++] = new StackEntry(first, a, depth + 1, ssLog(a - first));
first = a;
limit = -1;
} else {
last = a;
depth += 1;
limit = ssLog(a - first);
}
}
continue;
}
a = ssPivot(Td, pa, first, last);
v = T[Td + SA[pa + SA[a]]] & 0xff;
swapElements(SA, first, SA, a);
b = first + 1;
while (b < last && (x = T[Td + SA[pa + SA[b]]] & 0xff) == v) {
++b;
}
if ((a = b) < last && x < v) {
while (++b < last && (x = T[Td + SA[pa + SA[b]]] & 0xff) <= v) {
if (x == v) {
swapElements(SA, b, SA, a);
++a;
}
}
}
c = last - 1;
while (b < c && (x = T[Td + SA[pa + SA[c]]] & 0xff) == v) {
--c;
}
if (b < (d = c) && x > v) {
while (b < --c && (x = T[Td + SA[pa + SA[c]]] & 0xff) >= v) {
if (x == v) {
swapElements(SA, c, SA, d);
--d;
}
}
}
while (b < c) {
swapElements(SA, b, SA, c);
while (++b < c && (x = T[Td + SA[pa + SA[b]]] & 0xff) <= v) {
if (x == v) {
swapElements(SA, b, SA, a);
++a;
}
}
while (b < --c && (x = T[Td + SA[pa + SA[c]]] & 0xff) >= v) {
if (x == v) {
swapElements(SA, c, SA, 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) {
swapElements(SA, e, SA, f);
}
if ((s = d - c) > (t = last - d - 1)) {
s = t;
}
for (e = b, f = last - s; 0 < s; --s, ++e, ++f) {
swapElements(SA, e, SA, f);
}
a = first + (b - a);
c = last - (d - c);
b = v <= (T[Td + SA[pa + SA[a]] - 1] & 0xff) ? a : ssSubstringPartition(pa, a, c, depth);
if (a - first <= last - c) {
if (last - c <= c - b) {
stack[ssize++] = new StackEntry(b, c, depth + 1, ssLog(c - b));
stack[ssize++] = new StackEntry(c, last, depth, limit);
last = a;
} else if (a - first <= c - b) {
stack[ssize++] = new StackEntry(c, last, depth, limit);
stack[ssize++] = new StackEntry(b, c, depth + 1, ssLog(c - b));
last = a;
} else {
stack[ssize++] = new StackEntry(c, last, depth, limit);
stack[ssize++] = new StackEntry(first, a, depth, limit);
first = b;
last = c;
depth += 1;
limit = ssLog(c - b);
}
} else {
if (a - first <= c - b) {
stack[ssize++] = new StackEntry(b, c, depth + 1, ssLog(c - b));
stack[ssize++] = new StackEntry(first, a, depth, limit);
first = c;
} else if (last - c <= c - b) {
stack[ssize++] = new StackEntry(first, a, depth, limit);
stack[ssize++] = new StackEntry(b, c, depth + 1, ssLog(c - b));
first = c;
} else {
stack[ssize++] = new StackEntry(first, a, depth, limit);
stack[ssize++] = new StackEntry(c, last, depth, limit);
first = b;
last = c;
depth += 1;
limit = ssLog(c - b);
}
}
} else {
limit += 1;
if ((T[Td + SA[pa + SA[first]] - 1] & 0xff) < v) {
first = ssSubstringPartition(pa, first, last, depth);
limit = ssLog(last - first);
}
depth += 1;
}
}
}
private static void ssBlockSwap(final int[] array1, final int first1,
final int[] array2, final int first2, final int size) {
int a, b;
int i;
for (i = size, a = first1, b = first2; 0 < i; --i, ++a, ++b) {
swapElements(array1, a, array2, b);
}
}
private void ssMergeForward(final int pa, int[] buf, final int bufoffset,
final int first, final int middle, final int last, final int depth) {
final int[] SA = this.SA;
int bufend;
int i, j, k;
int t;
int r;
bufend = bufoffset + (middle - first) - 1;
ssBlockSwap(buf, bufoffset, SA, first, middle - first);
for (t = SA[first], i = first, j = bufoffset, k = middle;;) {
r = ssCompare(pa + buf[j], pa + SA[k], depth);
if (r < 0) {
do {
SA[i++] = buf[j];
if (bufend <= j) {
buf[j] = t;
return;
}
buf[j++] = SA[i];
} while (buf[j] < 0);
} else if (r > 0) {
do {
SA[i++] = SA[k];
SA[k++] = SA[i];
if (last <= k) {
while (j < bufend) { SA[i++] = buf[j]; buf[j++] = SA[i]; }
SA[i] = buf[j]; buf[j] = t;
return;
}
} while (SA[k] < 0);
} else {
SA[k] = ~SA[k];
do {
SA[i++] = buf[j];
if (bufend <= j) {
buf[j] = t;
return;
}
buf[j++] = SA[i];
} while (buf[j] < 0);
do {
SA[i++] = SA[k];
SA[k++] = SA[i];
if (last <= k) {
while (j < bufend) {
SA[i++] = buf[j];
buf[j++] = SA[i];
}
SA[i] = buf[j]; buf[j] = t;
return;
}
} while (SA[k] < 0);
}
}
}
private void ssMergeBackward(final int pa, int[] buf, final int bufoffset,
final int first, final int middle, final int last, final int depth) {
final int[] SA = this.SA;
int p1, p2;
int bufend;
int i, j, k;
int t;
int r;
int x;
bufend = bufoffset + (last - middle);
ssBlockSwap(buf, bufoffset, SA, middle, last - middle);
x = 0;
if (buf[bufend - 1] < 0) {
x |= 1;
p1 = pa + ~buf[bufend - 1];
} else {
p1 = pa + buf[bufend - 1];
}
if (SA[middle - 1] < 0) {
x |= 2;
p2 = pa + ~SA[middle - 1];
} else {
p2 = pa + SA[middle - 1];
}
for (t = SA[last - 1], i = last - 1, j = bufend - 1, k = middle - 1;;) {
r = ssCompare(p1, p2, depth);
if (r > 0) {
if ((x & 1) != 0) {
do {
SA[i--] = buf[j];
buf[j--] = SA[i];
} while (buf[j] < 0);
x ^= 1;
}
SA[i--] = buf[j];
if (j <= bufoffset) {
buf[j] = t;
return;
}
buf[j--] = SA[i];
if (buf[j] < 0) {
x |= 1;
p1 = pa + ~buf[j];
} else {
p1 = pa + buf[j];
}
} else if (r < 0) {
if ((x & 2) != 0) {
do {
SA[i--] = SA[k];
SA[k--] = SA[i];
} while (SA[k] < 0);
x ^= 2;
}
SA[i--] = SA[k];
SA[k--] = SA[i];
if (k < first) {
while (bufoffset < j) {
SA[i--] = buf[j];
buf[j--] = SA[i];
}
SA[i] = buf[j];
buf[j] = t;
return;
}
if (SA[k] < 0) {
x |= 2;
p2 = pa + ~SA[k];
} else {
p2 = pa + SA[k];
}
} else {
if ((x & 1) != 0) {
do {
SA[i--] = buf[j];
buf[j--] = SA[i];
} while (buf[j] < 0);
x ^= 1;
}
SA[i--] = ~buf[j];
if (j <= bufoffset) {
buf[j] = t;
return;
}
buf[j--] = SA[i];
if ((x & 2) != 0) {
do {
SA[i--] = SA[k];
SA[k--] = SA[i];
} while (SA[k] < 0);
x ^= 2;
}
SA[i--] = SA[k];
SA[k--] = SA[i];
if (k < first) {
while (bufoffset < j) {
SA[i--] = buf[j];
buf[j--] = SA[i];
}
SA[i] = buf[j];
buf[j] = t;
return;
}
if (buf[j] < 0) {
x |= 1;
p1 = pa + ~buf[j];
} else {
p1 = pa + buf[j];
}
if (SA[k] < 0) {
x |= 2;
p2 = pa + ~SA[k];
} else {
p2 = pa + SA[k];
}
}
}
}
private static int getIDX(final int a) {
return 0 <= a ? a : ~a;
}
private void ssMergeCheckEqual(final int pa, final int depth, final int a) {
final int[] SA = this.SA;
if (0 <= SA[a] && ssCompare(pa + getIDX(SA[a - 1]), pa + SA[a], depth) == 0) {
SA[a] = ~SA[a];
}
}
private void ssMerge(final int pa, int first, int middle, int last, int[] buf,
final int bufoffset, final int bufsize, final int depth) {
final int[] SA = this.SA;
final StackEntry[] stack = new StackEntry[STACK_SIZE];
int i, j;
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, buf, bufoffset, first, middle, last, depth);
}
if ((check & 1) != 0) {
ssMergeCheckEqual(pa, depth, first);
}
if ((check & 2) != 0) {
ssMergeCheckEqual(pa, depth, last);
}
if (ssize == 0) {
return;
}
StackEntry entry = stack[--ssize];
first = entry.a;
middle = entry.b;
last = entry.c;
check = entry.d;
continue;
}
if (middle - first <= bufsize) {
if (first < middle) {
ssMergeForward(pa, buf, bufoffset, first, middle, last, depth);
}
if ((check & 1) != 0) {
ssMergeCheckEqual(pa, depth, first);
}
if ((check & 2) != 0) {
ssMergeCheckEqual(pa, depth, last);
}
if (ssize == 0) {
return;
}
StackEntry entry = stack[--ssize];
first = entry.a;
middle = entry.b;
last = entry.c;
check = entry.d;
continue;
}
for (m = 0, len = Math.min(middle - first, last - middle), half = len >> 1;
0 < len;
len = half, half >>= 1) {
if (ssCompare(pa + getIDX(SA[middle + m + half]),
pa + getIDX(SA[middle - m - half - 1]), depth) < 0) {
m += half + 1;
half -= (len & 1) ^ 1;
}
}
if (0 < m) {
ssBlockSwap(SA, middle - m, SA, middle, m);
i = j = middle;
next = 0;
if (middle + m < last) {
if (SA[middle + m] < 0) {
while (SA[i - 1] < 0) {
--i;
}
SA[middle + m] = ~SA[middle + m];
}
for (j = middle; SA[j] < 0;) {
++j;
}
next = 1;
}
if (i - first <= last - j) {
stack[ssize++] = new StackEntry(j, middle + m, last, (check & 2) | (next & 1));
middle -= m;
last = i;
check &= 1;
} else {
if (i == middle && middle == j) {
next <<= 1;
}
stack[ssize++] = new StackEntry(first, middle - m, i, (check & 1) | (next & 2));
first = j;
middle += m;
check = (check & 2) | (next & 1);
}
} else {
if ((check & 1) != 0) {
ssMergeCheckEqual(pa, depth, first);
}
ssMergeCheckEqual(pa, depth, middle);
if ((check & 2) != 0) {
ssMergeCheckEqual(pa, depth, last);
}
if (ssize == 0) {
return;
}
StackEntry entry = stack[--ssize];
first = entry.a;
middle = entry.b;
last = entry.c;
check = entry.d;
}
}
}
private void subStringSort(final int pa, int first, final int last,
final int[] buf, final int bufoffset, final int bufsize,
final int depth, final boolean lastsuffix, final int size) {
final int[] SA = this.SA;
int a, b;
int[] curbuf;
int curbufoffset;
int i, j, k;
int curbufsize;
if (lastsuffix) {
++first;
}
for (a = first, i = 0; a + SS_BLOCKSIZE < last; a += SS_BLOCKSIZE, ++i) {
ssMultiKeyIntroSort(pa, a, a + SS_BLOCKSIZE, depth);
curbuf = SA;
curbufoffset = a + SS_BLOCKSIZE;
curbufsize = last - (a + SS_BLOCKSIZE);
if (curbufsize <= bufsize) {
curbufsize = bufsize;
curbuf = buf;
curbufoffset = bufoffset;
}
for (b = a, k = SS_BLOCKSIZE, j = i; (j & 1) != 0; b -= k, k <<= 1, j >>>= 1) {
ssMerge(pa, b - k, b, b + k, curbuf, curbufoffset, curbufsize, depth);
}
}
ssMultiKeyIntroSort(pa, a, last, depth);
for (k = SS_BLOCKSIZE; i != 0; k <<= 1, i >>= 1) {
if ((i & 1) != 0) {
ssMerge(pa, a - k, a, last, buf, bufoffset, bufsize, depth);
a -= k;
}
}
if (lastsuffix) {
int r;
for (a = first, i = SA[first - 1], r = 1;
a < last && (SA[a] < 0 || 0 < (r = ssCompareLast(pa, pa + i, pa + SA[a], depth, size)));
++a) {
SA[a - 1] = SA[a];
}
if (r == 0) {
SA[a] = ~SA[a];
}
SA[a - 1] = i;
}
}
private int trGetC(final int isa, final int isaD, final int isaN, final int p) {
return isaD + p < isaN ?
SA[isaD + p]
: SA[isa + ((isaD - isa + p) % (isaN - isa))];
}
private void trFixdown(final int isa, final int isaD, final int isaN, final int sa, int i, final int size) {
final int[] SA = this.SA;
int j, k;
int v;
int c, d, e;
for (v = SA[sa + i], c = trGetC(isa, isaD, isaN, v); (j = 2 * i + 1) < size; SA[sa + i] = SA[sa + k], i = k) {
k = j++;
d = trGetC(isa, isaD, isaN, SA[sa + k]);
if (d < (e = trGetC(isa, isaD, isaN, SA[sa + j]))) {
k = j;
d = e;
}
if (d <= c) {
break;
}
}
SA[sa + i] = v;
}
private void trHeapSort(final int isa, final int isaD, final int isaN, final int sa, final int size) {
final int[] SA = this.SA;
int i, m;
int t;
m = size;
if (size % 2 == 0) {
m--;
if (trGetC(isa, isaD, isaN, SA[sa + m / 2]) < trGetC(isa, isaD, isaN, SA[sa + m])) {
swapElements(SA, sa + m, SA, sa + m / 2);
}
}
for (i = m / 2 - 1; 0 <= i; --i) {
trFixdown(isa, isaD, isaN, sa, i, m);
}
if (size % 2 == 0) {
swapElements(SA, sa, SA, sa + m);
trFixdown(isa, isaD, isaN, sa, 0, m);
}
for (i = m - 1; 0 < i; --i) {
t = SA[sa];
SA[sa] = SA[sa + i];
trFixdown(isa, isaD, isaN, sa, 0, i);
SA[sa + i] = t;
}
}
private void trInsertionSort(final int isa, final int isaD, final int isaN, int first, int last) {
final int[] SA = this.SA;
int a, b;
int t, r;
for (a = first + 1; a < last; ++a) {
for (t = SA[a], b = a - 1; 0 > (r = trGetC(isa, isaD, isaN, t) - trGetC(isa, isaD, isaN, SA[b]));) {
do {
SA[b + 1] = SA[b];
} while (first <= --b && SA[b] < 0);
if (b < first) {
break;
}
}
if (r == 0) {
SA[b] = ~SA[b];
}
SA[b + 1] = t;
}
}
private static int trLog(int n) {
return (n & 0xffff0000) != 0 ?
(n & 0xff000000) != 0 ? 24 + LOG_2_TABLE[n >> 24 & 0xff] : LOG_2_TABLE[n >> 16 & 0xff + 16]
: (n & 0x0000ff00) != 0 ? 8 + LOG_2_TABLE[n >> 8 & 0xff] : LOG_2_TABLE[n & 0xff];
}
private int trMedian3(final int isa, final int isaD, final int isaN, int v1, int v2, int v3) {
final int[] SA = this.SA;
int SA_v1 = trGetC(isa, isaD, isaN, SA[v1]);
int SA_v2 = trGetC(isa, isaD, isaN, SA[v2]);
int SA_v3 = trGetC(isa, isaD, isaN, SA[v3]);
if (SA_v1 > SA_v2) {
final int temp = v1;
v1 = v2;
v2 = temp;
final int SA_vtemp = SA_v1;
SA_v1 = SA_v2;
SA_v2 = SA_vtemp;
}
if (SA_v2 > SA_v3) {
if (SA_v1 > SA_v3) {
return v1;
}
return v3;
}
return v2;
}
private int trMedian5(final int isa, final int isaD, final int isaN, int v1, int v2, int v3, int v4, int v5) {
final int[] SA = this.SA;
int SA_v1 = trGetC(isa, isaD, isaN, SA[v1]);
int SA_v2 = trGetC(isa, isaD, isaN, SA[v2]);
int SA_v3 = trGetC(isa, isaD, isaN, SA[v3]);
int SA_v4 = trGetC(isa, isaD, isaN, SA[v4]);
int SA_v5 = trGetC(isa, isaD, isaN, SA[v5]);
int temp;
int SA_vtemp;
if (SA_v2 > SA_v3) {
temp = v2;
v2 = v3;
v3 = temp;
SA_vtemp = SA_v2;
SA_v2 = SA_v3;
SA_v3 = SA_vtemp;
}
if (SA_v4 > SA_v5) {
temp = v4;
v4 = v5;
v5 = temp;
SA_vtemp = SA_v4;
SA_v4 = SA_v5;
SA_v5 = SA_vtemp;
}
if (SA_v2 > SA_v4) {
temp = v2;
v4 = temp;
SA_vtemp = SA_v2;
SA_v4 = SA_vtemp;
temp = v3;
v3 = v5;
v5 = temp;
SA_vtemp = SA_v3;
SA_v3 = SA_v5;
SA_v5 = SA_vtemp;
}
if (SA_v1 > SA_v3) {
temp = v1;
v1 = v3;
v3 = temp;
SA_vtemp = SA_v1;
SA_v1 = SA_v3;
SA_v3 = SA_vtemp;
}
if (SA_v1 > SA_v4) {
temp = v1;
v4 = temp;
SA_vtemp = SA_v1;
SA_v4 = SA_vtemp;
v3 = v5;
SA_v3 = SA_v5;
}
if (SA_v3 > SA_v4) {
return v4;
}
return v3;
}
private int trPivot(final int isa, final int isaD, final int isaN, final int first, final int last) {
final int middle;
int t;
t = last - first;
middle = first + t / 2;
if (t <= 512) {
if (t <= 32) {
return trMedian3(isa, isaD, isaN, first, middle, last - 1);
}
t >>= 2;
return trMedian5(
isa, isaD, isaN,
first, first + t,
middle,
last - 1 - t, last - 1
);
}
t >>= 3;
return trMedian3(
isa, isaD, isaN,
trMedian3(isa, isaD, isaN, first, first + t, first + (t << 1)),
trMedian3(isa, isaD, isaN, middle - t, middle, middle + t),
trMedian3(isa, isaD, isaN, last - 1 - (t << 1), last - 1 - t, last - 1)
);
}
private void lsUpdateGroup(final int isa, final int first, final int last) {
final int[] SA = this.SA;
int a, b;
int t;
for (a = first; a < last; ++a) {
if (0 <= SA[a]) {
b = a;
do {
SA[isa + SA[a]] = a;
} while (++a < last && 0 <= SA[a]);
SA[b] = b - a;
if (last <= a) {
break;
}
}
b = a;
do {
SA[a] = ~SA[a];
} while (SA[++a] < 0);
t = a;
do {
SA[isa + SA[b]] = t;
} while (++b <= a);
}
}
private void lsIntroSort(final int isa, final int isaD, final int isaN, int first, int last) {
final int[] SA = this.SA;
final StackEntry[] stack = new StackEntry[STACK_SIZE];
int a, b, c, d, e, f;
int s, t;
int limit;
int v, x = 0;
int ssize;
for (ssize = 0, limit = trLog(last - first);;) {
if (last - first <= INSERTIONSORT_THRESHOLD) {
if (1 < last - first) {
trInsertionSort(isa, isaD, isaN, first, last);
lsUpdateGroup(isa, first, last);
} else if (last - first == 1) {
SA[first] = -1;
}
if (ssize == 0) {
return;
}
StackEntry entry = stack[--ssize];
first = entry.a;
last = entry.b;
limit = entry.c;
continue;
}
if (limit-- == 0) {
trHeapSort(isa, isaD, isaN, first, last - first);
for (a = last - 1; first < a; a = b) {
for (x = trGetC(isa, isaD, isaN, SA[a]), b = a - 1;
first <= b && trGetC(isa, isaD, isaN, SA[b]) == x;
--b) {
SA[b] = ~SA[b];
}
}
lsUpdateGroup(isa, first, last);
if (ssize == 0) {
return;
}
StackEntry entry = stack[--ssize];
first = entry.a;
last = entry.b;
limit = entry.c;
continue;
}
a = trPivot(isa, isaD, isaN, first, last);
swapElements(SA, first, SA, a);
v = trGetC(isa, isaD, isaN, SA[first]);
b = first + 1;
while (b < last && (x = trGetC(isa, isaD, isaN, SA[b])) == v) {
++b;
}
if ((a = b) < last && x < v) {
while (++b < last && (x = trGetC(isa, isaD, isaN, SA[b])) <= v) {
if (x == v) {
swapElements(SA, b, SA, a);
++a;
}
}
}
c = last - 1;
while (b < c && (x = trGetC(isa, isaD, isaN, SA[c])) == v) {
--c;
}
if (b < (d = c) && x > v) {
while (b < --c && (x = trGetC(isa, isaD, isaN, SA[c])) >= v) {
if (x == v) {
swapElements(SA, c, SA, d);
--d;
}
}
}
while (b < c) {
swapElements(SA, b, SA, c);
while (++b < c && (x = trGetC(isa, isaD, isaN, SA[b])) <= v) {
if (x == v) {
swapElements(SA, b, SA, a);
++a;
}
}
while (b < --c && (x = trGetC(isa, isaD, isaN, SA[c])) >= v) {
if (x == v) {
swapElements(SA, c, SA, 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) {
swapElements(SA, e, SA, f);
}
if ((s = d - c) > (t = last - d - 1)) {
s = t;
}
for (e = b, f = last - s; 0 < s; --s, ++e, ++f) {
swapElements(SA, e, SA, f);
}
a = first + (b - a);
b = last - (d - c);
for (c = first, v = a - 1; c < a; ++c) {
SA[isa + SA[c]] = v;
}
if (b < last) {
for (c = a, v = b - 1; c < b; ++c) {
SA[isa + SA[c]] = v;
}
}
if ((b - a) == 1) {
SA[a] = - 1;
}
if (a - first <= last - b) {
if (first < a) {
stack[ssize++] = new StackEntry(b, last, limit, 0);
last = a;
} else {
first = b;
}
} else {
if (b < last) {
stack[ssize++] = new StackEntry(first, a, limit, 0);
first = b;
} else {
last = a;
}
}
} else {
if (ssize == 0) {
return;
}
StackEntry entry = stack[--ssize];
first = entry.a;
last = entry.b;
limit = entry.c;
}
}
}
private void lsSort(final int isa, final int n, final int depth) {
final int[] SA = this.SA;
int isaD;
int first, last, i;
int t, skip;
for (isaD = isa + depth; -n < SA[0]; isaD += isaD - isa) {
first = 0;
skip = 0;
do {
if ((t = SA[first]) < 0) {
first -= t;
skip += t;
} else {
if (skip != 0) {
SA[first + skip] = skip;
skip = 0;
}
last = SA[isa + t] + 1;
lsIntroSort(isa, isaD, isa + n, first, last);
first = last;
}
} while (first < n);
if (skip != 0) {
SA[first + skip] = skip;
}
if (n < isaD - isa) {
first = 0;
do {
if ((t = SA[first]) < 0) {
first -= t;
} else {
last = SA[isa + t] + 1;
for (i = first; i < last; ++i) {
SA[isa + SA[i]] = i;
}
first = last;
}
} while (first < n);
break;
}
}
}
private static class PartitionResult {
final int first;
final int last;
PartitionResult(final int first, final int last) {
this.first = first;
this.last = last;
}
}
private PartitionResult trPartition(final int isa, final int isaD, final int isaN,
int first, int last, final int v) {
final int[] SA = this.SA;
int a, b, c, d, e, f;
int t, s;
int x = 0;
b = first;
while (b < last && (x = trGetC(isa, isaD, isaN, SA[b])) == v) {
++b;
}
if ((a = b) < last && x < v) {
while (++b < last && (x = trGetC(isa, isaD, isaN, SA[b])) <= v) {
if (x == v) {
swapElements(SA, b, SA, a);
++a;
}
}
}
c = last - 1;
while (b < c && (x = trGetC(isa, isaD, isaN, SA[c])) == v) {
--c;
}
if (b < (d = c) && x > v) {
while (b < --c && (x = trGetC(isa, isaD, isaN, SA[c])) >= v) {
if (x == v) {
swapElements(SA, c, SA, d);
--d;
}
}
}
while (b < c) {
swapElements(SA, b, SA, c);
while (++b < c && (x = trGetC(isa, isaD, isaN, SA[b])) <= v) {
if (x == v) {
swapElements(SA, b, SA, a);
++a;
}
}
while (b < --c && (x = trGetC(isa, isaD, isaN, SA[c])) >= v) {
if (x == v) {
swapElements(SA, c, SA, 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) {
swapElements(SA, e, SA, f);
}
if ((s = d - c) > (t = last - d - 1)) {
s = t;
}
for (e = b, f = last - s; 0 < s; --s, ++e, ++f) {
swapElements(SA, e, SA, f);
}
first += b - a;
last -= d - c;
}
return new PartitionResult(first, last);
}
private void trCopy(final int isa, final int isaN, final int first,
final int a, final int b, final int last, final int depth) {
final int[] SA = this.SA;
int c, d, e;
int s, v;
v = b - 1;
for (c = first, d = a - 1; c <= d; ++c) {
if ((s = SA[c] - depth) < 0) {
s += isaN - isa;
}
if (SA[isa + s] == v) {
SA[++d] = s;
SA[isa + s] = d;
}
}
for (c = last - 1, e = d + 1, d = b; e < d; --c) {
if ((s = SA[c] - depth) < 0) {
s += isaN - isa;
}
if (SA[isa + s] == v) {
SA[--d] = s;
SA[isa + s] = d;
}
}
}
private void trIntroSort(final int isa, int isaD, int isaN, int first,
int last, final TRBudget budget, final int size) {
final int[] SA = this.SA;
final StackEntry[] stack = new StackEntry[STACK_SIZE];
int a, b, c, d, e, f;
int s, t;
int v, x = 0;
int limit, next;
int ssize;
for (ssize = 0, limit = trLog(last - first);;) {
if (limit < 0) {
if (limit == -1) {
if (!budget.update(size, last - first)) {
break;
}
PartitionResult result = trPartition(isa, isaD - 1, isaN, first, last, last - 1);
a = result.first;
b = result.last;
if (first < a || b < last) {
if (a < last) {
for (c = first, v = a - 1; c < a; ++c) {
SA[isa + SA[c]] = v;
}
}
if (b < last) {
for (c = a, v = b - 1; c < b; ++c) {
SA[isa + SA[c]] = v;
}
}
stack[ssize++] = new StackEntry(0, a, b, 0);
stack[ssize++] = new StackEntry(isaD - 1, first, last, -2);
if (a - first <= last - b) {
if (1 < a - first) {
stack[ssize++] = new StackEntry(isaD, b, last, trLog(last - b));
last = a; limit = trLog(a - first);
} else if (1 < last - b) {
first = b; limit = trLog(last - b);
} else {
if (ssize == 0) {
return;
}
StackEntry entry = stack[--ssize];
isaD = entry.a;
first = entry.b;
last = entry.c;
limit = entry.d;
}
} else {
if (1 < last - b) {
stack[ssize++] = new StackEntry(isaD, first, a, trLog(a - first));
first = b;
limit = trLog(last - b);
} else if (1 < a - first) {
last = a;
limit = trLog(a - first);
} else {
if (ssize == 0) {
return;
}
StackEntry entry = stack[--ssize];
isaD = entry.a;
first = entry.b;
last = entry.c;
limit = entry.d;
}
}
} else {
for (c = first; c < last; ++c) {
SA[isa + SA[c]] = c;
}
if (ssize == 0) {
return;
}
StackEntry entry = stack[--ssize];
isaD = entry.a;
first = entry.b;
last = entry.c;
limit = entry.d;
}
} else if (limit == -2) {
a = stack[--ssize].b;
b = stack[ssize].c;
trCopy(isa, isaN, first, a, b, last, isaD - isa);
if (ssize == 0) {
return;
}
StackEntry entry = stack[--ssize];
isaD = entry.a;
first = entry.b;
last = entry.c;
limit = entry.d;
} else {
if (0 <= SA[first]) {
a = first;
do {
SA[isa + SA[a]] = a;
} while (++a < last && 0 <= SA[a]);
first = a;
}
if (first < last) {
a = first;
do {
SA[a] = ~SA[a];
} while (SA[++a] < 0);
next = SA[isa + SA[a]] != SA[isaD + SA[a]] ? trLog(a - first + 1) : -1;
if (++a < last) {
for (b = first, v = a - 1; b < a; ++b) {
SA[isa + SA[b]] = v;
}
}
if (a - first <= last - a) {
stack[ssize++] = new StackEntry(isaD, a, last, -3);
isaD += 1; last = a; limit = next;
} else {
if (1 < last - a) {
stack[ssize++] = new StackEntry(isaD + 1, first, a, next);
first = a; limit = -3;
} else {
isaD += 1; last = a; limit = next;
}
}
} else {
if (ssize == 0) {
return;
}
StackEntry entry = stack[--ssize];
isaD = entry.a;
first = entry.b;
last = entry.c;
limit = entry.d;
}
}
continue;
}
if (last - first <= INSERTIONSORT_THRESHOLD) {
if (!budget.update(size, last - first)) {
break;
}
trInsertionSort(isa, isaD, isaN, first, last);
limit = -3;
continue;
}
if (limit-- == 0) {
if (!budget.update(size, last - first)) {
break;
}
trHeapSort(isa, isaD, isaN, first, last - first);
for (a = last - 1; first < a; a = b) {
for (x = trGetC(isa, isaD, isaN, SA[a]), b = a - 1;
first <= b && trGetC(isa, isaD, isaN, SA[b]) == x;
--b) {
SA[b] = ~SA[b];
}
}
limit = -3;
continue;
}
a = trPivot(isa, isaD, isaN, first, last);
swapElements(SA, first, SA, a);
v = trGetC(isa, isaD, isaN, SA[first]);
b = first + 1;
while (b < last && (x = trGetC(isa, isaD, isaN, SA[b])) == v) {
++b;
}
if ((a = b) < last && x < v) {
while (++b < last && (x = trGetC(isa, isaD, isaN, SA[b])) <= v) {
if (x == v) {
swapElements(SA, b, SA, a);
++a;
}
}
}
c = last - 1;
while (b < c && (x = trGetC(isa, isaD, isaN, SA[c])) == v) {
--c;
}
if (b < (d = c) && x > v) {
while (b < --c && (x = trGetC(isa, isaD, isaN, SA[c])) >= v) {
if (x == v) {
swapElements(SA, c, SA, d);
--d;
}
}
}
while (b < c) {
swapElements(SA, b, SA, c);
while (++b < c && (x = trGetC(isa, isaD, isaN, SA[b])) <= v) {
if (x == v) {
swapElements(SA, b, SA, a);
++a;
}
}
while (b < --c && (x = trGetC(isa, isaD, isaN, SA[c])) >= v) {
if (x == v) {
swapElements(SA, c, SA, 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) {
swapElements(SA, e, SA, f);
}
if ((s = d - c) > (t = last - d - 1)) {
s = t;
}
for (e = b, f = last - s; 0 < s; --s, ++e, ++f) {
swapElements(SA, e, SA, f);
}
a = first + (b - a);
b = last - (d - c);
next = SA[isa + SA[a]] != v ? trLog(b - a) : -1;
for (c = first, v = a - 1; c < a; ++c) {
SA[isa + SA[c]] = v;
}
if (b < last) {
for (c = a, v = b - 1; c < b; ++c) {
SA[isa + SA[c]] = v; }
}
if (a - first <= last - b) {
if (last - b <= b - a) {
if (1 < a - first) {
stack[ssize++] = new StackEntry(isaD + 1, a, b, next);
stack[ssize++] = new StackEntry(isaD, b, last, limit);
last = a;
} else if (1 < last - b) {
stack[ssize++] = new StackEntry(isaD + 1, a, b, next);
first = b;
} else if (1 < b - a) {
isaD += 1;
first = a;
last = b;
limit = next;
} else {
if (ssize == 0) {
return;
}
StackEntry entry = stack[--ssize];
isaD = entry.a;
first = entry.b;
last = entry.c;
limit = entry.d;
}
} else if (a - first <= b - a) {
if (1 < a - first) {
stack[ssize++] = new StackEntry(isaD, b, last, limit);
stack[ssize++] = new StackEntry(isaD + 1, a, b, next);
last = a;
} else if (1 < b - a) {
stack[ssize++] = new StackEntry(isaD, b, last, limit);
isaD += 1;
first = a;
last = b;
limit = next;
} else {
first = b;
}
} else {
if (1 < b - a) {
stack[ssize++] = new StackEntry(isaD, b, last, limit);
stack[ssize++] = new StackEntry(isaD, first, a, limit);
isaD += 1;
first = a;
last = b;
limit = next;
} else {
stack[ssize++] = new StackEntry(isaD, b, last, limit);
last = a;
}
}
} else {
if (a - first <= b - a) {
if (1 < last - b) {
stack[ssize++] = new StackEntry(isaD + 1, a, b, next);
stack[ssize++] = new StackEntry(isaD, first, a, limit);
first = b;
} else if (1 < a - first) {
stack[ssize++] = new StackEntry(isaD + 1, a, b, next);
last = a;
} else if (1 < b - a) {
isaD += 1;
first = a;
last = b;
limit = next;
} else {
stack[ssize++] = new StackEntry(isaD, first, last, limit);
}
} else if (last - b <= b - a) {
if (1 < last - b) {
stack[ssize++] = new StackEntry(isaD, first, a, limit);
stack[ssize++] = new StackEntry(isaD + 1, a, b, next);
first = b;
} else if (1 < b - a) {
stack[ssize++] = new StackEntry(isaD, first, a, limit);
isaD += 1;
first = a;
last = b;
limit = next;
} else {
last = a;
}
} else {
if (1 < b - a) {
stack[ssize++] = new StackEntry(isaD, first, a, limit);
stack[ssize++] = new StackEntry(isaD, b, last, limit);
isaD += 1;
first = a;
last = b;
limit = next;
} else {
stack[ssize++] = new StackEntry(isaD, first, a, limit);
first = b;
}
}
}
} else {
if (!budget.update(size, last - first)) {
break;
}
limit += 1; isaD += 1;
}
}
for (s = 0; s < ssize; ++s) {
if (stack[s].d == -3) {
lsUpdateGroup(isa, stack[s].b, stack[s].c);
}
}
}
private static class TRBudget {
int budget;
int chance;
TRBudget(final int budget, final int chance) {
this.budget = budget;
this.chance = chance;
}
boolean update(final int size, final int n) {
budget -= n;
if (budget <= 0) {
if (--chance == 0) {
return false;
}
budget += size;
}
return true;
}
}
private void trSort(final int isa, final int n, final int depth) {
final int[] SA = this.SA;
int first = 0, last;
int t;
if (-n < SA[0]) {
TRBudget budget = new TRBudget(n, trLog(n) * 2 / 3 + 1);
do {
if ((t = SA[first]) < 0) {
first -= t;
} else {
last = SA[isa + t] + 1;
if (1 < last - first) {
trIntroSort(isa, isa + depth, isa + n, first, last, budget, n);
if (budget.chance == 0) {
if (0 < first) {
SA[0] = -first;
}
lsSort(isa, n, depth);
break;
}
}
first = last;
}
} while (first < n);
}
}
private static int BUCKET_B(final int c0, final int c1) {
return (c1 << 8) | c0;
}
private static int BUCKET_BSTAR(final int c0, final int c1) {
return (c0 << 8) | c1;
}
private int sortTypeBstar(final int[] bucketA, final int[] bucketB) {
final byte[] T = this.T;
final int[] SA = this.SA;
final int n = this.n;
final int[] tempbuf = new int[256];
int[] buf;
int PAb, ISAb, bufoffset;
int i, j, k, t, m, bufsize;
int c0, c1;
int flag;
for (i = 1, flag = 1; i < n; ++i) {
if (T[i - 1] != T[i]) {
if ((T[i - 1] & 0xff) > (T[i] & 0xff)) {
flag = 0;
}
break;
}
}
i = n - 1;
m = n;
int ti, ti1, t0;
if ((ti = T[i] & 0xff) < (t0 = T[0] & 0xff) || (T[i] == T[0] && flag != 0)) {
if (flag == 0) {
++bucketB[BUCKET_BSTAR(ti, t0)];
SA[--m] = i;
} else {
++bucketB[BUCKET_B(ti, t0)];
}
for (--i; 0 <= i && (ti = T[i] & 0xff) <= (ti1 = T[i + 1] & 0xff); --i) {
++bucketB[BUCKET_B(ti, ti1)];
}
}
while (0 <= i) {
do {
++bucketA[T[i] & 0xff];
} while (0 <= --i && (T[i] & 0xff) >= (T[i + 1] & 0xff));
if (0 <= i) {
++bucketB[BUCKET_BSTAR(T[i] & 0xff, T[i + 1] & 0xff)];
SA[--m] = i;
for (--i; 0 <= i && (ti = T[i] & 0xff) <= (ti1 = T[i + 1] & 0xff); --i) {
++bucketB[BUCKET_B(ti, ti1)];
}
}
}
m = n - m;
if (m == 0) {
for (i = 0; i < n; ++i) {
SA[i] = i;
}
return 0;
}
for (c0 = 0, i = -1, j = 0; c0 < 256; ++c0) {
t = i + bucketA[c0];
bucketA[c0] = i + j;
i = t + bucketB[BUCKET_B(c0, c0)];
for (c1 = c0 + 1; c1 < 256; ++c1) {
j += bucketB[BUCKET_BSTAR(c0, c1)];
bucketB[(c0 << 8) | c1] = j;
i += bucketB[BUCKET_B(c0, c1)];
}
}
PAb = n - m;
ISAb = m;
for (i = m - 2; 0 <= i; --i) {
t = SA[PAb + i];
c0 = T[t] & 0xff;
c1 = T[t + 1] & 0xff;
SA[--bucketB[BUCKET_BSTAR(c0, c1)]] = i;
}
t = SA[PAb + m - 1];
c0 = T[t] & 0xff;
c1 = T[t + 1] & 0xff;
SA[--bucketB[BUCKET_BSTAR(c0, c1)]] = m - 1;
buf = SA;
bufoffset = m;
bufsize = n - 2 * m;
if (bufsize <= 256) {
buf = tempbuf;
bufoffset = 0;
bufsize = 256;
}
for (c0 = 255, j = m; 0 < j; --c0) {
for (c1 = 255; c0 < c1; j = i, --c1) {
i = bucketB[BUCKET_BSTAR(c0, c1)];
if (1 < j - i) {
subStringSort(PAb, i, j, buf, bufoffset, bufsize, 2, SA[i] == m - 1, n);
}
}
}
for (i = m - 1; 0 <= i; --i) {
if (0 <= SA[i]) {
j = i;
do {
SA[ISAb + SA[i]] = i;
} while (0 <= --i && 0 <= SA[i]);
SA[i + 1] = i - j;
if (i <= 0) {
break;
}
}
j = i;
do {
SA[ISAb + (SA[i] = ~SA[i])] = j;
} while (SA[--i] < 0);
SA[ISAb + SA[i]] = j;
}
trSort(ISAb, m, 1);
i = n - 1; j = m;
if ((T[i] & 0xff) < (T[0] & 0xff) || (T[i] == T[0] && flag != 0)) {
if (flag == 0) {
SA[SA[ISAb + --j]] = i;
}
for (--i; 0 <= i && (T[i] & 0xff) <= (T[i + 1] & 0xff);) {
--i;
}
}
while (0 <= i) {
for (--i; 0 <= i && (T[i] & 0xff) >= (T[i + 1] & 0xff);) {
--i;
}
if (0 <= i) {
SA[SA[ISAb + --j]] = i;
for (--i; 0 <= i && (T[i] & 0xff) <= (T[i + 1] & 0xff);) {
--i;
}
}
}
for (c0 = 255, i = n - 1, k = m - 1; 0 <= c0; --c0) {
for (c1 = 255; c0 < c1; --c1) {
t = i - bucketB[BUCKET_B(c0, c1)];
bucketB[BUCKET_B(c0, c1)] = i + 1;
for (i = t, j = bucketB[BUCKET_BSTAR(c0, c1)]; j <= k; --i, --k) {
SA[i] = SA[k];
}
}
t = i - bucketB[BUCKET_B(c0, c0)];
bucketB[BUCKET_B(c0, c0)] = i + 1;
if (c0 < 255) {
bucketB[BUCKET_BSTAR(c0, c0 + 1)] = t + 1;
}
i = bucketA[c0];
}
return m;
}
private int constructBWT(final int[] bucketA, final int[] bucketB) {
final byte[] T = this.T;
final int[] SA = this.SA;
final int n = this.n;
int i, j, t = 0;
int s, s1;
int c0, c1, c2 = 0;
int orig = -1;
for (c1 = 254; 0 <= c1; --c1) {
for (i = bucketB[BUCKET_BSTAR(c1, c1 + 1)], j = bucketA[c1 + 1], t = 0, c2 = -1;
i <= j;
--j) {
if (0 <= (s1 = s = SA[j])) {
if (--s < 0) {
s = n - 1;
}
if ((c0 = T[s] & 0xff) <= c1) {
SA[j] = ~s1;
if (0 < s && (T[s - 1] & 0xff) > c0) {
s = ~s;
}
if (c2 == c0) {
SA[--t] = s;
} else {
if (0 <= c2) {
bucketB[BUCKET_B(c2, c1)] = t;
}
SA[t = bucketB[BUCKET_B(c2 = c0, c1)] - 1] = s;
}
}
} else {
SA[j] = ~s;
}
}
}
for (i = 0; i < n; ++i) {
if (0 <= (s1 = s = SA[i])) {
if (--s < 0) {
s = n - 1;
}
if ((c0 = T[s] & 0xff) >= (T[s + 1] & 0xff)) {
if (0 < s && (T[s - 1] & 0xff) < c0) {
s = ~s;
}
if (c0 == c2) {
SA[++t] = s;
} else {
if (c2 != -1) {
bucketA[c2] = t;
}
SA[t = bucketA[c2 = c0] + 1] = s;
}
}
} else {
s1 = ~s1;
}
if (s1 == 0) {
SA[i] = T[n - 1];
orig = i;
} else {
SA[i] = T[s1 - 1];
}
}
return orig;
}
public int bwt() {
final int[] SA = this.SA;
final byte[] T = this.T;
final int n = this.n;
final int[] bucketA = new int[BUCKET_A_SIZE];
final int[] bucketB = new int[BUCKET_B_SIZE];
if (n == 0) {
return 0;
}
if (n == 1) {
SA[0] = T[0];
return 0;
}
int m = sortTypeBstar(bucketA, bucketB);
if (0 < m) {
return constructBWT(bucketA, bucketB);
}
return 0;
}
}