NVBIO
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
sais.h
Go to the documentation of this file.
1 /*
2  * sais.hxx for sais-lite
3  * Copyright (c) 2008-2010 Yuta Mori All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person
6  * obtaining a copy of this software and associated documentation
7  * files (the "Software"), to deal in the Software without
8  * restriction, including without limitation the rights to use,
9  * copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following
12  * conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24  * OTHER DEALINGS IN THE SOFTWARE.
25  */
26 
27 #ifndef _SAIS_HXX
28 #define _SAIS_HXX 1
29 #ifdef __cplusplus
30 
31 #include <cassert>
32 #include <iterator>
33 #include <limits>
34 
35 #ifdef __INTEL_COMPILER
36 #pragma warning(disable : 383 981 1418)
37 #endif
38 
39 #ifdef _MSC_VER
40 #pragma warning(push)
41 #pragma warning(disable : 4365)
42 #endif
43 
44 namespace saisxx_private {
45 
46 /* find the start or end of each bucket */
47 template<typename string_type, typename bucket_type, typename index_type>
48 void
49 getCounts(const string_type T, bucket_type C, index_type n, index_type k) {
50  index_type i;
51  for(i = 0; i < k; ++i) { C[i] = 0; }
52  for(i = 0; i < n; ++i) { ++C[T[i]]; }
53 }
54 template<typename bucketC_type, typename bucketB_type, typename index_type>
55 void
56 getBuckets(const bucketC_type C, bucketB_type B, index_type k, bool end) {
57  index_type i, sum = 0;
58  if(end != false) { for(i = 0; i < k; ++i) { sum += C[i]; B[i] = sum; } }
59  else { for(i = 0; i < k; ++i) { sum += C[i]; B[i] = sum - C[i]; } }
60 }
61 
62 template<typename string_type, typename sarray_type,
63  typename bucketC_type, typename bucketB_type, typename index_type>
64 void
65 LMSsort1(string_type T, sarray_type SA,
66  bucketC_type C, bucketB_type B,
67  index_type n, index_type k, bool recount) {
68 typedef typename std::iterator_traits<string_type>::value_type char_type;
69 typedef typename std::iterator_traits<bucketB_type>::value_type bucket_type;
70  sarray_type b;
71  index_type i, j;
72  char_type c0, c1;
73 
74  /* compute SAl */
75  if(recount != false) { getCounts(T, C, n, k); }
76  getBuckets(C, B, k, false); /* find starts of buckets */
77  j = n - 1;
78  b = SA + B[c1 = T[j]];
79  --j;
80  *b++ = (T[j] < c1) ? ~j : j;
81  for(i = 0; i < n; ++i) {
82  if(0 < (j = SA[i])) {
83  assert(T[j] >= T[j + 1]);
84  if((c0 = T[j]) != c1) { B[c1] = bucket_type(b - SA); b = SA + B[c1 = c0]; }
85  assert(i < (b - SA));
86  --j;
87  *b++ = (T[j] < c1) ? ~j : j;
88  SA[i] = 0;
89  } else if(j < 0) {
90  SA[i] = ~j;
91  }
92  }
93  /* compute SAs */
94  if(recount != false) { getCounts(T, C, n, k); }
95  getBuckets(C, B, k, true); /* find ends of buckets */
96  for(i = n - 1, b = SA + B[c1 = 0]; 0 <= i; --i) {
97  if(0 < (j = SA[i])) {
98  assert(T[j] <= T[j + 1]);
99  if((c0 = T[j]) != c1) { B[c1] = bucket_type(b - SA); b = SA + B[c1 = c0]; }
100  assert((b - SA) <= i);
101  --j;
102  *--b = (T[j] > c1) ? ~(j + 1) : j;
103  SA[i] = 0;
104  }
105  }
106 }
107 template<typename string_type, typename sarray_type, typename index_type>
108 index_type
109 LMSpostproc1(string_type T, sarray_type SA, index_type n, index_type m) {
110 typedef typename std::iterator_traits<string_type>::value_type char_type;
111  index_type i, j, p, q, plen, qlen, name;
112  char_type c0, c1;
113  bool diff;
114 
115  /* compact all the sorted substrings into the first m items of SA
116  2*m must be not larger than n (proveable) */
117  assert(0 < n);
118  for(i = 0; (p = SA[i]) < 0; ++i) { SA[i] = ~p; assert((i + 1) < n); }
119  if(i < m) {
120  for(j = i, ++i;; ++i) {
121  assert(i < n);
122  if((p = SA[i]) < 0) {
123  SA[j++] = ~p; SA[i] = 0;
124  if(j == m) { break; }
125  }
126  }
127  }
128 
129  /* store the length of all substrings */
130  i = n - 1; j = n - 1; c0 = T[n - 1];
131  do { c1 = c0; } while((0 <= --i) && ((c0 = T[i]) >= c1));
132  for(; 0 <= i;) {
133  do { c1 = c0; } while((0 <= --i) && ((c0 = T[i]) <= c1));
134  if(0 <= i) {
135  SA[m + ((i + 1) >> 1)] = j - i; j = i + 1;
136  do { c1 = c0; } while((0 <= --i) && ((c0 = T[i]) >= c1));
137  }
138  }
139 
140  /* find the lexicographic names of all substrings */
141  for(i = 0, name = 0, q = n, qlen = 0; i < m; ++i) {
142  p = SA[i], plen = SA[m + (p >> 1)], diff = true;
143  if((plen == qlen) && ((q + plen) < n)) {
144  for(j = 0; (j < plen) && (T[p + j] == T[q + j]); ++j) { }
145  if(j == plen) { diff = false; }
146  }
147  if(diff != false) { ++name, q = p, qlen = plen; }
148  SA[m + (p >> 1)] = name;
149  }
150 
151  return name;
152 }
153 template<typename string_type, typename sarray_type,
154  typename bucketC_type, typename bucketB_type, typename bucketD_type,
155  typename index_type>
156 void
157 LMSsort2(string_type T, sarray_type SA,
158  bucketC_type C, bucketB_type B, bucketD_type D,
159  index_type n, index_type k) {
160 typedef typename std::iterator_traits<string_type>::value_type char_type;
161 typedef typename std::iterator_traits<bucketB_type>::value_type bucket_type;
162  sarray_type b;
163  index_type i, j, t, d;
164  char_type c0, c1;
165 
166  /* compute SAl */
167  getBuckets(C, B, k, false); /* find starts of buckets */
168  j = n - 1;
169  b = SA + B[c1 = T[j]];
170  --j;
171  t = (T[j] < c1);
172  j += n;
173  *b++ = (t & 1) ? ~j : j;
174  for(i = 0, d = 0; i < n; ++i) {
175  if(0 < (j = SA[i])) {
176  if(n <= j) { d += 1; j -= n; }
177  assert(T[j] >= T[j + 1]);
178  if((c0 = T[j]) != c1) { B[c1] = bucket_type(b - SA); b = SA + B[c1 = c0]; }
179  assert(i < (b - SA));
180  --j;
181  t = c0; t = (t << 1) | (T[j] < c1);
182  if(D[t] != d) { j += n; D[t] = d; }
183  *b++ = (t & 1) ? ~j : j;
184  SA[i] = 0;
185  } else if(j < 0) {
186  SA[i] = ~j;
187  }
188  }
189  for(i = n - 1; 0 <= i; --i) {
190  if(0 < SA[i]) {
191  if(SA[i] < n) {
192  SA[i] += n;
193  for(j = i - 1; SA[j] < n; --j) { }
194  SA[j] -= n;
195  i = j;
196  }
197  }
198  }
199 
200  /* compute SAs */
201  getBuckets(C, B, k, true); /* find ends of buckets */
202  for(i = n - 1, d += 1, b = SA + B[c1 = 0]; 0 <= i; --i) {
203  if(0 < (j = SA[i])) {
204  if(n <= j) { d += 1; j -= n; }
205  assert(T[j] <= T[j + 1]);
206  if((c0 = T[j]) != c1) { B[c1] = bucket_type(b - SA); b = SA + B[c1 = c0]; }
207  assert((b - SA) <= i);
208  --j;
209  t = c0; t = (t << 1) | (T[j] > c1);
210  if(D[t] != d) { j += n; D[t] = d; }
211  *--b = (t & 1) ? ~(j + 1) : j;
212  SA[i] = 0;
213  }
214  }
215 }
216 template<typename sarray_type, typename index_type>
217 index_type
218 LMSpostproc2(sarray_type SA, index_type n, index_type m) {
219  index_type i, j, d, name;
220 
221  /* compact all the sorted LMS substrings into the first m items of SA */
222  assert(0 < n);
223  for(i = 0, name = 0; (j = SA[i]) < 0; ++i) {
224  j = ~j;
225  if(n <= j) { name += 1; }
226  SA[i] = j;
227  assert((i + 1) < n);
228  }
229  if(i < m) {
230  for(d = i, ++i;; ++i) {
231  assert(i < n);
232  if((j = SA[i]) < 0) {
233  j = ~j;
234  if(n <= j) { name += 1; }
235  SA[d++] = j; SA[i] = 0;
236  if(d == m) { break; }
237  }
238  }
239  }
240  if(name < m) {
241  /* store the lexicographic names */
242  for(i = m - 1, d = name + 1; 0 <= i; --i) {
243  if(n <= (j = SA[i])) { j -= n; --d; }
244  SA[m + (j >> 1)] = d;
245  }
246  } else {
247  /* unset flags */
248  for(i = 0; i < m; ++i) {
249  if(n <= (j = SA[i])) { j -= n; SA[i] = j; }
250  }
251  }
252 
253  return name;
254 }
255 
256 /* compute SA and BWT */
257 template<typename string_type, typename sarray_type,
258  typename bucketC_type, typename bucketB_type, typename index_type>
259 void
260 induceSA(string_type T, sarray_type SA, bucketC_type C, bucketB_type B,
261  index_type n, index_type k, bool recount) {
262 typedef typename std::iterator_traits<string_type>::value_type char_type;
263 typedef typename std::iterator_traits<bucketB_type>::value_type bucket_type;
264  sarray_type b;
265  index_type i, j;
266  char_type c0, c1;
267  /* compute SAl */
268  if(recount != false) { getCounts(T, C, n, k); }
269  getBuckets(C, B, k, false); /* find starts of buckets */
270  b = SA + B[c1 = T[j = n - 1]];
271  *b++ = ((0 < j) && (T[j - 1] < c1)) ? ~j : j;
272  for(i = 0; i < n; ++i) {
273  j = SA[i], SA[i] = ~j;
274  if(0 < j) {
275  if((c0 = T[--j]) != c1) { B[c1] = bucket_type(b - SA); b = SA + B[c1 = c0]; }
276  *b++ = ((0 < j) && (T[j - 1] < c1)) ? ~j : j;
277  }
278  }
279  /* compute SAs */
280  if(recount != false) { getCounts(T, C, n, k); }
281  getBuckets(C, B, k, true); /* find ends of buckets */
282  for(i = n - 1, b = SA + B[c1 = 0]; 0 <= i; --i) {
283  if(0 < (j = SA[i])) {
284  if((c0 = T[--j]) != c1) { B[c1] = bucket_type(b - SA); b = SA + B[c1 = c0]; }
285  *--b = ((j == 0) || (T[j - 1] > c1)) ? ~j : j;
286  } else {
287  SA[i] = ~j;
288  }
289  }
290 }
291 template<typename string_type, typename sarray_type,
292  typename bucketC_type, typename bucketB_type, typename index_type>
293 index_type
294 computeBWT(string_type T, sarray_type SA, bucketC_type C, bucketB_type B,
295  index_type n, index_type k, bool recount) {
296 typedef typename std::iterator_traits<string_type>::value_type char_type;
297 typedef typename std::iterator_traits<bucketB_type>::value_type bucket_type;
298  sarray_type b;
299  index_type i, j, pidx = -1;
300  char_type c0, c1;
301  /* compute SAl */
302  if(recount != false) { getCounts(T, C, n, k); }
303  getBuckets(C, B, k, false); /* find starts of buckets */
304  b = SA + B[c1 = T[j = n - 1]];
305  *b++ = ((0 < j) && (T[j - 1] < c1)) ? ~j : j;
306  for(i = 0; i < n; ++i) {
307  if(0 < (j = SA[i])) {
308  SA[i] = ~((index_type)(c0 = T[--j]));
309  if(c0 != c1) { B[c1] = bucket_type(b - SA); b = SA + B[c1 = c0]; }
310  *b++ = ((0 < j) && (T[j - 1] < c1)) ? ~j : j;
311  } else if(j != 0) {
312  SA[i] = ~j;
313  }
314  }
315  /* compute SAs */
316  if(recount != false) { getCounts(T, C, n, k); }
317  getBuckets(C, B, k, true); /* find ends of buckets */
318  for(i = n - 1, b = SA + B[c1 = 0]; 0 <= i; --i) {
319  if(0 < (j = SA[i])) {
320  SA[i] = (c0 = T[--j]);
321  if(c0 != c1) { B[c1] = bucket_type(b - SA); b = SA + B[c1 = c0]; }
322  *--b = ((0 < j) && (T[j - 1] > c1)) ? ~((index_type)T[j - 1]) : j;
323  } else if(j != 0) {
324  SA[i] = ~j;
325  } else {
326  pidx = i;
327  }
328  }
329  return pidx;
330 }
331 
332 template<typename string_type, typename sarray_type,
333  typename bucketC_type, typename bucketB_type,
334  typename index_type>
335 std::pair<index_type, index_type>
336 stage1sort(string_type T, sarray_type SA,
337  bucketC_type C, bucketB_type B,
338  index_type n, index_type k, unsigned flags) {
339 typedef typename std::iterator_traits<string_type>::value_type char_type;
340  sarray_type b;
341  index_type i, j, name, m;
342  char_type c0, c1;
343  getCounts(T, C, n, k); getBuckets(C, B, k, true); /* find ends of buckets */
344  for(i = 0; i < n; ++i) { SA[i] = 0; }
345  b = SA + n - 1; i = n - 1; j = n; m = 0; c0 = T[n - 1];
346  do { c1 = c0; } while((0 <= --i) && ((c0 = T[i]) >= c1));
347  for(; 0 <= i;) {
348  do { c1 = c0; } while((0 <= --i) && ((c0 = T[i]) <= c1));
349  if(0 <= i) {
350  *b = j; b = SA + --B[c1]; j = i; ++m; assert(B[c1] != (n - 1));
351  do { c1 = c0; } while((0 <= --i) && ((c0 = T[i]) >= c1));
352  }
353  }
354  SA[n - 1] = 0;
355 
356  if(1 < m) {
357  if(flags & (16 | 32)) {
358  assert((j + 1) < n);
359  ++B[T[j + 1]];
360  if(flags & 16) {
361  index_type *D;
362  try { D = new index_type[k * 2]; } catch(...) { D = 0; }
363  if(D == 0) { return std::make_pair(-2, -2); }
364  for(i = 0, j = 0; i < k; ++i) {
365  j += C[i];
366  if(B[i] != j) { assert(SA[B[i]] != 0); SA[B[i]] += n; }
367  D[i] = D[i + k] = 0;
368  }
369  LMSsort2(T, SA, C, B, D, n, k);
370  delete[] D;
371  } else {
372  bucketB_type D = B - k * 2;
373  for(i = 0, j = 0; i < k; ++i) {
374  j += C[i];
375  if(B[i] != j) { assert(SA[B[i]] != 0); SA[B[i]] += n; }
376  D[i] = D[i + k] = 0;
377  }
378  LMSsort2(T, SA, C, B, D, n, k);
379  }
380  name = LMSpostproc2(SA, n, m);
381  } else {
382  LMSsort1(T, SA, C, B, n, k, (flags & (4 | 64)) != 0);
383  name = LMSpostproc1(T, SA, n, m);
384  }
385  } else if(m == 1) {
386  *b = j + 1;
387  name = 1;
388  } else {
389  name = 0;
390  }
391  return std::make_pair(m, name);
392 }
393 template<typename string_type, typename sarray_type,
394  typename bucketC_type, typename bucketB_type, typename index_type>
395 index_type
396 stage3sort(string_type T, sarray_type SA, bucketC_type C, bucketB_type B,
397  index_type n, index_type m, index_type k,
398  unsigned flags, bool isbwt) {
399 typedef typename std::iterator_traits<string_type>::value_type char_type;
400  index_type i, j, p, q, pidx = 0;
401  char_type c0, c1;
402  if((flags & 8) != 0) { getCounts(T, C, n, k); }
403  /* put all left-most S characters into their buckets */
404  if(1 < m) {
405  getBuckets(C, B, k, 1); /* find ends of buckets */
406  i = m - 1, j = n, p = SA[m - 1], c1 = T[p];
407  do {
408  q = B[c0 = c1];
409  while(q < j) { SA[--j] = 0; }
410  do {
411  SA[--j] = p;
412  if(--i < 0) { break; }
413  p = SA[i];
414  } while((c1 = T[p]) == c0);
415  } while(0 <= i);
416  while(0 < j) { SA[--j] = 0; }
417  }
418  if(isbwt == false) { induceSA(T, SA, C, B, n, k, (flags & (4 | 64)) != 0); }
419  else { pidx = computeBWT(T, SA, C, B, n, k, (flags & (4 | 64)) != 0); }
420  return pidx;
421 }
422 
423 /* find the suffix array SA of T[0..n-1] in {0..k}^n
424  use a working space (excluding s and SA) of at most 2n+O(1) for a constant alphabet */
425 template<typename string_type, typename sarray_type, typename index_type>
426 index_type
427 suffixsort(string_type T, sarray_type SA,
428  index_type fs, index_type n, index_type k,
429  bool isbwt) {
430 typedef typename std::iterator_traits<string_type>::value_type char_type;
431  sarray_type RA, C, B;
432  index_type *Cp, *Bp;
433  index_type i, j, m, name, pidx, newfs;
434  unsigned flags = 0;
435  char_type c0, c1;
436 
437  /* stage 1: reduce the problem by at least 1/2
438  sort all the S-substrings */
439  C = B = SA; /* for warnings */
440  Cp = 0, Bp = 0;
441  if(k <= 256) {
442  try { Cp = new index_type[k]; } catch(...) { Cp = 0; }
443  if(Cp == 0) { return -2; }
444  if(k <= fs) {
445  B = SA + (n + fs - k);
446  flags = 1;
447  } else {
448  try { Bp = new index_type[k]; } catch(...) { Bp = 0; }
449  if(Bp == 0) { return -2; }
450  flags = 3;
451  }
452  } else if(k <= fs) {
453  C = SA + (n + fs - k);
454  if(k <= (fs - k)) {
455  B = C - k;
456  flags = 0;
457  } else if(k <= 1024) {
458  try { Bp = new index_type[k]; } catch(...) { Bp = 0; }
459  if(Bp == 0) { return -2; }
460  flags = 2;
461  } else {
462  B = C;
463  flags = 64 | 8;
464  }
465  } else {
466  try { Cp = new index_type[k]; } catch(...) { Cp = 0; }
467  if(Cp == 0) { return -2; }
468  Bp = Cp;
469  flags = 4 | 8;
470  }
471  if((n <= ((std::numeric_limits<index_type>::max)() / 2)) && (2 <= (n / k))) {
472  if(flags & 1) { flags |= ((k * 2) <= (fs - k)) ? 32 : 16; }
473  else if((flags == 0) && ((k * 2) <= (fs - k * 2))) { flags |= 32; }
474  }
475  {
476  std::pair<index_type, index_type> r;
477  if(Cp != 0) {
478  if(Bp != 0) { r = stage1sort(T, SA, Cp, Bp, n, k, flags); }
479  else { r = stage1sort(T, SA, Cp, B, n, k, flags); }
480  } else {
481  if(Bp != 0) { r = stage1sort(T, SA, C, Bp, n, k, flags); }
482  else { r = stage1sort(T, SA, C, B, n, k, flags); }
483  }
484  m = r.first, name = r.second;
485  }
486  if(m < 0) {
487  if(flags & (1 | 4)) { delete[] Cp; }
488  if(flags & 2) { delete[] Bp; }
489  return -2;
490  }
491 
492  /* stage 2: solve the reduced problem
493  recurse if names are not yet unique */
494  if(name < m) {
495  if(flags & 4) { delete[] Cp; }
496  if(flags & 2) { delete[] Bp; }
497  newfs = (n + fs) - (m * 2);
498  if((flags & (1 | 4 | 64)) == 0) {
499  if((k + name) <= newfs) { newfs -= k; }
500  else { flags |= 8; }
501  }
502  assert((n >> 1) <= (newfs + m));
503  RA = SA + m + newfs;
504  for(i = m + (n >> 1) - 1, j = m - 1; m <= i; --i) {
505  if(SA[i] != 0) { RA[j--] = SA[i] - 1; }
506  }
507  if(suffixsort(RA, SA, newfs, m, name, false) != 0) { if(flags & 1) { delete[] Cp; } return -2; }
508  i = n - 1; j = m - 1; c0 = T[n - 1];
509  do { c1 = c0; } while((0 <= --i) && ((c0 = T[i]) >= c1));
510  for(; 0 <= i;) {
511  do { c1 = c0; } while((0 <= --i) && ((c0 = T[i]) <= c1));
512  if(0 <= i) {
513  RA[j--] = i + 1;
514  do { c1 = c0; } while((0 <= --i) && ((c0 = T[i]) >= c1));
515  }
516  }
517  for(i = 0; i < m; ++i) { SA[i] = RA[SA[i]]; }
518  if(flags & 4) {
519  try { Cp = new index_type[k]; } catch(...) { Cp = 0; }
520  if(Cp == 0) { return -2; }
521  Bp = Cp;
522  }
523  if(flags & 2) {
524  try { Bp = new index_type[k]; } catch(...) { Bp = 0; }
525  if(Bp == 0) { if(flags & 1) { delete[] Cp; } return -2; }
526  }
527  }
528 
529  /* stage 3: induce the result for the original problem */
530  if(Cp != 0) {
531  if(Bp != 0) { pidx = stage3sort(T, SA, Cp, Bp, n, m, k, flags, isbwt); }
532  else { pidx = stage3sort(T, SA, Cp, B, n, m, k, flags, isbwt); }
533  } else {
534  if(Bp != 0) { pidx = stage3sort(T, SA, C, Bp, n, m, k, flags, isbwt); }
535  else { pidx = stage3sort(T, SA, C, B, n, m, k, flags, isbwt); }
536  }
537  if(flags & (1 | 4)) { delete[] Cp; }
538  if(flags & 2) { delete[] Bp; }
539 
540  return pidx;
541 }
542 
543 } /* namespace saisxx_private */
544 
545 
554 template<typename string_type, typename sarray_type, typename index_type>
555 index_type
556 saisxx(string_type T, sarray_type SA, index_type n, index_type k = 256) {
557 typedef typename std::iterator_traits<sarray_type>::value_type savalue_type;
558  assert((std::numeric_limits<index_type>::min)() < 0);
562  if((n < 0) || (k <= 0)) { return -1; }
563  if(n <= 1) { if(n == 1) { SA[0] = 0; } return 0; }
564  return saisxx_private::suffixsort(T, SA, 0, n, k, false);
565 }
566 
576 template<typename string_type, typename string_type2, typename sarray_type, typename index_type>
577 index_type
578 saisxx_bwt(string_type T, string_type2 U, sarray_type A, index_type n, index_type k = 256) {
579 typedef typename std::iterator_traits<sarray_type>::value_type savalue_type;
580 typedef typename std::iterator_traits<string_type>::value_type char_type;
581  index_type i, pidx;
582  assert((std::numeric_limits<index_type>::min)() < 0);
586  if((n < 0) || (k <= 0)) { return -1; }
587  if(n <= 1) { if(n == 1) { U[0] = T[0]; } return n; }
588  pidx = saisxx_private::suffixsort(T, A, index_type(0), n, k, true);
589  if(0 <= pidx) {
590  U[index_type(0)] = T[n - 1];
591  for(i = 0; i < pidx; ++i) { U[i + 1] = (char_type)A[i]; }
592  for(i += 1; i < n; ++i) { U[i] = (char_type)A[i]; }
593  pidx += 1;
594  }
595  return pidx;
596 }
597 
598 
599 #ifdef _MSC_VER
600 #pragma warning(pop)
601 #endif
602 
603 #endif /* __cplusplus */
604 #endif /* _SAIS_HXX */