Coverage Report

Created: 2026-10-31 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sentencepiece/third_party/libsais/libsais.c
Line
Count
Source
1
/*--
2
3
This file is a part of libsais, a library for linear time suffix array,
4
longest common prefix array and burrows wheeler transform construction.
5
6
   Copyright (c) 2021-2025 Ilya Grebnov <ilya.grebnov@gmail.com>
7
8
   Licensed under the Apache License, Version 2.0 (the "License");
9
   you may not use this file except in compliance with the License.
10
   You may obtain a copy of the License at
11
12
       http://www.apache.org/licenses/LICENSE-2.0
13
14
   Unless required by applicable law or agreed to in writing, software
15
   distributed under the License is distributed on an "AS IS" BASIS,
16
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
   See the License for the specific language governing permissions and
18
   limitations under the License.
19
20
Please see the file LICENSE for full copyright information.
21
22
--*/
23
24
#include "libsais.h"
25
26
#include <limits.h>
27
#include <stddef.h>
28
#include <stdint.h>
29
#include <stdlib.h>
30
#include <string.h>
31
32
#if defined(LIBSAIS_OPENMP)
33
#include <omp.h>
34
#else
35
496k
#define UNUSED(_x) (void)(_x)
36
#endif
37
38
typedef int32_t sa_sint_t;
39
typedef uint32_t sa_uint_t;
40
typedef ptrdiff_t fast_sint_t;
41
typedef size_t fast_uint_t;
42
43
72.9M
#define SAINT_BIT (32)
44
124M
#define SAINT_MAX INT32_MAX
45
64.1M
#define SAINT_MIN INT32_MIN
46
47
170k
#define ALPHABET_SIZE (1 << CHAR_BIT)
48
0
#define UNBWT_FASTBITS (17)
49
50
4.57M
#define SUFFIX_GROUP_BIT (SAINT_BIT - 1)
51
3.03M
#define SUFFIX_GROUP_MARKER (((sa_sint_t)1) << (SUFFIX_GROUP_BIT - 1))
52
53
93.0M
#define BUCKETS_INDEX2(_c, _s) (((_c) << 1) + (_s))
54
92.7M
#define BUCKETS_INDEX4(_c, _s) (((_c) << 2) + (_s))
55
56
32.4k
#define LIBSAIS_LOCAL_BUFFER_SIZE (2000)
57
0
#define LIBSAIS_PER_THREAD_CACHE_SIZE (24576)
58
59
8.54k
#define LIBSAIS_FLAGS_NONE (0)
60
8.54k
#define LIBSAIS_FLAGS_BWT (1)
61
68.0k
#define LIBSAIS_FLAGS_GSA (2)
62
63
typedef struct LIBSAIS_THREAD_CACHE {
64
  sa_sint_t symbol;
65
  sa_sint_t index;
66
} LIBSAIS_THREAD_CACHE;
67
68
typedef union LIBSAIS_THREAD_STATE {
69
  struct {
70
    fast_sint_t position;
71
    fast_sint_t count;
72
73
    fast_sint_t m;
74
    fast_sint_t last_lms_suffix;
75
76
    sa_sint_t* buckets;
77
    LIBSAIS_THREAD_CACHE* cache;
78
  } state;
79
80
  uint8_t padding[64];
81
} LIBSAIS_THREAD_STATE;
82
83
typedef struct LIBSAIS_CONTEXT {
84
  sa_sint_t* buckets;
85
  LIBSAIS_THREAD_STATE* thread_state;
86
  fast_sint_t threads;
87
} LIBSAIS_CONTEXT;
88
89
typedef struct LIBSAIS_UNBWT_CONTEXT {
90
  sa_uint_t* bucket2;
91
  uint16_t* fastbits;
92
  sa_uint_t* buckets;
93
  fast_sint_t threads;
94
} LIBSAIS_UNBWT_CONTEXT;
95
96
#if defined(__GNUC__) || defined(__clang__)
97
#define RESTRICT __restrict__
98
#elif defined(_MSC_VER) || defined(__INTEL_COMPILER)
99
#define RESTRICT __restrict
100
#else
101
#error Your compiler, configuration or platform is not supported.
102
#endif
103
104
#if defined(__has_builtin)
105
#if __has_builtin(__builtin_prefetch)
106
#define HAS_BUILTIN_PREFETCH
107
#endif
108
#elif defined(__GNUC__) && \
109
    (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 2)) || (__GNUC__ >= 4))
110
#define HAS_BUILTIN_PREFETCH
111
#endif
112
113
#if defined(__has_builtin)
114
#if __has_builtin(__builtin_bswap16)
115
#define HAS_BUILTIN_BSWAP16
116
#endif
117
#elif defined(__GNUC__) && \
118
    (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ >= 5))
119
#define HAS_BUILTIN_BSWAP16
120
#endif
121
122
#if defined(HAS_BUILTIN_PREFETCH)
123
#define libsais_prefetchr(address) \
124
308M
  __builtin_prefetch((const void*)(address), 0, 3)
125
#define libsais_prefetchw(address) \
126
158M
  __builtin_prefetch((const void*)(address), 1, 3)
127
#elif defined(_M_IX86) || defined(_M_AMD64)
128
#include <intrin.h>
129
#define libsais_prefetchr(address) \
130
  _mm_prefetch((const void*)(address), _MM_HINT_T0)
131
#define libsais_prefetchw(address) _m_prefetchw((const void*)(address))
132
#elif defined(_M_ARM)
133
#include <intrin.h>
134
#define libsais_prefetchr(address) __prefetch((const void*)(address))
135
#define libsais_prefetchw(address) __prefetchw((const void*)(address))
136
#elif defined(_M_ARM64)
137
#include <intrin.h>
138
#define libsais_prefetchr(address) __prefetch2((const void*)(address), 0)
139
#define libsais_prefetchw(address) __prefetch2((const void*)(address), 16)
140
#else
141
#error Your compiler, configuration or platform is not supported.
142
#endif
143
144
#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
145
#if defined(_LITTLE_ENDIAN) ||                                      \
146
    (defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) &&               \
147
     BYTE_ORDER == LITTLE_ENDIAN) ||                                \
148
    (defined(_BYTE_ORDER) && defined(_LITTLE_ENDIAN) &&             \
149
     _BYTE_ORDER == _LITTLE_ENDIAN) ||                              \
150
    (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) &&           \
151
     __BYTE_ORDER == __LITTLE_ENDIAN) ||                            \
152
    (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
153
     __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
154
#define __LITTLE_ENDIAN__
155
#elif defined(_BIG_ENDIAN) ||                                    \
156
    (defined(BYTE_ORDER) && defined(BIG_ENDIAN) &&               \
157
     BYTE_ORDER == BIG_ENDIAN) ||                                \
158
    (defined(_BYTE_ORDER) && defined(_BIG_ENDIAN) &&             \
159
     _BYTE_ORDER == _BIG_ENDIAN) ||                              \
160
    (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) &&           \
161
     __BYTE_ORDER == __BIG_ENDIAN) ||                            \
162
    (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
163
     __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
164
#define __BIG_ENDIAN__
165
#elif defined(_WIN32)
166
#define __LITTLE_ENDIAN__
167
#endif
168
#endif
169
170
#if defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
171
#if defined(HAS_BUILTIN_BSWAP16)
172
0
#define libsais_bswap16(x) (__builtin_bswap16(x))
173
#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER)
174
#define libsais_bswap16(x) (_byteswap_ushort(x))
175
#else
176
#define libsais_bswap16(x) ((uint16_t)(x >> 8) | (uint16_t)(x << 8))
177
#endif
178
#elif !defined(__LITTLE_ENDIAN__) && defined(__BIG_ENDIAN__)
179
#define libsais_bswap16(x) (x)
180
#else
181
#error Your compiler, configuration or platform is not supported.
182
#endif
183
184
18.8k
static void* libsais_align_up(const void* address, size_t alignment) {
185
18.8k
  return (void*)((((ptrdiff_t)address) + ((ptrdiff_t)alignment) - 1) &
186
18.8k
                 (-((ptrdiff_t)alignment)));
187
18.8k
}
188
189
8.54k
static void* libsais_alloc_aligned(size_t size, size_t alignment) {
190
8.54k
  void* address = malloc(size + sizeof(short) + alignment - 1);
191
8.54k
  if (address != NULL) {
192
8.54k
    void* aligned_address = libsais_align_up(
193
8.54k
        (void*)((ptrdiff_t)address + (ptrdiff_t)(sizeof(short))), alignment);
194
8.54k
    ((short*)aligned_address)[-1] =
195
8.54k
        (short)((ptrdiff_t)aligned_address - (ptrdiff_t)address);
196
197
8.54k
    return aligned_address;
198
8.54k
  }
199
200
0
  return NULL;
201
8.54k
}
202
203
8.72k
static void libsais_free_aligned(void* aligned_address) {
204
8.72k
  if (aligned_address != NULL) {
205
8.54k
    free((void*)((ptrdiff_t)aligned_address - ((short*)aligned_address)[-1]));
206
8.54k
  }
207
8.72k
}
208
209
0
static LIBSAIS_THREAD_STATE* libsais_alloc_thread_state(sa_sint_t threads) {
210
0
  LIBSAIS_THREAD_STATE* RESTRICT thread_state =
211
0
      (LIBSAIS_THREAD_STATE*)libsais_alloc_aligned(
212
0
          (size_t)threads * sizeof(LIBSAIS_THREAD_STATE), 4096);
213
0
  sa_sint_t* RESTRICT thread_buckets = (sa_sint_t*)libsais_alloc_aligned(
214
0
      (size_t)threads * 4 * ALPHABET_SIZE * sizeof(sa_sint_t), 4096);
215
0
  LIBSAIS_THREAD_CACHE* RESTRICT thread_cache =
216
0
      (LIBSAIS_THREAD_CACHE*)libsais_alloc_aligned(
217
0
          (size_t)threads * LIBSAIS_PER_THREAD_CACHE_SIZE *
218
0
              sizeof(LIBSAIS_THREAD_CACHE),
219
0
          4096);
220
221
0
  if (thread_state != NULL && thread_buckets != NULL && thread_cache != NULL) {
222
0
    fast_sint_t t;
223
0
    for (t = 0; t < threads; ++t) {
224
0
      thread_state[t].state.buckets = thread_buckets;
225
0
      thread_buckets += 4 * ALPHABET_SIZE;
226
0
      thread_state[t].state.cache = thread_cache;
227
0
      thread_cache += LIBSAIS_PER_THREAD_CACHE_SIZE;
228
0
    }
229
230
0
    return thread_state;
231
0
  }
232
233
0
  libsais_free_aligned(thread_cache);
234
0
  libsais_free_aligned(thread_buckets);
235
0
  libsais_free_aligned(thread_state);
236
0
  return NULL;
237
0
}
238
239
8.54k
static void libsais_free_thread_state(LIBSAIS_THREAD_STATE* thread_state) {
240
8.54k
  if (thread_state != NULL) {
241
0
    libsais_free_aligned(thread_state[0].state.cache);
242
0
    libsais_free_aligned(thread_state[0].state.buckets);
243
0
    libsais_free_aligned(thread_state);
244
0
  }
245
8.54k
}
246
247
0
static LIBSAIS_CONTEXT* libsais_create_ctx_main(sa_sint_t threads) {
248
0
  LIBSAIS_CONTEXT* RESTRICT ctx =
249
0
      (LIBSAIS_CONTEXT*)libsais_alloc_aligned(sizeof(LIBSAIS_CONTEXT), 64);
250
0
  sa_sint_t* RESTRICT buckets = (sa_sint_t*)libsais_alloc_aligned(
251
0
      (size_t)8 * ALPHABET_SIZE * sizeof(sa_sint_t), 4096);
252
0
  LIBSAIS_THREAD_STATE* RESTRICT thread_state =
253
0
      threads > 1 ? libsais_alloc_thread_state(threads) : NULL;
254
255
0
  if (ctx != NULL && buckets != NULL &&
256
0
      (thread_state != NULL || threads == 1)) {
257
0
    ctx->buckets = buckets;
258
0
    ctx->threads = threads;
259
0
    ctx->thread_state = thread_state;
260
261
0
    return ctx;
262
0
  }
263
264
0
  libsais_free_thread_state(thread_state);
265
0
  libsais_free_aligned(buckets);
266
0
  libsais_free_aligned(ctx);
267
0
  return NULL;
268
0
}
269
270
0
static void libsais_free_ctx_main(LIBSAIS_CONTEXT* ctx) {
271
0
  if (ctx != NULL) {
272
0
    libsais_free_thread_state(ctx->thread_state);
273
0
    libsais_free_aligned(ctx->buckets);
274
0
    libsais_free_aligned(ctx);
275
0
  }
276
0
}
277
278
#if defined(LIBSAIS_OPENMP)
279
280
static sa_sint_t libsais_count_negative_marked_suffixes(
281
    sa_sint_t* RESTRICT SA, fast_sint_t omp_block_start,
282
    fast_sint_t omp_block_size) {
283
  sa_sint_t count = 0;
284
285
  fast_sint_t i;
286
  for (i = omp_block_start; i < omp_block_start + omp_block_size; ++i) {
287
    count += (SA[i] < 0);
288
  }
289
290
  return count;
291
}
292
293
static sa_sint_t libsais_count_zero_marked_suffixes(
294
    sa_sint_t* RESTRICT SA, fast_sint_t omp_block_start,
295
    fast_sint_t omp_block_size) {
296
  sa_sint_t count = 0;
297
298
  fast_sint_t i;
299
  for (i = omp_block_start; i < omp_block_start + omp_block_size; ++i) {
300
    count += (SA[i] == 0);
301
  }
302
303
  return count;
304
}
305
306
static void libsais_place_cached_suffixes(sa_sint_t* RESTRICT SA,
307
                                          LIBSAIS_THREAD_CACHE* RESTRICT cache,
308
                                          fast_sint_t omp_block_start,
309
                                          fast_sint_t omp_block_size) {
310
  const fast_sint_t prefetch_distance = 64;
311
312
  fast_sint_t i, j;
313
  for (i = omp_block_start,
314
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
315
       i < j; i += 4) {
316
    libsais_prefetchr(&cache[i + 2 * prefetch_distance]);
317
318
    libsais_prefetchw(&SA[cache[i + prefetch_distance + 0].symbol]);
319
    libsais_prefetchw(&SA[cache[i + prefetch_distance + 1].symbol]);
320
    libsais_prefetchw(&SA[cache[i + prefetch_distance + 2].symbol]);
321
    libsais_prefetchw(&SA[cache[i + prefetch_distance + 3].symbol]);
322
323
    SA[cache[i + 0].symbol] = cache[i + 0].index;
324
    SA[cache[i + 1].symbol] = cache[i + 1].index;
325
    SA[cache[i + 2].symbol] = cache[i + 2].index;
326
    SA[cache[i + 3].symbol] = cache[i + 3].index;
327
  }
328
329
  for (j += prefetch_distance + 3; i < j; i += 1) {
330
    SA[cache[i].symbol] = cache[i].index;
331
  }
332
}
333
334
static void libsais_compact_and_place_cached_suffixes(
335
    sa_sint_t* RESTRICT SA, LIBSAIS_THREAD_CACHE* RESTRICT cache,
336
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
337
  const fast_sint_t prefetch_distance = 64;
338
339
  fast_sint_t i, j, l;
340
  for (i = omp_block_start, j = omp_block_start + omp_block_size - 3,
341
      l = omp_block_start;
342
       i < j; i += 4) {
343
    libsais_prefetchw(&cache[i + prefetch_distance]);
344
345
    cache[l] = cache[i + 0];
346
    l += cache[l].symbol >= 0;
347
    cache[l] = cache[i + 1];
348
    l += cache[l].symbol >= 0;
349
    cache[l] = cache[i + 2];
350
    l += cache[l].symbol >= 0;
351
    cache[l] = cache[i + 3];
352
    l += cache[l].symbol >= 0;
353
  }
354
355
  for (j += 3; i < j; i += 1) {
356
    cache[l] = cache[i];
357
    l += cache[l].symbol >= 0;
358
  }
359
360
  libsais_place_cached_suffixes(SA, cache, omp_block_start,
361
                                l - omp_block_start);
362
}
363
364
static void libsais_accumulate_counts_s32_2(sa_sint_t* RESTRICT bucket00,
365
                                            fast_sint_t bucket_size,
366
                                            fast_sint_t bucket_stride) {
367
  sa_sint_t* RESTRICT bucket01 = bucket00 - bucket_stride;
368
  fast_sint_t s;
369
  for (s = 0; s < bucket_size; s += 1) {
370
    bucket00[s] = bucket00[s] + bucket01[s];
371
  }
372
}
373
374
static void libsais_accumulate_counts_s32_3(sa_sint_t* RESTRICT bucket00,
375
                                            fast_sint_t bucket_size,
376
                                            fast_sint_t bucket_stride) {
377
  sa_sint_t* RESTRICT bucket01 = bucket00 - bucket_stride;
378
  sa_sint_t* RESTRICT bucket02 = bucket01 - bucket_stride;
379
  fast_sint_t s;
380
  for (s = 0; s < bucket_size; s += 1) {
381
    bucket00[s] = bucket00[s] + bucket01[s] + bucket02[s];
382
  }
383
}
384
385
static void libsais_accumulate_counts_s32_4(sa_sint_t* RESTRICT bucket00,
386
                                            fast_sint_t bucket_size,
387
                                            fast_sint_t bucket_stride) {
388
  sa_sint_t* RESTRICT bucket01 = bucket00 - bucket_stride;
389
  sa_sint_t* RESTRICT bucket02 = bucket01 - bucket_stride;
390
  sa_sint_t* RESTRICT bucket03 = bucket02 - bucket_stride;
391
  fast_sint_t s;
392
  for (s = 0; s < bucket_size; s += 1) {
393
    bucket00[s] = bucket00[s] + bucket01[s] + bucket02[s] + bucket03[s];
394
  }
395
}
396
397
static void libsais_accumulate_counts_s32_5(sa_sint_t* RESTRICT bucket00,
398
                                            fast_sint_t bucket_size,
399
                                            fast_sint_t bucket_stride) {
400
  sa_sint_t* RESTRICT bucket01 = bucket00 - bucket_stride;
401
  sa_sint_t* RESTRICT bucket02 = bucket01 - bucket_stride;
402
  sa_sint_t* RESTRICT bucket03 = bucket02 - bucket_stride;
403
  sa_sint_t* RESTRICT bucket04 = bucket03 - bucket_stride;
404
  fast_sint_t s;
405
  for (s = 0; s < bucket_size; s += 1) {
406
    bucket00[s] =
407
        bucket00[s] + bucket01[s] + bucket02[s] + bucket03[s] + bucket04[s];
408
  }
409
}
410
411
static void libsais_accumulate_counts_s32_6(sa_sint_t* RESTRICT bucket00,
412
                                            fast_sint_t bucket_size,
413
                                            fast_sint_t bucket_stride) {
414
  sa_sint_t* RESTRICT bucket01 = bucket00 - bucket_stride;
415
  sa_sint_t* RESTRICT bucket02 = bucket01 - bucket_stride;
416
  sa_sint_t* RESTRICT bucket03 = bucket02 - bucket_stride;
417
  sa_sint_t* RESTRICT bucket04 = bucket03 - bucket_stride;
418
  sa_sint_t* RESTRICT bucket05 = bucket04 - bucket_stride;
419
  fast_sint_t s;
420
  for (s = 0; s < bucket_size; s += 1) {
421
    bucket00[s] = bucket00[s] + bucket01[s] + bucket02[s] + bucket03[s] +
422
                  bucket04[s] + bucket05[s];
423
  }
424
}
425
426
static void libsais_accumulate_counts_s32_7(sa_sint_t* RESTRICT bucket00,
427
                                            fast_sint_t bucket_size,
428
                                            fast_sint_t bucket_stride) {
429
  sa_sint_t* RESTRICT bucket01 = bucket00 - bucket_stride;
430
  sa_sint_t* RESTRICT bucket02 = bucket01 - bucket_stride;
431
  sa_sint_t* RESTRICT bucket03 = bucket02 - bucket_stride;
432
  sa_sint_t* RESTRICT bucket04 = bucket03 - bucket_stride;
433
  sa_sint_t* RESTRICT bucket05 = bucket04 - bucket_stride;
434
  sa_sint_t* RESTRICT bucket06 = bucket05 - bucket_stride;
435
  fast_sint_t s;
436
  for (s = 0; s < bucket_size; s += 1) {
437
    bucket00[s] = bucket00[s] + bucket01[s] + bucket02[s] + bucket03[s] +
438
                  bucket04[s] + bucket05[s] + bucket06[s];
439
  }
440
}
441
442
static void libsais_accumulate_counts_s32_8(sa_sint_t* RESTRICT bucket00,
443
                                            fast_sint_t bucket_size,
444
                                            fast_sint_t bucket_stride) {
445
  sa_sint_t* RESTRICT bucket01 = bucket00 - bucket_stride;
446
  sa_sint_t* RESTRICT bucket02 = bucket01 - bucket_stride;
447
  sa_sint_t* RESTRICT bucket03 = bucket02 - bucket_stride;
448
  sa_sint_t* RESTRICT bucket04 = bucket03 - bucket_stride;
449
  sa_sint_t* RESTRICT bucket05 = bucket04 - bucket_stride;
450
  sa_sint_t* RESTRICT bucket06 = bucket05 - bucket_stride;
451
  sa_sint_t* RESTRICT bucket07 = bucket06 - bucket_stride;
452
  fast_sint_t s;
453
  for (s = 0; s < bucket_size; s += 1) {
454
    bucket00[s] = bucket00[s] + bucket01[s] + bucket02[s] + bucket03[s] +
455
                  bucket04[s] + bucket05[s] + bucket06[s] + bucket07[s];
456
  }
457
}
458
459
static void libsais_accumulate_counts_s32_9(sa_sint_t* RESTRICT bucket00,
460
                                            fast_sint_t bucket_size,
461
                                            fast_sint_t bucket_stride) {
462
  sa_sint_t* RESTRICT bucket01 = bucket00 - bucket_stride;
463
  sa_sint_t* RESTRICT bucket02 = bucket01 - bucket_stride;
464
  sa_sint_t* RESTRICT bucket03 = bucket02 - bucket_stride;
465
  sa_sint_t* RESTRICT bucket04 = bucket03 - bucket_stride;
466
  sa_sint_t* RESTRICT bucket05 = bucket04 - bucket_stride;
467
  sa_sint_t* RESTRICT bucket06 = bucket05 - bucket_stride;
468
  sa_sint_t* RESTRICT bucket07 = bucket06 - bucket_stride;
469
  sa_sint_t* RESTRICT bucket08 = bucket07 - bucket_stride;
470
  fast_sint_t s;
471
  for (s = 0; s < bucket_size; s += 1) {
472
    bucket00[s] = bucket00[s] + bucket01[s] + bucket02[s] + bucket03[s] +
473
                  bucket04[s] + bucket05[s] + bucket06[s] + bucket07[s] +
474
                  bucket08[s];
475
  }
476
}
477
478
static void libsais_accumulate_counts_s32(sa_sint_t* RESTRICT buckets,
479
                                          fast_sint_t bucket_size,
480
                                          fast_sint_t bucket_stride,
481
                                          fast_sint_t num_buckets) {
482
  while (num_buckets >= 9) {
483
    libsais_accumulate_counts_s32_9(buckets - (num_buckets - 9) * bucket_stride,
484
                                    bucket_size, bucket_stride);
485
    num_buckets -= 8;
486
  }
487
488
  switch (num_buckets) {
489
    case 1:
490
      break;
491
    case 2:
492
      libsais_accumulate_counts_s32_2(buckets, bucket_size, bucket_stride);
493
      break;
494
    case 3:
495
      libsais_accumulate_counts_s32_3(buckets, bucket_size, bucket_stride);
496
      break;
497
    case 4:
498
      libsais_accumulate_counts_s32_4(buckets, bucket_size, bucket_stride);
499
      break;
500
    case 5:
501
      libsais_accumulate_counts_s32_5(buckets, bucket_size, bucket_stride);
502
      break;
503
    case 6:
504
      libsais_accumulate_counts_s32_6(buckets, bucket_size, bucket_stride);
505
      break;
506
    case 7:
507
      libsais_accumulate_counts_s32_7(buckets, bucket_size, bucket_stride);
508
      break;
509
    case 8:
510
      libsais_accumulate_counts_s32_8(buckets, bucket_size, bucket_stride);
511
      break;
512
    default:
513
      break;
514
  }
515
}
516
517
#endif
518
519
static void libsais_flip_suffix_markers_omp(sa_sint_t* RESTRICT SA, sa_sint_t l,
520
0
                                            sa_sint_t threads) {
521
#if defined(LIBSAIS_OPENMP)
522
#pragma omp parallel num_threads(threads) if (threads > 1 && l >= 65536)
523
#endif
524
0
  {
525
#if defined(LIBSAIS_OPENMP)
526
    fast_sint_t omp_thread_num = omp_get_thread_num();
527
    fast_sint_t omp_num_threads = omp_get_num_threads();
528
#else
529
0
    UNUSED(threads);
530
531
0
    fast_sint_t omp_thread_num = 0;
532
0
    fast_sint_t omp_num_threads = 1;
533
0
#endif
534
0
    fast_sint_t omp_block_stride = (l / omp_num_threads) & (-16);
535
0
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
536
0
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
537
0
                                     ? omp_block_stride
538
0
                                     : l - omp_block_start;
539
540
0
    fast_sint_t i;
541
0
    for (i = omp_block_start; i < omp_block_start + omp_block_size; ++i) {
542
0
      SA[i] ^= SAINT_MIN;
543
0
    }
544
0
  }
545
0
}
546
547
static void libsais_gather_lms_suffixes_8u(const uint8_t* RESTRICT T,
548
                                           sa_sint_t* RESTRICT SA, sa_sint_t n,
549
                                           fast_sint_t m,
550
                                           fast_sint_t omp_block_start,
551
7.48k
                                           fast_sint_t omp_block_size) {
552
7.48k
  if (omp_block_size > 0) {
553
7.48k
    const fast_sint_t prefetch_distance = 256;
554
555
7.48k
    fast_sint_t i, j = omp_block_start + omp_block_size,
556
7.48k
                   c0 = T[omp_block_start + omp_block_size - 1], c1 = -1;
557
558
7.48k
    while (j < n && (c1 = T[j]) == c0) {
559
0
      ++j;
560
0
    }
561
562
7.48k
    fast_uint_t f0 = c0 >= c1, f1 = 0;
563
564
7.48k
    for (i = omp_block_start + omp_block_size - 2, j = omp_block_start + 3;
565
5.52M
         i >= j; i -= 4) {
566
5.51M
      libsais_prefetchr(&T[i - prefetch_distance]);
567
568
5.51M
      c1 = T[i - 0];
569
5.51M
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
570
5.51M
      SA[m] = (sa_sint_t)(i + 1);
571
5.51M
      m -= (f1 & ~f0);
572
5.51M
      c0 = T[i - 1];
573
5.51M
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
574
5.51M
      SA[m] = (sa_sint_t)(i - 0);
575
5.51M
      m -= (f0 & ~f1);
576
5.51M
      c1 = T[i - 2];
577
5.51M
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
578
5.51M
      SA[m] = (sa_sint_t)(i - 1);
579
5.51M
      m -= (f1 & ~f0);
580
5.51M
      c0 = T[i - 3];
581
5.51M
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
582
5.51M
      SA[m] = (sa_sint_t)(i - 2);
583
5.51M
      m -= (f0 & ~f1);
584
5.51M
    }
585
586
19.0k
    for (j -= 3; i >= j; i -= 1) {
587
11.5k
      c1 = c0;
588
11.5k
      c0 = T[i];
589
11.5k
      f1 = f0;
590
11.5k
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
591
11.5k
      SA[m] = (sa_sint_t)(i + 1);
592
11.5k
      m -= (f0 & ~f1);
593
11.5k
    }
594
595
7.48k
    SA[m] = (sa_sint_t)(i + 1);
596
7.48k
  }
597
7.48k
}
598
599
static void libsais_gather_lms_suffixes_8u_omp(
600
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
601
7.48k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
602
#if defined(LIBSAIS_OPENMP)
603
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 65536 && \
604
                                                  omp_get_dynamic() == 0)
605
#endif
606
7.48k
  {
607
#if defined(LIBSAIS_OPENMP)
608
    fast_sint_t omp_thread_num = omp_get_thread_num();
609
    fast_sint_t omp_num_threads = omp_get_num_threads();
610
#else
611
7.48k
    UNUSED(threads);
612
7.48k
    UNUSED(thread_state);
613
614
7.48k
    fast_sint_t omp_thread_num = 0;
615
7.48k
    fast_sint_t omp_num_threads = 1;
616
7.48k
#endif
617
7.48k
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
618
7.48k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
619
7.48k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
620
7.48k
                                     ? omp_block_stride
621
7.48k
                                     : n - omp_block_start;
622
623
7.48k
    if (omp_num_threads == 1) {
624
7.48k
      libsais_gather_lms_suffixes_8u(T, SA, n, (fast_sint_t)n - 1,
625
7.48k
                                     omp_block_start, omp_block_size);
626
7.48k
    }
627
#if defined(LIBSAIS_OPENMP)
628
    else {
629
      fast_sint_t t, m = 0;
630
      for (t = omp_num_threads - 1; t > omp_thread_num; --t) {
631
        m += thread_state[t].state.m;
632
      }
633
634
      libsais_gather_lms_suffixes_8u(T, SA, n, (fast_sint_t)n - 1 - m,
635
                                     omp_block_start, omp_block_size);
636
637
#pragma omp barrier
638
639
      if (thread_state[omp_thread_num].state.m > 0) {
640
        SA[(fast_sint_t)n - 1 - m] =
641
            (sa_sint_t)thread_state[omp_thread_num].state.last_lms_suffix;
642
      }
643
    }
644
#endif
645
7.48k
  }
646
7.48k
}
647
648
static sa_sint_t libsais_gather_lms_suffixes_32s(const sa_sint_t* RESTRICT T,
649
                                                 sa_sint_t* RESTRICT SA,
650
842
                                                 sa_sint_t n) {
651
842
  const fast_sint_t prefetch_distance = 64;
652
653
842
  sa_sint_t i = n - 2;
654
842
  sa_sint_t m = n - 1;
655
842
  fast_uint_t f0 = 1;
656
842
  fast_uint_t f1 = 0;
657
842
  fast_sint_t c0 = T[n - 1];
658
842
  fast_sint_t c1 = 0;
659
660
411k
  for (; i >= 3; i -= 4) {
661
411k
    libsais_prefetchr(&T[i - prefetch_distance]);
662
663
411k
    c1 = T[i - 0];
664
411k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
665
411k
    SA[m] = i + 1;
666
411k
    m -= (sa_sint_t)(f1 & ~f0);
667
411k
    c0 = T[i - 1];
668
411k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
669
411k
    SA[m] = i - 0;
670
411k
    m -= (sa_sint_t)(f0 & ~f1);
671
411k
    c1 = T[i - 2];
672
411k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
673
411k
    SA[m] = i - 1;
674
411k
    m -= (sa_sint_t)(f1 & ~f0);
675
411k
    c0 = T[i - 3];
676
411k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
677
411k
    SA[m] = i - 2;
678
411k
    m -= (sa_sint_t)(f0 & ~f1);
679
411k
  }
680
681
2.17k
  for (; i >= 0; i -= 1) {
682
1.33k
    c1 = c0;
683
1.33k
    c0 = T[i];
684
1.33k
    f1 = f0;
685
1.33k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
686
1.33k
    SA[m] = i + 1;
687
1.33k
    m -= (sa_sint_t)(f0 & ~f1);
688
1.33k
  }
689
690
842
  return n - 1 - m;
691
842
}
692
693
static sa_sint_t libsais_gather_compacted_lms_suffixes_32s(
694
160
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n) {
695
160
  const fast_sint_t prefetch_distance = 64;
696
697
160
  sa_sint_t i = n - 2;
698
160
  sa_sint_t m = n - 1;
699
160
  fast_uint_t f0 = 1;
700
160
  fast_uint_t f1 = 0;
701
160
  fast_sint_t c0 = T[n - 1];
702
160
  fast_sint_t c1 = 0;
703
704
76.3k
  for (; i >= 3; i -= 4) {
705
76.1k
    libsais_prefetchr(&T[i - prefetch_distance]);
706
707
76.1k
    c1 = T[i - 0];
708
76.1k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
709
76.1k
    SA[m] = i + 1;
710
76.1k
    m -= (sa_sint_t)(f1 & ~f0 & (c0 >= 0));
711
76.1k
    c0 = T[i - 1];
712
76.1k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
713
76.1k
    SA[m] = i - 0;
714
76.1k
    m -= (sa_sint_t)(f0 & ~f1 & (c1 >= 0));
715
76.1k
    c1 = T[i - 2];
716
76.1k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
717
76.1k
    SA[m] = i - 1;
718
76.1k
    m -= (sa_sint_t)(f1 & ~f0 & (c0 >= 0));
719
76.1k
    c0 = T[i - 3];
720
76.1k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
721
76.1k
    SA[m] = i - 2;
722
76.1k
    m -= (sa_sint_t)(f0 & ~f1 & (c1 >= 0));
723
76.1k
  }
724
725
419
  for (; i >= 0; i -= 1) {
726
259
    c1 = c0;
727
259
    c0 = T[i];
728
259
    f1 = f0;
729
259
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
730
259
    SA[m] = i + 1;
731
259
    m -= (sa_sint_t)(f0 & ~f1 & (c1 >= 0));
732
259
  }
733
734
160
  return n - 1 - m;
735
160
}
736
737
#if defined(LIBSAIS_OPENMP)
738
739
static void libsais_count_lms_suffixes_32s_4k(const sa_sint_t* RESTRICT T,
740
                                              sa_sint_t n, sa_sint_t k,
741
                                              sa_sint_t* RESTRICT buckets) {
742
  const fast_sint_t prefetch_distance = 64;
743
744
  memset(buckets, 0, 4 * (size_t)k * sizeof(sa_sint_t));
745
746
  sa_sint_t i = n - 2;
747
  fast_uint_t f0 = 1;
748
  fast_uint_t f1 = 0;
749
  fast_sint_t c0 = T[n - 1];
750
  fast_sint_t c1 = 0;
751
752
  for (; i >= prefetch_distance + 3; i -= 4) {
753
    libsais_prefetchr(&T[i - 2 * prefetch_distance]);
754
755
    libsais_prefetchw(
756
        &buckets[BUCKETS_INDEX4(T[i - prefetch_distance - 0], 0)]);
757
    libsais_prefetchw(
758
        &buckets[BUCKETS_INDEX4(T[i - prefetch_distance - 1], 0)]);
759
    libsais_prefetchw(
760
        &buckets[BUCKETS_INDEX4(T[i - prefetch_distance - 2], 0)]);
761
    libsais_prefetchw(
762
        &buckets[BUCKETS_INDEX4(T[i - prefetch_distance - 3], 0)]);
763
764
    c1 = T[i - 0];
765
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
766
    buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0 + f1)]++;
767
768
    c0 = T[i - 1];
769
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
770
    buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]++;
771
772
    c1 = T[i - 2];
773
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
774
    buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0 + f1)]++;
775
776
    c0 = T[i - 3];
777
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
778
    buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]++;
779
  }
780
781
  for (; i >= 0; i -= 1) {
782
    c1 = c0;
783
    c0 = T[i];
784
    f1 = f0;
785
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
786
    buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]++;
787
  }
788
789
  buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0)]++;
790
}
791
792
#endif
793
794
static void libsais_count_lms_suffixes_32s_2k(const sa_sint_t* RESTRICT T,
795
                                              sa_sint_t n, sa_sint_t k,
796
5.51k
                                              sa_sint_t* RESTRICT buckets) {
797
5.51k
  const fast_sint_t prefetch_distance = 64;
798
799
5.51k
  memset(buckets, 0, 2 * (size_t)k * sizeof(sa_sint_t));
800
801
5.51k
  sa_sint_t i = n - 2;
802
5.51k
  fast_uint_t f0 = 1;
803
5.51k
  fast_uint_t f1 = 0;
804
5.51k
  fast_sint_t c0 = T[n - 1];
805
5.51k
  fast_sint_t c1 = 0;
806
807
74.7k
  for (; i >= prefetch_distance + 3; i -= 4) {
808
69.2k
    libsais_prefetchr(&T[i - 2 * prefetch_distance]);
809
810
69.2k
    libsais_prefetchw(
811
69.2k
        &buckets[BUCKETS_INDEX2(T[i - prefetch_distance - 0], 0)]);
812
69.2k
    libsais_prefetchw(
813
69.2k
        &buckets[BUCKETS_INDEX2(T[i - prefetch_distance - 1], 0)]);
814
69.2k
    libsais_prefetchw(
815
69.2k
        &buckets[BUCKETS_INDEX2(T[i - prefetch_distance - 2], 0)]);
816
69.2k
    libsais_prefetchw(
817
69.2k
        &buckets[BUCKETS_INDEX2(T[i - prefetch_distance - 3], 0)]);
818
819
69.2k
    c1 = T[i - 0];
820
69.2k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
821
69.2k
    buckets[BUCKETS_INDEX2((fast_uint_t)c0, (f1 & ~f0))]++;
822
823
69.2k
    c0 = T[i - 1];
824
69.2k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
825
69.2k
    buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
826
827
69.2k
    c1 = T[i - 2];
828
69.2k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
829
69.2k
    buckets[BUCKETS_INDEX2((fast_uint_t)c0, (f1 & ~f0))]++;
830
831
69.2k
    c0 = T[i - 3];
832
69.2k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
833
69.2k
    buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
834
69.2k
  }
835
836
176k
  for (; i >= 0; i -= 1) {
837
170k
    c1 = c0;
838
170k
    c0 = T[i];
839
170k
    f1 = f0;
840
170k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
841
170k
    buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
842
170k
  }
843
844
5.51k
  buckets[BUCKETS_INDEX2((fast_uint_t)c0, 0)]++;
845
5.51k
}
846
847
#if defined(LIBSAIS_OPENMP)
848
849
static void libsais_count_compacted_lms_suffixes_32s_2k(
850
    const sa_sint_t* RESTRICT T, sa_sint_t n, sa_sint_t k,
851
    sa_sint_t* RESTRICT buckets) {
852
  const fast_sint_t prefetch_distance = 64;
853
854
  memset(buckets, 0, 2 * (size_t)k * sizeof(sa_sint_t));
855
856
  sa_sint_t i = n - 2;
857
  fast_uint_t f0 = 1;
858
  fast_uint_t f1 = 0;
859
  fast_sint_t c0 = T[n - 1];
860
  fast_sint_t c1 = 0;
861
862
  for (; i >= prefetch_distance + 3; i -= 4) {
863
    libsais_prefetchr(&T[i - 2 * prefetch_distance]);
864
865
    libsais_prefetchw(
866
        &buckets[BUCKETS_INDEX2(T[i - prefetch_distance - 0] & SAINT_MAX, 0)]);
867
    libsais_prefetchw(
868
        &buckets[BUCKETS_INDEX2(T[i - prefetch_distance - 1] & SAINT_MAX, 0)]);
869
    libsais_prefetchw(
870
        &buckets[BUCKETS_INDEX2(T[i - prefetch_distance - 2] & SAINT_MAX, 0)]);
871
    libsais_prefetchw(
872
        &buckets[BUCKETS_INDEX2(T[i - prefetch_distance - 3] & SAINT_MAX, 0)]);
873
874
    c1 = T[i - 0];
875
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
876
    c0 &= SAINT_MAX;
877
    buckets[BUCKETS_INDEX2((fast_uint_t)c0, (f1 & ~f0))]++;
878
879
    c0 = T[i - 1];
880
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
881
    c1 &= SAINT_MAX;
882
    buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
883
884
    c1 = T[i - 2];
885
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
886
    c0 &= SAINT_MAX;
887
    buckets[BUCKETS_INDEX2((fast_uint_t)c0, (f1 & ~f0))]++;
888
889
    c0 = T[i - 3];
890
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
891
    c1 &= SAINT_MAX;
892
    buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
893
  }
894
895
  for (; i >= 0; i -= 1) {
896
    c1 = c0;
897
    c0 = T[i];
898
    f1 = f0;
899
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
900
    c1 &= SAINT_MAX;
901
    buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
902
  }
903
904
  c0 &= SAINT_MAX;
905
  buckets[BUCKETS_INDEX2((fast_uint_t)c0, 0)]++;
906
}
907
908
#endif
909
910
static sa_sint_t libsais_count_and_gather_lms_suffixes_8u(
911
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
912
    sa_sint_t* RESTRICT buckets, fast_sint_t omp_block_start,
913
8.54k
    fast_sint_t omp_block_size) {
914
8.54k
  memset(buckets, 0, (size_t)4 * ALPHABET_SIZE * sizeof(sa_sint_t));
915
916
8.54k
  fast_sint_t m = omp_block_start + omp_block_size - 1;
917
918
8.54k
  if (omp_block_size > 0) {
919
8.54k
    const fast_sint_t prefetch_distance = 256;
920
921
8.54k
    fast_sint_t i, j = m + 1, c0 = T[m], c1 = -1;
922
923
8.54k
    while (j < n && (c1 = T[j]) == c0) {
924
0
      ++j;
925
0
    }
926
927
8.54k
    fast_uint_t f0 = c0 >= c1, f1 = 0;
928
929
5.53M
    for (i = m - 1, j = omp_block_start + 3; i >= j; i -= 4) {
930
5.52M
      libsais_prefetchr(&T[i - prefetch_distance]);
931
932
5.52M
      c1 = T[i - 0];
933
5.52M
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
934
5.52M
      SA[m] = (sa_sint_t)(i + 1);
935
5.52M
      m -= (f1 & ~f0);
936
5.52M
      buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0 + f1)]++;
937
938
5.52M
      c0 = T[i - 1];
939
5.52M
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
940
5.52M
      SA[m] = (sa_sint_t)(i - 0);
941
5.52M
      m -= (f0 & ~f1);
942
5.52M
      buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]++;
943
944
5.52M
      c1 = T[i - 2];
945
5.52M
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
946
5.52M
      SA[m] = (sa_sint_t)(i - 1);
947
5.52M
      m -= (f1 & ~f0);
948
5.52M
      buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0 + f1)]++;
949
950
5.52M
      c0 = T[i - 3];
951
5.52M
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
952
5.52M
      SA[m] = (sa_sint_t)(i - 2);
953
5.52M
      m -= (f0 & ~f1);
954
5.52M
      buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]++;
955
5.52M
    }
956
957
21.7k
    for (j -= 3; i >= j; i -= 1) {
958
13.1k
      c1 = c0;
959
13.1k
      c0 = T[i];
960
13.1k
      f1 = f0;
961
13.1k
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
962
13.1k
      SA[m] = (sa_sint_t)(i + 1);
963
13.1k
      m -= (f0 & ~f1);
964
13.1k
      buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]++;
965
13.1k
    }
966
967
8.54k
    c1 = (i >= 0) ? T[i] : -1;
968
8.54k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
969
8.54k
    SA[m] = (sa_sint_t)(i + 1);
970
8.54k
    m -= (f1 & ~f0);
971
8.54k
    buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0 + f1)]++;
972
8.54k
  }
973
974
8.54k
  return (sa_sint_t)(omp_block_start + omp_block_size - 1 - m);
975
8.54k
}
976
977
static sa_sint_t libsais_count_and_gather_lms_suffixes_8u_omp(
978
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
979
    sa_sint_t* RESTRICT buckets, sa_sint_t threads,
980
8.54k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
981
8.54k
  sa_sint_t m = 0;
982
983
#if defined(LIBSAIS_OPENMP)
984
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 65536 && \
985
                                                  omp_get_dynamic() == 0)
986
#endif
987
8.54k
  {
988
#if defined(LIBSAIS_OPENMP)
989
    fast_sint_t omp_thread_num = omp_get_thread_num();
990
    fast_sint_t omp_num_threads = omp_get_num_threads();
991
#else
992
8.54k
    UNUSED(threads);
993
8.54k
    UNUSED(thread_state);
994
995
8.54k
    fast_sint_t omp_thread_num = 0;
996
8.54k
    fast_sint_t omp_num_threads = 1;
997
8.54k
#endif
998
8.54k
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
999
8.54k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
1000
8.54k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
1001
8.54k
                                     ? omp_block_stride
1002
8.54k
                                     : n - omp_block_start;
1003
1004
8.54k
    if (omp_num_threads == 1) {
1005
8.54k
      m = libsais_count_and_gather_lms_suffixes_8u(
1006
8.54k
          T, SA, n, buckets, omp_block_start, omp_block_size);
1007
8.54k
    }
1008
#if defined(LIBSAIS_OPENMP)
1009
    else {
1010
      {
1011
        thread_state[omp_thread_num].state.position =
1012
            omp_block_start + omp_block_size;
1013
        thread_state[omp_thread_num].state.m =
1014
            libsais_count_and_gather_lms_suffixes_8u(
1015
                T, SA, n, thread_state[omp_thread_num].state.buckets,
1016
                omp_block_start, omp_block_size);
1017
1018
        if (thread_state[omp_thread_num].state.m > 0) {
1019
          thread_state[omp_thread_num].state.last_lms_suffix =
1020
              SA[thread_state[omp_thread_num].state.position - 1];
1021
        }
1022
      }
1023
1024
#pragma omp barrier
1025
1026
#pragma omp master
1027
      {
1028
        memset(buckets, 0, (size_t)4 * ALPHABET_SIZE * sizeof(sa_sint_t));
1029
1030
        fast_sint_t t;
1031
        for (t = omp_num_threads - 1; t >= 0; --t) {
1032
          m += (sa_sint_t)thread_state[t].state.m;
1033
1034
          if (t != omp_num_threads - 1 && thread_state[t].state.m > 0) {
1035
            memcpy(
1036
                &SA[n - m],
1037
                &SA[thread_state[t].state.position - thread_state[t].state.m],
1038
                (size_t)thread_state[t].state.m * sizeof(sa_sint_t));
1039
          }
1040
1041
          {
1042
            sa_sint_t* RESTRICT temp_bucket = thread_state[t].state.buckets;
1043
            fast_sint_t s;
1044
            for (s = 0; s < 4 * ALPHABET_SIZE; s += 1) {
1045
              sa_sint_t A = buckets[s], B = temp_bucket[s];
1046
              buckets[s] = A + B;
1047
              temp_bucket[s] = A;
1048
            }
1049
          }
1050
        }
1051
      }
1052
    }
1053
#endif
1054
8.54k
  }
1055
1056
8.54k
  return m;
1057
8.54k
}
1058
1059
static sa_sint_t libsais_count_and_gather_lms_suffixes_32s_4k(
1060
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
1061
    sa_sint_t k, sa_sint_t* RESTRICT buckets, fast_sint_t omp_block_start,
1062
15.8k
    fast_sint_t omp_block_size) {
1063
15.8k
  memset(buckets, 0, 4 * (size_t)k * sizeof(sa_sint_t));
1064
1065
15.8k
  fast_sint_t m = omp_block_start + omp_block_size - 1;
1066
1067
15.8k
  if (omp_block_size > 0) {
1068
15.8k
    const fast_sint_t prefetch_distance = 64;
1069
1070
15.8k
    fast_sint_t i, j = m + 1, c0 = T[m], c1 = -1;
1071
1072
15.8k
    while (j < n && (c1 = T[j]) == c0) {
1073
0
      ++j;
1074
0
    }
1075
1076
15.8k
    fast_uint_t f0 = c0 >= c1, f1 = 0;
1077
1078
2.34M
    for (i = m - 1, j = omp_block_start + prefetch_distance + 3; i >= j;
1079
2.32M
         i -= 4) {
1080
2.32M
      libsais_prefetchr(&T[i - 2 * prefetch_distance]);
1081
1082
2.32M
      libsais_prefetchw(
1083
2.32M
          &buckets[BUCKETS_INDEX4(T[i - prefetch_distance - 0], 0)]);
1084
2.32M
      libsais_prefetchw(
1085
2.32M
          &buckets[BUCKETS_INDEX4(T[i - prefetch_distance - 1], 0)]);
1086
2.32M
      libsais_prefetchw(
1087
2.32M
          &buckets[BUCKETS_INDEX4(T[i - prefetch_distance - 2], 0)]);
1088
2.32M
      libsais_prefetchw(
1089
2.32M
          &buckets[BUCKETS_INDEX4(T[i - prefetch_distance - 3], 0)]);
1090
1091
2.32M
      c1 = T[i - 0];
1092
2.32M
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
1093
2.32M
      SA[m] = (sa_sint_t)(i + 1);
1094
2.32M
      m -= (f1 & ~f0);
1095
2.32M
      buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0 + f1)]++;
1096
1097
2.32M
      c0 = T[i - 1];
1098
2.32M
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1099
2.32M
      SA[m] = (sa_sint_t)(i - 0);
1100
2.32M
      m -= (f0 & ~f1);
1101
2.32M
      buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]++;
1102
1103
2.32M
      c1 = T[i - 2];
1104
2.32M
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
1105
2.32M
      SA[m] = (sa_sint_t)(i - 1);
1106
2.32M
      m -= (f1 & ~f0);
1107
2.32M
      buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0 + f1)]++;
1108
1109
2.32M
      c0 = T[i - 3];
1110
2.32M
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1111
2.32M
      SA[m] = (sa_sint_t)(i - 2);
1112
2.32M
      m -= (f0 & ~f1);
1113
2.32M
      buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]++;
1114
2.32M
    }
1115
1116
656k
    for (j -= prefetch_distance + 3; i >= j; i -= 1) {
1117
640k
      c1 = c0;
1118
640k
      c0 = T[i];
1119
640k
      f1 = f0;
1120
640k
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1121
640k
      SA[m] = (sa_sint_t)(i + 1);
1122
640k
      m -= (f0 & ~f1);
1123
640k
      buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]++;
1124
640k
    }
1125
1126
15.8k
    c1 = (i >= 0) ? T[i] : -1;
1127
15.8k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
1128
15.8k
    SA[m] = (sa_sint_t)(i + 1);
1129
15.8k
    m -= (f1 & ~f0);
1130
15.8k
    buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0 + f1)]++;
1131
15.8k
  }
1132
1133
15.8k
  return (sa_sint_t)(omp_block_start + omp_block_size - 1 - m);
1134
15.8k
}
1135
1136
static sa_sint_t libsais_count_and_gather_lms_suffixes_32s_2k(
1137
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
1138
    sa_sint_t k, sa_sint_t* RESTRICT buckets, fast_sint_t omp_block_start,
1139
3.44k
    fast_sint_t omp_block_size) {
1140
3.44k
  memset(buckets, 0, 2 * (size_t)k * sizeof(sa_sint_t));
1141
1142
3.44k
  fast_sint_t m = omp_block_start + omp_block_size - 1;
1143
1144
3.44k
  if (omp_block_size > 0) {
1145
3.44k
    const fast_sint_t prefetch_distance = 64;
1146
1147
3.44k
    fast_sint_t i, j = m + 1, c0 = T[m], c1 = -1;
1148
1149
3.44k
    while (j < n && (c1 = T[j]) == c0) {
1150
0
      ++j;
1151
0
    }
1152
1153
3.44k
    fast_uint_t f0 = c0 >= c1, f1 = 0;
1154
1155
807k
    for (i = m - 1, j = omp_block_start + prefetch_distance + 3; i >= j;
1156
804k
         i -= 4) {
1157
804k
      libsais_prefetchr(&T[i - 2 * prefetch_distance]);
1158
1159
804k
      libsais_prefetchw(
1160
804k
          &buckets[BUCKETS_INDEX2(T[i - prefetch_distance - 0], 0)]);
1161
804k
      libsais_prefetchw(
1162
804k
          &buckets[BUCKETS_INDEX2(T[i - prefetch_distance - 1], 0)]);
1163
804k
      libsais_prefetchw(
1164
804k
          &buckets[BUCKETS_INDEX2(T[i - prefetch_distance - 2], 0)]);
1165
804k
      libsais_prefetchw(
1166
804k
          &buckets[BUCKETS_INDEX2(T[i - prefetch_distance - 3], 0)]);
1167
1168
804k
      c1 = T[i - 0];
1169
804k
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
1170
804k
      SA[m] = (sa_sint_t)(i + 1);
1171
804k
      m -= (f1 & ~f0);
1172
804k
      buckets[BUCKETS_INDEX2((fast_uint_t)c0, (f1 & ~f0))]++;
1173
1174
804k
      c0 = T[i - 1];
1175
804k
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1176
804k
      SA[m] = (sa_sint_t)(i - 0);
1177
804k
      m -= (f0 & ~f1);
1178
804k
      buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
1179
1180
804k
      c1 = T[i - 2];
1181
804k
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
1182
804k
      SA[m] = (sa_sint_t)(i - 1);
1183
804k
      m -= (f1 & ~f0);
1184
804k
      buckets[BUCKETS_INDEX2((fast_uint_t)c0, (f1 & ~f0))]++;
1185
1186
804k
      c0 = T[i - 3];
1187
804k
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1188
804k
      SA[m] = (sa_sint_t)(i - 2);
1189
804k
      m -= (f0 & ~f1);
1190
804k
      buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
1191
804k
    }
1192
1193
175k
    for (j -= prefetch_distance + 3; i >= j; i -= 1) {
1194
172k
      c1 = c0;
1195
172k
      c0 = T[i];
1196
172k
      f1 = f0;
1197
172k
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1198
172k
      SA[m] = (sa_sint_t)(i + 1);
1199
172k
      m -= (f0 & ~f1);
1200
172k
      buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
1201
172k
    }
1202
1203
3.44k
    c1 = (i >= 0) ? T[i] : -1;
1204
3.44k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
1205
3.44k
    SA[m] = (sa_sint_t)(i + 1);
1206
3.44k
    m -= (f1 & ~f0);
1207
3.44k
    buckets[BUCKETS_INDEX2((fast_uint_t)c0, (f1 & ~f0))]++;
1208
3.44k
  }
1209
1210
3.44k
  return (sa_sint_t)(omp_block_start + omp_block_size - 1 - m);
1211
3.44k
}
1212
1213
static sa_sint_t libsais_count_and_gather_compacted_lms_suffixes_32s_2k(
1214
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
1215
    sa_sint_t k, sa_sint_t* RESTRICT buckets, fast_sint_t omp_block_start,
1216
7.02k
    fast_sint_t omp_block_size) {
1217
7.02k
  memset(buckets, 0, 2 * (size_t)k * sizeof(sa_sint_t));
1218
1219
7.02k
  fast_sint_t m = omp_block_start + omp_block_size - 1;
1220
1221
7.02k
  if (omp_block_size > 0) {
1222
7.02k
    const fast_sint_t prefetch_distance = 64;
1223
1224
7.02k
    fast_sint_t i, j = m + 1, c0 = T[m], c1 = -1;
1225
1226
7.02k
    while (j < n && (c1 = T[j]) == c0) {
1227
0
      ++j;
1228
0
    }
1229
1230
7.02k
    fast_uint_t f0 = c0 >= c1, f1 = 0;
1231
1232
2.34M
    for (i = m - 1, j = omp_block_start + prefetch_distance + 3; i >= j;
1233
2.33M
         i -= 4) {
1234
2.33M
      libsais_prefetchr(&T[i - 2 * prefetch_distance]);
1235
1236
2.33M
      libsais_prefetchw(&buckets[BUCKETS_INDEX2(
1237
2.33M
          T[i - prefetch_distance - 0] & SAINT_MAX, 0)]);
1238
2.33M
      libsais_prefetchw(&buckets[BUCKETS_INDEX2(
1239
2.33M
          T[i - prefetch_distance - 1] & SAINT_MAX, 0)]);
1240
2.33M
      libsais_prefetchw(&buckets[BUCKETS_INDEX2(
1241
2.33M
          T[i - prefetch_distance - 2] & SAINT_MAX, 0)]);
1242
2.33M
      libsais_prefetchw(&buckets[BUCKETS_INDEX2(
1243
2.33M
          T[i - prefetch_distance - 3] & SAINT_MAX, 0)]);
1244
1245
2.33M
      c1 = T[i - 0];
1246
2.33M
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
1247
2.33M
      SA[m] = (sa_sint_t)(i + 1);
1248
2.33M
      m -= (f1 & ~f0 & (c0 >= 0));
1249
2.33M
      c0 &= SAINT_MAX;
1250
2.33M
      buckets[BUCKETS_INDEX2((fast_uint_t)c0, (f1 & ~f0))]++;
1251
1252
2.33M
      c0 = T[i - 1];
1253
2.33M
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1254
2.33M
      SA[m] = (sa_sint_t)(i - 0);
1255
2.33M
      m -= (f0 & ~f1 & (c1 >= 0));
1256
2.33M
      c1 &= SAINT_MAX;
1257
2.33M
      buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
1258
1259
2.33M
      c1 = T[i - 2];
1260
2.33M
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
1261
2.33M
      SA[m] = (sa_sint_t)(i - 1);
1262
2.33M
      m -= (f1 & ~f0 & (c0 >= 0));
1263
2.33M
      c0 &= SAINT_MAX;
1264
2.33M
      buckets[BUCKETS_INDEX2((fast_uint_t)c0, (f1 & ~f0))]++;
1265
1266
2.33M
      c0 = T[i - 3];
1267
2.33M
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1268
2.33M
      SA[m] = (sa_sint_t)(i - 2);
1269
2.33M
      m -= (f0 & ~f1 & (c1 >= 0));
1270
2.33M
      c1 &= SAINT_MAX;
1271
2.33M
      buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
1272
2.33M
    }
1273
1274
420k
    for (j -= prefetch_distance + 3; i >= j; i -= 1) {
1275
413k
      c1 = c0;
1276
413k
      c0 = T[i];
1277
413k
      f1 = f0;
1278
413k
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1279
413k
      SA[m] = (sa_sint_t)(i + 1);
1280
413k
      m -= (f0 & ~f1 & (c1 >= 0));
1281
413k
      c1 &= SAINT_MAX;
1282
413k
      buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
1283
413k
    }
1284
1285
7.02k
    c1 = (i >= 0) ? T[i] : -1;
1286
7.02k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
1287
7.02k
    SA[m] = (sa_sint_t)(i + 1);
1288
7.02k
    m -= (f1 & ~f0 & (c0 >= 0));
1289
7.02k
    c0 &= SAINT_MAX;
1290
7.02k
    buckets[BUCKETS_INDEX2((fast_uint_t)c0, (f1 & ~f0))]++;
1291
7.02k
  }
1292
1293
7.02k
  return (sa_sint_t)(omp_block_start + omp_block_size - 1 - m);
1294
7.02k
}
1295
1296
#if defined(LIBSAIS_OPENMP)
1297
1298
static fast_sint_t libsais_get_bucket_stride(fast_sint_t free_space,
1299
                                             fast_sint_t bucket_size,
1300
                                             fast_sint_t num_buckets) {
1301
  fast_sint_t bucket_size_1024 = (bucket_size + 1023) & (-1024);
1302
  if (free_space / (num_buckets - 1) >= bucket_size_1024) {
1303
    return bucket_size_1024;
1304
  }
1305
  fast_sint_t bucket_size_16 = (bucket_size + 15) & (-16);
1306
  if (free_space / (num_buckets - 1) >= bucket_size_16) {
1307
    return bucket_size_16;
1308
  }
1309
1310
  return bucket_size;
1311
}
1312
1313
static sa_sint_t libsais_count_and_gather_lms_suffixes_32s_4k_fs_omp(
1314
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
1315
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t local_buckets,
1316
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
1317
  sa_sint_t m = 0;
1318
1319
#if defined(LIBSAIS_OPENMP)
1320
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 65536)
1321
#endif
1322
  {
1323
#if defined(LIBSAIS_OPENMP)
1324
    fast_sint_t omp_thread_num = omp_get_thread_num();
1325
    fast_sint_t omp_num_threads = omp_get_num_threads();
1326
#else
1327
    UNUSED(local_buckets);
1328
    UNUSED(threads);
1329
    UNUSED(thread_state);
1330
1331
    fast_sint_t omp_thread_num = 0;
1332
    fast_sint_t omp_num_threads = 1;
1333
#endif
1334
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
1335
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
1336
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
1337
                                     ? omp_block_stride
1338
                                     : n - omp_block_start;
1339
1340
    if (omp_num_threads == 1) {
1341
      m = libsais_count_and_gather_lms_suffixes_32s_4k(
1342
          T, SA, n, k, buckets, omp_block_start, omp_block_size);
1343
    }
1344
#if defined(LIBSAIS_OPENMP)
1345
    else {
1346
      fast_sint_t bucket_size = 4 * (fast_sint_t)k;
1347
      fast_sint_t free_space =
1348
          local_buckets ? LIBSAIS_LOCAL_BUFFER_SIZE : (buckets - &SA[n]);
1349
      fast_sint_t bucket_stride =
1350
          libsais_get_bucket_stride(free_space, bucket_size, omp_num_threads);
1351
1352
      {
1353
        thread_state[omp_thread_num].state.position =
1354
            omp_block_start + omp_block_size;
1355
        thread_state[omp_thread_num].state.count =
1356
            libsais_count_and_gather_lms_suffixes_32s_4k(
1357
                T, SA, n, k, buckets - (omp_thread_num * bucket_stride),
1358
                omp_block_start, omp_block_size);
1359
      }
1360
1361
#pragma omp barrier
1362
1363
      if (omp_thread_num == omp_num_threads - 1) {
1364
        fast_sint_t t;
1365
        for (t = omp_num_threads - 1; t >= 0; --t) {
1366
          m += (sa_sint_t)thread_state[t].state.count;
1367
1368
          if (t != omp_num_threads - 1 && thread_state[t].state.count > 0) {
1369
            memcpy(&SA[n - m],
1370
                   &SA[thread_state[t].state.position -
1371
                       thread_state[t].state.count],
1372
                   (size_t)thread_state[t].state.count * sizeof(sa_sint_t));
1373
          }
1374
        }
1375
      } else {
1376
        omp_num_threads = omp_num_threads - 1;
1377
        omp_block_stride = (bucket_size / omp_num_threads) & (-16);
1378
        omp_block_start = omp_thread_num * omp_block_stride;
1379
        omp_block_size = omp_thread_num < omp_num_threads - 1
1380
                             ? omp_block_stride
1381
                             : bucket_size - omp_block_start;
1382
1383
        libsais_accumulate_counts_s32(buckets + omp_block_start, omp_block_size,
1384
                                      bucket_stride, omp_num_threads + 1);
1385
      }
1386
    }
1387
#endif
1388
  }
1389
1390
  return m;
1391
}
1392
1393
static sa_sint_t libsais_count_and_gather_lms_suffixes_32s_2k_fs_omp(
1394
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
1395
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t local_buckets,
1396
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
1397
  sa_sint_t m = 0;
1398
1399
#if defined(LIBSAIS_OPENMP)
1400
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 65536)
1401
#endif
1402
  {
1403
#if defined(LIBSAIS_OPENMP)
1404
    fast_sint_t omp_thread_num = omp_get_thread_num();
1405
    fast_sint_t omp_num_threads = omp_get_num_threads();
1406
#else
1407
    UNUSED(local_buckets);
1408
    UNUSED(threads);
1409
    UNUSED(thread_state);
1410
1411
    fast_sint_t omp_thread_num = 0;
1412
    fast_sint_t omp_num_threads = 1;
1413
#endif
1414
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
1415
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
1416
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
1417
                                     ? omp_block_stride
1418
                                     : n - omp_block_start;
1419
1420
    if (omp_num_threads == 1) {
1421
      m = libsais_count_and_gather_lms_suffixes_32s_2k(
1422
          T, SA, n, k, buckets, omp_block_start, omp_block_size);
1423
    }
1424
#if defined(LIBSAIS_OPENMP)
1425
    else {
1426
      fast_sint_t bucket_size = 2 * (fast_sint_t)k;
1427
      fast_sint_t free_space =
1428
          local_buckets ? LIBSAIS_LOCAL_BUFFER_SIZE : (buckets - &SA[n]);
1429
      fast_sint_t bucket_stride =
1430
          libsais_get_bucket_stride(free_space, bucket_size, omp_num_threads);
1431
1432
      {
1433
        thread_state[omp_thread_num].state.position =
1434
            omp_block_start + omp_block_size;
1435
        thread_state[omp_thread_num].state.count =
1436
            libsais_count_and_gather_lms_suffixes_32s_2k(
1437
                T, SA, n, k, buckets - (omp_thread_num * bucket_stride),
1438
                omp_block_start, omp_block_size);
1439
      }
1440
1441
#pragma omp barrier
1442
1443
      if (omp_thread_num == omp_num_threads - 1) {
1444
        fast_sint_t t;
1445
        for (t = omp_num_threads - 1; t >= 0; --t) {
1446
          m += (sa_sint_t)thread_state[t].state.count;
1447
1448
          if (t != omp_num_threads - 1 && thread_state[t].state.count > 0) {
1449
            memcpy(&SA[n - m],
1450
                   &SA[thread_state[t].state.position -
1451
                       thread_state[t].state.count],
1452
                   (size_t)thread_state[t].state.count * sizeof(sa_sint_t));
1453
          }
1454
        }
1455
      } else {
1456
        omp_num_threads = omp_num_threads - 1;
1457
        omp_block_stride = (bucket_size / omp_num_threads) & (-16);
1458
        omp_block_start = omp_thread_num * omp_block_stride;
1459
        omp_block_size = omp_thread_num < omp_num_threads - 1
1460
                             ? omp_block_stride
1461
                             : bucket_size - omp_block_start;
1462
1463
        libsais_accumulate_counts_s32(buckets + omp_block_start, omp_block_size,
1464
                                      bucket_stride, omp_num_threads + 1);
1465
      }
1466
    }
1467
#endif
1468
  }
1469
1470
  return m;
1471
}
1472
1473
static void libsais_count_and_gather_compacted_lms_suffixes_32s_2k_fs_omp(
1474
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
1475
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t local_buckets,
1476
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
1477
#if defined(LIBSAIS_OPENMP)
1478
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 65536)
1479
#endif
1480
  {
1481
#if defined(LIBSAIS_OPENMP)
1482
    fast_sint_t omp_thread_num = omp_get_thread_num();
1483
    fast_sint_t omp_num_threads = omp_get_num_threads();
1484
#else
1485
    UNUSED(local_buckets);
1486
    UNUSED(threads);
1487
    UNUSED(thread_state);
1488
1489
    fast_sint_t omp_thread_num = 0;
1490
    fast_sint_t omp_num_threads = 1;
1491
#endif
1492
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
1493
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
1494
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
1495
                                     ? omp_block_stride
1496
                                     : n - omp_block_start;
1497
1498
    if (omp_num_threads == 1) {
1499
      libsais_count_and_gather_compacted_lms_suffixes_32s_2k(
1500
          T, SA, n, k, buckets, omp_block_start, omp_block_size);
1501
    }
1502
#if defined(LIBSAIS_OPENMP)
1503
    else {
1504
      fast_sint_t bucket_size = 2 * (fast_sint_t)k;
1505
      fast_sint_t free_space =
1506
          local_buckets ? LIBSAIS_LOCAL_BUFFER_SIZE
1507
                        : (buckets - &SA[(fast_sint_t)n + (fast_sint_t)n]);
1508
      fast_sint_t bucket_stride =
1509
          libsais_get_bucket_stride(free_space, bucket_size, omp_num_threads);
1510
1511
      {
1512
        thread_state[omp_thread_num].state.position =
1513
            omp_block_start + omp_block_size;
1514
        thread_state[omp_thread_num].state.count =
1515
            libsais_count_and_gather_compacted_lms_suffixes_32s_2k(
1516
                T, SA + n, n, k, buckets - (omp_thread_num * bucket_stride),
1517
                omp_block_start, omp_block_size);
1518
      }
1519
1520
#pragma omp barrier
1521
1522
      {
1523
        fast_sint_t t, m = 0;
1524
        for (t = omp_num_threads - 1; t >= omp_thread_num; --t) {
1525
          m += (sa_sint_t)thread_state[t].state.count;
1526
        }
1527
1528
        if (thread_state[omp_thread_num].state.count > 0) {
1529
          memcpy(&SA[n - m],
1530
                 &SA[n + thread_state[omp_thread_num].state.position -
1531
                     thread_state[omp_thread_num].state.count],
1532
                 (size_t)thread_state[omp_thread_num].state.count *
1533
                     sizeof(sa_sint_t));
1534
        }
1535
      }
1536
1537
      {
1538
        omp_block_stride = (bucket_size / omp_num_threads) & (-16);
1539
        omp_block_start = omp_thread_num * omp_block_stride;
1540
        omp_block_size = omp_thread_num < omp_num_threads - 1
1541
                             ? omp_block_stride
1542
                             : bucket_size - omp_block_start;
1543
1544
        libsais_accumulate_counts_s32(buckets + omp_block_start, omp_block_size,
1545
                                      bucket_stride, omp_num_threads);
1546
      }
1547
    }
1548
#endif
1549
  }
1550
}
1551
1552
#endif
1553
1554
static sa_sint_t libsais_count_and_gather_lms_suffixes_32s_4k_nofs_omp(
1555
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
1556
15.8k
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t threads) {
1557
15.8k
  sa_sint_t m = 0;
1558
1559
#if defined(LIBSAIS_OPENMP)
1560
#pragma omp parallel num_threads(2) if (threads > 1 && n >= 65536)
1561
#endif
1562
15.8k
  {
1563
#if defined(LIBSAIS_OPENMP)
1564
    fast_sint_t omp_thread_num = omp_get_thread_num();
1565
    fast_sint_t omp_num_threads = omp_get_num_threads();
1566
#else
1567
15.8k
    UNUSED(threads);
1568
1569
15.8k
    fast_sint_t omp_num_threads = 1;
1570
15.8k
#endif
1571
15.8k
    if (omp_num_threads == 1) {
1572
15.8k
      m = libsais_count_and_gather_lms_suffixes_32s_4k(T, SA, n, k, buckets, 0,
1573
15.8k
                                                       n);
1574
15.8k
    }
1575
#if defined(LIBSAIS_OPENMP)
1576
    else if (omp_thread_num == 0) {
1577
      libsais_count_lms_suffixes_32s_4k(T, n, k, buckets);
1578
    } else {
1579
      m = libsais_gather_lms_suffixes_32s(T, SA, n);
1580
    }
1581
#endif
1582
15.8k
  }
1583
1584
15.8k
  return m;
1585
15.8k
}
1586
1587
static sa_sint_t libsais_count_and_gather_lms_suffixes_32s_2k_nofs_omp(
1588
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
1589
1.01k
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t threads) {
1590
1.01k
  sa_sint_t m = 0;
1591
1592
#if defined(LIBSAIS_OPENMP)
1593
#pragma omp parallel num_threads(2) if (threads > 1 && n >= 65536)
1594
#endif
1595
1.01k
  {
1596
#if defined(LIBSAIS_OPENMP)
1597
    fast_sint_t omp_thread_num = omp_get_thread_num();
1598
    fast_sint_t omp_num_threads = omp_get_num_threads();
1599
#else
1600
1.01k
    UNUSED(threads);
1601
1602
1.01k
    fast_sint_t omp_num_threads = 1;
1603
1.01k
#endif
1604
1.01k
    if (omp_num_threads == 1) {
1605
1.01k
      m = libsais_count_and_gather_lms_suffixes_32s_2k(T, SA, n, k, buckets, 0,
1606
1.01k
                                                       n);
1607
1.01k
    }
1608
#if defined(LIBSAIS_OPENMP)
1609
    else if (omp_thread_num == 0) {
1610
      libsais_count_lms_suffixes_32s_2k(T, n, k, buckets);
1611
    } else {
1612
      m = libsais_gather_lms_suffixes_32s(T, SA, n);
1613
    }
1614
#endif
1615
1.01k
  }
1616
1617
1.01k
  return m;
1618
1.01k
}
1619
1620
static sa_sint_t
1621
libsais_count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(
1622
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
1623
7.02k
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t threads) {
1624
7.02k
  sa_sint_t m = 0;
1625
1626
#if defined(LIBSAIS_OPENMP)
1627
#pragma omp parallel num_threads(2) if (threads > 1 && n >= 65536)
1628
#endif
1629
7.02k
  {
1630
#if defined(LIBSAIS_OPENMP)
1631
    fast_sint_t omp_thread_num = omp_get_thread_num();
1632
    fast_sint_t omp_num_threads = omp_get_num_threads();
1633
#else
1634
7.02k
    UNUSED(threads);
1635
1636
7.02k
    fast_sint_t omp_num_threads = 1;
1637
7.02k
#endif
1638
7.02k
    if (omp_num_threads == 1) {
1639
7.02k
      m = libsais_count_and_gather_compacted_lms_suffixes_32s_2k(T, SA, n, k,
1640
7.02k
                                                                 buckets, 0, n);
1641
7.02k
    }
1642
#if defined(LIBSAIS_OPENMP)
1643
    else if (omp_thread_num == 0) {
1644
      libsais_count_compacted_lms_suffixes_32s_2k(T, n, k, buckets);
1645
    } else {
1646
      m = libsais_gather_compacted_lms_suffixes_32s(T, SA, n);
1647
    }
1648
#endif
1649
7.02k
  }
1650
1651
7.02k
  return m;
1652
7.02k
}
1653
1654
static sa_sint_t libsais_count_and_gather_lms_suffixes_32s_4k_omp(
1655
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
1656
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t local_buckets,
1657
15.8k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
1658
15.8k
  sa_sint_t m;
1659
1660
#if defined(LIBSAIS_OPENMP)
1661
  fast_sint_t free_space =
1662
      local_buckets ? LIBSAIS_LOCAL_BUFFER_SIZE : (buckets - &SA[n]);
1663
  sa_sint_t max_threads =
1664
      (sa_sint_t)(free_space / ((4 * (fast_sint_t)k + 15) & (-16)));
1665
  if (max_threads > threads) {
1666
    max_threads = threads;
1667
  }
1668
1669
  if (max_threads > 1 && n >= 65536 && n / k >= 2) {
1670
    if (max_threads > n / 16 / k) {
1671
      max_threads = n / 16 / k;
1672
    }
1673
    m = libsais_count_and_gather_lms_suffixes_32s_4k_fs_omp(
1674
        T, SA, n, k, buckets, local_buckets, max_threads > 2 ? max_threads : 2,
1675
        thread_state);
1676
  } else
1677
#else
1678
15.8k
  UNUSED(local_buckets);
1679
15.8k
  UNUSED(thread_state);
1680
15.8k
#endif
1681
15.8k
  {
1682
15.8k
    m = libsais_count_and_gather_lms_suffixes_32s_4k_nofs_omp(T, SA, n, k,
1683
15.8k
                                                              buckets, threads);
1684
15.8k
  }
1685
1686
15.8k
  return m;
1687
15.8k
}
1688
1689
static sa_sint_t libsais_count_and_gather_lms_suffixes_32s_2k_omp(
1690
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
1691
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t local_buckets,
1692
1.01k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
1693
1.01k
  sa_sint_t m;
1694
1695
#if defined(LIBSAIS_OPENMP)
1696
  fast_sint_t free_space =
1697
      local_buckets ? LIBSAIS_LOCAL_BUFFER_SIZE : (buckets - &SA[n]);
1698
  sa_sint_t max_threads =
1699
      (sa_sint_t)(free_space / ((2 * (fast_sint_t)k + 15) & (-16)));
1700
  if (max_threads > threads) {
1701
    max_threads = threads;
1702
  }
1703
1704
  if (max_threads > 1 && n >= 65536 && n / k >= 2) {
1705
    if (max_threads > n / 8 / k) {
1706
      max_threads = n / 8 / k;
1707
    }
1708
    m = libsais_count_and_gather_lms_suffixes_32s_2k_fs_omp(
1709
        T, SA, n, k, buckets, local_buckets, max_threads > 2 ? max_threads : 2,
1710
        thread_state);
1711
  } else
1712
#else
1713
1.01k
  UNUSED(local_buckets);
1714
1.01k
  UNUSED(thread_state);
1715
1.01k
#endif
1716
1.01k
  {
1717
1.01k
    m = libsais_count_and_gather_lms_suffixes_32s_2k_nofs_omp(T, SA, n, k,
1718
1.01k
                                                              buckets, threads);
1719
1.01k
  }
1720
1721
1.01k
  return m;
1722
1.01k
}
1723
1724
static void libsais_count_and_gather_compacted_lms_suffixes_32s_2k_omp(
1725
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
1726
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t local_buckets,
1727
7.02k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
1728
#if defined(LIBSAIS_OPENMP)
1729
  fast_sint_t free_space =
1730
      local_buckets ? LIBSAIS_LOCAL_BUFFER_SIZE
1731
                    : (buckets - &SA[(fast_sint_t)n + (fast_sint_t)n]);
1732
  sa_sint_t max_threads =
1733
      (sa_sint_t)(free_space / ((2 * (fast_sint_t)k + 15) & (-16)));
1734
  if (max_threads > threads) {
1735
    max_threads = threads;
1736
  }
1737
1738
  if (!local_buckets && max_threads > 1 && n >= 65536 && n / k >= 2) {
1739
    if (max_threads > n / 8 / k) {
1740
      max_threads = n / 8 / k;
1741
    }
1742
    libsais_count_and_gather_compacted_lms_suffixes_32s_2k_fs_omp(
1743
        T, SA, n, k, buckets, local_buckets, max_threads > 2 ? max_threads : 2,
1744
        thread_state);
1745
  } else
1746
#else
1747
7.02k
  UNUSED(local_buckets);
1748
7.02k
  UNUSED(thread_state);
1749
7.02k
#endif
1750
7.02k
  {
1751
7.02k
    libsais_count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(
1752
7.02k
        T, SA, n, k, buckets, threads);
1753
7.02k
  }
1754
7.02k
}
1755
1756
static void libsais_count_suffixes_32s(const sa_sint_t* RESTRICT T, sa_sint_t n,
1757
                                       sa_sint_t k,
1758
1.06k
                                       sa_sint_t* RESTRICT buckets) {
1759
1.06k
  const fast_sint_t prefetch_distance = 64;
1760
1761
1.06k
  memset(buckets, 0, (size_t)k * sizeof(sa_sint_t));
1762
1763
1.06k
  fast_sint_t i, j;
1764
249k
  for (i = 0, j = (fast_sint_t)n - 7; i < j; i += 8) {
1765
248k
    libsais_prefetchr(&T[i + prefetch_distance]);
1766
1767
248k
    buckets[T[i + 0]]++;
1768
248k
    buckets[T[i + 1]]++;
1769
248k
    buckets[T[i + 2]]++;
1770
248k
    buckets[T[i + 3]]++;
1771
248k
    buckets[T[i + 4]]++;
1772
248k
    buckets[T[i + 5]]++;
1773
248k
    buckets[T[i + 6]]++;
1774
248k
    buckets[T[i + 7]]++;
1775
248k
  }
1776
1777
4.29k
  for (j += 7; i < j; i += 1) {
1778
3.22k
    buckets[T[i]]++;
1779
3.22k
  }
1780
1.06k
}
1781
1782
static sa_sint_t libsais_initialize_buckets_start_and_end_8u(
1783
8.54k
    sa_sint_t* RESTRICT buckets, sa_sint_t* RESTRICT freq) {
1784
8.54k
  sa_sint_t* RESTRICT bucket_start = &buckets[6 * ALPHABET_SIZE];
1785
8.54k
  sa_sint_t* RESTRICT bucket_end = &buckets[7 * ALPHABET_SIZE];
1786
1787
8.54k
  fast_sint_t k = -1;
1788
1789
8.54k
  if (freq != NULL) {
1790
0
    fast_sint_t i, j;
1791
0
    sa_sint_t sum = 0;
1792
0
    for (i = BUCKETS_INDEX4(0, 0), j = 0;
1793
0
         i <= BUCKETS_INDEX4(ALPHABET_SIZE - 1, 0);
1794
0
         i += BUCKETS_INDEX4(1, 0), j += 1) {
1795
0
      sa_sint_t total = buckets[i + BUCKETS_INDEX4(0, 0)] +
1796
0
                        buckets[i + BUCKETS_INDEX4(0, 1)] +
1797
0
                        buckets[i + BUCKETS_INDEX4(0, 2)] +
1798
0
                        buckets[i + BUCKETS_INDEX4(0, 3)];
1799
1800
0
      bucket_start[j] = sum;
1801
0
      sum += total;
1802
0
      bucket_end[j] = sum;
1803
0
      k = total > 0 ? j : k;
1804
0
      freq[j] = total;
1805
0
    }
1806
8.54k
  } else {
1807
8.54k
    fast_sint_t i, j;
1808
8.54k
    sa_sint_t sum = 0;
1809
8.54k
    for (i = BUCKETS_INDEX4(0, 0), j = 0;
1810
2.19M
         i <= BUCKETS_INDEX4(ALPHABET_SIZE - 1, 0);
1811
2.18M
         i += BUCKETS_INDEX4(1, 0), j += 1) {
1812
2.18M
      sa_sint_t total = buckets[i + BUCKETS_INDEX4(0, 0)] +
1813
2.18M
                        buckets[i + BUCKETS_INDEX4(0, 1)] +
1814
2.18M
                        buckets[i + BUCKETS_INDEX4(0, 2)] +
1815
2.18M
                        buckets[i + BUCKETS_INDEX4(0, 3)];
1816
1817
2.18M
      bucket_start[j] = sum;
1818
2.18M
      sum += total;
1819
2.18M
      bucket_end[j] = sum;
1820
2.18M
      k = total > 0 ? j : k;
1821
2.18M
    }
1822
8.54k
  }
1823
1824
8.54k
  return (sa_sint_t)(k + 1);
1825
8.54k
}
1826
1827
static void libsais_initialize_buckets_start_and_end_32s_6k(
1828
1.94k
    sa_sint_t k, sa_sint_t* RESTRICT buckets) {
1829
1.94k
  sa_sint_t* RESTRICT bucket_start = &buckets[4 * (fast_sint_t)k];
1830
1.94k
  sa_sint_t* RESTRICT bucket_end = &buckets[5 * (fast_sint_t)k];
1831
1832
1.94k
  fast_sint_t i, j;
1833
1.94k
  sa_sint_t sum = 0;
1834
1.94k
  for (i = BUCKETS_INDEX4(0, 0), j = 0;
1835
8.81k
       i <= BUCKETS_INDEX4((fast_sint_t)k - 1, 0);
1836
6.86k
       i += BUCKETS_INDEX4(1, 0), j += 1) {
1837
6.86k
    bucket_start[j] = sum;
1838
6.86k
    sum +=
1839
6.86k
        buckets[i + BUCKETS_INDEX4(0, 0)] + buckets[i + BUCKETS_INDEX4(0, 1)] +
1840
6.86k
        buckets[i + BUCKETS_INDEX4(0, 2)] + buckets[i + BUCKETS_INDEX4(0, 3)];
1841
6.86k
    bucket_end[j] = sum;
1842
6.86k
  }
1843
1.94k
}
1844
1845
static void libsais_initialize_buckets_start_and_end_32s_4k(
1846
14.3k
    sa_sint_t k, sa_sint_t* RESTRICT buckets) {
1847
14.3k
  sa_sint_t* RESTRICT bucket_start = &buckets[2 * (fast_sint_t)k];
1848
14.3k
  sa_sint_t* RESTRICT bucket_end = &buckets[3 * (fast_sint_t)k];
1849
1850
14.3k
  fast_sint_t i, j;
1851
14.3k
  sa_sint_t sum = 0;
1852
14.3k
  for (i = BUCKETS_INDEX2(0, 0), j = 0;
1853
717k
       i <= BUCKETS_INDEX2((fast_sint_t)k - 1, 0);
1854
703k
       i += BUCKETS_INDEX2(1, 0), j += 1) {
1855
703k
    bucket_start[j] = sum;
1856
703k
    sum +=
1857
703k
        buckets[i + BUCKETS_INDEX2(0, 0)] + buckets[i + BUCKETS_INDEX2(0, 1)];
1858
703k
    bucket_end[j] = sum;
1859
703k
  }
1860
14.3k
}
1861
1862
static void libsais_initialize_buckets_end_32s_2k(sa_sint_t k,
1863
664
                                                  sa_sint_t* RESTRICT buckets) {
1864
664
  fast_sint_t i;
1865
664
  sa_sint_t sum0 = 0;
1866
531k
  for (i = BUCKETS_INDEX2(0, 0); i <= BUCKETS_INDEX2((fast_sint_t)k - 1, 0);
1867
531k
       i += BUCKETS_INDEX2(1, 0)) {
1868
531k
    sum0 +=
1869
531k
        buckets[i + BUCKETS_INDEX2(0, 0)] + buckets[i + BUCKETS_INDEX2(0, 1)];
1870
531k
    buckets[i + BUCKETS_INDEX2(0, 0)] = sum0;
1871
531k
  }
1872
664
}
1873
1874
static void libsais_initialize_buckets_start_and_end_32s_2k(
1875
1.32k
    sa_sint_t k, sa_sint_t* RESTRICT buckets) {
1876
1.32k
  fast_sint_t i, j;
1877
1.32k
  for (i = BUCKETS_INDEX2(0, 0), j = 0;
1878
1.06M
       i <= BUCKETS_INDEX2((fast_sint_t)k - 1, 0);
1879
1.06M
       i += BUCKETS_INDEX2(1, 0), j += 1) {
1880
1.06M
    buckets[j] = buckets[i];
1881
1.06M
  }
1882
1883
1.32k
  buckets[k] = 0;
1884
1.32k
  memcpy(&buckets[k + 1], buckets, ((size_t)k - 1) * sizeof(sa_sint_t));
1885
1.32k
}
1886
1887
static void libsais_initialize_buckets_start_32s_1k(
1888
356
    sa_sint_t k, sa_sint_t* RESTRICT buckets) {
1889
356
  fast_sint_t i;
1890
356
  sa_sint_t sum = 0;
1891
372k
  for (i = 0; i <= (fast_sint_t)k - 1; i += 1) {
1892
372k
    sa_sint_t tmp = buckets[i];
1893
372k
    buckets[i] = sum;
1894
372k
    sum += tmp;
1895
372k
  }
1896
356
}
1897
1898
static void libsais_initialize_buckets_end_32s_1k(sa_sint_t k,
1899
712
                                                  sa_sint_t* RESTRICT buckets) {
1900
712
  fast_sint_t i;
1901
712
  sa_sint_t sum = 0;
1902
744k
  for (i = 0; i <= (fast_sint_t)k - 1; i += 1) {
1903
744k
    sum += buckets[i];
1904
744k
    buckets[i] = sum;
1905
744k
  }
1906
712
}
1907
1908
static sa_sint_t libsais_initialize_buckets_for_lms_suffixes_radix_sort_8u(
1909
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT buckets,
1910
8.47k
    sa_sint_t first_lms_suffix) {
1911
8.47k
  {
1912
8.47k
    fast_uint_t f0 = 0;
1913
8.47k
    fast_uint_t f1 = 0;
1914
8.47k
    fast_sint_t c0 = T[first_lms_suffix];
1915
8.47k
    fast_sint_t c1 = 0;
1916
1917
33.2k
    for (; --first_lms_suffix >= 0;) {
1918
24.8k
      c1 = c0;
1919
24.8k
      c0 = T[first_lms_suffix];
1920
24.8k
      f1 = f0;
1921
24.8k
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1922
24.8k
      buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]--;
1923
24.8k
    }
1924
1925
8.47k
    buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0)]--;
1926
8.47k
  }
1927
1928
8.47k
  {
1929
8.47k
    sa_sint_t* RESTRICT temp_bucket = &buckets[4 * ALPHABET_SIZE];
1930
1931
8.47k
    fast_sint_t i, j;
1932
8.47k
    sa_sint_t sum = 0;
1933
8.47k
    for (i = BUCKETS_INDEX4(0, 0), j = BUCKETS_INDEX2(0, 0);
1934
2.17M
         i <= BUCKETS_INDEX4(ALPHABET_SIZE - 1, 0);
1935
2.17M
         i += BUCKETS_INDEX4(1, 0), j += BUCKETS_INDEX2(1, 0)) {
1936
2.17M
      temp_bucket[j + BUCKETS_INDEX2(0, 1)] = sum;
1937
2.17M
      sum +=
1938
2.17M
          buckets[i + BUCKETS_INDEX4(0, 1)] + buckets[i + BUCKETS_INDEX4(0, 3)];
1939
2.17M
      temp_bucket[j] = sum;
1940
2.17M
    }
1941
1942
8.47k
    return sum;
1943
8.47k
  }
1944
8.47k
}
1945
1946
static void libsais_initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
1947
    const sa_sint_t* RESTRICT T, sa_sint_t k, sa_sint_t* RESTRICT buckets,
1948
664
    sa_sint_t first_lms_suffix) {
1949
664
  buckets[BUCKETS_INDEX2(T[first_lms_suffix], 0)]++;
1950
664
  buckets[BUCKETS_INDEX2(T[first_lms_suffix], 1)]--;
1951
1952
664
  fast_sint_t i;
1953
664
  sa_sint_t sum0 = 0, sum1 = 0;
1954
531k
  for (i = BUCKETS_INDEX2(0, 0); i <= BUCKETS_INDEX2((fast_sint_t)k - 1, 0);
1955
531k
       i += BUCKETS_INDEX2(1, 0)) {
1956
531k
    sum0 +=
1957
531k
        buckets[i + BUCKETS_INDEX2(0, 0)] + buckets[i + BUCKETS_INDEX2(0, 1)];
1958
531k
    sum1 += buckets[i + BUCKETS_INDEX2(0, 1)];
1959
1960
531k
    buckets[i + BUCKETS_INDEX2(0, 0)] = sum0;
1961
531k
    buckets[i + BUCKETS_INDEX2(0, 1)] = sum1;
1962
531k
  }
1963
664
}
1964
1965
static sa_sint_t libsais_initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
1966
    const sa_sint_t* RESTRICT T, sa_sint_t k, sa_sint_t* RESTRICT buckets,
1967
13.9k
    sa_sint_t first_lms_suffix) {
1968
13.9k
  {
1969
13.9k
    fast_uint_t f0 = 0;
1970
13.9k
    fast_uint_t f1 = 0;
1971
13.9k
    fast_sint_t c0 = T[first_lms_suffix];
1972
13.9k
    fast_sint_t c1 = 0;
1973
1974
56.7k
    for (; --first_lms_suffix >= 0;) {
1975
42.7k
      c1 = c0;
1976
42.7k
      c0 = T[first_lms_suffix];
1977
42.7k
      f1 = f0;
1978
42.7k
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1979
42.7k
      buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]--;
1980
42.7k
    }
1981
1982
13.9k
    buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0)]--;
1983
13.9k
  }
1984
1985
13.9k
  {
1986
13.9k
    sa_sint_t* RESTRICT temp_bucket = &buckets[4 * (fast_sint_t)k];
1987
1988
13.9k
    fast_sint_t i, j;
1989
13.9k
    sa_sint_t sum = 0;
1990
13.9k
    for (i = BUCKETS_INDEX4(0, 0), j = 0;
1991
565k
         i <= BUCKETS_INDEX4((fast_sint_t)k - 1, 0);
1992
551k
         i += BUCKETS_INDEX4(1, 0), j += 1) {
1993
551k
      sum +=
1994
551k
          buckets[i + BUCKETS_INDEX4(0, 1)] + buckets[i + BUCKETS_INDEX4(0, 3)];
1995
551k
      temp_bucket[j] = sum;
1996
551k
    }
1997
1998
13.9k
    return sum;
1999
13.9k
  }
2000
13.9k
}
2001
2002
static void libsais_initialize_buckets_for_radix_and_partial_sorting_32s_4k(
2003
    const sa_sint_t* RESTRICT T, sa_sint_t k, sa_sint_t* RESTRICT buckets,
2004
355
    sa_sint_t first_lms_suffix) {
2005
355
  sa_sint_t* RESTRICT bucket_start = &buckets[2 * (fast_sint_t)k];
2006
355
  sa_sint_t* RESTRICT bucket_end = &buckets[3 * (fast_sint_t)k];
2007
2008
355
  buckets[BUCKETS_INDEX2(T[first_lms_suffix], 0)]++;
2009
355
  buckets[BUCKETS_INDEX2(T[first_lms_suffix], 1)]--;
2010
2011
355
  fast_sint_t i, j;
2012
355
  sa_sint_t sum0 = 0, sum1 = 0;
2013
355
  for (i = BUCKETS_INDEX2(0, 0), j = 0;
2014
152k
       i <= BUCKETS_INDEX2((fast_sint_t)k - 1, 0);
2015
152k
       i += BUCKETS_INDEX2(1, 0), j += 1) {
2016
152k
    bucket_start[j] = sum1;
2017
2018
152k
    sum0 += buckets[i + BUCKETS_INDEX2(0, 1)];
2019
152k
    sum1 +=
2020
152k
        buckets[i + BUCKETS_INDEX2(0, 0)] + buckets[i + BUCKETS_INDEX2(0, 1)];
2021
152k
    buckets[i + BUCKETS_INDEX2(0, 1)] = sum0;
2022
2023
152k
    bucket_end[j] = sum1;
2024
152k
  }
2025
355
}
2026
2027
static void libsais_radix_sort_lms_suffixes_8u(
2028
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA,
2029
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t omp_block_start,
2030
8.47k
    fast_sint_t omp_block_size) {
2031
8.47k
  const fast_sint_t prefetch_distance = 64;
2032
2033
8.47k
  fast_sint_t i, j;
2034
8.47k
  for (i = omp_block_start + omp_block_size - 1,
2035
8.47k
      j = omp_block_start + prefetch_distance + 3;
2036
2.03M
       i >= j; i -= 4) {
2037
2.02M
    libsais_prefetchr(&SA[i - 2 * prefetch_distance]);
2038
2039
2.02M
    libsais_prefetchr(&T[SA[i - prefetch_distance - 0]]);
2040
2.02M
    libsais_prefetchr(&T[SA[i - prefetch_distance - 1]]);
2041
2.02M
    libsais_prefetchr(&T[SA[i - prefetch_distance - 2]]);
2042
2.02M
    libsais_prefetchr(&T[SA[i - prefetch_distance - 3]]);
2043
2044
2.02M
    sa_sint_t p0 = SA[i - 0];
2045
2.02M
    SA[--induction_bucket[BUCKETS_INDEX2(T[p0], 0)]] = p0;
2046
2.02M
    sa_sint_t p1 = SA[i - 1];
2047
2.02M
    SA[--induction_bucket[BUCKETS_INDEX2(T[p1], 0)]] = p1;
2048
2.02M
    sa_sint_t p2 = SA[i - 2];
2049
2.02M
    SA[--induction_bucket[BUCKETS_INDEX2(T[p2], 0)]] = p2;
2050
2.02M
    sa_sint_t p3 = SA[i - 3];
2051
2.02M
    SA[--induction_bucket[BUCKETS_INDEX2(T[p3], 0)]] = p3;
2052
2.02M
  }
2053
2054
392k
  for (j -= prefetch_distance + 3; i >= j; i -= 1) {
2055
384k
    sa_sint_t p = SA[i];
2056
384k
    SA[--induction_bucket[BUCKETS_INDEX2(T[p], 0)]] = p;
2057
384k
  }
2058
8.47k
}
2059
2060
static void libsais_radix_sort_lms_suffixes_8u_omp(
2061
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t m,
2062
    sa_sint_t flags, sa_sint_t* RESTRICT buckets, sa_sint_t threads,
2063
8.47k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
2064
8.47k
  if (flags & LIBSAIS_FLAGS_GSA) {
2065
0
    buckets[4 * ALPHABET_SIZE]--;
2066
0
  }
2067
2068
#if defined(LIBSAIS_OPENMP)
2069
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 65536 && \
2070
                                                  m >= 65536 &&            \
2071
                                                  omp_get_dynamic() == 0)
2072
#endif
2073
8.47k
  {
2074
#if defined(LIBSAIS_OPENMP)
2075
    fast_sint_t omp_thread_num = omp_get_thread_num();
2076
    fast_sint_t omp_num_threads = omp_get_num_threads();
2077
#else
2078
8.47k
    UNUSED(threads);
2079
8.47k
    UNUSED(thread_state);
2080
2081
8.47k
    fast_sint_t omp_num_threads = 1;
2082
8.47k
#endif
2083
8.47k
    if (omp_num_threads == 1) {
2084
8.47k
      libsais_radix_sort_lms_suffixes_8u(T, SA, &buckets[4 * ALPHABET_SIZE],
2085
8.47k
                                         (fast_sint_t)n - (fast_sint_t)m + 1,
2086
8.47k
                                         (fast_sint_t)m - 1);
2087
8.47k
    }
2088
#if defined(LIBSAIS_OPENMP)
2089
    else {
2090
      {
2091
        sa_sint_t* RESTRICT src_bucket = &buckets[4 * ALPHABET_SIZE];
2092
        sa_sint_t* RESTRICT dst_bucket =
2093
            thread_state[omp_thread_num].state.buckets;
2094
2095
        fast_sint_t i, j;
2096
        for (i = BUCKETS_INDEX2(0, 0), j = BUCKETS_INDEX4(0, 1);
2097
             i <= BUCKETS_INDEX2(ALPHABET_SIZE - 1, 0);
2098
             i += BUCKETS_INDEX2(1, 0), j += BUCKETS_INDEX4(1, 0)) {
2099
          dst_bucket[i] = src_bucket[i] - dst_bucket[j];
2100
        }
2101
      }
2102
2103
      {
2104
        fast_sint_t t, omp_block_start = 0,
2105
                       omp_block_size = thread_state[omp_thread_num].state.m;
2106
        for (t = omp_num_threads - 1; t >= omp_thread_num; --t)
2107
          omp_block_start += thread_state[t].state.m;
2108
2109
        if (omp_block_start == (fast_sint_t)m && omp_block_size > 0) {
2110
          omp_block_start -= 1;
2111
          omp_block_size -= 1;
2112
        }
2113
2114
        libsais_radix_sort_lms_suffixes_8u(
2115
            T, SA, thread_state[omp_thread_num].state.buckets,
2116
            (fast_sint_t)n - omp_block_start, omp_block_size);
2117
      }
2118
    }
2119
#endif
2120
8.47k
  }
2121
8.47k
}
2122
2123
static void libsais_radix_sort_lms_suffixes_32s_6k(
2124
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
2125
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t omp_block_start,
2126
13.9k
    fast_sint_t omp_block_size) {
2127
13.9k
  const fast_sint_t prefetch_distance = 64;
2128
2129
13.9k
  fast_sint_t i, j;
2130
13.9k
  for (i = omp_block_start + omp_block_size - 1,
2131
13.9k
      j = omp_block_start + 2 * prefetch_distance + 3;
2132
749k
       i >= j; i -= 4) {
2133
735k
    libsais_prefetchr(&SA[i - 3 * prefetch_distance]);
2134
2135
735k
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 0]]);
2136
735k
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 1]]);
2137
735k
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 2]]);
2138
735k
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 3]]);
2139
2140
735k
    libsais_prefetchw(&induction_bucket[T[SA[i - prefetch_distance - 0]]]);
2141
735k
    libsais_prefetchw(&induction_bucket[T[SA[i - prefetch_distance - 1]]]);
2142
735k
    libsais_prefetchw(&induction_bucket[T[SA[i - prefetch_distance - 2]]]);
2143
735k
    libsais_prefetchw(&induction_bucket[T[SA[i - prefetch_distance - 3]]]);
2144
2145
735k
    sa_sint_t p0 = SA[i - 0];
2146
735k
    SA[--induction_bucket[T[p0]]] = p0;
2147
735k
    sa_sint_t p1 = SA[i - 1];
2148
735k
    SA[--induction_bucket[T[p1]]] = p1;
2149
735k
    sa_sint_t p2 = SA[i - 2];
2150
735k
    SA[--induction_bucket[T[p2]]] = p2;
2151
735k
    sa_sint_t p3 = SA[i - 3];
2152
735k
    SA[--induction_bucket[T[p3]]] = p3;
2153
735k
  }
2154
2155
595k
  for (j -= 2 * prefetch_distance + 3; i >= j; i -= 1) {
2156
581k
    sa_sint_t p = SA[i];
2157
581k
    SA[--induction_bucket[T[p]]] = p;
2158
581k
  }
2159
13.9k
}
2160
2161
static void libsais_radix_sort_lms_suffixes_32s_2k(
2162
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
2163
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t omp_block_start,
2164
1.01k
    fast_sint_t omp_block_size) {
2165
1.01k
  const fast_sint_t prefetch_distance = 64;
2166
2167
1.01k
  fast_sint_t i, j;
2168
1.01k
  for (i = omp_block_start + omp_block_size - 1,
2169
1.01k
      j = omp_block_start + 2 * prefetch_distance + 3;
2170
118k
       i >= j; i -= 4) {
2171
117k
    libsais_prefetchr(&SA[i - 3 * prefetch_distance]);
2172
2173
117k
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 0]]);
2174
117k
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 1]]);
2175
117k
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 2]]);
2176
117k
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 3]]);
2177
2178
117k
    libsais_prefetchw(
2179
117k
        &induction_bucket[BUCKETS_INDEX2(T[SA[i - prefetch_distance - 0]], 0)]);
2180
117k
    libsais_prefetchw(
2181
117k
        &induction_bucket[BUCKETS_INDEX2(T[SA[i - prefetch_distance - 1]], 0)]);
2182
117k
    libsais_prefetchw(
2183
117k
        &induction_bucket[BUCKETS_INDEX2(T[SA[i - prefetch_distance - 2]], 0)]);
2184
117k
    libsais_prefetchw(
2185
117k
        &induction_bucket[BUCKETS_INDEX2(T[SA[i - prefetch_distance - 3]], 0)]);
2186
2187
117k
    sa_sint_t p0 = SA[i - 0];
2188
117k
    SA[--induction_bucket[BUCKETS_INDEX2(T[p0], 0)]] = p0;
2189
117k
    sa_sint_t p1 = SA[i - 1];
2190
117k
    SA[--induction_bucket[BUCKETS_INDEX2(T[p1], 0)]] = p1;
2191
117k
    sa_sint_t p2 = SA[i - 2];
2192
117k
    SA[--induction_bucket[BUCKETS_INDEX2(T[p2], 0)]] = p2;
2193
117k
    sa_sint_t p3 = SA[i - 3];
2194
117k
    SA[--induction_bucket[BUCKETS_INDEX2(T[p3], 0)]] = p3;
2195
117k
  }
2196
2197
131k
  for (j -= 2 * prefetch_distance + 3; i >= j; i -= 1) {
2198
130k
    sa_sint_t p = SA[i];
2199
130k
    SA[--induction_bucket[BUCKETS_INDEX2(T[p], 0)]] = p;
2200
130k
  }
2201
1.01k
}
2202
2203
#if defined(LIBSAIS_OPENMP)
2204
2205
static void libsais_radix_sort_lms_suffixes_32s_block_gather(
2206
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
2207
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t omp_block_start,
2208
    fast_sint_t omp_block_size) {
2209
  const fast_sint_t prefetch_distance = 64;
2210
2211
  fast_sint_t i, j;
2212
  for (i = omp_block_start,
2213
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
2214
       i < j; i += 4) {
2215
    libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
2216
2217
    libsais_prefetchr(&T[SA[i + prefetch_distance + 0]]);
2218
    libsais_prefetchr(&T[SA[i + prefetch_distance + 1]]);
2219
    libsais_prefetchr(&T[SA[i + prefetch_distance + 2]]);
2220
    libsais_prefetchr(&T[SA[i + prefetch_distance + 3]]);
2221
2222
    libsais_prefetchw(&cache[i + prefetch_distance]);
2223
2224
    cache[i + 0].symbol = T[cache[i + 0].index = SA[i + 0]];
2225
    cache[i + 1].symbol = T[cache[i + 1].index = SA[i + 1]];
2226
    cache[i + 2].symbol = T[cache[i + 2].index = SA[i + 2]];
2227
    cache[i + 3].symbol = T[cache[i + 3].index = SA[i + 3]];
2228
  }
2229
2230
  for (j += prefetch_distance + 3; i < j; i += 1) {
2231
    cache[i].symbol = T[cache[i].index = SA[i]];
2232
  }
2233
}
2234
2235
static void libsais_radix_sort_lms_suffixes_32s_6k_block_sort(
2236
    sa_sint_t* RESTRICT induction_bucket, LIBSAIS_THREAD_CACHE* RESTRICT cache,
2237
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
2238
  const fast_sint_t prefetch_distance = 64;
2239
2240
  fast_sint_t i, j;
2241
  for (i = omp_block_start + omp_block_size - 1,
2242
      j = omp_block_start + prefetch_distance + 3;
2243
       i >= j; i -= 4) {
2244
    libsais_prefetchw(&cache[i - 2 * prefetch_distance]);
2245
2246
    libsais_prefetchw(
2247
        &induction_bucket[cache[i - prefetch_distance - 0].symbol]);
2248
    libsais_prefetchw(
2249
        &induction_bucket[cache[i - prefetch_distance - 1].symbol]);
2250
    libsais_prefetchw(
2251
        &induction_bucket[cache[i - prefetch_distance - 2].symbol]);
2252
    libsais_prefetchw(
2253
        &induction_bucket[cache[i - prefetch_distance - 3].symbol]);
2254
2255
    cache[i - 0].symbol = --induction_bucket[cache[i - 0].symbol];
2256
    cache[i - 1].symbol = --induction_bucket[cache[i - 1].symbol];
2257
    cache[i - 2].symbol = --induction_bucket[cache[i - 2].symbol];
2258
    cache[i - 3].symbol = --induction_bucket[cache[i - 3].symbol];
2259
  }
2260
2261
  for (j -= prefetch_distance + 3; i >= j; i -= 1) {
2262
    cache[i].symbol = --induction_bucket[cache[i].symbol];
2263
  }
2264
}
2265
2266
static void libsais_radix_sort_lms_suffixes_32s_2k_block_sort(
2267
    sa_sint_t* RESTRICT induction_bucket, LIBSAIS_THREAD_CACHE* RESTRICT cache,
2268
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
2269
  const fast_sint_t prefetch_distance = 64;
2270
2271
  fast_sint_t i, j;
2272
  for (i = omp_block_start + omp_block_size - 1,
2273
      j = omp_block_start + prefetch_distance + 3;
2274
       i >= j; i -= 4) {
2275
    libsais_prefetchw(&cache[i - 2 * prefetch_distance]);
2276
2277
    libsais_prefetchw(&induction_bucket[BUCKETS_INDEX2(
2278
        cache[i - prefetch_distance - 0].symbol, 0)]);
2279
    libsais_prefetchw(&induction_bucket[BUCKETS_INDEX2(
2280
        cache[i - prefetch_distance - 1].symbol, 0)]);
2281
    libsais_prefetchw(&induction_bucket[BUCKETS_INDEX2(
2282
        cache[i - prefetch_distance - 2].symbol, 0)]);
2283
    libsais_prefetchw(&induction_bucket[BUCKETS_INDEX2(
2284
        cache[i - prefetch_distance - 3].symbol, 0)]);
2285
2286
    cache[i - 0].symbol =
2287
        --induction_bucket[BUCKETS_INDEX2(cache[i - 0].symbol, 0)];
2288
    cache[i - 1].symbol =
2289
        --induction_bucket[BUCKETS_INDEX2(cache[i - 1].symbol, 0)];
2290
    cache[i - 2].symbol =
2291
        --induction_bucket[BUCKETS_INDEX2(cache[i - 2].symbol, 0)];
2292
    cache[i - 3].symbol =
2293
        --induction_bucket[BUCKETS_INDEX2(cache[i - 3].symbol, 0)];
2294
  }
2295
2296
  for (j -= prefetch_distance + 3; i >= j; i -= 1) {
2297
    cache[i].symbol = --induction_bucket[BUCKETS_INDEX2(cache[i].symbol, 0)];
2298
  }
2299
}
2300
2301
static void libsais_radix_sort_lms_suffixes_32s_6k_block_omp(
2302
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
2303
    sa_sint_t* RESTRICT induction_bucket, LIBSAIS_THREAD_CACHE* RESTRICT cache,
2304
    fast_sint_t block_start, fast_sint_t block_size, sa_sint_t threads) {
2305
#if defined(LIBSAIS_OPENMP)
2306
#pragma omp parallel num_threads(threads) if (threads > 1 && \
2307
                                                  block_size >= 16384)
2308
#endif
2309
  {
2310
#if defined(LIBSAIS_OPENMP)
2311
    fast_sint_t omp_thread_num = omp_get_thread_num();
2312
    fast_sint_t omp_num_threads = omp_get_num_threads();
2313
#else
2314
    UNUSED(threads);
2315
    UNUSED(cache);
2316
2317
    fast_sint_t omp_thread_num = 0;
2318
    fast_sint_t omp_num_threads = 1;
2319
#endif
2320
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
2321
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
2322
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
2323
                                     ? omp_block_stride
2324
                                     : block_size - omp_block_start;
2325
2326
    omp_block_start += block_start;
2327
2328
    if (omp_num_threads == 1) {
2329
      libsais_radix_sort_lms_suffixes_32s_6k(T, SA, induction_bucket,
2330
                                             omp_block_start, omp_block_size);
2331
    }
2332
#if defined(LIBSAIS_OPENMP)
2333
    else {
2334
      {
2335
        libsais_radix_sort_lms_suffixes_32s_block_gather(
2336
            T, SA, cache - block_start, omp_block_start, omp_block_size);
2337
      }
2338
2339
#pragma omp barrier
2340
2341
#pragma omp master
2342
      {
2343
        libsais_radix_sort_lms_suffixes_32s_6k_block_sort(
2344
            induction_bucket, cache - block_start, block_start, block_size);
2345
      }
2346
2347
#pragma omp barrier
2348
2349
      {
2350
        libsais_place_cached_suffixes(SA, cache - block_start, omp_block_start,
2351
                                      omp_block_size);
2352
      }
2353
    }
2354
#endif
2355
  }
2356
}
2357
2358
static void libsais_radix_sort_lms_suffixes_32s_2k_block_omp(
2359
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
2360
    sa_sint_t* RESTRICT induction_bucket, LIBSAIS_THREAD_CACHE* RESTRICT cache,
2361
    fast_sint_t block_start, fast_sint_t block_size, sa_sint_t threads) {
2362
#if defined(LIBSAIS_OPENMP)
2363
#pragma omp parallel num_threads(threads) if (threads > 1 && \
2364
                                                  block_size >= 16384)
2365
#endif
2366
  {
2367
#if defined(LIBSAIS_OPENMP)
2368
    fast_sint_t omp_thread_num = omp_get_thread_num();
2369
    fast_sint_t omp_num_threads = omp_get_num_threads();
2370
#else
2371
    UNUSED(threads);
2372
    UNUSED(cache);
2373
2374
    fast_sint_t omp_thread_num = 0;
2375
    fast_sint_t omp_num_threads = 1;
2376
#endif
2377
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
2378
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
2379
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
2380
                                     ? omp_block_stride
2381
                                     : block_size - omp_block_start;
2382
2383
    omp_block_start += block_start;
2384
2385
    if (omp_num_threads == 1) {
2386
      libsais_radix_sort_lms_suffixes_32s_2k(T, SA, induction_bucket,
2387
                                             omp_block_start, omp_block_size);
2388
    }
2389
#if defined(LIBSAIS_OPENMP)
2390
    else {
2391
      {
2392
        libsais_radix_sort_lms_suffixes_32s_block_gather(
2393
            T, SA, cache - block_start, omp_block_start, omp_block_size);
2394
      }
2395
2396
#pragma omp barrier
2397
2398
#pragma omp master
2399
      {
2400
        libsais_radix_sort_lms_suffixes_32s_2k_block_sort(
2401
            induction_bucket, cache - block_start, block_start, block_size);
2402
      }
2403
2404
#pragma omp barrier
2405
2406
      {
2407
        libsais_place_cached_suffixes(SA, cache - block_start, omp_block_start,
2408
                                      omp_block_size);
2409
      }
2410
    }
2411
#endif
2412
  }
2413
}
2414
2415
#endif
2416
2417
static void libsais_radix_sort_lms_suffixes_32s_6k_omp(
2418
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
2419
    sa_sint_t m, sa_sint_t* RESTRICT induction_bucket, sa_sint_t threads,
2420
13.9k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
2421
13.9k
  if (threads == 1 || m < 65536) {
2422
13.9k
    libsais_radix_sort_lms_suffixes_32s_6k(T, SA, induction_bucket,
2423
13.9k
                                           (fast_sint_t)n - (fast_sint_t)m + 1,
2424
13.9k
                                           (fast_sint_t)m - 1);
2425
13.9k
  }
2426
#if defined(LIBSAIS_OPENMP)
2427
  else {
2428
    fast_sint_t block_start, block_end;
2429
    for (block_start = 0; block_start < (fast_sint_t)m - 1;
2430
         block_start = block_end) {
2431
      block_end =
2432
          block_start + (fast_sint_t)threads * LIBSAIS_PER_THREAD_CACHE_SIZE;
2433
      if (block_end >= m) {
2434
        block_end = (fast_sint_t)m - 1;
2435
      }
2436
2437
      libsais_radix_sort_lms_suffixes_32s_6k_block_omp(
2438
          T, SA, induction_bucket, thread_state[0].state.cache,
2439
          (fast_sint_t)n - block_end, block_end - block_start, threads);
2440
    }
2441
  }
2442
#else
2443
13.9k
  UNUSED(thread_state);
2444
13.9k
#endif
2445
13.9k
}
2446
2447
static void libsais_radix_sort_lms_suffixes_32s_2k_omp(
2448
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
2449
    sa_sint_t m, sa_sint_t* RESTRICT induction_bucket, sa_sint_t threads,
2450
1.01k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
2451
1.01k
  if (threads == 1 || m < 65536) {
2452
1.01k
    libsais_radix_sort_lms_suffixes_32s_2k(T, SA, induction_bucket,
2453
1.01k
                                           (fast_sint_t)n - (fast_sint_t)m + 1,
2454
1.01k
                                           (fast_sint_t)m - 1);
2455
1.01k
  }
2456
#if defined(LIBSAIS_OPENMP)
2457
  else {
2458
    fast_sint_t block_start, block_end;
2459
    for (block_start = 0; block_start < (fast_sint_t)m - 1;
2460
         block_start = block_end) {
2461
      block_end =
2462
          block_start + (fast_sint_t)threads * LIBSAIS_PER_THREAD_CACHE_SIZE;
2463
      if (block_end >= m) {
2464
        block_end = (fast_sint_t)m - 1;
2465
      }
2466
2467
      libsais_radix_sort_lms_suffixes_32s_2k_block_omp(
2468
          T, SA, induction_bucket, thread_state[0].state.cache,
2469
          (fast_sint_t)n - block_end, block_end - block_start, threads);
2470
    }
2471
  }
2472
#else
2473
1.01k
  UNUSED(thread_state);
2474
1.01k
#endif
2475
1.01k
}
2476
2477
static sa_sint_t libsais_radix_sort_lms_suffixes_32s_1k(
2478
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
2479
178
    sa_sint_t* RESTRICT buckets) {
2480
178
  const fast_sint_t prefetch_distance = 64;
2481
2482
178
  sa_sint_t i = n - 2;
2483
178
  sa_sint_t m = 0;
2484
178
  fast_uint_t f0 = 1;
2485
178
  fast_uint_t f1 = 0;
2486
178
  fast_sint_t c0 = T[n - 1];
2487
178
  fast_sint_t c1 = 0;
2488
178
  fast_sint_t c2 = 0;
2489
2490
80.1k
  for (; i >= prefetch_distance + 3; i -= 4) {
2491
80.0k
    libsais_prefetchr(&T[i - 2 * prefetch_distance]);
2492
2493
80.0k
    libsais_prefetchw(&buckets[T[i - prefetch_distance - 0]]);
2494
80.0k
    libsais_prefetchw(&buckets[T[i - prefetch_distance - 1]]);
2495
80.0k
    libsais_prefetchw(&buckets[T[i - prefetch_distance - 2]]);
2496
80.0k
    libsais_prefetchw(&buckets[T[i - prefetch_distance - 3]]);
2497
2498
80.0k
    c1 = T[i - 0];
2499
80.0k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
2500
80.0k
    if (f1 & ~f0) {
2501
26.4k
      SA[--buckets[c2 = c0]] = i + 1;
2502
26.4k
      m++;
2503
26.4k
    }
2504
2505
80.0k
    c0 = T[i - 1];
2506
80.0k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
2507
80.0k
    if (f0 & ~f1) {
2508
26.4k
      SA[--buckets[c2 = c1]] = i - 0;
2509
26.4k
      m++;
2510
26.4k
    }
2511
2512
80.0k
    c1 = T[i - 2];
2513
80.0k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
2514
80.0k
    if (f1 & ~f0) {
2515
26.4k
      SA[--buckets[c2 = c0]] = i - 1;
2516
26.4k
      m++;
2517
26.4k
    }
2518
2519
80.0k
    c0 = T[i - 3];
2520
80.0k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
2521
80.0k
    if (f0 & ~f1) {
2522
26.6k
      SA[--buckets[c2 = c1]] = i - 2;
2523
26.6k
      m++;
2524
26.6k
    }
2525
80.0k
  }
2526
2527
11.8k
  for (; i >= 0; i -= 1) {
2528
11.6k
    c1 = c0;
2529
11.6k
    c0 = T[i];
2530
11.6k
    f1 = f0;
2531
11.6k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
2532
11.6k
    if (f0 & ~f1) {
2533
3.69k
      SA[--buckets[c2 = c1]] = i + 1;
2534
3.69k
      m++;
2535
3.69k
    }
2536
11.6k
  }
2537
2538
178
  if (m > 1) {
2539
178
    SA[buckets[c2]] = 0;
2540
178
  }
2541
2542
178
  return m;
2543
178
}
2544
2545
static void libsais_radix_sort_set_markers_32s_6k(
2546
    sa_sint_t* RESTRICT SA, sa_sint_t* RESTRICT induction_bucket,
2547
13.9k
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
2548
13.9k
  const fast_sint_t prefetch_distance = 64;
2549
2550
13.9k
  fast_sint_t i, j;
2551
13.9k
  for (i = omp_block_start,
2552
13.9k
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
2553
61.0k
       i < j; i += 4) {
2554
47.0k
    libsais_prefetchr(&induction_bucket[i + 2 * prefetch_distance]);
2555
2556
47.0k
    libsais_prefetchw(&SA[induction_bucket[i + prefetch_distance + 0]]);
2557
47.0k
    libsais_prefetchw(&SA[induction_bucket[i + prefetch_distance + 1]]);
2558
47.0k
    libsais_prefetchw(&SA[induction_bucket[i + prefetch_distance + 2]]);
2559
47.0k
    libsais_prefetchw(&SA[induction_bucket[i + prefetch_distance + 3]]);
2560
2561
47.0k
    SA[induction_bucket[i + 0]] |= SAINT_MIN;
2562
47.0k
    SA[induction_bucket[i + 1]] |= SAINT_MIN;
2563
47.0k
    SA[induction_bucket[i + 2]] |= SAINT_MIN;
2564
47.0k
    SA[induction_bucket[i + 3]] |= SAINT_MIN;
2565
47.0k
  }
2566
2567
362k
  for (j += prefetch_distance + 3; i < j; i += 1) {
2568
348k
    SA[induction_bucket[i]] |= SAINT_MIN;
2569
348k
  }
2570
13.9k
}
2571
2572
static void libsais_radix_sort_set_markers_32s_4k(
2573
    sa_sint_t* RESTRICT SA, sa_sint_t* RESTRICT induction_bucket,
2574
355
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
2575
355
  const fast_sint_t prefetch_distance = 64;
2576
2577
355
  fast_sint_t i, j;
2578
355
  for (i = omp_block_start,
2579
355
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
2580
32.4k
       i < j; i += 4) {
2581
32.1k
    libsais_prefetchr(
2582
32.1k
        &induction_bucket[BUCKETS_INDEX2(i + 2 * prefetch_distance, 0)]);
2583
2584
32.1k
    libsais_prefetchw(
2585
32.1k
        &SA[induction_bucket[BUCKETS_INDEX2(i + prefetch_distance + 0, 0)]]);
2586
32.1k
    libsais_prefetchw(
2587
32.1k
        &SA[induction_bucket[BUCKETS_INDEX2(i + prefetch_distance + 1, 0)]]);
2588
32.1k
    libsais_prefetchw(
2589
32.1k
        &SA[induction_bucket[BUCKETS_INDEX2(i + prefetch_distance + 2, 0)]]);
2590
32.1k
    libsais_prefetchw(
2591
32.1k
        &SA[induction_bucket[BUCKETS_INDEX2(i + prefetch_distance + 3, 0)]]);
2592
2593
32.1k
    SA[induction_bucket[BUCKETS_INDEX2(i + 0, 0)]] |= SUFFIX_GROUP_MARKER;
2594
32.1k
    SA[induction_bucket[BUCKETS_INDEX2(i + 1, 0)]] |= SUFFIX_GROUP_MARKER;
2595
32.1k
    SA[induction_bucket[BUCKETS_INDEX2(i + 2, 0)]] |= SUFFIX_GROUP_MARKER;
2596
32.1k
    SA[induction_bucket[BUCKETS_INDEX2(i + 3, 0)]] |= SUFFIX_GROUP_MARKER;
2597
32.1k
  }
2598
2599
23.6k
  for (j += prefetch_distance + 3; i < j; i += 1) {
2600
23.2k
    SA[induction_bucket[BUCKETS_INDEX2(i, 0)]] |= SUFFIX_GROUP_MARKER;
2601
23.2k
  }
2602
355
}
2603
2604
static void libsais_radix_sort_set_markers_32s_6k_omp(
2605
    sa_sint_t* RESTRICT SA, sa_sint_t k, sa_sint_t* RESTRICT induction_bucket,
2606
13.9k
    sa_sint_t threads) {
2607
#if defined(LIBSAIS_OPENMP)
2608
#pragma omp parallel num_threads(threads) if (threads > 1 && k >= 65536)
2609
#endif
2610
13.9k
  {
2611
#if defined(LIBSAIS_OPENMP)
2612
    fast_sint_t omp_thread_num = omp_get_thread_num();
2613
    fast_sint_t omp_num_threads = omp_get_num_threads();
2614
    fast_sint_t omp_block_stride =
2615
        (((fast_sint_t)k - 1) / omp_num_threads) & (-16);
2616
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
2617
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
2618
                                     ? omp_block_stride
2619
                                     : (fast_sint_t)k - 1 - omp_block_start;
2620
#else
2621
13.9k
    UNUSED(threads);
2622
2623
13.9k
    fast_sint_t omp_block_start = 0;
2624
13.9k
    fast_sint_t omp_block_size = (fast_sint_t)k - 1;
2625
13.9k
#endif
2626
2627
13.9k
    libsais_radix_sort_set_markers_32s_6k(SA, induction_bucket, omp_block_start,
2628
13.9k
                                          omp_block_size);
2629
13.9k
  }
2630
13.9k
}
2631
2632
static void libsais_radix_sort_set_markers_32s_4k_omp(
2633
    sa_sint_t* RESTRICT SA, sa_sint_t k, sa_sint_t* RESTRICT induction_bucket,
2634
355
    sa_sint_t threads) {
2635
#if defined(LIBSAIS_OPENMP)
2636
#pragma omp parallel num_threads(threads) if (threads > 1 && k >= 65536)
2637
#endif
2638
355
  {
2639
#if defined(LIBSAIS_OPENMP)
2640
    fast_sint_t omp_thread_num = omp_get_thread_num();
2641
    fast_sint_t omp_num_threads = omp_get_num_threads();
2642
    fast_sint_t omp_block_stride =
2643
        (((fast_sint_t)k - 1) / omp_num_threads) & (-16);
2644
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
2645
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
2646
                                     ? omp_block_stride
2647
                                     : (fast_sint_t)k - 1 - omp_block_start;
2648
#else
2649
355
    UNUSED(threads);
2650
2651
355
    fast_sint_t omp_block_start = 0;
2652
355
    fast_sint_t omp_block_size = (fast_sint_t)k - 1;
2653
355
#endif
2654
2655
355
    libsais_radix_sort_set_markers_32s_4k(SA, induction_bucket, omp_block_start,
2656
355
                                          omp_block_size);
2657
355
  }
2658
355
}
2659
2660
static void libsais_initialize_buckets_for_partial_sorting_8u(
2661
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT buckets,
2662
8.47k
    sa_sint_t first_lms_suffix, sa_sint_t left_suffixes_count) {
2663
8.47k
  sa_sint_t* RESTRICT temp_bucket = &buckets[4 * ALPHABET_SIZE];
2664
2665
8.47k
  buckets[BUCKETS_INDEX4((fast_uint_t)T[first_lms_suffix], 1)]++;
2666
2667
8.47k
  fast_sint_t i, j;
2668
8.47k
  sa_sint_t sum0 = left_suffixes_count + 1, sum1 = 0;
2669
8.47k
  for (i = BUCKETS_INDEX4(0, 0), j = BUCKETS_INDEX2(0, 0);
2670
2.17M
       i <= BUCKETS_INDEX4(ALPHABET_SIZE - 1, 0);
2671
2.17M
       i += BUCKETS_INDEX4(1, 0), j += BUCKETS_INDEX2(1, 0)) {
2672
2.17M
    temp_bucket[j + BUCKETS_INDEX2(0, 0)] = sum0;
2673
2674
2.17M
    sum0 +=
2675
2.17M
        buckets[i + BUCKETS_INDEX4(0, 0)] + buckets[i + BUCKETS_INDEX4(0, 2)];
2676
2.17M
    sum1 += buckets[i + BUCKETS_INDEX4(0, 1)];
2677
2678
2.17M
    buckets[j + BUCKETS_INDEX2(0, 0)] = sum0;
2679
2.17M
    buckets[j + BUCKETS_INDEX2(0, 1)] = sum1;
2680
2.17M
  }
2681
8.47k
}
2682
2683
static void libsais_initialize_buckets_for_partial_sorting_32s_6k(
2684
    const sa_sint_t* RESTRICT T, sa_sint_t k, sa_sint_t* RESTRICT buckets,
2685
13.9k
    sa_sint_t first_lms_suffix, sa_sint_t left_suffixes_count) {
2686
13.9k
  sa_sint_t* RESTRICT temp_bucket = &buckets[4 * (fast_sint_t)k];
2687
2688
13.9k
  fast_sint_t i, j;
2689
13.9k
  sa_sint_t sum0 = left_suffixes_count + 1, sum1 = 0, sum2 = 0;
2690
13.9k
  for (first_lms_suffix = T[first_lms_suffix], i = BUCKETS_INDEX4(0, 0),
2691
13.9k
      j = BUCKETS_INDEX2(0, 0);
2692
213k
       i != BUCKETS_INDEX4((fast_sint_t)first_lms_suffix, 0);
2693
199k
       i += BUCKETS_INDEX4(1, 0), j += BUCKETS_INDEX2(1, 0)) {
2694
199k
    sa_sint_t SS = buckets[i + BUCKETS_INDEX4(0, 0)];
2695
199k
    sa_sint_t LS = buckets[i + BUCKETS_INDEX4(0, 1)];
2696
199k
    sa_sint_t SL = buckets[i + BUCKETS_INDEX4(0, 2)];
2697
199k
    sa_sint_t LL = buckets[i + BUCKETS_INDEX4(0, 3)];
2698
2699
199k
    buckets[i + BUCKETS_INDEX4(0, 0)] = sum0;
2700
199k
    buckets[i + BUCKETS_INDEX4(0, 1)] = sum2;
2701
199k
    buckets[i + BUCKETS_INDEX4(0, 2)] = 0;
2702
199k
    buckets[i + BUCKETS_INDEX4(0, 3)] = 0;
2703
2704
199k
    sum0 += SS + SL;
2705
199k
    sum1 += LS;
2706
199k
    sum2 += LS + LL;
2707
2708
199k
    temp_bucket[j + BUCKETS_INDEX2(0, 0)] = sum0;
2709
199k
    temp_bucket[j + BUCKETS_INDEX2(0, 1)] = sum1;
2710
199k
  }
2711
2712
365k
  for (sum1 += 1; i <= BUCKETS_INDEX4((fast_sint_t)k - 1, 0);
2713
351k
       i += BUCKETS_INDEX4(1, 0), j += BUCKETS_INDEX2(1, 0)) {
2714
351k
    sa_sint_t SS = buckets[i + BUCKETS_INDEX4(0, 0)];
2715
351k
    sa_sint_t LS = buckets[i + BUCKETS_INDEX4(0, 1)];
2716
351k
    sa_sint_t SL = buckets[i + BUCKETS_INDEX4(0, 2)];
2717
351k
    sa_sint_t LL = buckets[i + BUCKETS_INDEX4(0, 3)];
2718
2719
351k
    buckets[i + BUCKETS_INDEX4(0, 0)] = sum0;
2720
351k
    buckets[i + BUCKETS_INDEX4(0, 1)] = sum2;
2721
351k
    buckets[i + BUCKETS_INDEX4(0, 2)] = 0;
2722
351k
    buckets[i + BUCKETS_INDEX4(0, 3)] = 0;
2723
2724
351k
    sum0 += SS + SL;
2725
351k
    sum1 += LS;
2726
351k
    sum2 += LS + LL;
2727
2728
351k
    temp_bucket[j + BUCKETS_INDEX2(0, 0)] = sum0;
2729
351k
    temp_bucket[j + BUCKETS_INDEX2(0, 1)] = sum1;
2730
351k
  }
2731
13.9k
}
2732
2733
static sa_sint_t libsais_partial_sorting_scan_left_to_right_8u(
2734
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA,
2735
    sa_sint_t* RESTRICT buckets, sa_sint_t d, fast_sint_t omp_block_start,
2736
8.47k
    fast_sint_t omp_block_size) {
2737
8.47k
  const fast_sint_t prefetch_distance = 64;
2738
2739
8.47k
  sa_sint_t* RESTRICT induction_bucket = &buckets[4 * ALPHABET_SIZE];
2740
8.47k
  sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
2741
2742
8.47k
  fast_sint_t i, j;
2743
8.47k
  for (i = omp_block_start,
2744
8.47k
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
2745
6.07M
       i < j; i += 2) {
2746
6.06M
    libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
2747
2748
6.06M
    libsais_prefetchr(&T[SA[i + prefetch_distance + 0] & SAINT_MAX] - 1);
2749
6.06M
    libsais_prefetchr(&T[SA[i + prefetch_distance + 0] & SAINT_MAX] - 2);
2750
6.06M
    libsais_prefetchr(&T[SA[i + prefetch_distance + 1] & SAINT_MAX] - 1);
2751
6.06M
    libsais_prefetchr(&T[SA[i + prefetch_distance + 1] & SAINT_MAX] - 2);
2752
2753
6.06M
    sa_sint_t p0 = SA[i + 0];
2754
6.06M
    d += (p0 < 0);
2755
6.06M
    p0 &= SAINT_MAX;
2756
6.06M
    sa_sint_t v0 = BUCKETS_INDEX2(T[p0 - 1], T[p0 - 2] >= T[p0 - 1]);
2757
6.06M
    SA[induction_bucket[v0]++] =
2758
6.06M
        (p0 - 1) |
2759
6.06M
        (sa_sint_t)((sa_uint_t)(distinct_names[v0] != d) << (SAINT_BIT - 1));
2760
6.06M
    distinct_names[v0] = d;
2761
2762
6.06M
    sa_sint_t p1 = SA[i + 1];
2763
6.06M
    d += (p1 < 0);
2764
6.06M
    p1 &= SAINT_MAX;
2765
6.06M
    sa_sint_t v1 = BUCKETS_INDEX2(T[p1 - 1], T[p1 - 2] >= T[p1 - 1]);
2766
6.06M
    SA[induction_bucket[v1]++] =
2767
6.06M
        (p1 - 1) |
2768
6.06M
        (sa_sint_t)((sa_uint_t)(distinct_names[v1] != d) << (SAINT_BIT - 1));
2769
6.06M
    distinct_names[v1] = d;
2770
6.06M
  }
2771
2772
440k
  for (j += prefetch_distance + 1; i < j; i += 1) {
2773
432k
    sa_sint_t p = SA[i];
2774
432k
    d += (p < 0);
2775
432k
    p &= SAINT_MAX;
2776
432k
    sa_sint_t v = BUCKETS_INDEX2(T[p - 1], T[p - 2] >= T[p - 1]);
2777
432k
    SA[induction_bucket[v]++] =
2778
432k
        (p - 1) |
2779
432k
        (sa_sint_t)((sa_uint_t)(distinct_names[v] != d) << (SAINT_BIT - 1));
2780
432k
    distinct_names[v] = d;
2781
432k
  }
2782
2783
8.47k
  return d;
2784
8.47k
}
2785
2786
#if defined(LIBSAIS_OPENMP)
2787
2788
static void libsais_partial_sorting_scan_left_to_right_8u_block_prepare(
2789
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
2790
    sa_sint_t* RESTRICT buckets, LIBSAIS_THREAD_CACHE* RESTRICT cache,
2791
    fast_sint_t omp_block_start, fast_sint_t omp_block_size,
2792
    LIBSAIS_THREAD_STATE* RESTRICT state) {
2793
  const fast_sint_t prefetch_distance = 64;
2794
2795
  sa_sint_t* RESTRICT induction_bucket = &buckets[0 * ALPHABET_SIZE];
2796
  sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
2797
2798
  memset(induction_bucket, 0, (size_t)2 * (size_t)k * sizeof(sa_sint_t));
2799
  memset(distinct_names, 0, (size_t)2 * (size_t)k * sizeof(sa_sint_t));
2800
2801
  fast_sint_t i, j, count = 0;
2802
  sa_sint_t d = 1;
2803
  for (i = omp_block_start,
2804
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
2805
       i < j; i += 2) {
2806
    libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
2807
2808
    libsais_prefetchr(&T[SA[i + prefetch_distance + 0] & SAINT_MAX] - 1);
2809
    libsais_prefetchr(&T[SA[i + prefetch_distance + 0] & SAINT_MAX] - 2);
2810
    libsais_prefetchr(&T[SA[i + prefetch_distance + 1] & SAINT_MAX] - 1);
2811
    libsais_prefetchr(&T[SA[i + prefetch_distance + 1] & SAINT_MAX] - 2);
2812
2813
    sa_sint_t p0 = cache[count].index = SA[i + 0];
2814
    d += (p0 < 0);
2815
    p0 &= SAINT_MAX;
2816
    sa_sint_t v0 = cache[count++].symbol =
2817
        BUCKETS_INDEX2(T[p0 - 1], T[p0 - 2] >= T[p0 - 1]);
2818
    induction_bucket[v0]++;
2819
    distinct_names[v0] = d;
2820
    sa_sint_t p1 = cache[count].index = SA[i + 1];
2821
    d += (p1 < 0);
2822
    p1 &= SAINT_MAX;
2823
    sa_sint_t v1 = cache[count++].symbol =
2824
        BUCKETS_INDEX2(T[p1 - 1], T[p1 - 2] >= T[p1 - 1]);
2825
    induction_bucket[v1]++;
2826
    distinct_names[v1] = d;
2827
  }
2828
2829
  for (j += prefetch_distance + 1; i < j; i += 1) {
2830
    sa_sint_t p = cache[count].index = SA[i];
2831
    d += (p < 0);
2832
    p &= SAINT_MAX;
2833
    sa_sint_t v = cache[count++].symbol =
2834
        BUCKETS_INDEX2(T[p - 1], T[p - 2] >= T[p - 1]);
2835
    induction_bucket[v]++;
2836
    distinct_names[v] = d;
2837
  }
2838
2839
  state[0].state.position = (fast_sint_t)d - 1;
2840
  state[0].state.count = count;
2841
}
2842
2843
static void libsais_partial_sorting_scan_left_to_right_8u_block_place(
2844
    sa_sint_t* RESTRICT SA, sa_sint_t* RESTRICT buckets,
2845
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t count, sa_sint_t d) {
2846
  const fast_sint_t prefetch_distance = 64;
2847
2848
  sa_sint_t* RESTRICT induction_bucket = &buckets[0 * ALPHABET_SIZE];
2849
  sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
2850
2851
  fast_sint_t i, j;
2852
  for (i = 0, j = count - 1; i < j; i += 2) {
2853
    libsais_prefetchr(&cache[i + prefetch_distance]);
2854
2855
    sa_sint_t p0 = cache[i + 0].index;
2856
    d += (p0 < 0);
2857
    sa_sint_t v0 = cache[i + 0].symbol;
2858
    SA[induction_bucket[v0]++] =
2859
        (p0 - 1) |
2860
        (sa_sint_t)((sa_uint_t)(distinct_names[v0] != d) << (SAINT_BIT - 1));
2861
    distinct_names[v0] = d;
2862
2863
    sa_sint_t p1 = cache[i + 1].index;
2864
    d += (p1 < 0);
2865
    sa_sint_t v1 = cache[i + 1].symbol;
2866
    SA[induction_bucket[v1]++] =
2867
        (p1 - 1) |
2868
        (sa_sint_t)((sa_uint_t)(distinct_names[v1] != d) << (SAINT_BIT - 1));
2869
    distinct_names[v1] = d;
2870
  }
2871
2872
  for (j += 1; i < j; i += 1) {
2873
    sa_sint_t p = cache[i].index;
2874
    d += (p < 0);
2875
    sa_sint_t v = cache[i].symbol;
2876
    SA[induction_bucket[v]++] =
2877
        (p - 1) |
2878
        (sa_sint_t)((sa_uint_t)(distinct_names[v] != d) << (SAINT_BIT - 1));
2879
    distinct_names[v] = d;
2880
  }
2881
}
2882
2883
static sa_sint_t libsais_partial_sorting_scan_left_to_right_8u_block_omp(
2884
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
2885
    sa_sint_t* RESTRICT buckets, sa_sint_t d, fast_sint_t block_start,
2886
    fast_sint_t block_size, sa_sint_t threads,
2887
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
2888
#if defined(LIBSAIS_OPENMP)
2889
#pragma omp parallel num_threads(                                             \
2890
        threads) if (threads > 1 && block_size >= 64 * (k > 256 ? k : 256) && \
2891
                         omp_get_dynamic() == 0)
2892
#endif
2893
  {
2894
#if defined(LIBSAIS_OPENMP)
2895
    fast_sint_t omp_thread_num = omp_get_thread_num();
2896
    fast_sint_t omp_num_threads = omp_get_num_threads();
2897
#else
2898
    UNUSED(k);
2899
    UNUSED(threads);
2900
    UNUSED(thread_state);
2901
2902
    fast_sint_t omp_thread_num = 0;
2903
    fast_sint_t omp_num_threads = 1;
2904
#endif
2905
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
2906
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
2907
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
2908
                                     ? omp_block_stride
2909
                                     : block_size - omp_block_start;
2910
2911
    omp_block_start += block_start;
2912
2913
    if (omp_num_threads == 1) {
2914
      d = libsais_partial_sorting_scan_left_to_right_8u(
2915
          T, SA, buckets, d, omp_block_start, omp_block_size);
2916
    }
2917
#if defined(LIBSAIS_OPENMP)
2918
    else {
2919
      {
2920
        libsais_partial_sorting_scan_left_to_right_8u_block_prepare(
2921
            T, SA, k, thread_state[omp_thread_num].state.buckets,
2922
            thread_state[omp_thread_num].state.cache, omp_block_start,
2923
            omp_block_size, &thread_state[omp_thread_num]);
2924
      }
2925
2926
#pragma omp barrier
2927
2928
#pragma omp master
2929
      {
2930
        sa_sint_t* RESTRICT induction_bucket = &buckets[4 * ALPHABET_SIZE];
2931
        sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
2932
2933
        fast_sint_t t;
2934
        for (t = 0; t < omp_num_threads; ++t) {
2935
          sa_sint_t* RESTRICT temp_induction_bucket =
2936
              &thread_state[t].state.buckets[0 * ALPHABET_SIZE];
2937
          sa_sint_t* RESTRICT temp_distinct_names =
2938
              &thread_state[t].state.buckets[2 * ALPHABET_SIZE];
2939
2940
          fast_sint_t c;
2941
          for (c = 0; c < 2 * k; c += 1) {
2942
            sa_sint_t A = induction_bucket[c], B = temp_induction_bucket[c];
2943
            induction_bucket[c] = A + B;
2944
            temp_induction_bucket[c] = A;
2945
          }
2946
2947
          for (d -= 1, c = 0; c < 2 * k; c += 1) {
2948
            sa_sint_t A = distinct_names[c], B = temp_distinct_names[c],
2949
                      D = B + d;
2950
            distinct_names[c] = B > 0 ? D : A;
2951
            temp_distinct_names[c] = A;
2952
          }
2953
          d += 1 + (sa_sint_t)thread_state[t].state.position;
2954
          thread_state[t].state.position =
2955
              (fast_sint_t)d - thread_state[t].state.position;
2956
        }
2957
      }
2958
2959
#pragma omp barrier
2960
2961
      {
2962
        libsais_partial_sorting_scan_left_to_right_8u_block_place(
2963
            SA, thread_state[omp_thread_num].state.buckets,
2964
            thread_state[omp_thread_num].state.cache,
2965
            thread_state[omp_thread_num].state.count,
2966
            (sa_sint_t)thread_state[omp_thread_num].state.position);
2967
      }
2968
    }
2969
#endif
2970
  }
2971
2972
  return d;
2973
}
2974
2975
#endif
2976
2977
static sa_sint_t libsais_partial_sorting_scan_left_to_right_8u_omp(
2978
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t k,
2979
    sa_sint_t* RESTRICT buckets, sa_sint_t left_suffixes_count, sa_sint_t d,
2980
8.47k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
2981
8.47k
  sa_sint_t* RESTRICT induction_bucket = &buckets[4 * ALPHABET_SIZE];
2982
8.47k
  sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
2983
2984
8.47k
  SA[induction_bucket[BUCKETS_INDEX2(T[n - 1], T[n - 2] >= T[n - 1])]++] =
2985
8.47k
      (n - 1) | SAINT_MIN;
2986
8.47k
  distinct_names[BUCKETS_INDEX2(T[n - 1], T[n - 2] >= T[n - 1])] = ++d;
2987
2988
8.47k
  if (threads == 1 || left_suffixes_count < 65536) {
2989
8.47k
    d = libsais_partial_sorting_scan_left_to_right_8u(T, SA, buckets, d, 0,
2990
8.47k
                                                      left_suffixes_count);
2991
8.47k
  }
2992
#if defined(LIBSAIS_OPENMP)
2993
  else {
2994
    fast_sint_t block_start;
2995
    for (block_start = 0; block_start < left_suffixes_count;) {
2996
      if (SA[block_start] == 0) {
2997
        block_start++;
2998
      } else {
2999
        fast_sint_t block_max_end =
3000
            block_start +
3001
            ((fast_sint_t)threads) *
3002
                (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * (fast_sint_t)threads);
3003
        if (block_max_end > left_suffixes_count) {
3004
          block_max_end = left_suffixes_count;
3005
        }
3006
        fast_sint_t block_end = block_start + 1;
3007
        while (block_end < block_max_end && SA[block_end] != 0) {
3008
          block_end++;
3009
        }
3010
        fast_sint_t block_size = block_end - block_start;
3011
3012
        if (block_size < 32) {
3013
          for (; block_start < block_end; block_start += 1) {
3014
            sa_sint_t p = SA[block_start];
3015
            d += (p < 0);
3016
            p &= SAINT_MAX;
3017
            sa_sint_t v = BUCKETS_INDEX2(T[p - 1], T[p - 2] >= T[p - 1]);
3018
            SA[induction_bucket[v]++] =
3019
                (p - 1) | (sa_sint_t)((sa_uint_t)(distinct_names[v] != d)
3020
                                      << (SAINT_BIT - 1));
3021
            distinct_names[v] = d;
3022
          }
3023
        } else {
3024
          d = libsais_partial_sorting_scan_left_to_right_8u_block_omp(
3025
              T, SA, k, buckets, d, block_start, block_size, threads,
3026
              thread_state);
3027
          block_start = block_end;
3028
        }
3029
      }
3030
    }
3031
  }
3032
#else
3033
8.47k
  UNUSED(k);
3034
8.47k
  UNUSED(thread_state);
3035
8.47k
#endif
3036
3037
8.47k
  return d;
3038
8.47k
}
3039
3040
static sa_sint_t libsais_partial_sorting_scan_left_to_right_32s_6k(
3041
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
3042
    sa_sint_t* RESTRICT buckets, sa_sint_t d, fast_sint_t omp_block_start,
3043
13.9k
    fast_sint_t omp_block_size) {
3044
13.9k
  const fast_sint_t prefetch_distance = 64;
3045
3046
13.9k
  fast_sint_t i, j;
3047
13.9k
  for (i = omp_block_start,
3048
13.9k
      j = omp_block_start + omp_block_size - 2 * prefetch_distance - 1;
3049
1.92M
       i < j; i += 2) {
3050
1.91M
    libsais_prefetchr(&SA[i + 3 * prefetch_distance]);
3051
3052
1.91M
    libsais_prefetchr(&T[SA[i + 2 * prefetch_distance + 0] & SAINT_MAX] - 1);
3053
1.91M
    libsais_prefetchr(&T[SA[i + 2 * prefetch_distance + 0] & SAINT_MAX] - 2);
3054
1.91M
    libsais_prefetchr(&T[SA[i + 2 * prefetch_distance + 1] & SAINT_MAX] - 1);
3055
1.91M
    libsais_prefetchr(&T[SA[i + 2 * prefetch_distance + 1] & SAINT_MAX] - 2);
3056
3057
1.91M
    sa_sint_t p0 = SA[i + prefetch_distance + 0] & SAINT_MAX;
3058
1.91M
    sa_sint_t v0 = BUCKETS_INDEX4(T[p0 - (p0 > 0)], 0);
3059
1.91M
    libsais_prefetchw(&buckets[v0]);
3060
1.91M
    sa_sint_t p1 = SA[i + prefetch_distance + 1] & SAINT_MAX;
3061
1.91M
    sa_sint_t v1 = BUCKETS_INDEX4(T[p1 - (p1 > 0)], 0);
3062
1.91M
    libsais_prefetchw(&buckets[v1]);
3063
3064
1.91M
    sa_sint_t p2 = SA[i + 0];
3065
1.91M
    d += (p2 < 0);
3066
1.91M
    p2 &= SAINT_MAX;
3067
1.91M
    sa_sint_t v2 = BUCKETS_INDEX4(T[p2 - 1], T[p2 - 2] >= T[p2 - 1]);
3068
1.91M
    SA[buckets[v2]++] = (p2 - 1) | (sa_sint_t)((sa_uint_t)(buckets[2 + v2] != d)
3069
1.91M
                                               << (SAINT_BIT - 1));
3070
1.91M
    buckets[2 + v2] = d;
3071
3072
1.91M
    sa_sint_t p3 = SA[i + 1];
3073
1.91M
    d += (p3 < 0);
3074
1.91M
    p3 &= SAINT_MAX;
3075
1.91M
    sa_sint_t v3 = BUCKETS_INDEX4(T[p3 - 1], T[p3 - 2] >= T[p3 - 1]);
3076
1.91M
    SA[buckets[v3]++] = (p3 - 1) | (sa_sint_t)((sa_uint_t)(buckets[2 + v3] != d)
3077
1.91M
                                               << (SAINT_BIT - 1));
3078
1.91M
    buckets[2 + v3] = d;
3079
1.91M
  }
3080
3081
716k
  for (j += 2 * prefetch_distance + 1; i < j; i += 1) {
3082
702k
    sa_sint_t p = SA[i];
3083
702k
    d += (p < 0);
3084
702k
    p &= SAINT_MAX;
3085
702k
    sa_sint_t v = BUCKETS_INDEX4(T[p - 1], T[p - 2] >= T[p - 1]);
3086
702k
    SA[buckets[v]++] = (p - 1) | (sa_sint_t)((sa_uint_t)(buckets[2 + v] != d)
3087
702k
                                             << (SAINT_BIT - 1));
3088
702k
    buckets[2 + v] = d;
3089
702k
  }
3090
3091
13.9k
  return d;
3092
13.9k
}
3093
3094
static sa_sint_t libsais_partial_sorting_scan_left_to_right_32s_4k(
3095
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
3096
    sa_sint_t* RESTRICT buckets, sa_sint_t d, fast_sint_t omp_block_start,
3097
355
    fast_sint_t omp_block_size) {
3098
355
  const fast_sint_t prefetch_distance = 64;
3099
3100
355
  sa_sint_t* RESTRICT induction_bucket = &buckets[2 * (fast_sint_t)k];
3101
355
  sa_sint_t* RESTRICT distinct_names = &buckets[0 * (fast_sint_t)k];
3102
3103
355
  fast_sint_t i, j;
3104
355
  for (i = omp_block_start,
3105
355
      j = omp_block_start + omp_block_size - 2 * prefetch_distance - 1;
3106
235k
       i < j; i += 2) {
3107
235k
    libsais_prefetchw(&SA[i + 3 * prefetch_distance]);
3108
3109
235k
    sa_sint_t s0 = SA[i + 2 * prefetch_distance + 0];
3110
235k
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 & ~SUFFIX_GROUP_MARKER : 2];
3111
235k
    libsais_prefetchr(Ts0 - 1);
3112
235k
    libsais_prefetchr(Ts0 - 2);
3113
235k
    sa_sint_t s1 = SA[i + 2 * prefetch_distance + 1];
3114
235k
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 & ~SUFFIX_GROUP_MARKER : 2];
3115
235k
    libsais_prefetchr(Ts1 - 1);
3116
235k
    libsais_prefetchr(Ts1 - 2);
3117
235k
    sa_sint_t s2 = SA[i + 1 * prefetch_distance + 0];
3118
235k
    if (s2 > 0) {
3119
102k
      const fast_sint_t Ts2 = T[(s2 & ~SUFFIX_GROUP_MARKER) - 1];
3120
102k
      libsais_prefetchw(&induction_bucket[Ts2]);
3121
102k
      libsais_prefetchw(&distinct_names[BUCKETS_INDEX2(Ts2, 0)]);
3122
102k
    }
3123
235k
    sa_sint_t s3 = SA[i + 1 * prefetch_distance + 1];
3124
235k
    if (s3 > 0) {
3125
102k
      const fast_sint_t Ts3 = T[(s3 & ~SUFFIX_GROUP_MARKER) - 1];
3126
102k
      libsais_prefetchw(&induction_bucket[Ts3]);
3127
102k
      libsais_prefetchw(&distinct_names[BUCKETS_INDEX2(Ts3, 0)]);
3128
102k
    }
3129
3130
235k
    sa_sint_t p0 = SA[i + 0];
3131
235k
    SA[i + 0] = p0 & SAINT_MAX;
3132
235k
    if (p0 > 0) {
3133
120k
      SA[i + 0] = 0;
3134
120k
      d += (p0 >> (SUFFIX_GROUP_BIT - 1));
3135
120k
      p0 &= ~SUFFIX_GROUP_MARKER;
3136
120k
      sa_sint_t v0 = BUCKETS_INDEX2(T[p0 - 1], T[p0 - 2] < T[p0 - 1]);
3137
120k
      SA[induction_bucket[T[p0 - 1]]++] =
3138
120k
          (p0 - 1) |
3139
120k
          (sa_sint_t)((sa_uint_t)(T[p0 - 2] < T[p0 - 1]) << (SAINT_BIT - 1)) |
3140
120k
          ((sa_sint_t)(distinct_names[v0] != d) << (SUFFIX_GROUP_BIT - 1));
3141
120k
      distinct_names[v0] = d;
3142
120k
    }
3143
3144
235k
    sa_sint_t p1 = SA[i + 1];
3145
235k
    SA[i + 1] = p1 & SAINT_MAX;
3146
235k
    if (p1 > 0) {
3147
121k
      SA[i + 1] = 0;
3148
121k
      d += (p1 >> (SUFFIX_GROUP_BIT - 1));
3149
121k
      p1 &= ~SUFFIX_GROUP_MARKER;
3150
121k
      sa_sint_t v1 = BUCKETS_INDEX2(T[p1 - 1], T[p1 - 2] < T[p1 - 1]);
3151
121k
      SA[induction_bucket[T[p1 - 1]]++] =
3152
121k
          (p1 - 1) |
3153
121k
          (sa_sint_t)((sa_uint_t)(T[p1 - 2] < T[p1 - 1]) << (SAINT_BIT - 1)) |
3154
121k
          ((sa_sint_t)(distinct_names[v1] != d) << (SUFFIX_GROUP_BIT - 1));
3155
121k
      distinct_names[v1] = d;
3156
121k
    }
3157
235k
  }
3158
3159
45.9k
  for (j += 2 * prefetch_distance + 1; i < j; i += 1) {
3160
45.6k
    sa_sint_t p = SA[i];
3161
45.6k
    SA[i] = p & SAINT_MAX;
3162
45.6k
    if (p > 0) {
3163
20.5k
      SA[i] = 0;
3164
20.5k
      d += (p >> (SUFFIX_GROUP_BIT - 1));
3165
20.5k
      p &= ~SUFFIX_GROUP_MARKER;
3166
20.5k
      sa_sint_t v = BUCKETS_INDEX2(T[p - 1], T[p - 2] < T[p - 1]);
3167
20.5k
      SA[induction_bucket[T[p - 1]]++] =
3168
20.5k
          (p - 1) |
3169
20.5k
          (sa_sint_t)((sa_uint_t)(T[p - 2] < T[p - 1]) << (SAINT_BIT - 1)) |
3170
20.5k
          ((sa_sint_t)(distinct_names[v] != d) << (SUFFIX_GROUP_BIT - 1));
3171
20.5k
      distinct_names[v] = d;
3172
20.5k
    }
3173
45.6k
  }
3174
3175
355
  return d;
3176
355
}
3177
3178
static void libsais_partial_sorting_scan_left_to_right_32s_1k(
3179
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
3180
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t omp_block_start,
3181
842
    fast_sint_t omp_block_size) {
3182
842
  const fast_sint_t prefetch_distance = 64;
3183
3184
842
  fast_sint_t i, j;
3185
842
  for (i = omp_block_start,
3186
842
      j = omp_block_start + omp_block_size - 2 * prefetch_distance - 1;
3187
770k
       i < j; i += 2) {
3188
769k
    libsais_prefetchw(&SA[i + 3 * prefetch_distance]);
3189
3190
769k
    sa_sint_t s0 = SA[i + 2 * prefetch_distance + 0];
3191
769k
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 : 1];
3192
769k
    libsais_prefetchr(Ts0 - 1);
3193
769k
    sa_sint_t s1 = SA[i + 2 * prefetch_distance + 1];
3194
769k
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 : 1];
3195
769k
    libsais_prefetchr(Ts1 - 1);
3196
769k
    sa_sint_t s2 = SA[i + 1 * prefetch_distance + 0];
3197
769k
    if (s2 > 0) {
3198
343k
      libsais_prefetchw(&induction_bucket[T[s2 - 1]]);
3199
343k
      libsais_prefetchr(&T[s2] - 2);
3200
343k
    }
3201
769k
    sa_sint_t s3 = SA[i + 1 * prefetch_distance + 1];
3202
769k
    if (s3 > 0) {
3203
343k
      libsais_prefetchw(&induction_bucket[T[s3 - 1]]);
3204
343k
      libsais_prefetchr(&T[s3] - 2);
3205
343k
    }
3206
3207
769k
    sa_sint_t p0 = SA[i + 0];
3208
769k
    SA[i + 0] = p0 & SAINT_MAX;
3209
769k
    if (p0 > 0) {
3210
392k
      SA[i + 0] = 0;
3211
392k
      SA[induction_bucket[T[p0 - 1]]++] =
3212
392k
          (p0 - 1) |
3213
392k
          (sa_sint_t)((sa_uint_t)(T[p0 - 2] < T[p0 - 1]) << (SAINT_BIT - 1));
3214
392k
    }
3215
769k
    sa_sint_t p1 = SA[i + 1];
3216
769k
    SA[i + 1] = p1 & SAINT_MAX;
3217
769k
    if (p1 > 0) {
3218
392k
      SA[i + 1] = 0;
3219
392k
      SA[induction_bucket[T[p1 - 1]]++] =
3220
392k
          (p1 - 1) |
3221
392k
          (sa_sint_t)((sa_uint_t)(T[p1 - 2] < T[p1 - 1]) << (SAINT_BIT - 1));
3222
392k
    }
3223
769k
  }
3224
3225
109k
  for (j += 2 * prefetch_distance + 1; i < j; i += 1) {
3226
108k
    sa_sint_t p = SA[i];
3227
108k
    SA[i] = p & SAINT_MAX;
3228
108k
    if (p > 0) {
3229
51.1k
      SA[i] = 0;
3230
51.1k
      SA[induction_bucket[T[p - 1]]++] =
3231
51.1k
          (p - 1) |
3232
51.1k
          (sa_sint_t)((sa_uint_t)(T[p - 2] < T[p - 1]) << (SAINT_BIT - 1));
3233
51.1k
    }
3234
108k
  }
3235
842
}
3236
3237
#if defined(LIBSAIS_OPENMP)
3238
3239
static void libsais_partial_sorting_scan_left_to_right_32s_6k_block_gather(
3240
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
3241
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t omp_block_start,
3242
    fast_sint_t omp_block_size) {
3243
  const fast_sint_t prefetch_distance = 64;
3244
3245
  fast_sint_t i, j;
3246
  for (i = omp_block_start,
3247
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
3248
       i < j; i += 2) {
3249
    libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
3250
3251
    libsais_prefetchr(&T[SA[i + prefetch_distance + 0] & SAINT_MAX] - 1);
3252
    libsais_prefetchr(&T[SA[i + prefetch_distance + 0] & SAINT_MAX] - 2);
3253
    libsais_prefetchr(&T[SA[i + prefetch_distance + 1] & SAINT_MAX] - 1);
3254
    libsais_prefetchr(&T[SA[i + prefetch_distance + 1] & SAINT_MAX] - 2);
3255
3256
    libsais_prefetchw(&cache[i + prefetch_distance]);
3257
3258
    sa_sint_t p0 = cache[i + 0].index = SA[i + 0];
3259
    sa_sint_t symbol0 = 0;
3260
    p0 &= SAINT_MAX;
3261
    if (p0 != 0) {
3262
      symbol0 = BUCKETS_INDEX4(T[p0 - 1], T[p0 - 2] >= T[p0 - 1]);
3263
    }
3264
    cache[i + 0].symbol = symbol0;
3265
    sa_sint_t p1 = cache[i + 1].index = SA[i + 1];
3266
    sa_sint_t symbol1 = 0;
3267
    p1 &= SAINT_MAX;
3268
    if (p1 != 0) {
3269
      symbol1 = BUCKETS_INDEX4(T[p1 - 1], T[p1 - 2] >= T[p1 - 1]);
3270
    }
3271
    cache[i + 1].symbol = symbol1;
3272
  }
3273
3274
  for (j += prefetch_distance + 1; i < j; i += 1) {
3275
    sa_sint_t p = cache[i].index = SA[i];
3276
    sa_sint_t symbol = 0;
3277
    p &= SAINT_MAX;
3278
    if (p != 0) {
3279
      symbol = BUCKETS_INDEX4(T[p - 1], T[p - 2] >= T[p - 1]);
3280
    }
3281
    cache[i].symbol = symbol;
3282
  }
3283
}
3284
3285
static void libsais_partial_sorting_scan_left_to_right_32s_4k_block_gather(
3286
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
3287
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t omp_block_start,
3288
    fast_sint_t omp_block_size) {
3289
  const fast_sint_t prefetch_distance = 64;
3290
3291
  fast_sint_t i, j;
3292
  for (i = omp_block_start,
3293
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
3294
       i < j; i += 2) {
3295
    libsais_prefetchw(&SA[i + 2 * prefetch_distance]);
3296
3297
    sa_sint_t s0 = SA[i + prefetch_distance + 0];
3298
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 & ~SUFFIX_GROUP_MARKER : 2];
3299
    libsais_prefetchr(Ts0 - 1);
3300
    libsais_prefetchr(Ts0 - 2);
3301
    sa_sint_t s1 = SA[i + prefetch_distance + 1];
3302
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 & ~SUFFIX_GROUP_MARKER : 2];
3303
    libsais_prefetchr(Ts1 - 1);
3304
    libsais_prefetchr(Ts1 - 2);
3305
3306
    libsais_prefetchw(&cache[i + prefetch_distance]);
3307
3308
    sa_sint_t symbol0 = SAINT_MIN, p0 = SA[i + 0];
3309
    if (p0 > 0) {
3310
      cache[i + 0].index = p0;
3311
      p0 &= ~SUFFIX_GROUP_MARKER;
3312
      symbol0 = BUCKETS_INDEX2(T[p0 - 1], T[p0 - 2] < T[p0 - 1]);
3313
      p0 = 0;
3314
    }
3315
    cache[i + 0].symbol = symbol0;
3316
    SA[i + 0] = p0 & SAINT_MAX;
3317
    sa_sint_t symbol1 = SAINT_MIN, p1 = SA[i + 1];
3318
    if (p1 > 0) {
3319
      cache[i + 1].index = p1;
3320
      p1 &= ~SUFFIX_GROUP_MARKER;
3321
      symbol1 = BUCKETS_INDEX2(T[p1 - 1], T[p1 - 2] < T[p1 - 1]);
3322
      p1 = 0;
3323
    }
3324
    cache[i + 1].symbol = symbol1;
3325
    SA[i + 1] = p1 & SAINT_MAX;
3326
  }
3327
3328
  for (j += prefetch_distance + 1; i < j; i += 1) {
3329
    sa_sint_t symbol = SAINT_MIN, p = SA[i];
3330
    if (p > 0) {
3331
      cache[i].index = p;
3332
      p &= ~SUFFIX_GROUP_MARKER;
3333
      symbol = BUCKETS_INDEX2(T[p - 1], T[p - 2] < T[p - 1]);
3334
      p = 0;
3335
    }
3336
    cache[i].symbol = symbol;
3337
    SA[i] = p & SAINT_MAX;
3338
  }
3339
}
3340
3341
static void libsais_partial_sorting_scan_left_to_right_32s_1k_block_gather(
3342
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
3343
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t omp_block_start,
3344
    fast_sint_t omp_block_size) {
3345
  const fast_sint_t prefetch_distance = 64;
3346
3347
  fast_sint_t i, j;
3348
  for (i = omp_block_start,
3349
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
3350
       i < j; i += 2) {
3351
    libsais_prefetchw(&SA[i + 2 * prefetch_distance]);
3352
3353
    sa_sint_t s0 = SA[i + prefetch_distance + 0];
3354
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 : 2];
3355
    libsais_prefetchr(Ts0 - 1);
3356
    libsais_prefetchr(Ts0 - 2);
3357
    sa_sint_t s1 = SA[i + prefetch_distance + 1];
3358
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 : 2];
3359
    libsais_prefetchr(Ts1 - 1);
3360
    libsais_prefetchr(Ts1 - 2);
3361
3362
    libsais_prefetchw(&cache[i + prefetch_distance]);
3363
3364
    sa_sint_t symbol0 = SAINT_MIN, p0 = SA[i + 0];
3365
    if (p0 > 0) {
3366
      cache[i + 0].index =
3367
          (p0 - 1) |
3368
          (sa_sint_t)((sa_uint_t)(T[p0 - 2] < T[p0 - 1]) << (SAINT_BIT - 1));
3369
      symbol0 = T[p0 - 1];
3370
      p0 = 0;
3371
    }
3372
    cache[i + 0].symbol = symbol0;
3373
    SA[i + 0] = p0 & SAINT_MAX;
3374
    sa_sint_t symbol1 = SAINT_MIN, p1 = SA[i + 1];
3375
    if (p1 > 0) {
3376
      cache[i + 1].index =
3377
          (p1 - 1) |
3378
          (sa_sint_t)((sa_uint_t)(T[p1 - 2] < T[p1 - 1]) << (SAINT_BIT - 1));
3379
      symbol1 = T[p1 - 1];
3380
      p1 = 0;
3381
    }
3382
    cache[i + 1].symbol = symbol1;
3383
    SA[i + 1] = p1 & SAINT_MAX;
3384
  }
3385
3386
  for (j += prefetch_distance + 1; i < j; i += 1) {
3387
    sa_sint_t symbol = SAINT_MIN, p = SA[i];
3388
    if (p > 0) {
3389
      cache[i].index = (p - 1) | (sa_sint_t)((sa_uint_t)(T[p - 2] < T[p - 1])
3390
                                             << (SAINT_BIT - 1));
3391
      symbol = T[p - 1];
3392
      p = 0;
3393
    }
3394
    cache[i].symbol = symbol;
3395
    SA[i] = p & SAINT_MAX;
3396
  }
3397
}
3398
3399
static sa_sint_t libsais_partial_sorting_scan_left_to_right_32s_6k_block_sort(
3400
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT buckets, sa_sint_t d,
3401
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t omp_block_start,
3402
    fast_sint_t omp_block_size) {
3403
  const fast_sint_t prefetch_distance = 64;
3404
3405
  fast_sint_t i, j, omp_block_end = omp_block_start + omp_block_size;
3406
  for (i = omp_block_start, j = omp_block_end - prefetch_distance - 1; i < j;
3407
       i += 2) {
3408
    libsais_prefetchw(&cache[i + 2 * prefetch_distance]);
3409
3410
    libsais_prefetchw(&buckets[cache[i + prefetch_distance + 0].symbol]);
3411
    libsais_prefetchw(&buckets[cache[i + prefetch_distance + 1].symbol]);
3412
3413
    sa_sint_t v0 = cache[i + 0].symbol, p0 = cache[i + 0].index;
3414
    d += (p0 < 0);
3415
    cache[i + 0].symbol = buckets[v0]++;
3416
    cache[i + 0].index =
3417
        (p0 - 1) |
3418
        (sa_sint_t)((sa_uint_t)(buckets[2 + v0] != d) << (SAINT_BIT - 1));
3419
    buckets[2 + v0] = d;
3420
    if (cache[i + 0].symbol < omp_block_end) {
3421
      sa_sint_t s = cache[i + 0].symbol,
3422
                q = (cache[s].index = cache[i + 0].index) & SAINT_MAX;
3423
      cache[s].symbol = BUCKETS_INDEX4(T[q - 1], T[q - 2] >= T[q - 1]);
3424
    }
3425
3426
    sa_sint_t v1 = cache[i + 1].symbol, p1 = cache[i + 1].index;
3427
    d += (p1 < 0);
3428
    cache[i + 1].symbol = buckets[v1]++;
3429
    cache[i + 1].index =
3430
        (p1 - 1) |
3431
        (sa_sint_t)((sa_uint_t)(buckets[2 + v1] != d) << (SAINT_BIT - 1));
3432
    buckets[2 + v1] = d;
3433
    if (cache[i + 1].symbol < omp_block_end) {
3434
      sa_sint_t s = cache[i + 1].symbol,
3435
                q = (cache[s].index = cache[i + 1].index) & SAINT_MAX;
3436
      cache[s].symbol = BUCKETS_INDEX4(T[q - 1], T[q - 2] >= T[q - 1]);
3437
    }
3438
  }
3439
3440
  for (j += prefetch_distance + 1; i < j; i += 1) {
3441
    sa_sint_t v = cache[i].symbol, p = cache[i].index;
3442
    d += (p < 0);
3443
    cache[i].symbol = buckets[v]++;
3444
    cache[i].index = (p - 1) | (sa_sint_t)((sa_uint_t)(buckets[2 + v] != d)
3445
                                           << (SAINT_BIT - 1));
3446
    buckets[2 + v] = d;
3447
    if (cache[i].symbol < omp_block_end) {
3448
      sa_sint_t s = cache[i].symbol,
3449
                q = (cache[s].index = cache[i].index) & SAINT_MAX;
3450
      cache[s].symbol = BUCKETS_INDEX4(T[q - 1], T[q - 2] >= T[q - 1]);
3451
    }
3452
  }
3453
3454
  return d;
3455
}
3456
3457
static sa_sint_t libsais_partial_sorting_scan_left_to_right_32s_4k_block_sort(
3458
    const sa_sint_t* RESTRICT T, sa_sint_t k, sa_sint_t* RESTRICT buckets,
3459
    sa_sint_t d, LIBSAIS_THREAD_CACHE* RESTRICT cache,
3460
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
3461
  const fast_sint_t prefetch_distance = 64;
3462
3463
  sa_sint_t* RESTRICT induction_bucket = &buckets[2 * (fast_sint_t)k];
3464
  sa_sint_t* RESTRICT distinct_names = &buckets[0 * (fast_sint_t)k];
3465
3466
  fast_sint_t i, j, omp_block_end = omp_block_start + omp_block_size;
3467
  for (i = omp_block_start, j = omp_block_end - prefetch_distance - 1; i < j;
3468
       i += 2) {
3469
    libsais_prefetchw(&cache[i + 2 * prefetch_distance]);
3470
3471
    sa_sint_t s0 = cache[i + prefetch_distance + 0].symbol;
3472
    const sa_sint_t* Is0 = &induction_bucket[s0 > 0 ? s0 >> 1 : 0];
3473
    libsais_prefetchw(Is0);
3474
    const sa_sint_t* Ds0 = &distinct_names[s0 > 0 ? s0 : 0];
3475
    libsais_prefetchw(Ds0);
3476
    sa_sint_t s1 = cache[i + prefetch_distance + 1].symbol;
3477
    const sa_sint_t* Is1 = &induction_bucket[s1 > 0 ? s1 >> 1 : 0];
3478
    libsais_prefetchw(Is1);
3479
    const sa_sint_t* Ds1 = &distinct_names[s1 > 0 ? s1 : 0];
3480
    libsais_prefetchw(Ds1);
3481
3482
    sa_sint_t v0 = cache[i + 0].symbol;
3483
    if (v0 >= 0) {
3484
      sa_sint_t p0 = cache[i + 0].index;
3485
      d += (p0 >> (SUFFIX_GROUP_BIT - 1));
3486
      cache[i + 0].symbol = induction_bucket[v0 >> 1]++;
3487
      cache[i + 0].index =
3488
          (p0 - 1) | (sa_sint_t)((sa_uint_t)v0 << (SAINT_BIT - 1)) |
3489
          ((sa_sint_t)(distinct_names[v0] != d) << (SUFFIX_GROUP_BIT - 1));
3490
      distinct_names[v0] = d;
3491
      if (cache[i + 0].symbol < omp_block_end) {
3492
        sa_sint_t ni = cache[i + 0].symbol, np = cache[i + 0].index;
3493
        if (np > 0) {
3494
          cache[ni].index = np;
3495
          np &= ~SUFFIX_GROUP_MARKER;
3496
          cache[ni].symbol = BUCKETS_INDEX2(T[np - 1], T[np - 2] < T[np - 1]);
3497
          np = 0;
3498
        }
3499
        cache[i + 0].index = np & SAINT_MAX;
3500
      }
3501
    }
3502
3503
    sa_sint_t v1 = cache[i + 1].symbol;
3504
    if (v1 >= 0) {
3505
      sa_sint_t p1 = cache[i + 1].index;
3506
      d += (p1 >> (SUFFIX_GROUP_BIT - 1));
3507
      cache[i + 1].symbol = induction_bucket[v1 >> 1]++;
3508
      cache[i + 1].index =
3509
          (p1 - 1) | (sa_sint_t)((sa_uint_t)v1 << (SAINT_BIT - 1)) |
3510
          ((sa_sint_t)(distinct_names[v1] != d) << (SUFFIX_GROUP_BIT - 1));
3511
      distinct_names[v1] = d;
3512
      if (cache[i + 1].symbol < omp_block_end) {
3513
        sa_sint_t ni = cache[i + 1].symbol, np = cache[i + 1].index;
3514
        if (np > 0) {
3515
          cache[ni].index = np;
3516
          np &= ~SUFFIX_GROUP_MARKER;
3517
          cache[ni].symbol = BUCKETS_INDEX2(T[np - 1], T[np - 2] < T[np - 1]);
3518
          np = 0;
3519
        }
3520
        cache[i + 1].index = np & SAINT_MAX;
3521
      }
3522
    }
3523
  }
3524
3525
  for (j += prefetch_distance + 1; i < j; i += 1) {
3526
    sa_sint_t v = cache[i].symbol;
3527
    if (v >= 0) {
3528
      sa_sint_t p = cache[i].index;
3529
      d += (p >> (SUFFIX_GROUP_BIT - 1));
3530
      cache[i].symbol = induction_bucket[v >> 1]++;
3531
      cache[i].index =
3532
          (p - 1) | (sa_sint_t)((sa_uint_t)v << (SAINT_BIT - 1)) |
3533
          ((sa_sint_t)(distinct_names[v] != d) << (SUFFIX_GROUP_BIT - 1));
3534
      distinct_names[v] = d;
3535
      if (cache[i].symbol < omp_block_end) {
3536
        sa_sint_t ni = cache[i].symbol, np = cache[i].index;
3537
        if (np > 0) {
3538
          cache[ni].index = np;
3539
          np &= ~SUFFIX_GROUP_MARKER;
3540
          cache[ni].symbol = BUCKETS_INDEX2(T[np - 1], T[np - 2] < T[np - 1]);
3541
          np = 0;
3542
        }
3543
        cache[i].index = np & SAINT_MAX;
3544
      }
3545
    }
3546
  }
3547
3548
  return d;
3549
}
3550
3551
static void libsais_partial_sorting_scan_left_to_right_32s_1k_block_sort(
3552
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT induction_bucket,
3553
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t omp_block_start,
3554
    fast_sint_t omp_block_size) {
3555
  const fast_sint_t prefetch_distance = 64;
3556
3557
  fast_sint_t i, j, omp_block_end = omp_block_start + omp_block_size;
3558
  for (i = omp_block_start, j = omp_block_end - prefetch_distance - 1; i < j;
3559
       i += 2) {
3560
    libsais_prefetchw(&cache[i + 2 * prefetch_distance]);
3561
3562
    sa_sint_t s0 = cache[i + prefetch_distance + 0].symbol;
3563
    const sa_sint_t* Is0 = &induction_bucket[s0 > 0 ? s0 : 0];
3564
    libsais_prefetchw(Is0);
3565
    sa_sint_t s1 = cache[i + prefetch_distance + 1].symbol;
3566
    const sa_sint_t* Is1 = &induction_bucket[s1 > 0 ? s1 : 0];
3567
    libsais_prefetchw(Is1);
3568
3569
    sa_sint_t v0 = cache[i + 0].symbol;
3570
    if (v0 >= 0) {
3571
      cache[i + 0].symbol = induction_bucket[v0]++;
3572
      if (cache[i + 0].symbol < omp_block_end) {
3573
        sa_sint_t ni = cache[i + 0].symbol, np = cache[i + 0].index;
3574
        if (np > 0) {
3575
          cache[ni].index =
3576
              (np - 1) | (sa_sint_t)((sa_uint_t)(T[np - 2] < T[np - 1])
3577
                                     << (SAINT_BIT - 1));
3578
          cache[ni].symbol = T[np - 1];
3579
          np = 0;
3580
        }
3581
        cache[i + 0].index = np & SAINT_MAX;
3582
      }
3583
    }
3584
3585
    sa_sint_t v1 = cache[i + 1].symbol;
3586
    if (v1 >= 0) {
3587
      cache[i + 1].symbol = induction_bucket[v1]++;
3588
      if (cache[i + 1].symbol < omp_block_end) {
3589
        sa_sint_t ni = cache[i + 1].symbol, np = cache[i + 1].index;
3590
        if (np > 0) {
3591
          cache[ni].index =
3592
              (np - 1) | (sa_sint_t)((sa_uint_t)(T[np - 2] < T[np - 1])
3593
                                     << (SAINT_BIT - 1));
3594
          cache[ni].symbol = T[np - 1];
3595
          np = 0;
3596
        }
3597
        cache[i + 1].index = np & SAINT_MAX;
3598
      }
3599
    }
3600
  }
3601
3602
  for (j += prefetch_distance + 1; i < j; i += 1) {
3603
    sa_sint_t v = cache[i].symbol;
3604
    if (v >= 0) {
3605
      cache[i].symbol = induction_bucket[v]++;
3606
      if (cache[i].symbol < omp_block_end) {
3607
        sa_sint_t ni = cache[i].symbol, np = cache[i].index;
3608
        if (np > 0) {
3609
          cache[ni].index =
3610
              (np - 1) | (sa_sint_t)((sa_uint_t)(T[np - 2] < T[np - 1])
3611
                                     << (SAINT_BIT - 1));
3612
          cache[ni].symbol = T[np - 1];
3613
          np = 0;
3614
        }
3615
        cache[i].index = np & SAINT_MAX;
3616
      }
3617
    }
3618
  }
3619
}
3620
3621
static sa_sint_t libsais_partial_sorting_scan_left_to_right_32s_6k_block_omp(
3622
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
3623
    sa_sint_t* RESTRICT buckets, sa_sint_t d,
3624
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t block_start,
3625
    fast_sint_t block_size, sa_sint_t threads) {
3626
#if defined(LIBSAIS_OPENMP)
3627
#pragma omp parallel num_threads(threads) if (threads > 1 && \
3628
                                                  block_size >= 16384)
3629
#endif
3630
  {
3631
#if defined(LIBSAIS_OPENMP)
3632
    fast_sint_t omp_thread_num = omp_get_thread_num();
3633
    fast_sint_t omp_num_threads = omp_get_num_threads();
3634
#else
3635
    UNUSED(threads);
3636
    UNUSED(cache);
3637
3638
    fast_sint_t omp_thread_num = 0;
3639
    fast_sint_t omp_num_threads = 1;
3640
#endif
3641
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
3642
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
3643
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
3644
                                     ? omp_block_stride
3645
                                     : block_size - omp_block_start;
3646
3647
    omp_block_start += block_start;
3648
3649
    if (omp_num_threads == 1) {
3650
      d = libsais_partial_sorting_scan_left_to_right_32s_6k(
3651
          T, SA, buckets, d, omp_block_start, omp_block_size);
3652
    }
3653
#if defined(LIBSAIS_OPENMP)
3654
    else {
3655
      {
3656
        libsais_partial_sorting_scan_left_to_right_32s_6k_block_gather(
3657
            T, SA, cache - block_start, omp_block_start, omp_block_size);
3658
      }
3659
3660
#pragma omp barrier
3661
3662
#pragma omp master
3663
      {
3664
        d = libsais_partial_sorting_scan_left_to_right_32s_6k_block_sort(
3665
            T, buckets, d, cache - block_start, block_start, block_size);
3666
      }
3667
3668
#pragma omp barrier
3669
3670
      {
3671
        libsais_place_cached_suffixes(SA, cache - block_start, omp_block_start,
3672
                                      omp_block_size);
3673
      }
3674
    }
3675
#endif
3676
  }
3677
3678
  return d;
3679
}
3680
3681
static sa_sint_t libsais_partial_sorting_scan_left_to_right_32s_4k_block_omp(
3682
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
3683
    sa_sint_t* RESTRICT buckets, sa_sint_t d,
3684
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t block_start,
3685
    fast_sint_t block_size, sa_sint_t threads) {
3686
#if defined(LIBSAIS_OPENMP)
3687
#pragma omp parallel num_threads(threads) if (threads > 1 && \
3688
                                                  block_size >= 16384)
3689
#endif
3690
  {
3691
#if defined(LIBSAIS_OPENMP)
3692
    fast_sint_t omp_thread_num = omp_get_thread_num();
3693
    fast_sint_t omp_num_threads = omp_get_num_threads();
3694
#else
3695
    UNUSED(threads);
3696
    UNUSED(cache);
3697
3698
    fast_sint_t omp_thread_num = 0;
3699
    fast_sint_t omp_num_threads = 1;
3700
#endif
3701
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
3702
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
3703
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
3704
                                     ? omp_block_stride
3705
                                     : block_size - omp_block_start;
3706
3707
    omp_block_start += block_start;
3708
3709
    if (omp_num_threads == 1) {
3710
      d = libsais_partial_sorting_scan_left_to_right_32s_4k(
3711
          T, SA, k, buckets, d, omp_block_start, omp_block_size);
3712
    }
3713
#if defined(LIBSAIS_OPENMP)
3714
    else {
3715
      {
3716
        libsais_partial_sorting_scan_left_to_right_32s_4k_block_gather(
3717
            T, SA, cache - block_start, omp_block_start, omp_block_size);
3718
      }
3719
3720
#pragma omp barrier
3721
3722
#pragma omp master
3723
      {
3724
        d = libsais_partial_sorting_scan_left_to_right_32s_4k_block_sort(
3725
            T, k, buckets, d, cache - block_start, block_start, block_size);
3726
      }
3727
3728
#pragma omp barrier
3729
3730
      {
3731
        libsais_compact_and_place_cached_suffixes(
3732
            SA, cache - block_start, omp_block_start, omp_block_size);
3733
      }
3734
    }
3735
#endif
3736
  }
3737
3738
  return d;
3739
}
3740
3741
static void libsais_partial_sorting_scan_left_to_right_32s_1k_block_omp(
3742
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
3743
    sa_sint_t* RESTRICT buckets, LIBSAIS_THREAD_CACHE* RESTRICT cache,
3744
    fast_sint_t block_start, fast_sint_t block_size, sa_sint_t threads) {
3745
#if defined(LIBSAIS_OPENMP)
3746
#pragma omp parallel num_threads(threads) if (threads > 1 && \
3747
                                                  block_size >= 16384)
3748
#endif
3749
  {
3750
#if defined(LIBSAIS_OPENMP)
3751
    fast_sint_t omp_thread_num = omp_get_thread_num();
3752
    fast_sint_t omp_num_threads = omp_get_num_threads();
3753
#else
3754
    UNUSED(threads);
3755
    UNUSED(cache);
3756
3757
    fast_sint_t omp_thread_num = 0;
3758
    fast_sint_t omp_num_threads = 1;
3759
#endif
3760
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
3761
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
3762
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
3763
                                     ? omp_block_stride
3764
                                     : block_size - omp_block_start;
3765
3766
    omp_block_start += block_start;
3767
3768
    if (omp_num_threads == 1) {
3769
      libsais_partial_sorting_scan_left_to_right_32s_1k(
3770
          T, SA, buckets, omp_block_start, omp_block_size);
3771
    }
3772
#if defined(LIBSAIS_OPENMP)
3773
    else {
3774
      {
3775
        libsais_partial_sorting_scan_left_to_right_32s_1k_block_gather(
3776
            T, SA, cache - block_start, omp_block_start, omp_block_size);
3777
      }
3778
3779
#pragma omp barrier
3780
3781
#pragma omp master
3782
      {
3783
        libsais_partial_sorting_scan_left_to_right_32s_1k_block_sort(
3784
            T, buckets, cache - block_start, block_start, block_size);
3785
      }
3786
3787
#pragma omp barrier
3788
3789
      {
3790
        libsais_compact_and_place_cached_suffixes(
3791
            SA, cache - block_start, omp_block_start, omp_block_size);
3792
      }
3793
    }
3794
#endif
3795
  }
3796
}
3797
3798
#endif
3799
3800
static sa_sint_t libsais_partial_sorting_scan_left_to_right_32s_6k_omp(
3801
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
3802
    sa_sint_t* RESTRICT buckets, sa_sint_t left_suffixes_count, sa_sint_t d,
3803
13.9k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
3804
13.9k
  SA[buckets[BUCKETS_INDEX4(T[n - 1], T[n - 2] >= T[n - 1])]++] =
3805
13.9k
      (n - 1) | SAINT_MIN;
3806
13.9k
  buckets[2 + BUCKETS_INDEX4(T[n - 1], T[n - 2] >= T[n - 1])] = ++d;
3807
3808
13.9k
  if (threads == 1 || left_suffixes_count < 65536) {
3809
13.9k
    d = libsais_partial_sorting_scan_left_to_right_32s_6k(T, SA, buckets, d, 0,
3810
13.9k
                                                          left_suffixes_count);
3811
13.9k
  }
3812
#if defined(LIBSAIS_OPENMP)
3813
  else {
3814
    fast_sint_t block_start, block_end;
3815
    for (block_start = 0; block_start < left_suffixes_count;
3816
         block_start = block_end) {
3817
      block_end =
3818
          block_start + (fast_sint_t)threads * LIBSAIS_PER_THREAD_CACHE_SIZE;
3819
      if (block_end > left_suffixes_count) {
3820
        block_end = left_suffixes_count;
3821
      }
3822
3823
      d = libsais_partial_sorting_scan_left_to_right_32s_6k_block_omp(
3824
          T, SA, buckets, d, thread_state[0].state.cache, block_start,
3825
          block_end - block_start, threads);
3826
    }
3827
  }
3828
#else
3829
13.9k
  UNUSED(thread_state);
3830
13.9k
#endif
3831
3832
13.9k
  return d;
3833
13.9k
}
3834
3835
static sa_sint_t libsais_partial_sorting_scan_left_to_right_32s_4k_omp(
3836
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
3837
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t d, sa_sint_t threads,
3838
355
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
3839
355
  sa_sint_t* RESTRICT induction_bucket = &buckets[2 * (fast_sint_t)k];
3840
355
  sa_sint_t* RESTRICT distinct_names = &buckets[0 * (fast_sint_t)k];
3841
3842
355
  SA[induction_bucket[T[n - 1]]++] =
3843
355
      (n - 1) |
3844
355
      (sa_sint_t)((sa_uint_t)(T[n - 2] < T[n - 1]) << (SAINT_BIT - 1)) |
3845
355
      SUFFIX_GROUP_MARKER;
3846
355
  distinct_names[BUCKETS_INDEX2(T[n - 1], T[n - 2] < T[n - 1])] = ++d;
3847
3848
355
  if (threads == 1 || n < 65536) {
3849
355
    d = libsais_partial_sorting_scan_left_to_right_32s_4k(T, SA, k, buckets, d,
3850
355
                                                          0, n);
3851
355
  }
3852
#if defined(LIBSAIS_OPENMP)
3853
  else {
3854
    fast_sint_t block_start, block_end;
3855
    for (block_start = 0; block_start < n; block_start = block_end) {
3856
      block_end =
3857
          block_start + (fast_sint_t)threads * LIBSAIS_PER_THREAD_CACHE_SIZE;
3858
      if (block_end > n) {
3859
        block_end = n;
3860
      }
3861
3862
      d = libsais_partial_sorting_scan_left_to_right_32s_4k_block_omp(
3863
          T, SA, k, buckets, d, thread_state[0].state.cache, block_start,
3864
          block_end - block_start, threads);
3865
    }
3866
  }
3867
#else
3868
355
  UNUSED(thread_state);
3869
355
#endif
3870
3871
355
  return d;
3872
355
}
3873
3874
static void libsais_partial_sorting_scan_left_to_right_32s_1k_omp(
3875
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
3876
    sa_sint_t* RESTRICT buckets, sa_sint_t threads,
3877
842
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
3878
842
  SA[buckets[T[n - 1]]++] =
3879
842
      (n - 1) |
3880
842
      (sa_sint_t)((sa_uint_t)(T[n - 2] < T[n - 1]) << (SAINT_BIT - 1));
3881
3882
842
  if (threads == 1 || n < 65536) {
3883
842
    libsais_partial_sorting_scan_left_to_right_32s_1k(T, SA, buckets, 0, n);
3884
842
  }
3885
#if defined(LIBSAIS_OPENMP)
3886
  else {
3887
    fast_sint_t block_start, block_end;
3888
    for (block_start = 0; block_start < n; block_start = block_end) {
3889
      block_end =
3890
          block_start + (fast_sint_t)threads * LIBSAIS_PER_THREAD_CACHE_SIZE;
3891
      if (block_end > n) {
3892
        block_end = n;
3893
      }
3894
3895
      libsais_partial_sorting_scan_left_to_right_32s_1k_block_omp(
3896
          T, SA, buckets, thread_state[0].state.cache, block_start,
3897
          block_end - block_start, threads);
3898
    }
3899
  }
3900
#else
3901
842
  UNUSED(thread_state);
3902
842
#endif
3903
842
}
3904
3905
static void libsais_partial_sorting_shift_markers_8u_omp(
3906
    sa_sint_t* RESTRICT SA, sa_sint_t n, const sa_sint_t* RESTRICT buckets,
3907
8.47k
    sa_sint_t threads) {
3908
8.47k
  const fast_sint_t prefetch_distance = 64;
3909
3910
8.47k
  const sa_sint_t* RESTRICT temp_bucket = &buckets[4 * ALPHABET_SIZE];
3911
3912
8.47k
  fast_sint_t c;
3913
3914
#if defined(LIBSAIS_OPENMP)
3915
#pragma omp parallel for schedule(static, 1) \
3916
    num_threads(threads) if (threads > 1 && n >= 65536)
3917
#else
3918
8.47k
  UNUSED(threads);
3919
8.47k
  UNUSED(n);
3920
8.47k
#endif
3921
2.17M
  for (c = BUCKETS_INDEX2(ALPHABET_SIZE - 1, 0); c >= BUCKETS_INDEX2(1, 0);
3922
2.16M
       c -= BUCKETS_INDEX2(1, 0)) {
3923
2.16M
    fast_sint_t i, j;
3924
2.16M
    sa_sint_t s = SAINT_MIN;
3925
2.16M
    for (i = (fast_sint_t)temp_bucket[c] - 1,
3926
2.16M
        j = (fast_sint_t)buckets[c - BUCKETS_INDEX2(1, 0)] + 3;
3927
4.25M
         i >= j; i -= 4) {
3928
2.09M
      libsais_prefetchw(&SA[i - prefetch_distance]);
3929
3930
2.09M
      sa_sint_t p0 = SA[i - 0], q0 = (p0 & SAINT_MIN) ^ s;
3931
2.09M
      s = s ^ q0;
3932
2.09M
      SA[i - 0] = p0 ^ q0;
3933
2.09M
      sa_sint_t p1 = SA[i - 1], q1 = (p1 & SAINT_MIN) ^ s;
3934
2.09M
      s = s ^ q1;
3935
2.09M
      SA[i - 1] = p1 ^ q1;
3936
2.09M
      sa_sint_t p2 = SA[i - 2], q2 = (p2 & SAINT_MIN) ^ s;
3937
2.09M
      s = s ^ q2;
3938
2.09M
      SA[i - 2] = p2 ^ q2;
3939
2.09M
      sa_sint_t p3 = SA[i - 3], q3 = (p3 & SAINT_MIN) ^ s;
3940
2.09M
      s = s ^ q3;
3941
2.09M
      SA[i - 3] = p3 ^ q3;
3942
2.09M
    }
3943
3944
2.29M
    for (j -= 3; i >= j; i -= 1) {
3945
130k
      sa_sint_t p = SA[i], q = (p & SAINT_MIN) ^ s;
3946
130k
      s = s ^ q;
3947
130k
      SA[i] = p ^ q;
3948
130k
    }
3949
2.16M
  }
3950
8.47k
}
3951
3952
static void libsais_partial_sorting_shift_markers_32s_6k_omp(
3953
    sa_sint_t* RESTRICT SA, sa_sint_t k, const sa_sint_t* RESTRICT buckets,
3954
13.9k
    sa_sint_t threads) {
3955
13.9k
  const fast_sint_t prefetch_distance = 64;
3956
3957
13.9k
  const sa_sint_t* RESTRICT temp_bucket = &buckets[4 * (fast_sint_t)k];
3958
3959
13.9k
  fast_sint_t c;
3960
3961
#if defined(LIBSAIS_OPENMP)
3962
#pragma omp parallel for schedule(static, 1) \
3963
    num_threads(threads) if (threads > 1 && k >= 65536)
3964
#else
3965
13.9k
  UNUSED(threads);
3966
13.9k
#endif
3967
551k
  for (c = (fast_sint_t)k - 1; c >= 1; c -= 1) {
3968
537k
    fast_sint_t i, j;
3969
537k
    sa_sint_t s = SAINT_MIN;
3970
537k
    for (i = (fast_sint_t)buckets[BUCKETS_INDEX4(c, 0)] - 1,
3971
537k
        j = (fast_sint_t)temp_bucket[BUCKETS_INDEX2(c - 1, 0)] + 3;
3972
1.35M
         i >= j; i -= 4) {
3973
822k
      libsais_prefetchw(&SA[i - prefetch_distance]);
3974
3975
822k
      sa_sint_t p0 = SA[i - 0], q0 = (p0 & SAINT_MIN) ^ s;
3976
822k
      s = s ^ q0;
3977
822k
      SA[i - 0] = p0 ^ q0;
3978
822k
      sa_sint_t p1 = SA[i - 1], q1 = (p1 & SAINT_MIN) ^ s;
3979
822k
      s = s ^ q1;
3980
822k
      SA[i - 1] = p1 ^ q1;
3981
822k
      sa_sint_t p2 = SA[i - 2], q2 = (p2 & SAINT_MIN) ^ s;
3982
822k
      s = s ^ q2;
3983
822k
      SA[i - 2] = p2 ^ q2;
3984
822k
      sa_sint_t p3 = SA[i - 3], q3 = (p3 & SAINT_MIN) ^ s;
3985
822k
      s = s ^ q3;
3986
822k
      SA[i - 3] = p3 ^ q3;
3987
822k
    }
3988
3989
786k
    for (j -= 3; i >= j; i -= 1) {
3990
249k
      sa_sint_t p = SA[i], q = (p & SAINT_MIN) ^ s;
3991
249k
      s = s ^ q;
3992
249k
      SA[i] = p ^ q;
3993
249k
    }
3994
537k
  }
3995
13.9k
}
3996
3997
static void libsais_partial_sorting_shift_markers_32s_4k(sa_sint_t* RESTRICT SA,
3998
355
                                                         sa_sint_t n) {
3999
355
  const fast_sint_t prefetch_distance = 64;
4000
4001
355
  fast_sint_t i;
4002
355
  sa_sint_t s = SUFFIX_GROUP_MARKER;
4003
129k
  for (i = (fast_sint_t)n - 1; i >= 3; i -= 4) {
4004
128k
    libsais_prefetchw(&SA[i - prefetch_distance]);
4005
4006
128k
    sa_sint_t p0 = SA[i - 0],
4007
128k
              q0 = ((p0 & SUFFIX_GROUP_MARKER) ^ s) &
4008
128k
                   ((sa_sint_t)(p0 > 0) << ((SUFFIX_GROUP_BIT - 1)));
4009
128k
    s = s ^ q0;
4010
128k
    SA[i - 0] = p0 ^ q0;
4011
128k
    sa_sint_t p1 = SA[i - 1],
4012
128k
              q1 = ((p1 & SUFFIX_GROUP_MARKER) ^ s) &
4013
128k
                   ((sa_sint_t)(p1 > 0) << ((SUFFIX_GROUP_BIT - 1)));
4014
128k
    s = s ^ q1;
4015
128k
    SA[i - 1] = p1 ^ q1;
4016
128k
    sa_sint_t p2 = SA[i - 2],
4017
128k
              q2 = ((p2 & SUFFIX_GROUP_MARKER) ^ s) &
4018
128k
                   ((sa_sint_t)(p2 > 0) << ((SUFFIX_GROUP_BIT - 1)));
4019
128k
    s = s ^ q2;
4020
128k
    SA[i - 2] = p2 ^ q2;
4021
128k
    sa_sint_t p3 = SA[i - 3],
4022
128k
              q3 = ((p3 & SUFFIX_GROUP_MARKER) ^ s) &
4023
128k
                   ((sa_sint_t)(p3 > 0) << ((SUFFIX_GROUP_BIT - 1)));
4024
128k
    s = s ^ q3;
4025
128k
    SA[i - 3] = p3 ^ q3;
4026
128k
  }
4027
4028
881
  for (; i >= 0; i -= 1) {
4029
526
    sa_sint_t p = SA[i], q = ((p & SUFFIX_GROUP_MARKER) ^ s) &
4030
526
                             ((sa_sint_t)(p > 0) << ((SUFFIX_GROUP_BIT - 1)));
4031
526
    s = s ^ q;
4032
526
    SA[i] = p ^ q;
4033
526
  }
4034
355
}
4035
4036
static void libsais_partial_sorting_shift_buckets_32s_6k(
4037
13.9k
    sa_sint_t k, sa_sint_t* RESTRICT buckets) {
4038
13.9k
  sa_sint_t* RESTRICT temp_bucket = &buckets[4 * (fast_sint_t)k];
4039
4040
13.9k
  fast_sint_t i;
4041
565k
  for (i = BUCKETS_INDEX2(0, 0); i <= BUCKETS_INDEX2((fast_sint_t)k - 1, 0);
4042
551k
       i += BUCKETS_INDEX2(1, 0)) {
4043
551k
    buckets[2 * i + BUCKETS_INDEX4(0, 0)] =
4044
551k
        temp_bucket[i + BUCKETS_INDEX2(0, 0)];
4045
551k
    buckets[2 * i + BUCKETS_INDEX4(0, 1)] =
4046
551k
        temp_bucket[i + BUCKETS_INDEX2(0, 1)];
4047
551k
  }
4048
13.9k
}
4049
4050
static sa_sint_t libsais_partial_sorting_scan_right_to_left_8u(
4051
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA,
4052
    sa_sint_t* RESTRICT buckets, sa_sint_t d, fast_sint_t omp_block_start,
4053
8.47k
    fast_sint_t omp_block_size) {
4054
8.47k
  const fast_sint_t prefetch_distance = 64;
4055
4056
8.47k
  sa_sint_t* RESTRICT induction_bucket = &buckets[0 * ALPHABET_SIZE];
4057
8.47k
  sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
4058
4059
8.47k
  fast_sint_t i, j;
4060
8.47k
  for (i = omp_block_start + omp_block_size - 1,
4061
8.47k
      j = omp_block_start + prefetch_distance + 1;
4062
4.57M
       i >= j; i -= 2) {
4063
4.56M
    libsais_prefetchr(&SA[i - 2 * prefetch_distance]);
4064
4065
4.56M
    libsais_prefetchr(&T[SA[i - prefetch_distance - 0] & SAINT_MAX] - 1);
4066
4.56M
    libsais_prefetchr(&T[SA[i - prefetch_distance - 0] & SAINT_MAX] - 2);
4067
4.56M
    libsais_prefetchr(&T[SA[i - prefetch_distance - 1] & SAINT_MAX] - 1);
4068
4.56M
    libsais_prefetchr(&T[SA[i - prefetch_distance - 1] & SAINT_MAX] - 2);
4069
4070
4.56M
    sa_sint_t p0 = SA[i - 0];
4071
4.56M
    d += (p0 < 0);
4072
4.56M
    p0 &= SAINT_MAX;
4073
4.56M
    sa_sint_t v0 = BUCKETS_INDEX2(T[p0 - 1], T[p0 - 2] > T[p0 - 1]);
4074
4.56M
    SA[--induction_bucket[v0]] =
4075
4.56M
        (p0 - 1) |
4076
4.56M
        (sa_sint_t)((sa_uint_t)(distinct_names[v0] != d) << (SAINT_BIT - 1));
4077
4.56M
    distinct_names[v0] = d;
4078
4079
4.56M
    sa_sint_t p1 = SA[i - 1];
4080
4.56M
    d += (p1 < 0);
4081
4.56M
    p1 &= SAINT_MAX;
4082
4.56M
    sa_sint_t v1 = BUCKETS_INDEX2(T[p1 - 1], T[p1 - 2] > T[p1 - 1]);
4083
4.56M
    SA[--induction_bucket[v1]] =
4084
4.56M
        (p1 - 1) |
4085
4.56M
        (sa_sint_t)((sa_uint_t)(distinct_names[v1] != d) << (SAINT_BIT - 1));
4086
4.56M
    distinct_names[v1] = d;
4087
4.56M
  }
4088
4089
418k
  for (j -= prefetch_distance + 1; i >= j; i -= 1) {
4090
410k
    sa_sint_t p = SA[i];
4091
410k
    d += (p < 0);
4092
410k
    p &= SAINT_MAX;
4093
410k
    sa_sint_t v = BUCKETS_INDEX2(T[p - 1], T[p - 2] > T[p - 1]);
4094
410k
    SA[--induction_bucket[v]] =
4095
410k
        (p - 1) |
4096
410k
        (sa_sint_t)((sa_uint_t)(distinct_names[v] != d) << (SAINT_BIT - 1));
4097
410k
    distinct_names[v] = d;
4098
410k
  }
4099
4100
8.47k
  return d;
4101
8.47k
}
4102
4103
static sa_sint_t libsais_partial_gsa_scan_right_to_left_8u(
4104
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA,
4105
    sa_sint_t* RESTRICT buckets, sa_sint_t d, fast_sint_t omp_block_start,
4106
0
    fast_sint_t omp_block_size) {
4107
0
  const fast_sint_t prefetch_distance = 64;
4108
4109
0
  sa_sint_t* RESTRICT induction_bucket = &buckets[0 * ALPHABET_SIZE];
4110
0
  sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
4111
4112
0
  fast_sint_t i, j;
4113
0
  for (i = omp_block_start + omp_block_size - 1,
4114
0
      j = omp_block_start + prefetch_distance + 1;
4115
0
       i >= j; i -= 2) {
4116
0
    libsais_prefetchr(&SA[i - 2 * prefetch_distance]);
4117
4118
0
    libsais_prefetchr(&T[SA[i - prefetch_distance - 0] & SAINT_MAX] - 1);
4119
0
    libsais_prefetchr(&T[SA[i - prefetch_distance - 0] & SAINT_MAX] - 2);
4120
0
    libsais_prefetchr(&T[SA[i - prefetch_distance - 1] & SAINT_MAX] - 1);
4121
0
    libsais_prefetchr(&T[SA[i - prefetch_distance - 1] & SAINT_MAX] - 2);
4122
4123
0
    sa_sint_t p0 = SA[i - 0];
4124
0
    d += (p0 < 0);
4125
0
    p0 &= SAINT_MAX;
4126
0
    sa_sint_t v0 = BUCKETS_INDEX2(T[p0 - 1], T[p0 - 2] > T[p0 - 1]);
4127
0
    if (v0 != 1) {
4128
0
      SA[--induction_bucket[v0]] =
4129
0
          (p0 - 1) |
4130
0
          (sa_sint_t)((sa_uint_t)(distinct_names[v0] != d) << (SAINT_BIT - 1));
4131
0
      distinct_names[v0] = d;
4132
0
    }
4133
4134
0
    sa_sint_t p1 = SA[i - 1];
4135
0
    d += (p1 < 0);
4136
0
    p1 &= SAINT_MAX;
4137
0
    sa_sint_t v1 = BUCKETS_INDEX2(T[p1 - 1], T[p1 - 2] > T[p1 - 1]);
4138
0
    if (v1 != 1) {
4139
0
      SA[--induction_bucket[v1]] =
4140
0
          (p1 - 1) |
4141
0
          (sa_sint_t)((sa_uint_t)(distinct_names[v1] != d) << (SAINT_BIT - 1));
4142
0
      distinct_names[v1] = d;
4143
0
    }
4144
0
  }
4145
4146
0
  for (j -= prefetch_distance + 1; i >= j; i -= 1) {
4147
0
    sa_sint_t p = SA[i];
4148
0
    d += (p < 0);
4149
0
    p &= SAINT_MAX;
4150
0
    sa_sint_t v = BUCKETS_INDEX2(T[p - 1], T[p - 2] > T[p - 1]);
4151
0
    if (v != 1) {
4152
0
      SA[--induction_bucket[v]] =
4153
0
          (p - 1) |
4154
0
          (sa_sint_t)((sa_uint_t)(distinct_names[v] != d) << (SAINT_BIT - 1));
4155
0
      distinct_names[v] = d;
4156
0
    }
4157
0
  }
4158
4159
0
  return d;
4160
0
}
4161
4162
#if defined(LIBSAIS_OPENMP)
4163
4164
static void libsais_partial_sorting_scan_right_to_left_8u_block_prepare(
4165
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
4166
    sa_sint_t* RESTRICT buckets, LIBSAIS_THREAD_CACHE* RESTRICT cache,
4167
    fast_sint_t omp_block_start, fast_sint_t omp_block_size,
4168
    LIBSAIS_THREAD_STATE* RESTRICT state) {
4169
  const fast_sint_t prefetch_distance = 64;
4170
4171
  sa_sint_t* RESTRICT induction_bucket = &buckets[0 * ALPHABET_SIZE];
4172
  sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
4173
4174
  memset(induction_bucket, 0, (size_t)2 * (size_t)k * sizeof(sa_sint_t));
4175
  memset(distinct_names, 0, (size_t)2 * (size_t)k * sizeof(sa_sint_t));
4176
4177
  fast_sint_t i, j, count = 0;
4178
  sa_sint_t d = 1;
4179
  for (i = omp_block_start + omp_block_size - 1,
4180
      j = omp_block_start + prefetch_distance + 1;
4181
       i >= j; i -= 2) {
4182
    libsais_prefetchr(&SA[i - 2 * prefetch_distance]);
4183
4184
    libsais_prefetchr(&T[SA[i - prefetch_distance - 0] & SAINT_MAX] - 1);
4185
    libsais_prefetchr(&T[SA[i - prefetch_distance - 0] & SAINT_MAX] - 2);
4186
    libsais_prefetchr(&T[SA[i - prefetch_distance - 1] & SAINT_MAX] - 1);
4187
    libsais_prefetchr(&T[SA[i - prefetch_distance - 1] & SAINT_MAX] - 2);
4188
4189
    sa_sint_t p0 = cache[count].index = SA[i - 0];
4190
    d += (p0 < 0);
4191
    p0 &= SAINT_MAX;
4192
    sa_sint_t v0 = cache[count++].symbol =
4193
        BUCKETS_INDEX2(T[p0 - 1], T[p0 - 2] > T[p0 - 1]);
4194
    induction_bucket[v0]++;
4195
    distinct_names[v0] = d;
4196
    sa_sint_t p1 = cache[count].index = SA[i - 1];
4197
    d += (p1 < 0);
4198
    p1 &= SAINT_MAX;
4199
    sa_sint_t v1 = cache[count++].symbol =
4200
        BUCKETS_INDEX2(T[p1 - 1], T[p1 - 2] > T[p1 - 1]);
4201
    induction_bucket[v1]++;
4202
    distinct_names[v1] = d;
4203
  }
4204
4205
  for (j -= prefetch_distance + 1; i >= j; i -= 1) {
4206
    sa_sint_t p = cache[count].index = SA[i];
4207
    d += (p < 0);
4208
    p &= SAINT_MAX;
4209
    sa_sint_t v = cache[count++].symbol =
4210
        BUCKETS_INDEX2(T[p - 1], T[p - 2] > T[p - 1]);
4211
    induction_bucket[v]++;
4212
    distinct_names[v] = d;
4213
  }
4214
4215
  state[0].state.position = (fast_sint_t)d - 1;
4216
  state[0].state.count = count;
4217
}
4218
4219
static void libsais_partial_sorting_scan_right_to_left_8u_block_place(
4220
    sa_sint_t* RESTRICT SA, sa_sint_t* RESTRICT buckets,
4221
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t count, sa_sint_t d) {
4222
  const fast_sint_t prefetch_distance = 64;
4223
4224
  sa_sint_t* RESTRICT induction_bucket = &buckets[0 * ALPHABET_SIZE];
4225
  sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
4226
4227
  fast_sint_t i, j;
4228
  for (i = 0, j = count - 1; i < j; i += 2) {
4229
    libsais_prefetchr(&cache[i + prefetch_distance]);
4230
4231
    sa_sint_t p0 = cache[i + 0].index;
4232
    d += (p0 < 0);
4233
    sa_sint_t v0 = cache[i + 0].symbol;
4234
    SA[--induction_bucket[v0]] =
4235
        (p0 - 1) |
4236
        (sa_sint_t)((sa_uint_t)(distinct_names[v0] != d) << (SAINT_BIT - 1));
4237
    distinct_names[v0] = d;
4238
4239
    sa_sint_t p1 = cache[i + 1].index;
4240
    d += (p1 < 0);
4241
    sa_sint_t v1 = cache[i + 1].symbol;
4242
    SA[--induction_bucket[v1]] =
4243
        (p1 - 1) |
4244
        (sa_sint_t)((sa_uint_t)(distinct_names[v1] != d) << (SAINT_BIT - 1));
4245
    distinct_names[v1] = d;
4246
  }
4247
4248
  for (j += 1; i < j; i += 1) {
4249
    sa_sint_t p = cache[i].index;
4250
    d += (p < 0);
4251
    sa_sint_t v = cache[i].symbol;
4252
    SA[--induction_bucket[v]] =
4253
        (p - 1) |
4254
        (sa_sint_t)((sa_uint_t)(distinct_names[v] != d) << (SAINT_BIT - 1));
4255
    distinct_names[v] = d;
4256
  }
4257
}
4258
4259
static void libsais_partial_gsa_scan_right_to_left_8u_block_place(
4260
    sa_sint_t* RESTRICT SA, sa_sint_t* RESTRICT buckets,
4261
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t count, sa_sint_t d) {
4262
  const fast_sint_t prefetch_distance = 64;
4263
4264
  sa_sint_t* RESTRICT induction_bucket = &buckets[0 * ALPHABET_SIZE];
4265
  sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
4266
4267
  fast_sint_t i, j;
4268
  for (i = 0, j = count - 1; i < j; i += 2) {
4269
    libsais_prefetchr(&cache[i + prefetch_distance]);
4270
4271
    sa_sint_t p0 = cache[i + 0].index;
4272
    d += (p0 < 0);
4273
    sa_sint_t v0 = cache[i + 0].symbol;
4274
    if (v0 != 1) {
4275
      SA[--induction_bucket[v0]] =
4276
          (p0 - 1) |
4277
          (sa_sint_t)((sa_uint_t)(distinct_names[v0] != d) << (SAINT_BIT - 1));
4278
      distinct_names[v0] = d;
4279
    }
4280
4281
    sa_sint_t p1 = cache[i + 1].index;
4282
    d += (p1 < 0);
4283
    sa_sint_t v1 = cache[i + 1].symbol;
4284
    if (v1 != 1) {
4285
      SA[--induction_bucket[v1]] =
4286
          (p1 - 1) |
4287
          (sa_sint_t)((sa_uint_t)(distinct_names[v1] != d) << (SAINT_BIT - 1));
4288
      distinct_names[v1] = d;
4289
    }
4290
  }
4291
4292
  for (j += 1; i < j; i += 1) {
4293
    sa_sint_t p = cache[i].index;
4294
    d += (p < 0);
4295
    sa_sint_t v = cache[i].symbol;
4296
    if (v != 1) {
4297
      SA[--induction_bucket[v]] =
4298
          (p - 1) |
4299
          (sa_sint_t)((sa_uint_t)(distinct_names[v] != d) << (SAINT_BIT - 1));
4300
      distinct_names[v] = d;
4301
    }
4302
  }
4303
}
4304
4305
static sa_sint_t libsais_partial_sorting_scan_right_to_left_8u_block_omp(
4306
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
4307
    sa_sint_t* RESTRICT buckets, sa_sint_t d, fast_sint_t block_start,
4308
    fast_sint_t block_size, sa_sint_t threads,
4309
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
4310
#if defined(LIBSAIS_OPENMP)
4311
#pragma omp parallel num_threads(                                             \
4312
        threads) if (threads > 1 && block_size >= 64 * (k > 256 ? k : 256) && \
4313
                         omp_get_dynamic() == 0)
4314
#endif
4315
  {
4316
#if defined(LIBSAIS_OPENMP)
4317
    fast_sint_t omp_thread_num = omp_get_thread_num();
4318
    fast_sint_t omp_num_threads = omp_get_num_threads();
4319
#else
4320
    UNUSED(k);
4321
    UNUSED(threads);
4322
    UNUSED(thread_state);
4323
4324
    fast_sint_t omp_thread_num = 0;
4325
    fast_sint_t omp_num_threads = 1;
4326
#endif
4327
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
4328
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
4329
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
4330
                                     ? omp_block_stride
4331
                                     : block_size - omp_block_start;
4332
4333
    omp_block_start += block_start;
4334
4335
    if (omp_num_threads == 1) {
4336
      d = libsais_partial_sorting_scan_right_to_left_8u(
4337
          T, SA, buckets, d, omp_block_start, omp_block_size);
4338
    }
4339
#if defined(LIBSAIS_OPENMP)
4340
    else {
4341
      {
4342
        libsais_partial_sorting_scan_right_to_left_8u_block_prepare(
4343
            T, SA, k, thread_state[omp_thread_num].state.buckets,
4344
            thread_state[omp_thread_num].state.cache, omp_block_start,
4345
            omp_block_size, &thread_state[omp_thread_num]);
4346
      }
4347
4348
#pragma omp barrier
4349
4350
#pragma omp master
4351
      {
4352
        sa_sint_t* RESTRICT induction_bucket = &buckets[0 * ALPHABET_SIZE];
4353
        sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
4354
4355
        fast_sint_t t;
4356
        for (t = omp_num_threads - 1; t >= 0; --t) {
4357
          sa_sint_t* RESTRICT temp_induction_bucket =
4358
              &thread_state[t].state.buckets[0 * ALPHABET_SIZE];
4359
          sa_sint_t* RESTRICT temp_distinct_names =
4360
              &thread_state[t].state.buckets[2 * ALPHABET_SIZE];
4361
4362
          fast_sint_t c;
4363
          for (c = 0; c < 2 * k; c += 1) {
4364
            sa_sint_t A = induction_bucket[c], B = temp_induction_bucket[c];
4365
            induction_bucket[c] = A - B;
4366
            temp_induction_bucket[c] = A;
4367
          }
4368
4369
          for (d -= 1, c = 0; c < 2 * k; c += 1) {
4370
            sa_sint_t A = distinct_names[c], B = temp_distinct_names[c],
4371
                      D = B + d;
4372
            distinct_names[c] = B > 0 ? D : A;
4373
            temp_distinct_names[c] = A;
4374
          }
4375
          d += 1 + (sa_sint_t)thread_state[t].state.position;
4376
          thread_state[t].state.position =
4377
              (fast_sint_t)d - thread_state[t].state.position;
4378
        }
4379
      }
4380
4381
#pragma omp barrier
4382
4383
      {
4384
        libsais_partial_sorting_scan_right_to_left_8u_block_place(
4385
            SA, thread_state[omp_thread_num].state.buckets,
4386
            thread_state[omp_thread_num].state.cache,
4387
            thread_state[omp_thread_num].state.count,
4388
            (sa_sint_t)thread_state[omp_thread_num].state.position);
4389
      }
4390
    }
4391
#endif
4392
  }
4393
4394
  return d;
4395
}
4396
4397
static sa_sint_t libsais_partial_gsa_scan_right_to_left_8u_block_omp(
4398
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
4399
    sa_sint_t* RESTRICT buckets, sa_sint_t d, fast_sint_t block_start,
4400
    fast_sint_t block_size, sa_sint_t threads,
4401
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
4402
#if defined(LIBSAIS_OPENMP)
4403
#pragma omp parallel num_threads(                                             \
4404
        threads) if (threads > 1 && block_size >= 64 * (k > 256 ? k : 256) && \
4405
                         omp_get_dynamic() == 0)
4406
#endif
4407
  {
4408
#if defined(LIBSAIS_OPENMP)
4409
    fast_sint_t omp_thread_num = omp_get_thread_num();
4410
    fast_sint_t omp_num_threads = omp_get_num_threads();
4411
#else
4412
    UNUSED(k);
4413
    UNUSED(threads);
4414
    UNUSED(thread_state);
4415
4416
    fast_sint_t omp_thread_num = 0;
4417
    fast_sint_t omp_num_threads = 1;
4418
#endif
4419
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
4420
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
4421
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
4422
                                     ? omp_block_stride
4423
                                     : block_size - omp_block_start;
4424
4425
    omp_block_start += block_start;
4426
4427
    if (omp_num_threads == 1) {
4428
      d = libsais_partial_gsa_scan_right_to_left_8u(
4429
          T, SA, buckets, d, omp_block_start, omp_block_size);
4430
    }
4431
#if defined(LIBSAIS_OPENMP)
4432
    else {
4433
      {
4434
        libsais_partial_sorting_scan_right_to_left_8u_block_prepare(
4435
            T, SA, k, thread_state[omp_thread_num].state.buckets,
4436
            thread_state[omp_thread_num].state.cache, omp_block_start,
4437
            omp_block_size, &thread_state[omp_thread_num]);
4438
      }
4439
4440
#pragma omp barrier
4441
4442
#pragma omp master
4443
      {
4444
        sa_sint_t* RESTRICT induction_bucket = &buckets[0 * ALPHABET_SIZE];
4445
        sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
4446
4447
        fast_sint_t t;
4448
        for (t = omp_num_threads - 1; t >= 0; --t) {
4449
          sa_sint_t* RESTRICT temp_induction_bucket =
4450
              &thread_state[t].state.buckets[0 * ALPHABET_SIZE];
4451
          sa_sint_t* RESTRICT temp_distinct_names =
4452
              &thread_state[t].state.buckets[2 * ALPHABET_SIZE];
4453
4454
          fast_sint_t c;
4455
          for (c = 0; c < 2 * k; c += 1) {
4456
            sa_sint_t A = induction_bucket[c], B = temp_induction_bucket[c];
4457
            induction_bucket[c] = A - B;
4458
            temp_induction_bucket[c] = A;
4459
          }
4460
4461
          for (d -= 1, c = 0; c < 2 * k; c += 1) {
4462
            sa_sint_t A = distinct_names[c], B = temp_distinct_names[c],
4463
                      D = B + d;
4464
            distinct_names[c] = B > 0 ? D : A;
4465
            temp_distinct_names[c] = A;
4466
          }
4467
          d += 1 + (sa_sint_t)thread_state[t].state.position;
4468
          thread_state[t].state.position =
4469
              (fast_sint_t)d - thread_state[t].state.position;
4470
        }
4471
      }
4472
4473
#pragma omp barrier
4474
4475
      {
4476
        libsais_partial_gsa_scan_right_to_left_8u_block_place(
4477
            SA, thread_state[omp_thread_num].state.buckets,
4478
            thread_state[omp_thread_num].state.cache,
4479
            thread_state[omp_thread_num].state.count,
4480
            (sa_sint_t)thread_state[omp_thread_num].state.position);
4481
      }
4482
    }
4483
#endif
4484
  }
4485
4486
  return d;
4487
}
4488
4489
#endif
4490
4491
static void libsais_partial_sorting_scan_right_to_left_8u_omp(
4492
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t k,
4493
    sa_sint_t* RESTRICT buckets, sa_sint_t first_lms_suffix,
4494
    sa_sint_t left_suffixes_count, sa_sint_t d, sa_sint_t threads,
4495
8.47k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
4496
8.47k
  fast_sint_t scan_start = (fast_sint_t)left_suffixes_count + 1;
4497
8.47k
  fast_sint_t scan_end = (fast_sint_t)n - (fast_sint_t)first_lms_suffix;
4498
4499
8.47k
  if (threads == 1 || (scan_end - scan_start) < 65536) {
4500
8.47k
    libsais_partial_sorting_scan_right_to_left_8u(T, SA, buckets, d, scan_start,
4501
8.47k
                                                  scan_end - scan_start);
4502
8.47k
  }
4503
#if defined(LIBSAIS_OPENMP)
4504
  else {
4505
    sa_sint_t* RESTRICT induction_bucket = &buckets[0 * ALPHABET_SIZE];
4506
    sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
4507
4508
    fast_sint_t block_start;
4509
    for (block_start = scan_end - 1; block_start >= scan_start;) {
4510
      if (SA[block_start] == 0) {
4511
        block_start--;
4512
      } else {
4513
        fast_sint_t block_max_end =
4514
            block_start -
4515
            ((fast_sint_t)threads) *
4516
                (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * (fast_sint_t)threads);
4517
        if (block_max_end < scan_start) {
4518
          block_max_end = scan_start - 1;
4519
        }
4520
        fast_sint_t block_end = block_start - 1;
4521
        while (block_end > block_max_end && SA[block_end] != 0) {
4522
          block_end--;
4523
        }
4524
        fast_sint_t block_size = block_start - block_end;
4525
4526
        if (block_size < 32) {
4527
          for (; block_start > block_end; block_start -= 1) {
4528
            sa_sint_t p = SA[block_start];
4529
            d += (p < 0);
4530
            p &= SAINT_MAX;
4531
            sa_sint_t v = BUCKETS_INDEX2(T[p - 1], T[p - 2] > T[p - 1]);
4532
            SA[--induction_bucket[v]] =
4533
                (p - 1) | (sa_sint_t)((sa_uint_t)(distinct_names[v] != d)
4534
                                      << (SAINT_BIT - 1));
4535
            distinct_names[v] = d;
4536
          }
4537
        } else {
4538
          d = libsais_partial_sorting_scan_right_to_left_8u_block_omp(
4539
              T, SA, k, buckets, d, block_end + 1, block_size, threads,
4540
              thread_state);
4541
          block_start = block_end;
4542
        }
4543
      }
4544
    }
4545
  }
4546
#else
4547
8.47k
  UNUSED(k);
4548
8.47k
  UNUSED(thread_state);
4549
8.47k
#endif
4550
8.47k
}
4551
4552
static void libsais_partial_gsa_scan_right_to_left_8u_omp(
4553
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t k,
4554
    sa_sint_t* RESTRICT buckets, sa_sint_t first_lms_suffix,
4555
    sa_sint_t left_suffixes_count, sa_sint_t d, sa_sint_t threads,
4556
0
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
4557
0
  fast_sint_t scan_start = (fast_sint_t)left_suffixes_count + 1;
4558
0
  fast_sint_t scan_end = (fast_sint_t)n - (fast_sint_t)first_lms_suffix;
4559
4560
0
  if (threads == 1 || (scan_end - scan_start) < 65536) {
4561
0
    libsais_partial_gsa_scan_right_to_left_8u(T, SA, buckets, d, scan_start,
4562
0
                                              scan_end - scan_start);
4563
0
  }
4564
#if defined(LIBSAIS_OPENMP)
4565
  else {
4566
    sa_sint_t* RESTRICT induction_bucket = &buckets[0 * ALPHABET_SIZE];
4567
    sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
4568
4569
    fast_sint_t block_start;
4570
    for (block_start = scan_end - 1; block_start >= scan_start;) {
4571
      if (SA[block_start] == 0) {
4572
        block_start--;
4573
      } else {
4574
        fast_sint_t block_max_end =
4575
            block_start -
4576
            ((fast_sint_t)threads) *
4577
                (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * (fast_sint_t)threads);
4578
        if (block_max_end < scan_start) {
4579
          block_max_end = scan_start - 1;
4580
        }
4581
        fast_sint_t block_end = block_start - 1;
4582
        while (block_end > block_max_end && SA[block_end] != 0) {
4583
          block_end--;
4584
        }
4585
        fast_sint_t block_size = block_start - block_end;
4586
4587
        if (block_size < 32) {
4588
          for (; block_start > block_end; block_start -= 1) {
4589
            sa_sint_t p = SA[block_start];
4590
            d += (p < 0);
4591
            p &= SAINT_MAX;
4592
            sa_sint_t v = BUCKETS_INDEX2(T[p - 1], T[p - 2] > T[p - 1]);
4593
            if (v != 1) {
4594
              SA[--induction_bucket[v]] =
4595
                  (p - 1) | (sa_sint_t)((sa_uint_t)(distinct_names[v] != d)
4596
                                        << (SAINT_BIT - 1));
4597
              distinct_names[v] = d;
4598
            }
4599
          }
4600
        } else {
4601
          d = libsais_partial_gsa_scan_right_to_left_8u_block_omp(
4602
              T, SA, k, buckets, d, block_end + 1, block_size, threads,
4603
              thread_state);
4604
          block_start = block_end;
4605
        }
4606
      }
4607
    }
4608
  }
4609
#else
4610
0
  UNUSED(k);
4611
0
  UNUSED(thread_state);
4612
0
#endif
4613
0
}
4614
4615
static sa_sint_t libsais_partial_sorting_scan_right_to_left_32s_6k(
4616
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
4617
    sa_sint_t* RESTRICT buckets, sa_sint_t d, fast_sint_t omp_block_start,
4618
13.9k
    fast_sint_t omp_block_size) {
4619
13.9k
  const fast_sint_t prefetch_distance = 64;
4620
4621
13.9k
  fast_sint_t i, j;
4622
13.9k
  for (i = omp_block_start + omp_block_size - 1,
4623
13.9k
      j = omp_block_start + 2 * prefetch_distance + 1;
4624
2.32M
       i >= j; i -= 2) {
4625
2.31M
    libsais_prefetchr(&SA[i - 3 * prefetch_distance]);
4626
4627
2.31M
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 0] & SAINT_MAX] - 1);
4628
2.31M
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 0] & SAINT_MAX] - 2);
4629
2.31M
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 1] & SAINT_MAX] - 1);
4630
2.31M
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 1] & SAINT_MAX] - 2);
4631
4632
2.31M
    sa_sint_t p0 = SA[i - prefetch_distance - 0] & SAINT_MAX;
4633
2.31M
    sa_sint_t v0 = BUCKETS_INDEX4(T[p0 - (p0 > 0)], 0);
4634
2.31M
    libsais_prefetchw(&buckets[v0]);
4635
2.31M
    sa_sint_t p1 = SA[i - prefetch_distance - 1] & SAINT_MAX;
4636
2.31M
    sa_sint_t v1 = BUCKETS_INDEX4(T[p1 - (p1 > 0)], 0);
4637
2.31M
    libsais_prefetchw(&buckets[v1]);
4638
4639
2.31M
    sa_sint_t p2 = SA[i - 0];
4640
2.31M
    d += (p2 < 0);
4641
2.31M
    p2 &= SAINT_MAX;
4642
2.31M
    sa_sint_t v2 = BUCKETS_INDEX4(T[p2 - 1], T[p2 - 2] > T[p2 - 1]);
4643
2.31M
    SA[--buckets[v2]] = (p2 - 1) | (sa_sint_t)((sa_uint_t)(buckets[2 + v2] != d)
4644
2.31M
                                               << (SAINT_BIT - 1));
4645
2.31M
    buckets[2 + v2] = d;
4646
4647
2.31M
    sa_sint_t p3 = SA[i - 1];
4648
2.31M
    d += (p3 < 0);
4649
2.31M
    p3 &= SAINT_MAX;
4650
2.31M
    sa_sint_t v3 = BUCKETS_INDEX4(T[p3 - 1], T[p3 - 2] > T[p3 - 1]);
4651
2.31M
    SA[--buckets[v3]] = (p3 - 1) | (sa_sint_t)((sa_uint_t)(buckets[2 + v3] != d)
4652
2.31M
                                               << (SAINT_BIT - 1));
4653
2.31M
    buckets[2 + v3] = d;
4654
2.31M
  }
4655
4656
746k
  for (j -= 2 * prefetch_distance + 1; i >= j; i -= 1) {
4657
732k
    sa_sint_t p = SA[i];
4658
732k
    d += (p < 0);
4659
732k
    p &= SAINT_MAX;
4660
732k
    sa_sint_t v = BUCKETS_INDEX4(T[p - 1], T[p - 2] > T[p - 1]);
4661
732k
    SA[--buckets[v]] = (p - 1) | (sa_sint_t)((sa_uint_t)(buckets[2 + v] != d)
4662
732k
                                             << (SAINT_BIT - 1));
4663
732k
    buckets[2 + v] = d;
4664
732k
  }
4665
4666
13.9k
  return d;
4667
13.9k
}
4668
4669
static sa_sint_t libsais_partial_sorting_scan_right_to_left_32s_4k(
4670
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
4671
    sa_sint_t* RESTRICT buckets, sa_sint_t d, fast_sint_t omp_block_start,
4672
355
    fast_sint_t omp_block_size) {
4673
355
  const fast_sint_t prefetch_distance = 64;
4674
4675
355
  sa_sint_t* RESTRICT induction_bucket = &buckets[3 * (fast_sint_t)k];
4676
355
  sa_sint_t* RESTRICT distinct_names = &buckets[0 * (fast_sint_t)k];
4677
4678
355
  fast_sint_t i, j;
4679
355
  for (i = omp_block_start + omp_block_size - 1,
4680
355
      j = omp_block_start + 2 * prefetch_distance + 1;
4681
235k
       i >= j; i -= 2) {
4682
235k
    libsais_prefetchw(&SA[i - 3 * prefetch_distance]);
4683
4684
235k
    sa_sint_t s0 = SA[i - 2 * prefetch_distance - 0];
4685
235k
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 & ~SUFFIX_GROUP_MARKER : 2];
4686
235k
    libsais_prefetchr(Ts0 - 1);
4687
235k
    libsais_prefetchr(Ts0 - 2);
4688
235k
    sa_sint_t s1 = SA[i - 2 * prefetch_distance - 1];
4689
235k
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 & ~SUFFIX_GROUP_MARKER : 2];
4690
235k
    libsais_prefetchr(Ts1 - 1);
4691
235k
    libsais_prefetchr(Ts1 - 2);
4692
235k
    sa_sint_t s2 = SA[i - 1 * prefetch_distance - 0];
4693
235k
    if (s2 > 0) {
4694
112k
      const fast_sint_t Ts2 = T[(s2 & ~SUFFIX_GROUP_MARKER) - 1];
4695
112k
      libsais_prefetchw(&induction_bucket[Ts2]);
4696
112k
      libsais_prefetchw(&distinct_names[BUCKETS_INDEX2(Ts2, 0)]);
4697
112k
    }
4698
235k
    sa_sint_t s3 = SA[i - 1 * prefetch_distance - 1];
4699
235k
    if (s3 > 0) {
4700
112k
      const fast_sint_t Ts3 = T[(s3 & ~SUFFIX_GROUP_MARKER) - 1];
4701
112k
      libsais_prefetchw(&induction_bucket[Ts3]);
4702
112k
      libsais_prefetchw(&distinct_names[BUCKETS_INDEX2(Ts3, 0)]);
4703
112k
    }
4704
4705
235k
    sa_sint_t p0 = SA[i - 0];
4706
235k
    if (p0 > 0) {
4707
121k
      SA[i - 0] = 0;
4708
121k
      d += (p0 >> (SUFFIX_GROUP_BIT - 1));
4709
121k
      p0 &= ~SUFFIX_GROUP_MARKER;
4710
121k
      sa_sint_t v0 = BUCKETS_INDEX2(T[p0 - 1], T[p0 - 2] > T[p0 - 1]);
4711
121k
      SA[--induction_bucket[T[p0 - 1]]] =
4712
121k
          (p0 - 1) |
4713
121k
          (sa_sint_t)((sa_uint_t)(T[p0 - 2] > T[p0 - 1]) << (SAINT_BIT - 1)) |
4714
121k
          ((sa_sint_t)(distinct_names[v0] != d) << (SUFFIX_GROUP_BIT - 1));
4715
121k
      distinct_names[v0] = d;
4716
121k
    }
4717
4718
235k
    sa_sint_t p1 = SA[i - 1];
4719
235k
    if (p1 > 0) {
4720
121k
      SA[i - 1] = 0;
4721
121k
      d += (p1 >> (SUFFIX_GROUP_BIT - 1));
4722
121k
      p1 &= ~SUFFIX_GROUP_MARKER;
4723
121k
      sa_sint_t v1 = BUCKETS_INDEX2(T[p1 - 1], T[p1 - 2] > T[p1 - 1]);
4724
121k
      SA[--induction_bucket[T[p1 - 1]]] =
4725
121k
          (p1 - 1) |
4726
121k
          (sa_sint_t)((sa_uint_t)(T[p1 - 2] > T[p1 - 1]) << (SAINT_BIT - 1)) |
4727
121k
          ((sa_sint_t)(distinct_names[v1] != d) << (SUFFIX_GROUP_BIT - 1));
4728
121k
      distinct_names[v1] = d;
4729
121k
    }
4730
235k
  }
4731
4732
45.9k
  for (j -= 2 * prefetch_distance + 1; i >= j; i -= 1) {
4733
45.6k
    sa_sint_t p = SA[i];
4734
45.6k
    if (p > 0) {
4735
8.16k
      SA[i] = 0;
4736
8.16k
      d += (p >> (SUFFIX_GROUP_BIT - 1));
4737
8.16k
      p &= ~SUFFIX_GROUP_MARKER;
4738
8.16k
      sa_sint_t v = BUCKETS_INDEX2(T[p - 1], T[p - 2] > T[p - 1]);
4739
8.16k
      SA[--induction_bucket[T[p - 1]]] =
4740
8.16k
          (p - 1) |
4741
8.16k
          (sa_sint_t)((sa_uint_t)(T[p - 2] > T[p - 1]) << (SAINT_BIT - 1)) |
4742
8.16k
          ((sa_sint_t)(distinct_names[v] != d) << (SUFFIX_GROUP_BIT - 1));
4743
8.16k
      distinct_names[v] = d;
4744
8.16k
    }
4745
45.6k
  }
4746
4747
355
  return d;
4748
355
}
4749
4750
static void libsais_partial_sorting_scan_right_to_left_32s_1k(
4751
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
4752
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t omp_block_start,
4753
842
    fast_sint_t omp_block_size) {
4754
842
  const fast_sint_t prefetch_distance = 64;
4755
4756
842
  fast_sint_t i, j;
4757
842
  for (i = omp_block_start + omp_block_size - 1,
4758
842
      j = omp_block_start + 2 * prefetch_distance + 1;
4759
770k
       i >= j; i -= 2) {
4760
769k
    libsais_prefetchw(&SA[i - 3 * prefetch_distance]);
4761
4762
769k
    sa_sint_t s0 = SA[i - 2 * prefetch_distance - 0];
4763
769k
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 : 1];
4764
769k
    libsais_prefetchr(Ts0 - 1);
4765
769k
    sa_sint_t s1 = SA[i - 2 * prefetch_distance - 1];
4766
769k
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 : 1];
4767
769k
    libsais_prefetchr(Ts1 - 1);
4768
769k
    sa_sint_t s2 = SA[i - 1 * prefetch_distance - 0];
4769
769k
    if (s2 > 0) {
4770
369k
      libsais_prefetchw(&induction_bucket[T[s2 - 1]]);
4771
369k
      libsais_prefetchr(&T[s2] - 2);
4772
369k
    }
4773
769k
    sa_sint_t s3 = SA[i - 1 * prefetch_distance - 1];
4774
769k
    if (s3 > 0) {
4775
369k
      libsais_prefetchw(&induction_bucket[T[s3 - 1]]);
4776
369k
      libsais_prefetchr(&T[s3] - 2);
4777
369k
    }
4778
4779
769k
    sa_sint_t p0 = SA[i - 0];
4780
769k
    if (p0 > 0) {
4781
396k
      SA[i - 0] = 0;
4782
396k
      SA[--induction_bucket[T[p0 - 1]]] =
4783
396k
          (p0 - 1) |
4784
396k
          (sa_sint_t)((sa_uint_t)(T[p0 - 2] > T[p0 - 1]) << (SAINT_BIT - 1));
4785
396k
    }
4786
769k
    sa_sint_t p1 = SA[i - 1];
4787
769k
    if (p1 > 0) {
4788
396k
      SA[i - 1] = 0;
4789
396k
      SA[--induction_bucket[T[p1 - 1]]] =
4790
396k
          (p1 - 1) |
4791
396k
          (sa_sint_t)((sa_uint_t)(T[p1 - 2] > T[p1 - 1]) << (SAINT_BIT - 1));
4792
396k
    }
4793
769k
  }
4794
4795
109k
  for (j -= 2 * prefetch_distance + 1; i >= j; i -= 1) {
4796
108k
    sa_sint_t p = SA[i];
4797
108k
    if (p > 0) {
4798
12.7k
      SA[i] = 0;
4799
12.7k
      SA[--induction_bucket[T[p - 1]]] =
4800
12.7k
          (p - 1) |
4801
12.7k
          (sa_sint_t)((sa_uint_t)(T[p - 2] > T[p - 1]) << (SAINT_BIT - 1));
4802
12.7k
    }
4803
108k
  }
4804
842
}
4805
4806
#if defined(LIBSAIS_OPENMP)
4807
4808
static void libsais_partial_sorting_scan_right_to_left_32s_6k_block_gather(
4809
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
4810
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t omp_block_start,
4811
    fast_sint_t omp_block_size) {
4812
  const fast_sint_t prefetch_distance = 64;
4813
4814
  fast_sint_t i, j;
4815
  for (i = omp_block_start,
4816
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
4817
       i < j; i += 2) {
4818
    libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
4819
4820
    libsais_prefetchr(&T[SA[i + prefetch_distance + 0] & SAINT_MAX] - 1);
4821
    libsais_prefetchr(&T[SA[i + prefetch_distance + 0] & SAINT_MAX] - 2);
4822
    libsais_prefetchr(&T[SA[i + prefetch_distance + 1] & SAINT_MAX] - 1);
4823
    libsais_prefetchr(&T[SA[i + prefetch_distance + 1] & SAINT_MAX] - 2);
4824
4825
    libsais_prefetchw(&cache[i + prefetch_distance]);
4826
4827
    sa_sint_t p0 = cache[i + 0].index = SA[i + 0];
4828
    sa_sint_t symbol0 = 0;
4829
    p0 &= SAINT_MAX;
4830
    if (p0 != 0) {
4831
      symbol0 = BUCKETS_INDEX4(T[p0 - 1], T[p0 - 2] > T[p0 - 1]);
4832
    }
4833
    cache[i + 0].symbol = symbol0;
4834
    sa_sint_t p1 = cache[i + 1].index = SA[i + 1];
4835
    sa_sint_t symbol1 = 0;
4836
    p1 &= SAINT_MAX;
4837
    if (p1 != 0) {
4838
      symbol1 = BUCKETS_INDEX4(T[p1 - 1], T[p1 - 2] > T[p1 - 1]);
4839
    }
4840
    cache[i + 1].symbol = symbol1;
4841
  }
4842
4843
  for (j += prefetch_distance + 1; i < j; i += 1) {
4844
    sa_sint_t p = cache[i].index = SA[i];
4845
    sa_sint_t symbol = 0;
4846
    p &= SAINT_MAX;
4847
    if (p != 0) {
4848
      symbol = BUCKETS_INDEX4(T[p - 1], T[p - 2] > T[p - 1]);
4849
    }
4850
    cache[i].symbol = symbol;
4851
  }
4852
}
4853
4854
static void libsais_partial_sorting_scan_right_to_left_32s_4k_block_gather(
4855
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
4856
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t omp_block_start,
4857
    fast_sint_t omp_block_size) {
4858
  const fast_sint_t prefetch_distance = 64;
4859
4860
  fast_sint_t i, j;
4861
  for (i = omp_block_start,
4862
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
4863
       i < j; i += 2) {
4864
    libsais_prefetchw(&SA[i + 2 * prefetch_distance]);
4865
4866
    sa_sint_t s0 = SA[i + prefetch_distance + 0];
4867
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 & ~SUFFIX_GROUP_MARKER : 2];
4868
    libsais_prefetchr(Ts0 - 1);
4869
    libsais_prefetchr(Ts0 - 2);
4870
    sa_sint_t s1 = SA[i + prefetch_distance + 1];
4871
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 & ~SUFFIX_GROUP_MARKER : 2];
4872
    libsais_prefetchr(Ts1 - 1);
4873
    libsais_prefetchr(Ts1 - 2);
4874
4875
    libsais_prefetchw(&cache[i + prefetch_distance]);
4876
4877
    sa_sint_t symbol0 = SAINT_MIN, p0 = SA[i + 0];
4878
    if (p0 > 0) {
4879
      SA[i + 0] = 0;
4880
      cache[i + 0].index = p0;
4881
      p0 &= ~SUFFIX_GROUP_MARKER;
4882
      symbol0 = BUCKETS_INDEX2(T[p0 - 1], T[p0 - 2] > T[p0 - 1]);
4883
    }
4884
    cache[i + 0].symbol = symbol0;
4885
    sa_sint_t symbol1 = SAINT_MIN, p1 = SA[i + 1];
4886
    if (p1 > 0) {
4887
      SA[i + 1] = 0;
4888
      cache[i + 1].index = p1;
4889
      p1 &= ~SUFFIX_GROUP_MARKER;
4890
      symbol1 = BUCKETS_INDEX2(T[p1 - 1], T[p1 - 2] > T[p1 - 1]);
4891
    }
4892
    cache[i + 1].symbol = symbol1;
4893
  }
4894
4895
  for (j += prefetch_distance + 1; i < j; i += 1) {
4896
    sa_sint_t symbol = SAINT_MIN, p = SA[i];
4897
    if (p > 0) {
4898
      SA[i] = 0;
4899
      cache[i].index = p;
4900
      p &= ~SUFFIX_GROUP_MARKER;
4901
      symbol = BUCKETS_INDEX2(T[p - 1], T[p - 2] > T[p - 1]);
4902
    }
4903
    cache[i].symbol = symbol;
4904
  }
4905
}
4906
4907
static void libsais_partial_sorting_scan_right_to_left_32s_1k_block_gather(
4908
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
4909
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t omp_block_start,
4910
    fast_sint_t omp_block_size) {
4911
  const fast_sint_t prefetch_distance = 64;
4912
4913
  fast_sint_t i, j;
4914
  for (i = omp_block_start,
4915
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
4916
       i < j; i += 2) {
4917
    libsais_prefetchw(&SA[i + 2 * prefetch_distance]);
4918
4919
    sa_sint_t s0 = SA[i + prefetch_distance + 0];
4920
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 : 2];
4921
    libsais_prefetchr(Ts0 - 1);
4922
    libsais_prefetchr(Ts0 - 2);
4923
    sa_sint_t s1 = SA[i + prefetch_distance + 1];
4924
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 : 2];
4925
    libsais_prefetchr(Ts1 - 1);
4926
    libsais_prefetchr(Ts1 - 2);
4927
4928
    libsais_prefetchw(&cache[i + prefetch_distance]);
4929
4930
    sa_sint_t symbol0 = SAINT_MIN, p0 = SA[i + 0];
4931
    if (p0 > 0) {
4932
      SA[i + 0] = 0;
4933
      cache[i + 0].index =
4934
          (p0 - 1) |
4935
          (sa_sint_t)((sa_uint_t)(T[p0 - 2] > T[p0 - 1]) << (SAINT_BIT - 1));
4936
      symbol0 = T[p0 - 1];
4937
    }
4938
    cache[i + 0].symbol = symbol0;
4939
    sa_sint_t symbol1 = SAINT_MIN, p1 = SA[i + 1];
4940
    if (p1 > 0) {
4941
      SA[i + 1] = 0;
4942
      cache[i + 1].index =
4943
          (p1 - 1) |
4944
          (sa_sint_t)((sa_uint_t)(T[p1 - 2] > T[p1 - 1]) << (SAINT_BIT - 1));
4945
      symbol1 = T[p1 - 1];
4946
    }
4947
    cache[i + 1].symbol = symbol1;
4948
  }
4949
4950
  for (j += prefetch_distance + 1; i < j; i += 1) {
4951
    sa_sint_t symbol = SAINT_MIN, p = SA[i];
4952
    if (p > 0) {
4953
      SA[i] = 0;
4954
      cache[i].index = (p - 1) | (sa_sint_t)((sa_uint_t)(T[p - 2] > T[p - 1])
4955
                                             << (SAINT_BIT - 1));
4956
      symbol = T[p - 1];
4957
    }
4958
    cache[i].symbol = symbol;
4959
  }
4960
}
4961
4962
static sa_sint_t libsais_partial_sorting_scan_right_to_left_32s_6k_block_sort(
4963
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT buckets, sa_sint_t d,
4964
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t omp_block_start,
4965
    fast_sint_t omp_block_size) {
4966
  const fast_sint_t prefetch_distance = 64;
4967
4968
  fast_sint_t i, j;
4969
  for (i = omp_block_start + omp_block_size - 1,
4970
      j = omp_block_start + prefetch_distance + 1;
4971
       i >= j; i -= 2) {
4972
    libsais_prefetchw(&cache[i - 2 * prefetch_distance]);
4973
4974
    libsais_prefetchw(&buckets[cache[i - prefetch_distance - 0].symbol]);
4975
    libsais_prefetchw(&buckets[cache[i - prefetch_distance - 1].symbol]);
4976
4977
    sa_sint_t v0 = cache[i - 0].symbol, p0 = cache[i - 0].index;
4978
    d += (p0 < 0);
4979
    cache[i - 0].symbol = --buckets[v0];
4980
    cache[i - 0].index =
4981
        (p0 - 1) |
4982
        (sa_sint_t)((sa_uint_t)(buckets[2 + v0] != d) << (SAINT_BIT - 1));
4983
    buckets[2 + v0] = d;
4984
    if (cache[i - 0].symbol >= omp_block_start) {
4985
      sa_sint_t s = cache[i - 0].symbol,
4986
                q = (cache[s].index = cache[i - 0].index) & SAINT_MAX;
4987
      cache[s].symbol = BUCKETS_INDEX4(T[q - 1], T[q - 2] > T[q - 1]);
4988
    }
4989
4990
    sa_sint_t v1 = cache[i - 1].symbol, p1 = cache[i - 1].index;
4991
    d += (p1 < 0);
4992
    cache[i - 1].symbol = --buckets[v1];
4993
    cache[i - 1].index =
4994
        (p1 - 1) |
4995
        (sa_sint_t)((sa_uint_t)(buckets[2 + v1] != d) << (SAINT_BIT - 1));
4996
    buckets[2 + v1] = d;
4997
    if (cache[i - 1].symbol >= omp_block_start) {
4998
      sa_sint_t s = cache[i - 1].symbol,
4999
                q = (cache[s].index = cache[i - 1].index) & SAINT_MAX;
5000
      cache[s].symbol = BUCKETS_INDEX4(T[q - 1], T[q - 2] > T[q - 1]);
5001
    }
5002
  }
5003
5004
  for (j -= prefetch_distance + 1; i >= j; i -= 1) {
5005
    sa_sint_t v = cache[i].symbol, p = cache[i].index;
5006
    d += (p < 0);
5007
    cache[i].symbol = --buckets[v];
5008
    cache[i].index = (p - 1) | (sa_sint_t)((sa_uint_t)(buckets[2 + v] != d)
5009
                                           << (SAINT_BIT - 1));
5010
    buckets[2 + v] = d;
5011
    if (cache[i].symbol >= omp_block_start) {
5012
      sa_sint_t s = cache[i].symbol,
5013
                q = (cache[s].index = cache[i].index) & SAINT_MAX;
5014
      cache[s].symbol = BUCKETS_INDEX4(T[q - 1], T[q - 2] > T[q - 1]);
5015
    }
5016
  }
5017
5018
  return d;
5019
}
5020
5021
static sa_sint_t libsais_partial_sorting_scan_right_to_left_32s_4k_block_sort(
5022
    const sa_sint_t* RESTRICT T, sa_sint_t k, sa_sint_t* RESTRICT buckets,
5023
    sa_sint_t d, LIBSAIS_THREAD_CACHE* RESTRICT cache,
5024
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
5025
  const fast_sint_t prefetch_distance = 64;
5026
5027
  sa_sint_t* RESTRICT induction_bucket = &buckets[3 * (fast_sint_t)k];
5028
  sa_sint_t* RESTRICT distinct_names = &buckets[0 * (fast_sint_t)k];
5029
5030
  fast_sint_t i, j;
5031
  for (i = omp_block_start + omp_block_size - 1,
5032
      j = omp_block_start + prefetch_distance + 1;
5033
       i >= j; i -= 2) {
5034
    libsais_prefetchw(&cache[i - 2 * prefetch_distance]);
5035
5036
    sa_sint_t s0 = cache[i - prefetch_distance - 0].symbol;
5037
    const sa_sint_t* Is0 = &induction_bucket[s0 > 0 ? s0 >> 1 : 0];
5038
    libsais_prefetchw(Is0);
5039
    const sa_sint_t* Ds0 = &distinct_names[s0 > 0 ? s0 : 0];
5040
    libsais_prefetchw(Ds0);
5041
    sa_sint_t s1 = cache[i - prefetch_distance - 1].symbol;
5042
    const sa_sint_t* Is1 = &induction_bucket[s1 > 0 ? s1 >> 1 : 0];
5043
    libsais_prefetchw(Is1);
5044
    const sa_sint_t* Ds1 = &distinct_names[s1 > 0 ? s1 : 0];
5045
    libsais_prefetchw(Ds1);
5046
5047
    sa_sint_t v0 = cache[i - 0].symbol;
5048
    if (v0 >= 0) {
5049
      sa_sint_t p0 = cache[i - 0].index;
5050
      d += (p0 >> (SUFFIX_GROUP_BIT - 1));
5051
      cache[i - 0].symbol = --induction_bucket[v0 >> 1];
5052
      cache[i - 0].index =
5053
          (p0 - 1) | (sa_sint_t)((sa_uint_t)v0 << (SAINT_BIT - 1)) |
5054
          ((sa_sint_t)(distinct_names[v0] != d) << (SUFFIX_GROUP_BIT - 1));
5055
      distinct_names[v0] = d;
5056
      if (cache[i - 0].symbol >= omp_block_start) {
5057
        sa_sint_t ni = cache[i - 0].symbol, np = cache[i - 0].index;
5058
        if (np > 0) {
5059
          cache[i - 0].index = 0;
5060
          cache[ni].index = np;
5061
          np &= ~SUFFIX_GROUP_MARKER;
5062
          cache[ni].symbol = BUCKETS_INDEX2(T[np - 1], T[np - 2] > T[np - 1]);
5063
        }
5064
      }
5065
    }
5066
5067
    sa_sint_t v1 = cache[i - 1].symbol;
5068
    if (v1 >= 0) {
5069
      sa_sint_t p1 = cache[i - 1].index;
5070
      d += (p1 >> (SUFFIX_GROUP_BIT - 1));
5071
      cache[i - 1].symbol = --induction_bucket[v1 >> 1];
5072
      cache[i - 1].index =
5073
          (p1 - 1) | (sa_sint_t)((sa_uint_t)v1 << (SAINT_BIT - 1)) |
5074
          ((sa_sint_t)(distinct_names[v1] != d) << (SUFFIX_GROUP_BIT - 1));
5075
      distinct_names[v1] = d;
5076
      if (cache[i - 1].symbol >= omp_block_start) {
5077
        sa_sint_t ni = cache[i - 1].symbol, np = cache[i - 1].index;
5078
        if (np > 0) {
5079
          cache[i - 1].index = 0;
5080
          cache[ni].index = np;
5081
          np &= ~SUFFIX_GROUP_MARKER;
5082
          cache[ni].symbol = BUCKETS_INDEX2(T[np - 1], T[np - 2] > T[np - 1]);
5083
        }
5084
      }
5085
    }
5086
  }
5087
5088
  for (j -= prefetch_distance + 1; i >= j; i -= 1) {
5089
    sa_sint_t v = cache[i].symbol;
5090
    if (v >= 0) {
5091
      sa_sint_t p = cache[i].index;
5092
      d += (p >> (SUFFIX_GROUP_BIT - 1));
5093
      cache[i].symbol = --induction_bucket[v >> 1];
5094
      cache[i].index =
5095
          (p - 1) | (sa_sint_t)((sa_uint_t)v << (SAINT_BIT - 1)) |
5096
          ((sa_sint_t)(distinct_names[v] != d) << (SUFFIX_GROUP_BIT - 1));
5097
      distinct_names[v] = d;
5098
      if (cache[i].symbol >= omp_block_start) {
5099
        sa_sint_t ni = cache[i].symbol, np = cache[i].index;
5100
        if (np > 0) {
5101
          cache[i].index = 0;
5102
          cache[ni].index = np;
5103
          np &= ~SUFFIX_GROUP_MARKER;
5104
          cache[ni].symbol = BUCKETS_INDEX2(T[np - 1], T[np - 2] > T[np - 1]);
5105
        }
5106
      }
5107
    }
5108
  }
5109
5110
  return d;
5111
}
5112
5113
static void libsais_partial_sorting_scan_right_to_left_32s_1k_block_sort(
5114
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT induction_bucket,
5115
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t omp_block_start,
5116
    fast_sint_t omp_block_size) {
5117
  const fast_sint_t prefetch_distance = 64;
5118
5119
  fast_sint_t i, j;
5120
  for (i = omp_block_start + omp_block_size - 1,
5121
      j = omp_block_start + prefetch_distance + 1;
5122
       i >= j; i -= 2) {
5123
    libsais_prefetchw(&cache[i - 2 * prefetch_distance]);
5124
5125
    sa_sint_t s0 = cache[i - prefetch_distance - 0].symbol;
5126
    const sa_sint_t* Is0 = &induction_bucket[s0 > 0 ? s0 : 0];
5127
    libsais_prefetchw(Is0);
5128
    sa_sint_t s1 = cache[i - prefetch_distance - 1].symbol;
5129
    const sa_sint_t* Is1 = &induction_bucket[s1 > 0 ? s1 : 0];
5130
    libsais_prefetchw(Is1);
5131
5132
    sa_sint_t v0 = cache[i - 0].symbol;
5133
    if (v0 >= 0) {
5134
      cache[i - 0].symbol = --induction_bucket[v0];
5135
      if (cache[i - 0].symbol >= omp_block_start) {
5136
        sa_sint_t ni = cache[i - 0].symbol, np = cache[i - 0].index;
5137
        if (np > 0) {
5138
          cache[i - 0].index = 0;
5139
          cache[ni].index =
5140
              (np - 1) | (sa_sint_t)((sa_uint_t)(T[np - 2] > T[np - 1])
5141
                                     << (SAINT_BIT - 1));
5142
          cache[ni].symbol = T[np - 1];
5143
        }
5144
      }
5145
    }
5146
5147
    sa_sint_t v1 = cache[i - 1].symbol;
5148
    if (v1 >= 0) {
5149
      cache[i - 1].symbol = --induction_bucket[v1];
5150
      if (cache[i - 1].symbol >= omp_block_start) {
5151
        sa_sint_t ni = cache[i - 1].symbol, np = cache[i - 1].index;
5152
        if (np > 0) {
5153
          cache[i - 1].index = 0;
5154
          cache[ni].index =
5155
              (np - 1) | (sa_sint_t)((sa_uint_t)(T[np - 2] > T[np - 1])
5156
                                     << (SAINT_BIT - 1));
5157
          cache[ni].symbol = T[np - 1];
5158
        }
5159
      }
5160
    }
5161
  }
5162
5163
  for (j -= prefetch_distance + 1; i >= j; i -= 1) {
5164
    sa_sint_t v = cache[i].symbol;
5165
    if (v >= 0) {
5166
      cache[i].symbol = --induction_bucket[v];
5167
      if (cache[i].symbol >= omp_block_start) {
5168
        sa_sint_t ni = cache[i].symbol, np = cache[i].index;
5169
        if (np > 0) {
5170
          cache[i].index = 0;
5171
          cache[ni].index =
5172
              (np - 1) | (sa_sint_t)((sa_uint_t)(T[np - 2] > T[np - 1])
5173
                                     << (SAINT_BIT - 1));
5174
          cache[ni].symbol = T[np - 1];
5175
        }
5176
      }
5177
    }
5178
  }
5179
}
5180
5181
static sa_sint_t libsais_partial_sorting_scan_right_to_left_32s_6k_block_omp(
5182
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
5183
    sa_sint_t* RESTRICT buckets, sa_sint_t d,
5184
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t block_start,
5185
    fast_sint_t block_size, sa_sint_t threads) {
5186
#if defined(LIBSAIS_OPENMP)
5187
#pragma omp parallel num_threads(threads) if (threads > 1 && \
5188
                                                  block_size >= 16384)
5189
#endif
5190
  {
5191
#if defined(LIBSAIS_OPENMP)
5192
    fast_sint_t omp_thread_num = omp_get_thread_num();
5193
    fast_sint_t omp_num_threads = omp_get_num_threads();
5194
#else
5195
    UNUSED(threads);
5196
    UNUSED(cache);
5197
5198
    fast_sint_t omp_thread_num = 0;
5199
    fast_sint_t omp_num_threads = 1;
5200
#endif
5201
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
5202
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
5203
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
5204
                                     ? omp_block_stride
5205
                                     : block_size - omp_block_start;
5206
5207
    omp_block_start += block_start;
5208
5209
    if (omp_num_threads == 1) {
5210
      d = libsais_partial_sorting_scan_right_to_left_32s_6k(
5211
          T, SA, buckets, d, omp_block_start, omp_block_size);
5212
    }
5213
#if defined(LIBSAIS_OPENMP)
5214
    else {
5215
      {
5216
        libsais_partial_sorting_scan_right_to_left_32s_6k_block_gather(
5217
            T, SA, cache - block_start, omp_block_start, omp_block_size);
5218
      }
5219
5220
#pragma omp barrier
5221
5222
#pragma omp master
5223
      {
5224
        d = libsais_partial_sorting_scan_right_to_left_32s_6k_block_sort(
5225
            T, buckets, d, cache - block_start, block_start, block_size);
5226
      }
5227
5228
#pragma omp barrier
5229
5230
      {
5231
        libsais_place_cached_suffixes(SA, cache - block_start, omp_block_start,
5232
                                      omp_block_size);
5233
      }
5234
    }
5235
#endif
5236
  }
5237
5238
  return d;
5239
}
5240
5241
static sa_sint_t libsais_partial_sorting_scan_right_to_left_32s_4k_block_omp(
5242
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
5243
    sa_sint_t* RESTRICT buckets, sa_sint_t d,
5244
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t block_start,
5245
    fast_sint_t block_size, sa_sint_t threads) {
5246
#if defined(LIBSAIS_OPENMP)
5247
#pragma omp parallel num_threads(threads) if (threads > 1 && \
5248
                                                  block_size >= 16384)
5249
#endif
5250
  {
5251
#if defined(LIBSAIS_OPENMP)
5252
    fast_sint_t omp_thread_num = omp_get_thread_num();
5253
    fast_sint_t omp_num_threads = omp_get_num_threads();
5254
#else
5255
    UNUSED(threads);
5256
    UNUSED(cache);
5257
5258
    fast_sint_t omp_thread_num = 0;
5259
    fast_sint_t omp_num_threads = 1;
5260
#endif
5261
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
5262
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
5263
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
5264
                                     ? omp_block_stride
5265
                                     : block_size - omp_block_start;
5266
5267
    omp_block_start += block_start;
5268
5269
    if (omp_num_threads == 1) {
5270
      d = libsais_partial_sorting_scan_right_to_left_32s_4k(
5271
          T, SA, k, buckets, d, omp_block_start, omp_block_size);
5272
    }
5273
#if defined(LIBSAIS_OPENMP)
5274
    else {
5275
      {
5276
        libsais_partial_sorting_scan_right_to_left_32s_4k_block_gather(
5277
            T, SA, cache - block_start, omp_block_start, omp_block_size);
5278
      }
5279
5280
#pragma omp barrier
5281
5282
#pragma omp master
5283
      {
5284
        d = libsais_partial_sorting_scan_right_to_left_32s_4k_block_sort(
5285
            T, k, buckets, d, cache - block_start, block_start, block_size);
5286
      }
5287
5288
#pragma omp barrier
5289
5290
      {
5291
        libsais_compact_and_place_cached_suffixes(
5292
            SA, cache - block_start, omp_block_start, omp_block_size);
5293
      }
5294
    }
5295
#endif
5296
  }
5297
5298
  return d;
5299
}
5300
5301
static void libsais_partial_sorting_scan_right_to_left_32s_1k_block_omp(
5302
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
5303
    sa_sint_t* RESTRICT buckets, LIBSAIS_THREAD_CACHE* RESTRICT cache,
5304
    fast_sint_t block_start, fast_sint_t block_size, sa_sint_t threads) {
5305
#if defined(LIBSAIS_OPENMP)
5306
#pragma omp parallel num_threads(threads) if (threads > 1 && \
5307
                                                  block_size >= 16384)
5308
#endif
5309
  {
5310
#if defined(LIBSAIS_OPENMP)
5311
    fast_sint_t omp_thread_num = omp_get_thread_num();
5312
    fast_sint_t omp_num_threads = omp_get_num_threads();
5313
#else
5314
    UNUSED(threads);
5315
    UNUSED(cache);
5316
5317
    fast_sint_t omp_thread_num = 0;
5318
    fast_sint_t omp_num_threads = 1;
5319
#endif
5320
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
5321
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
5322
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
5323
                                     ? omp_block_stride
5324
                                     : block_size - omp_block_start;
5325
5326
    omp_block_start += block_start;
5327
5328
    if (omp_num_threads == 1) {
5329
      libsais_partial_sorting_scan_right_to_left_32s_1k(
5330
          T, SA, buckets, omp_block_start, omp_block_size);
5331
    }
5332
#if defined(LIBSAIS_OPENMP)
5333
    else {
5334
      {
5335
        libsais_partial_sorting_scan_right_to_left_32s_1k_block_gather(
5336
            T, SA, cache - block_start, omp_block_start, omp_block_size);
5337
      }
5338
5339
#pragma omp barrier
5340
5341
#pragma omp master
5342
      {
5343
        libsais_partial_sorting_scan_right_to_left_32s_1k_block_sort(
5344
            T, buckets, cache - block_start, block_start, block_size);
5345
      }
5346
5347
#pragma omp barrier
5348
5349
      {
5350
        libsais_compact_and_place_cached_suffixes(
5351
            SA, cache - block_start, omp_block_start, omp_block_size);
5352
      }
5353
    }
5354
#endif
5355
  }
5356
}
5357
5358
#endif
5359
5360
static sa_sint_t libsais_partial_sorting_scan_right_to_left_32s_6k_omp(
5361
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
5362
    sa_sint_t* RESTRICT buckets, sa_sint_t first_lms_suffix,
5363
    sa_sint_t left_suffixes_count, sa_sint_t d, sa_sint_t threads,
5364
13.9k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5365
13.9k
  fast_sint_t scan_start = (fast_sint_t)left_suffixes_count + 1;
5366
13.9k
  fast_sint_t scan_end = (fast_sint_t)n - (fast_sint_t)first_lms_suffix;
5367
5368
13.9k
  if (threads == 1 || (scan_end - scan_start) < 65536) {
5369
13.9k
    d = libsais_partial_sorting_scan_right_to_left_32s_6k(
5370
13.9k
        T, SA, buckets, d, scan_start, scan_end - scan_start);
5371
13.9k
  }
5372
#if defined(LIBSAIS_OPENMP)
5373
  else {
5374
    fast_sint_t block_start, block_end;
5375
    for (block_start = scan_end - 1; block_start >= scan_start;
5376
         block_start = block_end) {
5377
      block_end =
5378
          block_start - (fast_sint_t)threads * LIBSAIS_PER_THREAD_CACHE_SIZE;
5379
      if (block_end < scan_start) {
5380
        block_end = scan_start - 1;
5381
      }
5382
5383
      d = libsais_partial_sorting_scan_right_to_left_32s_6k_block_omp(
5384
          T, SA, buckets, d, thread_state[0].state.cache, block_end + 1,
5385
          block_start - block_end, threads);
5386
    }
5387
  }
5388
#else
5389
13.9k
  UNUSED(thread_state);
5390
13.9k
#endif
5391
5392
13.9k
  return d;
5393
13.9k
}
5394
5395
static sa_sint_t libsais_partial_sorting_scan_right_to_left_32s_4k_omp(
5396
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
5397
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t d, sa_sint_t threads,
5398
355
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5399
355
  if (threads == 1 || n < 65536) {
5400
355
    d = libsais_partial_sorting_scan_right_to_left_32s_4k(T, SA, k, buckets, d,
5401
355
                                                          0, n);
5402
355
  }
5403
#if defined(LIBSAIS_OPENMP)
5404
  else {
5405
    fast_sint_t block_start, block_end;
5406
    for (block_start = (fast_sint_t)n - 1; block_start >= 0;
5407
         block_start = block_end) {
5408
      block_end =
5409
          block_start - (fast_sint_t)threads * LIBSAIS_PER_THREAD_CACHE_SIZE;
5410
      if (block_end < 0) {
5411
        block_end = -1;
5412
      }
5413
5414
      d = libsais_partial_sorting_scan_right_to_left_32s_4k_block_omp(
5415
          T, SA, k, buckets, d, thread_state[0].state.cache, block_end + 1,
5416
          block_start - block_end, threads);
5417
    }
5418
  }
5419
#else
5420
355
  UNUSED(thread_state);
5421
355
#endif
5422
5423
355
  return d;
5424
355
}
5425
5426
static void libsais_partial_sorting_scan_right_to_left_32s_1k_omp(
5427
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
5428
    sa_sint_t* RESTRICT buckets, sa_sint_t threads,
5429
842
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5430
842
  if (threads == 1 || n < 65536) {
5431
842
    libsais_partial_sorting_scan_right_to_left_32s_1k(T, SA, buckets, 0, n);
5432
842
  }
5433
#if defined(LIBSAIS_OPENMP)
5434
  else {
5435
    fast_sint_t block_start, block_end;
5436
    for (block_start = (fast_sint_t)n - 1; block_start >= 0;
5437
         block_start = block_end) {
5438
      block_end =
5439
          block_start - (fast_sint_t)threads * LIBSAIS_PER_THREAD_CACHE_SIZE;
5440
      if (block_end < 0) {
5441
        block_end = -1;
5442
      }
5443
5444
      libsais_partial_sorting_scan_right_to_left_32s_1k_block_omp(
5445
          T, SA, buckets, thread_state[0].state.cache, block_end + 1,
5446
          block_start - block_end, threads);
5447
    }
5448
  }
5449
#else
5450
842
  UNUSED(thread_state);
5451
842
#endif
5452
842
}
5453
5454
static fast_sint_t libsais_partial_sorting_gather_lms_suffixes_32s_4k(
5455
    sa_sint_t* RESTRICT SA, fast_sint_t omp_block_start,
5456
355
    fast_sint_t omp_block_size) {
5457
355
  const fast_sint_t prefetch_distance = 64;
5458
5459
355
  fast_sint_t i, j, l;
5460
355
  for (i = omp_block_start, j = omp_block_start + omp_block_size - 3,
5461
355
      l = omp_block_start;
5462
129k
       i < j; i += 4) {
5463
128k
    libsais_prefetchr(&SA[i + prefetch_distance]);
5464
5465
128k
    sa_uint_t s0 = (sa_uint_t)SA[i + 0];
5466
128k
    SA[l] = (sa_sint_t)((s0 - (sa_uint_t)SUFFIX_GROUP_MARKER) &
5467
128k
                        (sa_uint_t)(~SUFFIX_GROUP_MARKER));
5468
128k
    l += ((sa_sint_t)s0 < 0);
5469
128k
    sa_uint_t s1 = (sa_uint_t)SA[i + 1];
5470
128k
    SA[l] = (sa_sint_t)((s1 - (sa_uint_t)SUFFIX_GROUP_MARKER) &
5471
128k
                        (sa_uint_t)(~SUFFIX_GROUP_MARKER));
5472
128k
    l += ((sa_sint_t)s1 < 0);
5473
128k
    sa_uint_t s2 = (sa_uint_t)SA[i + 2];
5474
128k
    SA[l] = (sa_sint_t)((s2 - (sa_uint_t)SUFFIX_GROUP_MARKER) &
5475
128k
                        (sa_uint_t)(~SUFFIX_GROUP_MARKER));
5476
128k
    l += ((sa_sint_t)s2 < 0);
5477
128k
    sa_uint_t s3 = (sa_uint_t)SA[i + 3];
5478
128k
    SA[l] = (sa_sint_t)((s3 - (sa_uint_t)SUFFIX_GROUP_MARKER) &
5479
128k
                        (sa_uint_t)(~SUFFIX_GROUP_MARKER));
5480
128k
    l += ((sa_sint_t)s3 < 0);
5481
128k
  }
5482
5483
881
  for (j += 3; i < j; i += 1) {
5484
526
    sa_uint_t s = (sa_uint_t)SA[i];
5485
526
    SA[l] = (sa_sint_t)((s - (sa_uint_t)SUFFIX_GROUP_MARKER) &
5486
526
                        (sa_uint_t)(~SUFFIX_GROUP_MARKER));
5487
526
    l += ((sa_sint_t)s < 0);
5488
526
  }
5489
5490
355
  return l;
5491
355
}
5492
5493
static fast_sint_t libsais_partial_sorting_gather_lms_suffixes_32s_1k(
5494
    sa_sint_t* RESTRICT SA, fast_sint_t omp_block_start,
5495
842
    fast_sint_t omp_block_size) {
5496
842
  const fast_sint_t prefetch_distance = 64;
5497
5498
842
  fast_sint_t i, j, l;
5499
842
  for (i = omp_block_start, j = omp_block_start + omp_block_size - 3,
5500
842
      l = omp_block_start;
5501
412k
       i < j; i += 4) {
5502
411k
    libsais_prefetchr(&SA[i + prefetch_distance]);
5503
5504
411k
    sa_sint_t s0 = SA[i + 0];
5505
411k
    SA[l] = s0 & SAINT_MAX;
5506
411k
    l += (s0 < 0);
5507
411k
    sa_sint_t s1 = SA[i + 1];
5508
411k
    SA[l] = s1 & SAINT_MAX;
5509
411k
    l += (s1 < 0);
5510
411k
    sa_sint_t s2 = SA[i + 2];
5511
411k
    SA[l] = s2 & SAINT_MAX;
5512
411k
    l += (s2 < 0);
5513
411k
    sa_sint_t s3 = SA[i + 3];
5514
411k
    SA[l] = s3 & SAINT_MAX;
5515
411k
    l += (s3 < 0);
5516
411k
  }
5517
5518
2.03k
  for (j += 3; i < j; i += 1) {
5519
1.19k
    sa_sint_t s = SA[i];
5520
1.19k
    SA[l] = s & SAINT_MAX;
5521
1.19k
    l += (s < 0);
5522
1.19k
  }
5523
5524
842
  return l;
5525
842
}
5526
5527
static void libsais_partial_sorting_gather_lms_suffixes_32s_4k_omp(
5528
    sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t threads,
5529
355
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5530
#if defined(LIBSAIS_OPENMP)
5531
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 65536)
5532
#endif
5533
355
  {
5534
#if defined(LIBSAIS_OPENMP)
5535
    fast_sint_t omp_thread_num = omp_get_thread_num();
5536
    fast_sint_t omp_num_threads = omp_get_num_threads();
5537
#else
5538
355
    UNUSED(threads);
5539
355
    UNUSED(thread_state);
5540
5541
355
    fast_sint_t omp_thread_num = 0;
5542
355
    fast_sint_t omp_num_threads = 1;
5543
355
#endif
5544
355
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
5545
355
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
5546
355
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
5547
355
                                     ? omp_block_stride
5548
355
                                     : n - omp_block_start;
5549
5550
355
    if (omp_num_threads == 1) {
5551
355
      libsais_partial_sorting_gather_lms_suffixes_32s_4k(SA, omp_block_start,
5552
355
                                                         omp_block_size);
5553
355
    }
5554
#if defined(LIBSAIS_OPENMP)
5555
    else {
5556
      {
5557
        thread_state[omp_thread_num].state.position = omp_block_start;
5558
        thread_state[omp_thread_num].state.count =
5559
            libsais_partial_sorting_gather_lms_suffixes_32s_4k(
5560
                SA, omp_block_start, omp_block_size) -
5561
            omp_block_start;
5562
      }
5563
5564
#pragma omp barrier
5565
5566
#pragma omp master
5567
      {
5568
        fast_sint_t t, position = 0;
5569
        for (t = 0; t < omp_num_threads; ++t) {
5570
          if (t > 0 && thread_state[t].state.count > 0) {
5571
            memmove(&SA[position], &SA[thread_state[t].state.position],
5572
                    (size_t)thread_state[t].state.count * sizeof(sa_sint_t));
5573
          }
5574
5575
          position += thread_state[t].state.count;
5576
        }
5577
      }
5578
    }
5579
#endif
5580
355
  }
5581
355
}
5582
5583
static void libsais_partial_sorting_gather_lms_suffixes_32s_1k_omp(
5584
    sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t threads,
5585
842
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5586
#if defined(LIBSAIS_OPENMP)
5587
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 65536)
5588
#endif
5589
842
  {
5590
#if defined(LIBSAIS_OPENMP)
5591
    fast_sint_t omp_thread_num = omp_get_thread_num();
5592
    fast_sint_t omp_num_threads = omp_get_num_threads();
5593
#else
5594
842
    UNUSED(threads);
5595
842
    UNUSED(thread_state);
5596
5597
842
    fast_sint_t omp_thread_num = 0;
5598
842
    fast_sint_t omp_num_threads = 1;
5599
842
#endif
5600
842
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
5601
842
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
5602
842
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
5603
842
                                     ? omp_block_stride
5604
842
                                     : n - omp_block_start;
5605
5606
842
    if (omp_num_threads == 1) {
5607
842
      libsais_partial_sorting_gather_lms_suffixes_32s_1k(SA, omp_block_start,
5608
842
                                                         omp_block_size);
5609
842
    }
5610
#if defined(LIBSAIS_OPENMP)
5611
    else {
5612
      {
5613
        thread_state[omp_thread_num].state.position = omp_block_start;
5614
        thread_state[omp_thread_num].state.count =
5615
            libsais_partial_sorting_gather_lms_suffixes_32s_1k(
5616
                SA, omp_block_start, omp_block_size) -
5617
            omp_block_start;
5618
      }
5619
5620
#pragma omp barrier
5621
5622
#pragma omp master
5623
      {
5624
        fast_sint_t t, position = 0;
5625
        for (t = 0; t < omp_num_threads; ++t) {
5626
          if (t > 0 && thread_state[t].state.count > 0) {
5627
            memmove(&SA[position], &SA[thread_state[t].state.position],
5628
                    (size_t)thread_state[t].state.count * sizeof(sa_sint_t));
5629
          }
5630
5631
          position += thread_state[t].state.count;
5632
        }
5633
      }
5634
    }
5635
#endif
5636
842
  }
5637
842
}
5638
5639
static void libsais_induce_partial_order_8u_omp(
5640
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t k,
5641
    sa_sint_t flags, sa_sint_t* RESTRICT buckets, sa_sint_t first_lms_suffix,
5642
    sa_sint_t left_suffixes_count, sa_sint_t threads,
5643
8.47k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5644
8.47k
  memset(&buckets[2 * ALPHABET_SIZE], 0,
5645
8.47k
         (size_t)2 * ALPHABET_SIZE * sizeof(sa_sint_t));
5646
5647
8.47k
  if (flags & LIBSAIS_FLAGS_GSA) {
5648
0
    buckets[4 * ALPHABET_SIZE + BUCKETS_INDEX2(0, 1)] =
5649
0
        buckets[4 * ALPHABET_SIZE + BUCKETS_INDEX2(1, 1)] - 1;
5650
0
    libsais_flip_suffix_markers_omp(
5651
0
        SA, buckets[4 * ALPHABET_SIZE + BUCKETS_INDEX2(0, 1)], threads);
5652
0
  }
5653
5654
8.47k
  sa_sint_t d = libsais_partial_sorting_scan_left_to_right_8u_omp(
5655
8.47k
      T, SA, n, k, buckets, left_suffixes_count, 0, threads, thread_state);
5656
8.47k
  libsais_partial_sorting_shift_markers_8u_omp(SA, n, buckets, threads);
5657
5658
8.47k
  if (flags & LIBSAIS_FLAGS_GSA) {
5659
0
    libsais_partial_gsa_scan_right_to_left_8u_omp(
5660
0
        T, SA, n, k, buckets, first_lms_suffix, left_suffixes_count, d, threads,
5661
0
        thread_state);
5662
5663
0
    if (T[first_lms_suffix] == 0) {
5664
0
      memmove(&SA[1], &SA[0],
5665
0
              (size_t)(buckets[BUCKETS_INDEX2(1, 1)] - 1) * sizeof(sa_sint_t));
5666
0
      SA[0] = first_lms_suffix | SAINT_MIN;
5667
0
    }
5668
5669
0
    buckets[BUCKETS_INDEX2(0, 1)] = 0;
5670
8.47k
  } else {
5671
8.47k
    libsais_partial_sorting_scan_right_to_left_8u_omp(
5672
8.47k
        T, SA, n, k, buckets, first_lms_suffix, left_suffixes_count, d, threads,
5673
8.47k
        thread_state);
5674
8.47k
  }
5675
8.47k
}
5676
5677
static void libsais_induce_partial_order_32s_6k_omp(
5678
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
5679
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t first_lms_suffix,
5680
    sa_sint_t left_suffixes_count, sa_sint_t threads,
5681
13.9k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5682
13.9k
  sa_sint_t d = libsais_partial_sorting_scan_left_to_right_32s_6k_omp(
5683
13.9k
      T, SA, n, buckets, left_suffixes_count, 0, threads, thread_state);
5684
13.9k
  libsais_partial_sorting_shift_markers_32s_6k_omp(SA, k, buckets, threads);
5685
13.9k
  libsais_partial_sorting_shift_buckets_32s_6k(k, buckets);
5686
13.9k
  libsais_partial_sorting_scan_right_to_left_32s_6k_omp(
5687
13.9k
      T, SA, n, buckets, first_lms_suffix, left_suffixes_count, d, threads,
5688
13.9k
      thread_state);
5689
13.9k
}
5690
5691
static void libsais_induce_partial_order_32s_4k_omp(
5692
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
5693
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t threads,
5694
355
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5695
355
  memset(buckets, 0, 2 * (size_t)k * sizeof(sa_sint_t));
5696
5697
355
  sa_sint_t d = libsais_partial_sorting_scan_left_to_right_32s_4k_omp(
5698
355
      T, SA, n, k, buckets, 0, threads, thread_state);
5699
355
  libsais_partial_sorting_shift_markers_32s_4k(SA, n);
5700
355
  libsais_partial_sorting_scan_right_to_left_32s_4k_omp(T, SA, n, k, buckets, d,
5701
355
                                                        threads, thread_state);
5702
355
  libsais_partial_sorting_gather_lms_suffixes_32s_4k_omp(SA, n, threads,
5703
355
                                                         thread_state);
5704
355
}
5705
5706
static void libsais_induce_partial_order_32s_2k_omp(
5707
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
5708
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t threads,
5709
664
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5710
664
  libsais_partial_sorting_scan_left_to_right_32s_1k_omp(
5711
664
      T, SA, n, &buckets[1 * (fast_sint_t)k], threads, thread_state);
5712
664
  libsais_partial_sorting_scan_right_to_left_32s_1k_omp(
5713
664
      T, SA, n, &buckets[0 * (fast_sint_t)k], threads, thread_state);
5714
664
  libsais_partial_sorting_gather_lms_suffixes_32s_1k_omp(SA, n, threads,
5715
664
                                                         thread_state);
5716
664
}
5717
5718
static void libsais_induce_partial_order_32s_1k_omp(
5719
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
5720
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t threads,
5721
178
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5722
178
  libsais_count_suffixes_32s(T, n, k, buckets);
5723
178
  libsais_initialize_buckets_start_32s_1k(k, buckets);
5724
178
  libsais_partial_sorting_scan_left_to_right_32s_1k_omp(T, SA, n, buckets,
5725
178
                                                        threads, thread_state);
5726
5727
178
  libsais_count_suffixes_32s(T, n, k, buckets);
5728
178
  libsais_initialize_buckets_end_32s_1k(k, buckets);
5729
178
  libsais_partial_sorting_scan_right_to_left_32s_1k_omp(T, SA, n, buckets,
5730
178
                                                        threads, thread_state);
5731
5732
178
  libsais_partial_sorting_gather_lms_suffixes_32s_1k_omp(SA, n, threads,
5733
178
                                                         thread_state);
5734
178
}
5735
5736
static sa_sint_t libsais_renumber_lms_suffixes_8u(sa_sint_t* RESTRICT SA,
5737
                                                  sa_sint_t m, sa_sint_t name,
5738
                                                  fast_sint_t omp_block_start,
5739
8.47k
                                                  fast_sint_t omp_block_size) {
5740
8.47k
  const fast_sint_t prefetch_distance = 64;
5741
5742
8.47k
  sa_sint_t* RESTRICT SAm = &SA[m];
5743
5744
8.47k
  fast_sint_t i, j;
5745
8.47k
  for (i = omp_block_start,
5746
8.47k
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
5747
2.03M
       i < j; i += 4) {
5748
2.02M
    libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
5749
5750
2.02M
    libsais_prefetchw(&SAm[(SA[i + prefetch_distance + 0] & SAINT_MAX) >> 1]);
5751
2.02M
    libsais_prefetchw(&SAm[(SA[i + prefetch_distance + 1] & SAINT_MAX) >> 1]);
5752
2.02M
    libsais_prefetchw(&SAm[(SA[i + prefetch_distance + 2] & SAINT_MAX) >> 1]);
5753
2.02M
    libsais_prefetchw(&SAm[(SA[i + prefetch_distance + 3] & SAINT_MAX) >> 1]);
5754
5755
2.02M
    sa_sint_t p0 = SA[i + 0];
5756
2.02M
    SAm[(p0 & SAINT_MAX) >> 1] = name | SAINT_MIN;
5757
2.02M
    name += p0 < 0;
5758
2.02M
    sa_sint_t p1 = SA[i + 1];
5759
2.02M
    SAm[(p1 & SAINT_MAX) >> 1] = name | SAINT_MIN;
5760
2.02M
    name += p1 < 0;
5761
2.02M
    sa_sint_t p2 = SA[i + 2];
5762
2.02M
    SAm[(p2 & SAINT_MAX) >> 1] = name | SAINT_MIN;
5763
2.02M
    name += p2 < 0;
5764
2.02M
    sa_sint_t p3 = SA[i + 3];
5765
2.02M
    SAm[(p3 & SAINT_MAX) >> 1] = name | SAINT_MIN;
5766
2.02M
    name += p3 < 0;
5767
2.02M
  }
5768
5769
396k
  for (j += prefetch_distance + 3; i < j; i += 1) {
5770
388k
    sa_sint_t p = SA[i];
5771
388k
    SAm[(p & SAINT_MAX) >> 1] = name | SAINT_MIN;
5772
388k
    name += p < 0;
5773
388k
  }
5774
5775
8.47k
  return name;
5776
8.47k
}
5777
5778
static fast_sint_t libsais_gather_marked_lms_suffixes(
5779
    sa_sint_t* RESTRICT SA, sa_sint_t m, fast_sint_t l,
5780
7.48k
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
5781
7.48k
  const fast_sint_t prefetch_distance = 64;
5782
5783
7.48k
  l -= 1;
5784
5785
7.48k
  fast_sint_t i, j;
5786
7.48k
  for (i = (fast_sint_t)m + omp_block_start + omp_block_size - 1,
5787
7.48k
      j = (fast_sint_t)m + omp_block_start + 3;
5788
2.76M
       i >= j; i -= 4) {
5789
2.75M
    libsais_prefetchr(&SA[i - prefetch_distance]);
5790
5791
2.75M
    sa_sint_t s0 = SA[i - 0];
5792
2.75M
    SA[l] = s0 & SAINT_MAX;
5793
2.75M
    l -= s0 < 0;
5794
2.75M
    sa_sint_t s1 = SA[i - 1];
5795
2.75M
    SA[l] = s1 & SAINT_MAX;
5796
2.75M
    l -= s1 < 0;
5797
2.75M
    sa_sint_t s2 = SA[i - 2];
5798
2.75M
    SA[l] = s2 & SAINT_MAX;
5799
2.75M
    l -= s2 < 0;
5800
2.75M
    sa_sint_t s3 = SA[i - 3];
5801
2.75M
    SA[l] = s3 & SAINT_MAX;
5802
2.75M
    l -= s3 < 0;
5803
2.75M
  }
5804
5805
18.9k
  for (j -= 3; i >= j; i -= 1) {
5806
11.4k
    sa_sint_t s = SA[i];
5807
11.4k
    SA[l] = s & SAINT_MAX;
5808
11.4k
    l -= s < 0;
5809
11.4k
  }
5810
5811
7.48k
  l += 1;
5812
5813
7.48k
  return l;
5814
7.48k
}
5815
5816
static sa_sint_t libsais_renumber_lms_suffixes_8u_omp(
5817
    sa_sint_t* RESTRICT SA, sa_sint_t m, sa_sint_t threads,
5818
8.47k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5819
8.47k
  sa_sint_t name = 0;
5820
5821
#if defined(LIBSAIS_OPENMP)
5822
#pragma omp parallel num_threads(threads) if (threads > 1 && m >= 65536)
5823
#endif
5824
8.47k
  {
5825
#if defined(LIBSAIS_OPENMP)
5826
    fast_sint_t omp_thread_num = omp_get_thread_num();
5827
    fast_sint_t omp_num_threads = omp_get_num_threads();
5828
#else
5829
8.47k
    UNUSED(threads);
5830
8.47k
    UNUSED(thread_state);
5831
5832
8.47k
    fast_sint_t omp_thread_num = 0;
5833
8.47k
    fast_sint_t omp_num_threads = 1;
5834
8.47k
#endif
5835
8.47k
    fast_sint_t omp_block_stride = (m / omp_num_threads) & (-16);
5836
8.47k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
5837
8.47k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
5838
8.47k
                                     ? omp_block_stride
5839
8.47k
                                     : m - omp_block_start;
5840
5841
8.47k
    if (omp_num_threads == 1) {
5842
8.47k
      name = libsais_renumber_lms_suffixes_8u(SA, m, 0, omp_block_start,
5843
8.47k
                                              omp_block_size);
5844
8.47k
    }
5845
#if defined(LIBSAIS_OPENMP)
5846
    else {
5847
      {
5848
        thread_state[omp_thread_num].state.count =
5849
            libsais_count_negative_marked_suffixes(SA, omp_block_start,
5850
                                                   omp_block_size);
5851
      }
5852
5853
#pragma omp barrier
5854
5855
      {
5856
        fast_sint_t t, count = 0;
5857
        for (t = 0; t < omp_thread_num; ++t) {
5858
          count += thread_state[t].state.count;
5859
        }
5860
5861
        if (omp_thread_num == omp_num_threads - 1) {
5862
          name = (sa_sint_t)(count + thread_state[omp_thread_num].state.count);
5863
        }
5864
5865
        libsais_renumber_lms_suffixes_8u(SA, m, (sa_sint_t)count,
5866
                                         omp_block_start, omp_block_size);
5867
      }
5868
    }
5869
#endif
5870
8.47k
  }
5871
5872
8.47k
  return name;
5873
8.47k
}
5874
5875
static void libsais_gather_marked_lms_suffixes_omp(
5876
    sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t m, sa_sint_t fs,
5877
7.48k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5878
#if defined(LIBSAIS_OPENMP)
5879
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 131072)
5880
#endif
5881
7.48k
  {
5882
#if defined(LIBSAIS_OPENMP)
5883
    fast_sint_t omp_thread_num = omp_get_thread_num();
5884
    fast_sint_t omp_num_threads = omp_get_num_threads();
5885
#else
5886
7.48k
    UNUSED(threads);
5887
7.48k
    UNUSED(thread_state);
5888
5889
7.48k
    fast_sint_t omp_thread_num = 0;
5890
7.48k
    fast_sint_t omp_num_threads = 1;
5891
7.48k
#endif
5892
7.48k
    fast_sint_t omp_block_stride =
5893
7.48k
        (((fast_sint_t)n >> 1) / omp_num_threads) & (-16);
5894
7.48k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
5895
7.48k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
5896
7.48k
                                     ? omp_block_stride
5897
7.48k
                                     : ((fast_sint_t)n >> 1) - omp_block_start;
5898
5899
7.48k
    if (omp_num_threads == 1) {
5900
7.48k
      libsais_gather_marked_lms_suffixes(SA, m,
5901
7.48k
                                         (fast_sint_t)n + (fast_sint_t)fs,
5902
7.48k
                                         omp_block_start, omp_block_size);
5903
7.48k
    }
5904
#if defined(LIBSAIS_OPENMP)
5905
    else {
5906
      {
5907
        if (omp_thread_num < omp_num_threads - 1) {
5908
          thread_state[omp_thread_num].state.position =
5909
              libsais_gather_marked_lms_suffixes(
5910
                  SA, m, (fast_sint_t)m + omp_block_start + omp_block_size,
5911
                  omp_block_start, omp_block_size);
5912
          thread_state[omp_thread_num].state.count =
5913
              (fast_sint_t)m + omp_block_start + omp_block_size -
5914
              thread_state[omp_thread_num].state.position;
5915
        } else {
5916
          thread_state[omp_thread_num].state.position =
5917
              libsais_gather_marked_lms_suffixes(
5918
                  SA, m, (fast_sint_t)n + (fast_sint_t)fs, omp_block_start,
5919
                  omp_block_size);
5920
          thread_state[omp_thread_num].state.count =
5921
              (fast_sint_t)n + (fast_sint_t)fs -
5922
              thread_state[omp_thread_num].state.position;
5923
        }
5924
      }
5925
5926
#pragma omp barrier
5927
5928
#pragma omp master
5929
      {
5930
        fast_sint_t t, position = (fast_sint_t)n + (fast_sint_t)fs;
5931
5932
        for (t = omp_num_threads - 1; t >= 0; --t) {
5933
          position -= thread_state[t].state.count;
5934
          if (t != omp_num_threads - 1 && thread_state[t].state.count > 0) {
5935
            memmove(&SA[position], &SA[thread_state[t].state.position],
5936
                    (size_t)thread_state[t].state.count * sizeof(sa_sint_t));
5937
          }
5938
        }
5939
      }
5940
    }
5941
#endif
5942
7.48k
  }
5943
7.48k
}
5944
5945
static sa_sint_t libsais_renumber_and_gather_lms_suffixes_omp(
5946
    sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t m, sa_sint_t fs,
5947
8.47k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5948
8.47k
  memset(&SA[m], 0, ((size_t)n >> 1) * sizeof(sa_sint_t));
5949
5950
8.47k
  sa_sint_t name =
5951
8.47k
      libsais_renumber_lms_suffixes_8u_omp(SA, m, threads, thread_state);
5952
8.47k
  if (name < m) {
5953
7.48k
    libsais_gather_marked_lms_suffixes_omp(SA, n, m, fs, threads, thread_state);
5954
7.48k
  } else {
5955
997
    fast_sint_t i;
5956
8.58k
    for (i = 0; i < m; i += 1) {
5957
7.58k
      SA[i] &= SAINT_MAX;
5958
7.58k
    }
5959
997
  }
5960
5961
8.47k
  return name;
5962
8.47k
}
5963
5964
static sa_sint_t libsais_renumber_distinct_lms_suffixes_32s_4k(
5965
    sa_sint_t* RESTRICT SA, sa_sint_t m, sa_sint_t name,
5966
14.3k
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
5967
14.3k
  const fast_sint_t prefetch_distance = 64;
5968
5969
14.3k
  sa_sint_t* RESTRICT SAm = &SA[m];
5970
5971
14.3k
  fast_sint_t i, j;
5972
14.3k
  sa_sint_t p0, p1, p2, p3 = 0;
5973
14.3k
  for (i = omp_block_start,
5974
14.3k
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
5975
834k
       i < j; i += 4) {
5976
819k
    libsais_prefetchw(&SA[i + 2 * prefetch_distance]);
5977
5978
819k
    libsais_prefetchw(&SAm[(SA[i + prefetch_distance + 0] & SAINT_MAX) >> 1]);
5979
819k
    libsais_prefetchw(&SAm[(SA[i + prefetch_distance + 1] & SAINT_MAX) >> 1]);
5980
819k
    libsais_prefetchw(&SAm[(SA[i + prefetch_distance + 2] & SAINT_MAX) >> 1]);
5981
819k
    libsais_prefetchw(&SAm[(SA[i + prefetch_distance + 3] & SAINT_MAX) >> 1]);
5982
5983
819k
    p0 = SA[i + 0];
5984
819k
    SAm[(SA[i + 0] = p0 & SAINT_MAX) >> 1] = name | (p0 & p3 & SAINT_MIN);
5985
819k
    name += p0 < 0;
5986
819k
    p1 = SA[i + 1];
5987
819k
    SAm[(SA[i + 1] = p1 & SAINT_MAX) >> 1] = name | (p1 & p0 & SAINT_MIN);
5988
819k
    name += p1 < 0;
5989
819k
    p2 = SA[i + 2];
5990
819k
    SAm[(SA[i + 2] = p2 & SAINT_MAX) >> 1] = name | (p2 & p1 & SAINT_MIN);
5991
819k
    name += p2 < 0;
5992
819k
    p3 = SA[i + 3];
5993
819k
    SAm[(SA[i + 3] = p3 & SAINT_MAX) >> 1] = name | (p3 & p2 & SAINT_MIN);
5994
819k
    name += p3 < 0;
5995
819k
  }
5996
5997
443k
  for (j += prefetch_distance + 3; i < j; i += 1) {
5998
429k
    p2 = p3;
5999
429k
    p3 = SA[i];
6000
429k
    SAm[(SA[i] = p3 & SAINT_MAX) >> 1] = name | (p3 & p2 & SAINT_MIN);
6001
429k
    name += p3 < 0;
6002
429k
  }
6003
6004
14.3k
  return name;
6005
14.3k
}
6006
6007
static void libsais_mark_distinct_lms_suffixes_32s(sa_sint_t* RESTRICT SA,
6008
                                                   sa_sint_t m,
6009
                                                   fast_sint_t omp_block_start,
6010
9.61k
                                                   fast_sint_t omp_block_size) {
6011
9.61k
  const fast_sint_t prefetch_distance = 64;
6012
6013
9.61k
  fast_sint_t i, j;
6014
9.61k
  sa_sint_t p0, p1, p2, p3 = 0;
6015
9.61k
  for (i = (fast_sint_t)m + omp_block_start,
6016
9.61k
      j = (fast_sint_t)m + omp_block_start + omp_block_size - 3;
6017
1.45M
       i < j; i += 4) {
6018
1.44M
    libsais_prefetchw(&SA[i + prefetch_distance]);
6019
6020
1.44M
    p0 = SA[i + 0];
6021
1.44M
    SA[i + 0] = p0 & (p3 | SAINT_MAX);
6022
1.44M
    p0 = (p0 == 0) ? p3 : p0;
6023
1.44M
    p1 = SA[i + 1];
6024
1.44M
    SA[i + 1] = p1 & (p0 | SAINT_MAX);
6025
1.44M
    p1 = (p1 == 0) ? p0 : p1;
6026
1.44M
    p2 = SA[i + 2];
6027
1.44M
    SA[i + 2] = p2 & (p1 | SAINT_MAX);
6028
1.44M
    p2 = (p2 == 0) ? p1 : p2;
6029
1.44M
    p3 = SA[i + 3];
6030
1.44M
    SA[i + 3] = p3 & (p2 | SAINT_MAX);
6031
1.44M
    p3 = (p3 == 0) ? p2 : p3;
6032
1.44M
  }
6033
6034
24.4k
  for (j += 3; i < j; i += 1) {
6035
14.8k
    p2 = p3;
6036
14.8k
    p3 = SA[i];
6037
14.8k
    SA[i] = p3 & (p2 | SAINT_MAX);
6038
14.8k
    p3 = (p3 == 0) ? p2 : p3;
6039
14.8k
  }
6040
9.61k
}
6041
6042
static void libsais_clamp_lms_suffixes_length_32s(sa_sint_t* RESTRICT SA,
6043
                                                  sa_sint_t m,
6044
                                                  fast_sint_t omp_block_start,
6045
842
                                                  fast_sint_t omp_block_size) {
6046
842
  const fast_sint_t prefetch_distance = 64;
6047
6048
842
  sa_sint_t* RESTRICT SAm = &SA[m];
6049
6050
842
  fast_sint_t i, j;
6051
206k
  for (i = omp_block_start, j = omp_block_start + omp_block_size - 3; i < j;
6052
205k
       i += 4) {
6053
205k
    libsais_prefetchw(&SAm[i + prefetch_distance]);
6054
6055
205k
    SAm[i + 0] = (SAm[i + 0] < 0 ? SAm[i + 0] : 0) & SAINT_MAX;
6056
205k
    SAm[i + 1] = (SAm[i + 1] < 0 ? SAm[i + 1] : 0) & SAINT_MAX;
6057
205k
    SAm[i + 2] = (SAm[i + 2] < 0 ? SAm[i + 2] : 0) & SAINT_MAX;
6058
205k
    SAm[i + 3] = (SAm[i + 3] < 0 ? SAm[i + 3] : 0) & SAINT_MAX;
6059
205k
  }
6060
6061
2.06k
  for (j += 3; i < j; i += 1) {
6062
1.21k
    SAm[i] = (SAm[i] < 0 ? SAm[i] : 0) & SAINT_MAX;
6063
1.21k
  }
6064
842
}
6065
6066
static sa_sint_t libsais_renumber_distinct_lms_suffixes_32s_4k_omp(
6067
    sa_sint_t* RESTRICT SA, sa_sint_t m, sa_sint_t threads,
6068
14.3k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
6069
14.3k
  sa_sint_t name = 0;
6070
6071
#if defined(LIBSAIS_OPENMP)
6072
#pragma omp parallel num_threads(threads) if (threads > 1 && m >= 65536)
6073
#endif
6074
14.3k
  {
6075
#if defined(LIBSAIS_OPENMP)
6076
    fast_sint_t omp_thread_num = omp_get_thread_num();
6077
    fast_sint_t omp_num_threads = omp_get_num_threads();
6078
#else
6079
14.3k
    UNUSED(threads);
6080
14.3k
    UNUSED(thread_state);
6081
6082
14.3k
    fast_sint_t omp_thread_num = 0;
6083
14.3k
    fast_sint_t omp_num_threads = 1;
6084
14.3k
#endif
6085
14.3k
    fast_sint_t omp_block_stride = (m / omp_num_threads) & (-16);
6086
14.3k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
6087
14.3k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
6088
14.3k
                                     ? omp_block_stride
6089
14.3k
                                     : m - omp_block_start;
6090
6091
14.3k
    if (omp_num_threads == 1) {
6092
14.3k
      name = libsais_renumber_distinct_lms_suffixes_32s_4k(
6093
14.3k
          SA, m, 1, omp_block_start, omp_block_size);
6094
14.3k
    }
6095
#if defined(LIBSAIS_OPENMP)
6096
    else {
6097
      {
6098
        thread_state[omp_thread_num].state.count =
6099
            libsais_count_negative_marked_suffixes(SA, omp_block_start,
6100
                                                   omp_block_size);
6101
      }
6102
6103
#pragma omp barrier
6104
6105
      {
6106
        fast_sint_t t, count = 1;
6107
        for (t = 0; t < omp_thread_num; ++t) {
6108
          count += thread_state[t].state.count;
6109
        }
6110
6111
        if (omp_thread_num == omp_num_threads - 1) {
6112
          name = (sa_sint_t)(count + thread_state[omp_thread_num].state.count);
6113
        }
6114
6115
        libsais_renumber_distinct_lms_suffixes_32s_4k(
6116
            SA, m, (sa_sint_t)count, omp_block_start, omp_block_size);
6117
      }
6118
    }
6119
#endif
6120
14.3k
  }
6121
6122
14.3k
  return name - 1;
6123
14.3k
}
6124
6125
static void libsais_mark_distinct_lms_suffixes_32s_omp(sa_sint_t* RESTRICT SA,
6126
                                                       sa_sint_t n, sa_sint_t m,
6127
9.61k
                                                       sa_sint_t threads) {
6128
#if defined(LIBSAIS_OPENMP)
6129
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 131072)
6130
#endif
6131
9.61k
  {
6132
#if defined(LIBSAIS_OPENMP)
6133
    fast_sint_t omp_thread_num = omp_get_thread_num();
6134
    fast_sint_t omp_num_threads = omp_get_num_threads();
6135
    fast_sint_t omp_block_stride =
6136
        (((fast_sint_t)n >> 1) / omp_num_threads) & (-16);
6137
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
6138
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
6139
                                     ? omp_block_stride
6140
                                     : ((fast_sint_t)n >> 1) - omp_block_start;
6141
#else
6142
9.61k
    UNUSED(threads);
6143
6144
9.61k
    fast_sint_t omp_block_start = 0;
6145
9.61k
    fast_sint_t omp_block_size = (fast_sint_t)n >> 1;
6146
9.61k
#endif
6147
9.61k
    libsais_mark_distinct_lms_suffixes_32s(SA, m, omp_block_start,
6148
9.61k
                                           omp_block_size);
6149
9.61k
  }
6150
9.61k
}
6151
6152
static void libsais_clamp_lms_suffixes_length_32s_omp(sa_sint_t* RESTRICT SA,
6153
                                                      sa_sint_t n, sa_sint_t m,
6154
842
                                                      sa_sint_t threads) {
6155
#if defined(LIBSAIS_OPENMP)
6156
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 131072)
6157
#endif
6158
842
  {
6159
#if defined(LIBSAIS_OPENMP)
6160
    fast_sint_t omp_thread_num = omp_get_thread_num();
6161
    fast_sint_t omp_num_threads = omp_get_num_threads();
6162
    fast_sint_t omp_block_stride =
6163
        (((fast_sint_t)n >> 1) / omp_num_threads) & (-16);
6164
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
6165
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
6166
                                     ? omp_block_stride
6167
                                     : ((fast_sint_t)n >> 1) - omp_block_start;
6168
#else
6169
842
    UNUSED(threads);
6170
6171
842
    fast_sint_t omp_block_start = 0;
6172
842
    fast_sint_t omp_block_size = (fast_sint_t)n >> 1;
6173
842
#endif
6174
842
    libsais_clamp_lms_suffixes_length_32s(SA, m, omp_block_start,
6175
842
                                          omp_block_size);
6176
842
  }
6177
842
}
6178
6179
static sa_sint_t libsais_renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
6180
    sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t m, sa_sint_t threads,
6181
14.3k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
6182
14.3k
  memset(&SA[m], 0, ((size_t)n >> 1) * sizeof(sa_sint_t));
6183
6184
14.3k
  sa_sint_t name = libsais_renumber_distinct_lms_suffixes_32s_4k_omp(
6185
14.3k
      SA, m, threads, thread_state);
6186
14.3k
  if (name < m) {
6187
8.91k
    libsais_mark_distinct_lms_suffixes_32s_omp(SA, n, m, threads);
6188
8.91k
  }
6189
6190
14.3k
  return name;
6191
14.3k
}
6192
6193
static sa_sint_t libsais_renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(
6194
    sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t m,
6195
842
    sa_sint_t threads) {
6196
842
  const fast_sint_t prefetch_distance = 64;
6197
6198
842
  sa_sint_t* RESTRICT SAm = &SA[m];
6199
6200
842
  {
6201
842
    libsais_gather_lms_suffixes_32s(T, SA, n);
6202
6203
842
    memset(&SA[m], 0, ((size_t)n - (size_t)m - (size_t)m) * sizeof(sa_sint_t));
6204
6205
842
    fast_sint_t i, j;
6206
842
    for (i = (fast_sint_t)n - (fast_sint_t)m,
6207
842
        j = (fast_sint_t)n - 1 - prefetch_distance - 3;
6208
122k
         i < j; i += 4) {
6209
121k
      libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
6210
6211
121k
      libsais_prefetchw(&SAm[((sa_uint_t)SA[i + prefetch_distance + 0]) >> 1]);
6212
121k
      libsais_prefetchw(&SAm[((sa_uint_t)SA[i + prefetch_distance + 1]) >> 1]);
6213
121k
      libsais_prefetchw(&SAm[((sa_uint_t)SA[i + prefetch_distance + 2]) >> 1]);
6214
121k
      libsais_prefetchw(&SAm[((sa_uint_t)SA[i + prefetch_distance + 3]) >> 1]);
6215
6216
121k
      SAm[((sa_uint_t)SA[i + 0]) >> 1] = SA[i + 1] - SA[i + 0] + 1 + SAINT_MIN;
6217
121k
      SAm[((sa_uint_t)SA[i + 1]) >> 1] = SA[i + 2] - SA[i + 1] + 1 + SAINT_MIN;
6218
121k
      SAm[((sa_uint_t)SA[i + 2]) >> 1] = SA[i + 3] - SA[i + 2] + 1 + SAINT_MIN;
6219
121k
      SAm[((sa_uint_t)SA[i + 3]) >> 1] = SA[i + 4] - SA[i + 3] + 1 + SAINT_MIN;
6220
121k
    }
6221
6222
56.0k
    for (j += prefetch_distance + 3; i < j; i += 1) {
6223
55.1k
      SAm[((sa_uint_t)SA[i]) >> 1] = SA[i + 1] - SA[i] + 1 + SAINT_MIN;
6224
55.1k
    }
6225
6226
842
    SAm[((sa_uint_t)SA[n - 1]) >> 1] = 1 + SAINT_MIN;
6227
842
  }
6228
6229
842
  {
6230
842
    libsais_clamp_lms_suffixes_length_32s_omp(SA, n, m, threads);
6231
842
  }
6232
6233
842
  sa_sint_t name = 1;
6234
6235
842
  {
6236
842
    fast_sint_t i, j, p = SA[0], plen = SAm[p >> 1];
6237
842
    sa_sint_t pdiff = SAINT_MIN;
6238
243k
    for (i = 1, j = m - prefetch_distance - 1; i < j; i += 2) {
6239
242k
      libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
6240
6241
242k
      libsais_prefetchw(&SAm[((sa_uint_t)SA[i + prefetch_distance + 0]) >> 1]);
6242
242k
      libsais_prefetchr(&T[((sa_uint_t)SA[i + prefetch_distance + 0])]);
6243
242k
      libsais_prefetchw(&SAm[((sa_uint_t)SA[i + prefetch_distance + 1]) >> 1]);
6244
242k
      libsais_prefetchr(&T[((sa_uint_t)SA[i + prefetch_distance + 1])]);
6245
6246
242k
      fast_sint_t q = SA[i + 0], qlen = SAm[q >> 1];
6247
242k
      sa_sint_t qdiff = SAINT_MIN;
6248
242k
      if (plen == qlen) {
6249
140k
        fast_sint_t l = 0;
6250
408k
        do {
6251
408k
          if (T[p + l] != T[q + l]) {
6252
41.8k
            break;
6253
41.8k
          }
6254
408k
        } while (++l < qlen);
6255
140k
        qdiff = (sa_sint_t)(l - qlen) & SAINT_MIN;
6256
140k
      }
6257
242k
      SAm[p >> 1] = name | (pdiff & qdiff);
6258
242k
      name += (qdiff < 0);
6259
6260
242k
      p = SA[i + 1];
6261
242k
      plen = SAm[p >> 1];
6262
242k
      pdiff = SAINT_MIN;
6263
242k
      if (qlen == plen) {
6264
140k
        fast_sint_t l = 0;
6265
407k
        do {
6266
407k
          if (T[q + l] != T[p + l]) {
6267
41.8k
            break;
6268
41.8k
          }
6269
407k
        } while (++l < plen);
6270
140k
        pdiff = (sa_sint_t)(l - plen) & SAINT_MIN;
6271
140k
      }
6272
242k
      SAm[q >> 1] = name | (qdiff & pdiff);
6273
242k
      name += (pdiff < 0);
6274
242k
    }
6275
6276
55.1k
    for (j += prefetch_distance + 1; i < j; i += 1) {
6277
54.3k
      fast_sint_t q = SA[i], qlen = SAm[q >> 1];
6278
54.3k
      sa_sint_t qdiff = SAINT_MIN;
6279
54.3k
      if (plen == qlen) {
6280
32.8k
        fast_sint_t l = 0;
6281
100k
        do {
6282
100k
          if (T[p + l] != T[q + l]) {
6283
9.30k
            break;
6284
9.30k
          }
6285
100k
        } while (++l < plen);
6286
32.8k
        qdiff = (sa_sint_t)(l - plen) & SAINT_MIN;
6287
32.8k
      }
6288
54.3k
      SAm[p >> 1] = name | (pdiff & qdiff);
6289
54.3k
      name += (qdiff < 0);
6290
6291
54.3k
      p = q;
6292
54.3k
      plen = qlen;
6293
54.3k
      pdiff = qdiff;
6294
54.3k
    }
6295
6296
842
    SAm[p >> 1] = name | pdiff;
6297
842
    name++;
6298
842
  }
6299
6300
842
  if (name <= m) {
6301
695
    libsais_mark_distinct_lms_suffixes_32s_omp(SA, n, m, threads);
6302
695
  }
6303
6304
842
  return name - 1;
6305
842
}
6306
6307
static void libsais_reconstruct_lms_suffixes(sa_sint_t* RESTRICT SA,
6308
                                             sa_sint_t n, sa_sint_t m,
6309
                                             fast_sint_t omp_block_start,
6310
17.0k
                                             fast_sint_t omp_block_size) {
6311
17.0k
  const fast_sint_t prefetch_distance = 64;
6312
6313
17.0k
  const sa_sint_t* RESTRICT SAnm = &SA[n - m];
6314
6315
17.0k
  fast_sint_t i, j;
6316
17.0k
  for (i = omp_block_start,
6317
17.0k
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
6318
2.86M
       i < j; i += 4) {
6319
2.85M
    libsais_prefetchw(&SA[i + 2 * prefetch_distance]);
6320
6321
2.85M
    libsais_prefetchr(&SAnm[SA[i + prefetch_distance + 0]]);
6322
2.85M
    libsais_prefetchr(&SAnm[SA[i + prefetch_distance + 1]]);
6323
2.85M
    libsais_prefetchr(&SAnm[SA[i + prefetch_distance + 2]]);
6324
2.85M
    libsais_prefetchr(&SAnm[SA[i + prefetch_distance + 3]]);
6325
6326
2.85M
    SA[i + 0] = SAnm[SA[i + 0]];
6327
2.85M
    SA[i + 1] = SAnm[SA[i + 1]];
6328
2.85M
    SA[i + 2] = SAnm[SA[i + 2]];
6329
2.85M
    SA[i + 3] = SAnm[SA[i + 3]];
6330
2.85M
  }
6331
6332
745k
  for (j += prefetch_distance + 3; i < j; i += 1) {
6333
728k
    SA[i] = SAnm[SA[i]];
6334
728k
  }
6335
17.0k
}
6336
6337
static void libsais_reconstruct_lms_suffixes_omp(sa_sint_t* RESTRICT SA,
6338
                                                 sa_sint_t n, sa_sint_t m,
6339
17.0k
                                                 sa_sint_t threads) {
6340
#if defined(LIBSAIS_OPENMP)
6341
#pragma omp parallel num_threads(threads) if (threads > 1 && m >= 65536)
6342
#endif
6343
17.0k
  {
6344
#if defined(LIBSAIS_OPENMP)
6345
    fast_sint_t omp_thread_num = omp_get_thread_num();
6346
    fast_sint_t omp_num_threads = omp_get_num_threads();
6347
    fast_sint_t omp_block_stride = (m / omp_num_threads) & (-16);
6348
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
6349
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
6350
                                     ? omp_block_stride
6351
                                     : m - omp_block_start;
6352
#else
6353
17.0k
    UNUSED(threads);
6354
6355
17.0k
    fast_sint_t omp_block_start = 0;
6356
17.0k
    fast_sint_t omp_block_size = m;
6357
17.0k
#endif
6358
6359
17.0k
    libsais_reconstruct_lms_suffixes(SA, n, m, omp_block_start, omp_block_size);
6360
17.0k
  }
6361
17.0k
}
6362
6363
static void libsais_place_lms_suffixes_interval_8u(
6364
    sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t m, sa_sint_t flags,
6365
8.47k
    sa_sint_t* RESTRICT buckets) {
6366
8.47k
  if (flags & LIBSAIS_FLAGS_GSA) {
6367
0
    buckets[7 * ALPHABET_SIZE]--;
6368
0
  }
6369
6370
8.47k
  {
6371
8.47k
    const sa_sint_t* RESTRICT bucket_end = &buckets[7 * ALPHABET_SIZE];
6372
6373
8.47k
    fast_sint_t c, j = n;
6374
2.17M
    for (c = ALPHABET_SIZE - 2; c >= 0; --c) {
6375
2.16M
      fast_sint_t l =
6376
2.16M
          (fast_sint_t)buckets[BUCKETS_INDEX2(c, 1) + BUCKETS_INDEX2(1, 0)] -
6377
2.16M
          (fast_sint_t)buckets[BUCKETS_INDEX2(c, 1)];
6378
2.16M
      if (l > 0) {
6379
176k
        fast_sint_t i = bucket_end[c];
6380
176k
        if (j - i > 0) {
6381
103k
          memset(&SA[i], 0, (size_t)(j - i) * sizeof(sa_sint_t));
6382
103k
        }
6383
6384
176k
        memmove(&SA[j = (i - l)], &SA[m -= (sa_sint_t)l],
6385
176k
                (size_t)l * sizeof(sa_sint_t));
6386
176k
      }
6387
2.16M
    }
6388
6389
8.47k
    memset(&SA[0], 0, (size_t)j * sizeof(sa_sint_t));
6390
8.47k
  }
6391
6392
8.47k
  if (flags & LIBSAIS_FLAGS_GSA) {
6393
0
    buckets[7 * ALPHABET_SIZE]++;
6394
0
  }
6395
8.47k
}
6396
6397
static void libsais_place_lms_suffixes_interval_32s_4k(
6398
    sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t k, sa_sint_t m,
6399
355
    const sa_sint_t* RESTRICT buckets) {
6400
355
  const sa_sint_t* RESTRICT bucket_end = &buckets[3 * (fast_sint_t)k];
6401
6402
355
  fast_sint_t c, j = n;
6403
152k
  for (c = (fast_sint_t)k - 2; c >= 0; --c) {
6404
151k
    fast_sint_t l =
6405
151k
        (fast_sint_t)buckets[BUCKETS_INDEX2(c, 1) + BUCKETS_INDEX2(1, 0)] -
6406
151k
        (fast_sint_t)buckets[BUCKETS_INDEX2(c, 1)];
6407
151k
    if (l > 0) {
6408
54.5k
      fast_sint_t i = bucket_end[c];
6409
54.5k
      if (j - i > 0) {
6410
25.1k
        memset(&SA[i], 0, (size_t)(j - i) * sizeof(sa_sint_t));
6411
25.1k
      }
6412
6413
54.5k
      memmove(&SA[j = (i - l)], &SA[m -= (sa_sint_t)l],
6414
54.5k
              (size_t)l * sizeof(sa_sint_t));
6415
54.5k
    }
6416
151k
  }
6417
6418
355
  memset(&SA[0], 0, (size_t)j * sizeof(sa_sint_t));
6419
355
}
6420
6421
static void libsais_place_lms_suffixes_interval_32s_2k(
6422
    sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t k, sa_sint_t m,
6423
664
    const sa_sint_t* RESTRICT buckets) {
6424
664
  fast_sint_t j = n;
6425
6426
664
  if (k > 1) {
6427
664
    fast_sint_t c;
6428
531k
    for (c = BUCKETS_INDEX2((fast_sint_t)k - 2, 0); c >= BUCKETS_INDEX2(0, 0);
6429
530k
         c -= BUCKETS_INDEX2(1, 0)) {
6430
530k
      fast_sint_t l = (fast_sint_t)buckets[c + BUCKETS_INDEX2(1, 1)] -
6431
530k
                      (fast_sint_t)buckets[c + BUCKETS_INDEX2(0, 1)];
6432
530k
      if (l > 0) {
6433
189k
        fast_sint_t i = buckets[c];
6434
189k
        if (j - i > 0) {
6435
87.4k
          memset(&SA[i], 0, (size_t)(j - i) * sizeof(sa_sint_t));
6436
87.4k
        }
6437
6438
189k
        memmove(&SA[j = (i - l)], &SA[m -= (sa_sint_t)l],
6439
189k
                (size_t)l * sizeof(sa_sint_t));
6440
189k
      }
6441
530k
    }
6442
664
  }
6443
6444
664
  memset(&SA[0], 0, (size_t)j * sizeof(sa_sint_t));
6445
664
}
6446
6447
static void libsais_place_lms_suffixes_interval_32s_1k(
6448
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
6449
178
    sa_sint_t m, sa_sint_t* RESTRICT buckets) {
6450
178
  const fast_sint_t prefetch_distance = 64;
6451
6452
178
  sa_sint_t c = k - 1;
6453
178
  fast_sint_t i, l = buckets[c];
6454
24.6k
  for (i = (fast_sint_t)m - 1; i >= prefetch_distance + 3; i -= 4) {
6455
24.4k
    libsais_prefetchr(&SA[i - 2 * prefetch_distance]);
6456
6457
24.4k
    libsais_prefetchr(&T[SA[i - prefetch_distance - 0]]);
6458
24.4k
    libsais_prefetchr(&T[SA[i - prefetch_distance - 1]]);
6459
24.4k
    libsais_prefetchr(&T[SA[i - prefetch_distance - 2]]);
6460
24.4k
    libsais_prefetchr(&T[SA[i - prefetch_distance - 3]]);
6461
6462
24.4k
    sa_sint_t p0 = SA[i - 0];
6463
24.4k
    if (T[p0] != c) {
6464
14.1k
      c = T[p0];
6465
14.1k
      memset(&SA[buckets[c]], 0, (size_t)(l - buckets[c]) * sizeof(sa_sint_t));
6466
14.1k
      l = buckets[c];
6467
14.1k
    }
6468
24.4k
    SA[--l] = p0;
6469
24.4k
    sa_sint_t p1 = SA[i - 1];
6470
24.4k
    if (T[p1] != c) {
6471
14.0k
      c = T[p1];
6472
14.0k
      memset(&SA[buckets[c]], 0, (size_t)(l - buckets[c]) * sizeof(sa_sint_t));
6473
14.0k
      l = buckets[c];
6474
14.0k
    }
6475
24.4k
    SA[--l] = p1;
6476
24.4k
    sa_sint_t p2 = SA[i - 2];
6477
24.4k
    if (T[p2] != c) {
6478
14.2k
      c = T[p2];
6479
14.2k
      memset(&SA[buckets[c]], 0, (size_t)(l - buckets[c]) * sizeof(sa_sint_t));
6480
14.2k
      l = buckets[c];
6481
14.2k
    }
6482
24.4k
    SA[--l] = p2;
6483
24.4k
    sa_sint_t p3 = SA[i - 3];
6484
24.4k
    if (T[p3] != c) {
6485
14.1k
      c = T[p3];
6486
14.1k
      memset(&SA[buckets[c]], 0, (size_t)(l - buckets[c]) * sizeof(sa_sint_t));
6487
14.1k
      l = buckets[c];
6488
14.1k
    }
6489
24.4k
    SA[--l] = p3;
6490
24.4k
  }
6491
6492
11.8k
  for (; i >= 0; i -= 1) {
6493
11.6k
    sa_sint_t p = SA[i];
6494
11.6k
    if (T[p] != c) {
6495
10.2k
      c = T[p];
6496
10.2k
      memset(&SA[buckets[c]], 0, (size_t)(l - buckets[c]) * sizeof(sa_sint_t));
6497
10.2k
      l = buckets[c];
6498
10.2k
    }
6499
11.6k
    SA[--l] = p;
6500
11.6k
  }
6501
6502
178
  memset(&SA[0], 0, (size_t)l * sizeof(sa_sint_t));
6503
178
}
6504
6505
static void libsais_place_lms_suffixes_histogram_32s_6k(
6506
    sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t k, sa_sint_t m,
6507
1.94k
    const sa_sint_t* RESTRICT buckets) {
6508
1.94k
  const sa_sint_t* RESTRICT bucket_end = &buckets[5 * (fast_sint_t)k];
6509
6510
1.94k
  fast_sint_t c, j = n;
6511
6.86k
  for (c = (fast_sint_t)k - 2; c >= 0; --c) {
6512
4.91k
    fast_sint_t l = (fast_sint_t)buckets[BUCKETS_INDEX4(c, 1)];
6513
4.91k
    if (l > 0) {
6514
1.27k
      fast_sint_t i = bucket_end[c];
6515
1.27k
      if (j - i > 0) {
6516
1.27k
        memset(&SA[i], 0, (size_t)(j - i) * sizeof(sa_sint_t));
6517
1.27k
      }
6518
6519
1.27k
      memmove(&SA[j = (i - l)], &SA[m -= (sa_sint_t)l],
6520
1.27k
              (size_t)l * sizeof(sa_sint_t));
6521
1.27k
    }
6522
4.91k
  }
6523
6524
1.94k
  memset(&SA[0], 0, (size_t)j * sizeof(sa_sint_t));
6525
1.94k
}
6526
6527
static void libsais_place_lms_suffixes_histogram_32s_4k(
6528
    sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t k, sa_sint_t m,
6529
14.3k
    const sa_sint_t* RESTRICT buckets) {
6530
14.3k
  const sa_sint_t* RESTRICT bucket_end = &buckets[3 * (fast_sint_t)k];
6531
6532
14.3k
  fast_sint_t c, j = n;
6533
703k
  for (c = (fast_sint_t)k - 2; c >= 0; --c) {
6534
688k
    fast_sint_t l = (fast_sint_t)buckets[BUCKETS_INDEX2(c, 1)];
6535
688k
    if (l > 0) {
6536
251k
      fast_sint_t i = bucket_end[c];
6537
251k
      if (j - i > 0) {
6538
118k
        memset(&SA[i], 0, (size_t)(j - i) * sizeof(sa_sint_t));
6539
118k
      }
6540
6541
251k
      memmove(&SA[j = (i - l)], &SA[m -= (sa_sint_t)l],
6542
251k
              (size_t)l * sizeof(sa_sint_t));
6543
251k
    }
6544
688k
  }
6545
6546
14.3k
  memset(&SA[0], 0, (size_t)j * sizeof(sa_sint_t));
6547
14.3k
}
6548
6549
static void libsais_place_lms_suffixes_histogram_32s_2k(
6550
    sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t k, sa_sint_t m,
6551
664
    const sa_sint_t* RESTRICT buckets) {
6552
664
  fast_sint_t j = n;
6553
6554
664
  if (k > 1) {
6555
664
    fast_sint_t c;
6556
531k
    for (c = BUCKETS_INDEX2((fast_sint_t)k - 2, 0); c >= BUCKETS_INDEX2(0, 0);
6557
530k
         c -= BUCKETS_INDEX2(1, 0)) {
6558
530k
      fast_sint_t l = (fast_sint_t)buckets[c + BUCKETS_INDEX2(0, 1)];
6559
530k
      if (l > 0) {
6560
190k
        fast_sint_t i = buckets[c];
6561
190k
        if (j - i > 0) {
6562
87.5k
          memset(&SA[i], 0, (size_t)(j - i) * sizeof(sa_sint_t));
6563
87.5k
        }
6564
6565
190k
        memmove(&SA[j = (i - l)], &SA[m -= (sa_sint_t)l],
6566
190k
                (size_t)l * sizeof(sa_sint_t));
6567
190k
      }
6568
530k
    }
6569
664
  }
6570
6571
664
  memset(&SA[0], 0, (size_t)j * sizeof(sa_sint_t));
6572
664
}
6573
6574
static void libsais_final_bwt_scan_left_to_right_8u(
6575
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA,
6576
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t omp_block_start,
6577
0
    fast_sint_t omp_block_size) {
6578
0
  const fast_sint_t prefetch_distance = 64;
6579
6580
0
  fast_sint_t i, j;
6581
0
  for (i = omp_block_start,
6582
0
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
6583
0
       i < j; i += 2) {
6584
0
    libsais_prefetchw(&SA[i + 2 * prefetch_distance]);
6585
6586
0
    sa_sint_t s0 = SA[i + prefetch_distance + 0];
6587
0
    const uint8_t* Ts0 = &T[s0 > 0 ? s0 : 2];
6588
0
    libsais_prefetchr(Ts0 - 1);
6589
0
    libsais_prefetchr(Ts0 - 2);
6590
0
    sa_sint_t s1 = SA[i + prefetch_distance + 1];
6591
0
    const uint8_t* Ts1 = &T[s1 > 0 ? s1 : 2];
6592
0
    libsais_prefetchr(Ts1 - 1);
6593
0
    libsais_prefetchr(Ts1 - 2);
6594
6595
0
    sa_sint_t p0 = SA[i + 0];
6596
0
    SA[i + 0] = p0 & SAINT_MAX;
6597
0
    if (p0 > 0) {
6598
0
      p0--;
6599
0
      SA[i + 0] = T[p0] | SAINT_MIN;
6600
0
      SA[induction_bucket[T[p0]]++] =
6601
0
          p0 |
6602
0
          (sa_sint_t)((sa_uint_t)(T[p0 - (p0 > 0)] < T[p0]) << (SAINT_BIT - 1));
6603
0
    }
6604
0
    sa_sint_t p1 = SA[i + 1];
6605
0
    SA[i + 1] = p1 & SAINT_MAX;
6606
0
    if (p1 > 0) {
6607
0
      p1--;
6608
0
      SA[i + 1] = T[p1] | SAINT_MIN;
6609
0
      SA[induction_bucket[T[p1]]++] =
6610
0
          p1 |
6611
0
          (sa_sint_t)((sa_uint_t)(T[p1 - (p1 > 0)] < T[p1]) << (SAINT_BIT - 1));
6612
0
    }
6613
0
  }
6614
6615
0
  for (j += prefetch_distance + 1; i < j; i += 1) {
6616
0
    sa_sint_t p = SA[i];
6617
0
    SA[i] = p & SAINT_MAX;
6618
0
    if (p > 0) {
6619
0
      p--;
6620
0
      SA[i] = T[p] | SAINT_MIN;
6621
0
      SA[induction_bucket[T[p]]++] =
6622
0
          p |
6623
0
          (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] < T[p]) << (SAINT_BIT - 1));
6624
0
    }
6625
0
  }
6626
0
}
6627
6628
static void libsais_final_bwt_aux_scan_left_to_right_8u(
6629
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t rm,
6630
    sa_sint_t* RESTRICT I, sa_sint_t* RESTRICT induction_bucket,
6631
0
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
6632
0
  const fast_sint_t prefetch_distance = 64;
6633
6634
0
  fast_sint_t i, j;
6635
0
  for (i = omp_block_start,
6636
0
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
6637
0
       i < j; i += 2) {
6638
0
    libsais_prefetchw(&SA[i + 2 * prefetch_distance]);
6639
6640
0
    sa_sint_t s0 = SA[i + prefetch_distance + 0];
6641
0
    const uint8_t* Ts0 = &T[s0 > 0 ? s0 : 2];
6642
0
    libsais_prefetchr(Ts0 - 1);
6643
0
    libsais_prefetchr(Ts0 - 2);
6644
0
    sa_sint_t s1 = SA[i + prefetch_distance + 1];
6645
0
    const uint8_t* Ts1 = &T[s1 > 0 ? s1 : 2];
6646
0
    libsais_prefetchr(Ts1 - 1);
6647
0
    libsais_prefetchr(Ts1 - 2);
6648
6649
0
    sa_sint_t p0 = SA[i + 0];
6650
0
    SA[i + 0] = p0 & SAINT_MAX;
6651
0
    if (p0 > 0) {
6652
0
      p0--;
6653
0
      SA[i + 0] = T[p0] | SAINT_MIN;
6654
0
      SA[induction_bucket[T[p0]]++] =
6655
0
          p0 |
6656
0
          (sa_sint_t)((sa_uint_t)(T[p0 - (p0 > 0)] < T[p0]) << (SAINT_BIT - 1));
6657
0
      if ((p0 & rm) == 0) {
6658
0
        I[p0 / (rm + 1)] = induction_bucket[T[p0]];
6659
0
      }
6660
0
    }
6661
0
    sa_sint_t p1 = SA[i + 1];
6662
0
    SA[i + 1] = p1 & SAINT_MAX;
6663
0
    if (p1 > 0) {
6664
0
      p1--;
6665
0
      SA[i + 1] = T[p1] | SAINT_MIN;
6666
0
      SA[induction_bucket[T[p1]]++] =
6667
0
          p1 |
6668
0
          (sa_sint_t)((sa_uint_t)(T[p1 - (p1 > 0)] < T[p1]) << (SAINT_BIT - 1));
6669
0
      if ((p1 & rm) == 0) {
6670
0
        I[p1 / (rm + 1)] = induction_bucket[T[p1]];
6671
0
      }
6672
0
    }
6673
0
  }
6674
6675
0
  for (j += prefetch_distance + 1; i < j; i += 1) {
6676
0
    sa_sint_t p = SA[i];
6677
0
    SA[i] = p & SAINT_MAX;
6678
0
    if (p > 0) {
6679
0
      p--;
6680
0
      SA[i] = T[p] | SAINT_MIN;
6681
0
      SA[induction_bucket[T[p]]++] =
6682
0
          p |
6683
0
          (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] < T[p]) << (SAINT_BIT - 1));
6684
0
      if ((p & rm) == 0) {
6685
0
        I[p / (rm + 1)] = induction_bucket[T[p]];
6686
0
      }
6687
0
    }
6688
0
  }
6689
0
}
6690
6691
static void libsais_final_sorting_scan_left_to_right_8u(
6692
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA,
6693
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t omp_block_start,
6694
8.54k
    fast_sint_t omp_block_size) {
6695
8.54k
  const fast_sint_t prefetch_distance = 64;
6696
6697
8.54k
  fast_sint_t i, j;
6698
8.54k
  for (i = omp_block_start,
6699
8.54k
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
6700
10.8M
       i < j; i += 2) {
6701
10.8M
    libsais_prefetchw(&SA[i + 2 * prefetch_distance]);
6702
6703
10.8M
    sa_sint_t s0 = SA[i + prefetch_distance + 0];
6704
10.8M
    const uint8_t* Ts0 = &T[s0 > 0 ? s0 : 2];
6705
10.8M
    libsais_prefetchr(Ts0 - 1);
6706
10.8M
    libsais_prefetchr(Ts0 - 2);
6707
10.8M
    sa_sint_t s1 = SA[i + prefetch_distance + 1];
6708
10.8M
    const uint8_t* Ts1 = &T[s1 > 0 ? s1 : 2];
6709
10.8M
    libsais_prefetchr(Ts1 - 1);
6710
10.8M
    libsais_prefetchr(Ts1 - 2);
6711
6712
10.8M
    sa_sint_t p0 = SA[i + 0];
6713
10.8M
    SA[i + 0] = p0 ^ SAINT_MIN;
6714
10.8M
    if (p0 > 0) {
6715
6.24M
      p0--;
6716
6.24M
      SA[induction_bucket[T[p0]]++] =
6717
6.24M
          p0 |
6718
6.24M
          (sa_sint_t)((sa_uint_t)(T[p0 - (p0 > 0)] < T[p0]) << (SAINT_BIT - 1));
6719
6.24M
    }
6720
10.8M
    sa_sint_t p1 = SA[i + 1];
6721
10.8M
    SA[i + 1] = p1 ^ SAINT_MIN;
6722
10.8M
    if (p1 > 0) {
6723
6.24M
      p1--;
6724
6.24M
      SA[induction_bucket[T[p1]]++] =
6725
6.24M
          p1 |
6726
6.24M
          (sa_sint_t)((sa_uint_t)(T[p1 - (p1 > 0)] < T[p1]) << (SAINT_BIT - 1));
6727
6.24M
    }
6728
10.8M
  }
6729
6730
494k
  for (j += prefetch_distance + 1; i < j; i += 1) {
6731
485k
    sa_sint_t p = SA[i];
6732
485k
    SA[i] = p ^ SAINT_MIN;
6733
485k
    if (p > 0) {
6734
103k
      p--;
6735
103k
      SA[induction_bucket[T[p]]++] =
6736
103k
          p |
6737
103k
          (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] < T[p]) << (SAINT_BIT - 1));
6738
103k
    }
6739
485k
  }
6740
8.54k
}
6741
6742
static void libsais_final_sorting_scan_left_to_right_32s(
6743
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
6744
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t omp_block_start,
6745
17.0k
    fast_sint_t omp_block_size) {
6746
17.0k
  const fast_sint_t prefetch_distance = 64;
6747
6748
17.0k
  fast_sint_t i, j;
6749
17.0k
  for (i = omp_block_start,
6750
17.0k
      j = omp_block_start + omp_block_size - 2 * prefetch_distance - 1;
6751
5.50M
       i < j; i += 2) {
6752
5.48M
    libsais_prefetchw(&SA[i + 3 * prefetch_distance]);
6753
6754
5.48M
    sa_sint_t s0 = SA[i + 2 * prefetch_distance + 0];
6755
5.48M
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 : 1];
6756
5.48M
    libsais_prefetchr(Ts0 - 1);
6757
5.48M
    sa_sint_t s1 = SA[i + 2 * prefetch_distance + 1];
6758
5.48M
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 : 1];
6759
5.48M
    libsais_prefetchr(Ts1 - 1);
6760
5.48M
    sa_sint_t s2 = SA[i + 1 * prefetch_distance + 0];
6761
5.48M
    if (s2 > 0) {
6762
2.36M
      libsais_prefetchw(&induction_bucket[T[s2 - 1]]);
6763
2.36M
      libsais_prefetchr(&T[s2] - 2);
6764
2.36M
    }
6765
5.48M
    sa_sint_t s3 = SA[i + 1 * prefetch_distance + 1];
6766
5.48M
    if (s3 > 0) {
6767
2.36M
      libsais_prefetchw(&induction_bucket[T[s3 - 1]]);
6768
2.36M
      libsais_prefetchr(&T[s3] - 2);
6769
2.36M
    }
6770
6771
5.48M
    sa_sint_t p0 = SA[i + 0];
6772
5.48M
    SA[i + 0] = p0 ^ SAINT_MIN;
6773
5.48M
    if (p0 > 0) {
6774
2.62M
      p0--;
6775
2.62M
      SA[induction_bucket[T[p0]]++] =
6776
2.62M
          p0 |
6777
2.62M
          (sa_sint_t)((sa_uint_t)(T[p0 - (p0 > 0)] < T[p0]) << (SAINT_BIT - 1));
6778
2.62M
    }
6779
5.48M
    sa_sint_t p1 = SA[i + 1];
6780
5.48M
    SA[i + 1] = p1 ^ SAINT_MIN;
6781
5.48M
    if (p1 > 0) {
6782
2.62M
      p1--;
6783
2.62M
      SA[induction_bucket[T[p1]]++] =
6784
2.62M
          p1 |
6785
2.62M
          (sa_sint_t)((sa_uint_t)(T[p1 - (p1 > 0)] < T[p1]) << (SAINT_BIT - 1));
6786
2.62M
    }
6787
5.48M
  }
6788
6789
1.17M
  for (j += 2 * prefetch_distance + 1; i < j; i += 1) {
6790
1.15M
    sa_sint_t p = SA[i];
6791
1.15M
    SA[i] = p ^ SAINT_MIN;
6792
1.15M
    if (p > 0) {
6793
435k
      p--;
6794
435k
      SA[induction_bucket[T[p]]++] =
6795
435k
          p |
6796
435k
          (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] < T[p]) << (SAINT_BIT - 1));
6797
435k
    }
6798
1.15M
  }
6799
17.0k
}
6800
6801
#if defined(LIBSAIS_OPENMP)
6802
6803
static fast_sint_t libsais_final_bwt_scan_left_to_right_8u_block_prepare(
6804
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
6805
    sa_sint_t* RESTRICT buckets, LIBSAIS_THREAD_CACHE* RESTRICT cache,
6806
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
6807
  const fast_sint_t prefetch_distance = 64;
6808
6809
  memset(buckets, 0, (size_t)k * sizeof(sa_sint_t));
6810
6811
  fast_sint_t i, j, count = 0;
6812
  for (i = omp_block_start,
6813
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
6814
       i < j; i += 2) {
6815
    libsais_prefetchw(&SA[i + 2 * prefetch_distance]);
6816
6817
    sa_sint_t s0 = SA[i + prefetch_distance + 0];
6818
    const uint8_t* Ts0 = &T[s0 > 0 ? s0 : 2];
6819
    libsais_prefetchr(Ts0 - 1);
6820
    libsais_prefetchr(Ts0 - 2);
6821
    sa_sint_t s1 = SA[i + prefetch_distance + 1];
6822
    const uint8_t* Ts1 = &T[s1 > 0 ? s1 : 2];
6823
    libsais_prefetchr(Ts1 - 1);
6824
    libsais_prefetchr(Ts1 - 2);
6825
6826
    sa_sint_t p0 = SA[i + 0];
6827
    SA[i + 0] = p0 & SAINT_MAX;
6828
    if (p0 > 0) {
6829
      p0--;
6830
      SA[i + 0] = T[p0] | SAINT_MIN;
6831
      buckets[cache[count].symbol = T[p0]]++;
6832
      cache[count++].index =
6833
          p0 |
6834
          (sa_sint_t)((sa_uint_t)(T[p0 - (p0 > 0)] < T[p0]) << (SAINT_BIT - 1));
6835
    }
6836
    sa_sint_t p1 = SA[i + 1];
6837
    SA[i + 1] = p1 & SAINT_MAX;
6838
    if (p1 > 0) {
6839
      p1--;
6840
      SA[i + 1] = T[p1] | SAINT_MIN;
6841
      buckets[cache[count].symbol = T[p1]]++;
6842
      cache[count++].index =
6843
          p1 |
6844
          (sa_sint_t)((sa_uint_t)(T[p1 - (p1 > 0)] < T[p1]) << (SAINT_BIT - 1));
6845
    }
6846
  }
6847
6848
  for (j += prefetch_distance + 1; i < j; i += 1) {
6849
    sa_sint_t p = SA[i];
6850
    SA[i] = p & SAINT_MAX;
6851
    if (p > 0) {
6852
      p--;
6853
      SA[i] = T[p] | SAINT_MIN;
6854
      buckets[cache[count].symbol = T[p]]++;
6855
      cache[count++].index = p | (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] < T[p])
6856
                                             << (SAINT_BIT - 1));
6857
    }
6858
  }
6859
6860
  return count;
6861
}
6862
6863
static fast_sint_t libsais_final_sorting_scan_left_to_right_8u_block_prepare(
6864
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
6865
    sa_sint_t* RESTRICT buckets, LIBSAIS_THREAD_CACHE* RESTRICT cache,
6866
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
6867
  const fast_sint_t prefetch_distance = 64;
6868
6869
  memset(buckets, 0, (size_t)k * sizeof(sa_sint_t));
6870
6871
  fast_sint_t i, j, count = 0;
6872
  for (i = omp_block_start,
6873
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
6874
       i < j; i += 2) {
6875
    libsais_prefetchw(&SA[i + 2 * prefetch_distance]);
6876
6877
    sa_sint_t s0 = SA[i + prefetch_distance + 0];
6878
    const uint8_t* Ts0 = &T[s0 > 0 ? s0 : 2];
6879
    libsais_prefetchr(Ts0 - 1);
6880
    libsais_prefetchr(Ts0 - 2);
6881
    sa_sint_t s1 = SA[i + prefetch_distance + 1];
6882
    const uint8_t* Ts1 = &T[s1 > 0 ? s1 : 2];
6883
    libsais_prefetchr(Ts1 - 1);
6884
    libsais_prefetchr(Ts1 - 2);
6885
6886
    sa_sint_t p0 = SA[i + 0];
6887
    SA[i + 0] = p0 ^ SAINT_MIN;
6888
    if (p0 > 0) {
6889
      p0--;
6890
      buckets[cache[count].symbol = T[p0]]++;
6891
      cache[count++].index =
6892
          p0 |
6893
          (sa_sint_t)((sa_uint_t)(T[p0 - (p0 > 0)] < T[p0]) << (SAINT_BIT - 1));
6894
    }
6895
    sa_sint_t p1 = SA[i + 1];
6896
    SA[i + 1] = p1 ^ SAINT_MIN;
6897
    if (p1 > 0) {
6898
      p1--;
6899
      buckets[cache[count].symbol = T[p1]]++;
6900
      cache[count++].index =
6901
          p1 |
6902
          (sa_sint_t)((sa_uint_t)(T[p1 - (p1 > 0)] < T[p1]) << (SAINT_BIT - 1));
6903
    }
6904
  }
6905
6906
  for (j += prefetch_distance + 1; i < j; i += 1) {
6907
    sa_sint_t p = SA[i];
6908
    SA[i] = p ^ SAINT_MIN;
6909
    if (p > 0) {
6910
      p--;
6911
      buckets[cache[count].symbol = T[p]]++;
6912
      cache[count++].index = p | (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] < T[p])
6913
                                             << (SAINT_BIT - 1));
6914
    }
6915
  }
6916
6917
  return count;
6918
}
6919
6920
static void libsais_final_order_scan_left_to_right_8u_block_place(
6921
    sa_sint_t* RESTRICT SA, sa_sint_t* RESTRICT buckets,
6922
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t count) {
6923
  const fast_sint_t prefetch_distance = 64;
6924
6925
  fast_sint_t i, j;
6926
  for (i = 0, j = count - 3; i < j; i += 4) {
6927
    libsais_prefetchr(&cache[i + prefetch_distance]);
6928
6929
    SA[buckets[cache[i + 0].symbol]++] = cache[i + 0].index;
6930
    SA[buckets[cache[i + 1].symbol]++] = cache[i + 1].index;
6931
    SA[buckets[cache[i + 2].symbol]++] = cache[i + 2].index;
6932
    SA[buckets[cache[i + 3].symbol]++] = cache[i + 3].index;
6933
  }
6934
6935
  for (j += 3; i < j; i += 1) {
6936
    SA[buckets[cache[i].symbol]++] = cache[i].index;
6937
  }
6938
}
6939
6940
static void libsais_final_bwt_aux_scan_left_to_right_8u_block_place(
6941
    sa_sint_t* RESTRICT SA, sa_sint_t rm, sa_sint_t* RESTRICT I,
6942
    sa_sint_t* RESTRICT buckets, LIBSAIS_THREAD_CACHE* RESTRICT cache,
6943
    fast_sint_t count) {
6944
  const fast_sint_t prefetch_distance = 64;
6945
6946
  fast_sint_t i, j;
6947
  for (i = 0, j = count - 3; i < j; i += 4) {
6948
    libsais_prefetchr(&cache[i + prefetch_distance]);
6949
6950
    SA[buckets[cache[i + 0].symbol]++] = cache[i + 0].index;
6951
    if ((cache[i + 0].index & rm) == 0) {
6952
      I[(cache[i + 0].index & SAINT_MAX) / (rm + 1)] =
6953
          buckets[cache[i + 0].symbol];
6954
    }
6955
    SA[buckets[cache[i + 1].symbol]++] = cache[i + 1].index;
6956
    if ((cache[i + 1].index & rm) == 0) {
6957
      I[(cache[i + 1].index & SAINT_MAX) / (rm + 1)] =
6958
          buckets[cache[i + 1].symbol];
6959
    }
6960
    SA[buckets[cache[i + 2].symbol]++] = cache[i + 2].index;
6961
    if ((cache[i + 2].index & rm) == 0) {
6962
      I[(cache[i + 2].index & SAINT_MAX) / (rm + 1)] =
6963
          buckets[cache[i + 2].symbol];
6964
    }
6965
    SA[buckets[cache[i + 3].symbol]++] = cache[i + 3].index;
6966
    if ((cache[i + 3].index & rm) == 0) {
6967
      I[(cache[i + 3].index & SAINT_MAX) / (rm + 1)] =
6968
          buckets[cache[i + 3].symbol];
6969
    }
6970
  }
6971
6972
  for (j += 3; i < j; i += 1) {
6973
    SA[buckets[cache[i].symbol]++] = cache[i].index;
6974
    if ((cache[i].index & rm) == 0) {
6975
      I[(cache[i].index & SAINT_MAX) / (rm + 1)] = buckets[cache[i].symbol];
6976
    }
6977
  }
6978
}
6979
6980
static void libsais_final_sorting_scan_left_to_right_32s_block_gather(
6981
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
6982
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t omp_block_start,
6983
    fast_sint_t omp_block_size) {
6984
  const fast_sint_t prefetch_distance = 64;
6985
6986
  fast_sint_t i, j;
6987
  for (i = omp_block_start,
6988
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
6989
       i < j; i += 2) {
6990
    libsais_prefetchw(&SA[i + 2 * prefetch_distance]);
6991
6992
    sa_sint_t s0 = SA[i + prefetch_distance + 0];
6993
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 : 2];
6994
    libsais_prefetchr(Ts0 - 1);
6995
    libsais_prefetchr(Ts0 - 2);
6996
    sa_sint_t s1 = SA[i + prefetch_distance + 1];
6997
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 : 2];
6998
    libsais_prefetchr(Ts1 - 1);
6999
    libsais_prefetchr(Ts1 - 2);
7000
7001
    libsais_prefetchw(&cache[i + prefetch_distance]);
7002
7003
    sa_sint_t symbol0 = SAINT_MIN, p0 = SA[i + 0];
7004
    SA[i + 0] = p0 ^ SAINT_MIN;
7005
    if (p0 > 0) {
7006
      p0--;
7007
      cache[i + 0].index =
7008
          p0 |
7009
          (sa_sint_t)((sa_uint_t)(T[p0 - (p0 > 0)] < T[p0]) << (SAINT_BIT - 1));
7010
      symbol0 = T[p0];
7011
    }
7012
    cache[i + 0].symbol = symbol0;
7013
    sa_sint_t symbol1 = SAINT_MIN, p1 = SA[i + 1];
7014
    SA[i + 1] = p1 ^ SAINT_MIN;
7015
    if (p1 > 0) {
7016
      p1--;
7017
      cache[i + 1].index =
7018
          p1 |
7019
          (sa_sint_t)((sa_uint_t)(T[p1 - (p1 > 0)] < T[p1]) << (SAINT_BIT - 1));
7020
      symbol1 = T[p1];
7021
    }
7022
    cache[i + 1].symbol = symbol1;
7023
  }
7024
7025
  for (j += prefetch_distance + 1; i < j; i += 1) {
7026
    sa_sint_t symbol = SAINT_MIN, p = SA[i];
7027
    SA[i] = p ^ SAINT_MIN;
7028
    if (p > 0) {
7029
      p--;
7030
      cache[i].index = p | (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] < T[p])
7031
                                       << (SAINT_BIT - 1));
7032
      symbol = T[p];
7033
    }
7034
    cache[i].symbol = symbol;
7035
  }
7036
}
7037
7038
static void libsais_final_sorting_scan_left_to_right_32s_block_sort(
7039
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT induction_bucket,
7040
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t omp_block_start,
7041
    fast_sint_t omp_block_size) {
7042
  const fast_sint_t prefetch_distance = 64;
7043
7044
  fast_sint_t i, j, omp_block_end = omp_block_start + omp_block_size;
7045
  for (i = omp_block_start, j = omp_block_end - prefetch_distance - 1; i < j;
7046
       i += 2) {
7047
    libsais_prefetchw(&cache[i + 2 * prefetch_distance]);
7048
7049
    sa_sint_t s0 = cache[i + prefetch_distance + 0].symbol;
7050
    const sa_sint_t* Is0 = &induction_bucket[s0 > 0 ? s0 : 0];
7051
    libsais_prefetchw(Is0);
7052
    sa_sint_t s1 = cache[i + prefetch_distance + 1].symbol;
7053
    const sa_sint_t* Is1 = &induction_bucket[s1 > 0 ? s1 : 0];
7054
    libsais_prefetchw(Is1);
7055
7056
    sa_sint_t v0 = cache[i + 0].symbol;
7057
    if (v0 >= 0) {
7058
      cache[i + 0].symbol = induction_bucket[v0]++;
7059
      if (cache[i + 0].symbol < omp_block_end) {
7060
        sa_sint_t ni = cache[i + 0].symbol, np = cache[i + 0].index;
7061
        cache[i + 0].index = np ^ SAINT_MIN;
7062
        if (np > 0) {
7063
          np--;
7064
          cache[ni].index =
7065
              np | (sa_sint_t)((sa_uint_t)(T[np - (np > 0)] < T[np])
7066
                               << (SAINT_BIT - 1));
7067
          cache[ni].symbol = T[np];
7068
        }
7069
      }
7070
    }
7071
7072
    sa_sint_t v1 = cache[i + 1].symbol;
7073
    if (v1 >= 0) {
7074
      cache[i + 1].symbol = induction_bucket[v1]++;
7075
      if (cache[i + 1].symbol < omp_block_end) {
7076
        sa_sint_t ni = cache[i + 1].symbol, np = cache[i + 1].index;
7077
        cache[i + 1].index = np ^ SAINT_MIN;
7078
        if (np > 0) {
7079
          np--;
7080
          cache[ni].index =
7081
              np | (sa_sint_t)((sa_uint_t)(T[np - (np > 0)] < T[np])
7082
                               << (SAINT_BIT - 1));
7083
          cache[ni].symbol = T[np];
7084
        }
7085
      }
7086
    }
7087
  }
7088
7089
  for (j += prefetch_distance + 1; i < j; i += 1) {
7090
    sa_sint_t v = cache[i].symbol;
7091
    if (v >= 0) {
7092
      cache[i].symbol = induction_bucket[v]++;
7093
      if (cache[i].symbol < omp_block_end) {
7094
        sa_sint_t ni = cache[i].symbol, np = cache[i].index;
7095
        cache[i].index = np ^ SAINT_MIN;
7096
        if (np > 0) {
7097
          np--;
7098
          cache[ni].index =
7099
              np | (sa_sint_t)((sa_uint_t)(T[np - (np > 0)] < T[np])
7100
                               << (SAINT_BIT - 1));
7101
          cache[ni].symbol = T[np];
7102
        }
7103
      }
7104
    }
7105
  }
7106
}
7107
7108
static void libsais_final_bwt_scan_left_to_right_8u_block_omp(
7109
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
7110
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t block_start,
7111
    fast_sint_t block_size, sa_sint_t threads,
7112
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
7113
#if defined(LIBSAIS_OPENMP)
7114
#pragma omp parallel num_threads(                                             \
7115
        threads) if (threads > 1 && block_size >= 64 * (k > 256 ? k : 256) && \
7116
                         omp_get_dynamic() == 0)
7117
#endif
7118
  {
7119
#if defined(LIBSAIS_OPENMP)
7120
    fast_sint_t omp_thread_num = omp_get_thread_num();
7121
    fast_sint_t omp_num_threads = omp_get_num_threads();
7122
#else
7123
    UNUSED(k);
7124
    UNUSED(threads);
7125
    UNUSED(thread_state);
7126
7127
    fast_sint_t omp_thread_num = 0;
7128
    fast_sint_t omp_num_threads = 1;
7129
#endif
7130
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
7131
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
7132
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
7133
                                     ? omp_block_stride
7134
                                     : block_size - omp_block_start;
7135
7136
    omp_block_start += block_start;
7137
7138
    if (omp_num_threads == 1) {
7139
      libsais_final_bwt_scan_left_to_right_8u(T, SA, induction_bucket,
7140
                                              omp_block_start, omp_block_size);
7141
    }
7142
#if defined(LIBSAIS_OPENMP)
7143
    else {
7144
      {
7145
        thread_state[omp_thread_num].state.count =
7146
            libsais_final_bwt_scan_left_to_right_8u_block_prepare(
7147
                T, SA, k, thread_state[omp_thread_num].state.buckets,
7148
                thread_state[omp_thread_num].state.cache, omp_block_start,
7149
                omp_block_size);
7150
      }
7151
7152
#pragma omp barrier
7153
7154
#pragma omp master
7155
      {
7156
        fast_sint_t t;
7157
        for (t = 0; t < omp_num_threads; ++t) {
7158
          sa_sint_t* RESTRICT temp_bucket = thread_state[t].state.buckets;
7159
          fast_sint_t c;
7160
          for (c = 0; c < k; c += 1) {
7161
            sa_sint_t A = induction_bucket[c], B = temp_bucket[c];
7162
            induction_bucket[c] = A + B;
7163
            temp_bucket[c] = A;
7164
          }
7165
        }
7166
      }
7167
7168
#pragma omp barrier
7169
7170
      {
7171
        libsais_final_order_scan_left_to_right_8u_block_place(
7172
            SA, thread_state[omp_thread_num].state.buckets,
7173
            thread_state[omp_thread_num].state.cache,
7174
            thread_state[omp_thread_num].state.count);
7175
      }
7176
    }
7177
#endif
7178
  }
7179
}
7180
7181
static void libsais_final_bwt_aux_scan_left_to_right_8u_block_omp(
7182
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
7183
    sa_sint_t rm, sa_sint_t* RESTRICT I, sa_sint_t* RESTRICT induction_bucket,
7184
    fast_sint_t block_start, fast_sint_t block_size, sa_sint_t threads,
7185
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
7186
#if defined(LIBSAIS_OPENMP)
7187
#pragma omp parallel num_threads(                                             \
7188
        threads) if (threads > 1 && block_size >= 64 * (k > 256 ? k : 256) && \
7189
                         omp_get_dynamic() == 0)
7190
#endif
7191
  {
7192
#if defined(LIBSAIS_OPENMP)
7193
    fast_sint_t omp_thread_num = omp_get_thread_num();
7194
    fast_sint_t omp_num_threads = omp_get_num_threads();
7195
#else
7196
    UNUSED(k);
7197
    UNUSED(threads);
7198
    UNUSED(thread_state);
7199
7200
    fast_sint_t omp_thread_num = 0;
7201
    fast_sint_t omp_num_threads = 1;
7202
#endif
7203
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
7204
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
7205
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
7206
                                     ? omp_block_stride
7207
                                     : block_size - omp_block_start;
7208
7209
    omp_block_start += block_start;
7210
7211
    if (omp_num_threads == 1) {
7212
      libsais_final_bwt_aux_scan_left_to_right_8u(
7213
          T, SA, rm, I, induction_bucket, omp_block_start, omp_block_size);
7214
    }
7215
#if defined(LIBSAIS_OPENMP)
7216
    else {
7217
      {
7218
        thread_state[omp_thread_num].state.count =
7219
            libsais_final_bwt_scan_left_to_right_8u_block_prepare(
7220
                T, SA, k, thread_state[omp_thread_num].state.buckets,
7221
                thread_state[omp_thread_num].state.cache, omp_block_start,
7222
                omp_block_size);
7223
      }
7224
7225
#pragma omp barrier
7226
7227
#pragma omp master
7228
      {
7229
        fast_sint_t t;
7230
        for (t = 0; t < omp_num_threads; ++t) {
7231
          sa_sint_t* RESTRICT temp_bucket = thread_state[t].state.buckets;
7232
          fast_sint_t c;
7233
          for (c = 0; c < k; c += 1) {
7234
            sa_sint_t A = induction_bucket[c], B = temp_bucket[c];
7235
            induction_bucket[c] = A + B;
7236
            temp_bucket[c] = A;
7237
          }
7238
        }
7239
      }
7240
7241
#pragma omp barrier
7242
7243
      {
7244
        libsais_final_bwt_aux_scan_left_to_right_8u_block_place(
7245
            SA, rm, I, thread_state[omp_thread_num].state.buckets,
7246
            thread_state[omp_thread_num].state.cache,
7247
            thread_state[omp_thread_num].state.count);
7248
      }
7249
    }
7250
#endif
7251
  }
7252
}
7253
7254
static void libsais_final_sorting_scan_left_to_right_8u_block_omp(
7255
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
7256
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t block_start,
7257
    fast_sint_t block_size, sa_sint_t threads,
7258
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
7259
#if defined(LIBSAIS_OPENMP)
7260
#pragma omp parallel num_threads(                                             \
7261
        threads) if (threads > 1 && block_size >= 64 * (k > 256 ? k : 256) && \
7262
                         omp_get_dynamic() == 0)
7263
#endif
7264
  {
7265
#if defined(LIBSAIS_OPENMP)
7266
    fast_sint_t omp_thread_num = omp_get_thread_num();
7267
    fast_sint_t omp_num_threads = omp_get_num_threads();
7268
#else
7269
    UNUSED(k);
7270
    UNUSED(threads);
7271
    UNUSED(thread_state);
7272
7273
    fast_sint_t omp_thread_num = 0;
7274
    fast_sint_t omp_num_threads = 1;
7275
#endif
7276
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
7277
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
7278
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
7279
                                     ? omp_block_stride
7280
                                     : block_size - omp_block_start;
7281
7282
    omp_block_start += block_start;
7283
7284
    if (omp_num_threads == 1) {
7285
      libsais_final_sorting_scan_left_to_right_8u(
7286
          T, SA, induction_bucket, omp_block_start, omp_block_size);
7287
    }
7288
#if defined(LIBSAIS_OPENMP)
7289
    else {
7290
      {
7291
        thread_state[omp_thread_num].state.count =
7292
            libsais_final_sorting_scan_left_to_right_8u_block_prepare(
7293
                T, SA, k, thread_state[omp_thread_num].state.buckets,
7294
                thread_state[omp_thread_num].state.cache, omp_block_start,
7295
                omp_block_size);
7296
      }
7297
7298
#pragma omp barrier
7299
7300
#pragma omp master
7301
      {
7302
        fast_sint_t t;
7303
        for (t = 0; t < omp_num_threads; ++t) {
7304
          sa_sint_t* RESTRICT temp_bucket = thread_state[t].state.buckets;
7305
          fast_sint_t c;
7306
          for (c = 0; c < k; c += 1) {
7307
            sa_sint_t A = induction_bucket[c], B = temp_bucket[c];
7308
            induction_bucket[c] = A + B;
7309
            temp_bucket[c] = A;
7310
          }
7311
        }
7312
      }
7313
7314
#pragma omp barrier
7315
7316
      {
7317
        libsais_final_order_scan_left_to_right_8u_block_place(
7318
            SA, thread_state[omp_thread_num].state.buckets,
7319
            thread_state[omp_thread_num].state.cache,
7320
            thread_state[omp_thread_num].state.count);
7321
      }
7322
    }
7323
#endif
7324
  }
7325
}
7326
7327
static void libsais_final_sorting_scan_left_to_right_32s_block_omp(
7328
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
7329
    sa_sint_t* RESTRICT buckets, LIBSAIS_THREAD_CACHE* RESTRICT cache,
7330
    fast_sint_t block_start, fast_sint_t block_size, sa_sint_t threads) {
7331
#if defined(LIBSAIS_OPENMP)
7332
#pragma omp parallel num_threads(threads) if (threads > 1 && \
7333
                                                  block_size >= 16384)
7334
#endif
7335
  {
7336
#if defined(LIBSAIS_OPENMP)
7337
    fast_sint_t omp_thread_num = omp_get_thread_num();
7338
    fast_sint_t omp_num_threads = omp_get_num_threads();
7339
#else
7340
    UNUSED(threads);
7341
    UNUSED(cache);
7342
7343
    fast_sint_t omp_thread_num = 0;
7344
    fast_sint_t omp_num_threads = 1;
7345
#endif
7346
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
7347
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
7348
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
7349
                                     ? omp_block_stride
7350
                                     : block_size - omp_block_start;
7351
7352
    omp_block_start += block_start;
7353
7354
    if (omp_num_threads == 1) {
7355
      libsais_final_sorting_scan_left_to_right_32s(
7356
          T, SA, buckets, omp_block_start, omp_block_size);
7357
    }
7358
#if defined(LIBSAIS_OPENMP)
7359
    else {
7360
      {
7361
        libsais_final_sorting_scan_left_to_right_32s_block_gather(
7362
            T, SA, cache - block_start, omp_block_start, omp_block_size);
7363
      }
7364
7365
#pragma omp barrier
7366
7367
#pragma omp master
7368
      {
7369
        libsais_final_sorting_scan_left_to_right_32s_block_sort(
7370
            T, buckets, cache - block_start, block_start, block_size);
7371
      }
7372
7373
#pragma omp barrier
7374
7375
      {
7376
        libsais_compact_and_place_cached_suffixes(
7377
            SA, cache - block_start, omp_block_start, omp_block_size);
7378
      }
7379
    }
7380
#endif
7381
  }
7382
}
7383
7384
#endif
7385
7386
static void libsais_final_bwt_scan_left_to_right_8u_omp(
7387
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, fast_sint_t n,
7388
    sa_sint_t k, sa_sint_t* RESTRICT induction_bucket, sa_sint_t threads,
7389
0
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
7390
0
  SA[induction_bucket[T[(sa_sint_t)n - 1]]++] =
7391
0
      ((sa_sint_t)n - 1) |
7392
0
      (sa_sint_t)((sa_uint_t)(T[(sa_sint_t)n - 2] < T[(sa_sint_t)n - 1])
7393
0
                  << (SAINT_BIT - 1));
7394
7395
0
  if (threads == 1 || n < 65536) {
7396
0
    libsais_final_bwt_scan_left_to_right_8u(T, SA, induction_bucket, 0, n);
7397
0
  }
7398
#if defined(LIBSAIS_OPENMP)
7399
  else {
7400
    fast_sint_t block_start;
7401
    for (block_start = 0; block_start < n;) {
7402
      if (SA[block_start] == 0) {
7403
        block_start++;
7404
      } else {
7405
        fast_sint_t block_max_end =
7406
            block_start +
7407
            ((fast_sint_t)threads) *
7408
                (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * (fast_sint_t)threads);
7409
        if (block_max_end > n) {
7410
          block_max_end = n;
7411
        }
7412
        fast_sint_t block_end = block_start + 1;
7413
        while (block_end < block_max_end && SA[block_end] != 0) {
7414
          block_end++;
7415
        }
7416
        fast_sint_t block_size = block_end - block_start;
7417
7418
        if (block_size < 32) {
7419
          for (; block_start < block_end; block_start += 1) {
7420
            sa_sint_t p = SA[block_start];
7421
            SA[block_start] = p & SAINT_MAX;
7422
            if (p > 0) {
7423
              p--;
7424
              SA[block_start] = T[p] | SAINT_MIN;
7425
              SA[induction_bucket[T[p]]++] =
7426
                  p | (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] < T[p])
7427
                                  << (SAINT_BIT - 1));
7428
            }
7429
          }
7430
        } else {
7431
          libsais_final_bwt_scan_left_to_right_8u_block_omp(
7432
              T, SA, k, induction_bucket, block_start, block_size, threads,
7433
              thread_state);
7434
          block_start = block_end;
7435
        }
7436
      }
7437
    }
7438
  }
7439
#else
7440
0
  UNUSED(k);
7441
0
  UNUSED(thread_state);
7442
0
#endif
7443
0
}
7444
7445
static void libsais_final_bwt_aux_scan_left_to_right_8u_omp(
7446
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, fast_sint_t n,
7447
    sa_sint_t k, sa_sint_t rm, sa_sint_t* RESTRICT I,
7448
    sa_sint_t* RESTRICT induction_bucket, sa_sint_t threads,
7449
0
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
7450
0
  SA[induction_bucket[T[(sa_sint_t)n - 1]]++] =
7451
0
      ((sa_sint_t)n - 1) |
7452
0
      (sa_sint_t)((sa_uint_t)(T[(sa_sint_t)n - 2] < T[(sa_sint_t)n - 1])
7453
0
                  << (SAINT_BIT - 1));
7454
7455
0
  if ((((sa_sint_t)n - 1) & rm) == 0) {
7456
0
    I[((sa_sint_t)n - 1) / (rm + 1)] = induction_bucket[T[(sa_sint_t)n - 1]];
7457
0
  }
7458
7459
0
  if (threads == 1 || n < 65536) {
7460
0
    libsais_final_bwt_aux_scan_left_to_right_8u(T, SA, rm, I, induction_bucket,
7461
0
                                                0, n);
7462
0
  }
7463
#if defined(LIBSAIS_OPENMP)
7464
  else {
7465
    fast_sint_t block_start;
7466
    for (block_start = 0; block_start < n;) {
7467
      if (SA[block_start] == 0) {
7468
        block_start++;
7469
      } else {
7470
        fast_sint_t block_max_end =
7471
            block_start +
7472
            ((fast_sint_t)threads) *
7473
                (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * (fast_sint_t)threads);
7474
        if (block_max_end > n) {
7475
          block_max_end = n;
7476
        }
7477
        fast_sint_t block_end = block_start + 1;
7478
        while (block_end < block_max_end && SA[block_end] != 0) {
7479
          block_end++;
7480
        }
7481
        fast_sint_t block_size = block_end - block_start;
7482
7483
        if (block_size < 32) {
7484
          for (; block_start < block_end; block_start += 1) {
7485
            sa_sint_t p = SA[block_start];
7486
            SA[block_start] = p & SAINT_MAX;
7487
            if (p > 0) {
7488
              p--;
7489
              SA[block_start] = T[p] | SAINT_MIN;
7490
              SA[induction_bucket[T[p]]++] =
7491
                  p | (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] < T[p])
7492
                                  << (SAINT_BIT - 1));
7493
              if ((p & rm) == 0) {
7494
                I[p / (rm + 1)] = induction_bucket[T[p]];
7495
              }
7496
            }
7497
          }
7498
        } else {
7499
          libsais_final_bwt_aux_scan_left_to_right_8u_block_omp(
7500
              T, SA, k, rm, I, induction_bucket, block_start, block_size,
7501
              threads, thread_state);
7502
          block_start = block_end;
7503
        }
7504
      }
7505
    }
7506
  }
7507
#else
7508
0
  UNUSED(k);
7509
0
  UNUSED(thread_state);
7510
0
#endif
7511
0
}
7512
7513
static void libsais_final_sorting_scan_left_to_right_8u_omp(
7514
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, fast_sint_t n,
7515
    sa_sint_t k, sa_sint_t* RESTRICT induction_bucket, sa_sint_t threads,
7516
8.54k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
7517
8.54k
  SA[induction_bucket[T[(sa_sint_t)n - 1]]++] =
7518
8.54k
      ((sa_sint_t)n - 1) |
7519
8.54k
      (sa_sint_t)((sa_uint_t)(T[(sa_sint_t)n - 2] < T[(sa_sint_t)n - 1])
7520
8.54k
                  << (SAINT_BIT - 1));
7521
7522
8.54k
  if (threads == 1 || n < 65536) {
7523
8.54k
    libsais_final_sorting_scan_left_to_right_8u(T, SA, induction_bucket, 0, n);
7524
8.54k
  }
7525
#if defined(LIBSAIS_OPENMP)
7526
  else {
7527
    fast_sint_t block_start;
7528
    for (block_start = 0; block_start < n;) {
7529
      if (SA[block_start] == 0) {
7530
        block_start++;
7531
      } else {
7532
        fast_sint_t block_max_end =
7533
            block_start +
7534
            ((fast_sint_t)threads) *
7535
                (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * (fast_sint_t)threads);
7536
        if (block_max_end > n) {
7537
          block_max_end = n;
7538
        }
7539
        fast_sint_t block_end = block_start + 1;
7540
        while (block_end < block_max_end && SA[block_end] != 0) {
7541
          block_end++;
7542
        }
7543
        fast_sint_t block_size = block_end - block_start;
7544
7545
        if (block_size < 32) {
7546
          for (; block_start < block_end; block_start += 1) {
7547
            sa_sint_t p = SA[block_start];
7548
            SA[block_start] = p ^ SAINT_MIN;
7549
            if (p > 0) {
7550
              p--;
7551
              SA[induction_bucket[T[p]]++] =
7552
                  p | (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] < T[p])
7553
                                  << (SAINT_BIT - 1));
7554
            }
7555
          }
7556
        } else {
7557
          libsais_final_sorting_scan_left_to_right_8u_block_omp(
7558
              T, SA, k, induction_bucket, block_start, block_size, threads,
7559
              thread_state);
7560
          block_start = block_end;
7561
        }
7562
      }
7563
    }
7564
  }
7565
#else
7566
8.54k
  UNUSED(k);
7567
8.54k
  UNUSED(thread_state);
7568
8.54k
#endif
7569
8.54k
}
7570
7571
static void libsais_final_sorting_scan_left_to_right_32s_omp(
7572
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
7573
    sa_sint_t* RESTRICT induction_bucket, sa_sint_t threads,
7574
17.0k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
7575
17.0k
  SA[induction_bucket[T[n - 1]]++] =
7576
17.0k
      (n - 1) |
7577
17.0k
      (sa_sint_t)((sa_uint_t)(T[n - 2] < T[n - 1]) << (SAINT_BIT - 1));
7578
7579
17.0k
  if (threads == 1 || n < 65536) {
7580
17.0k
    libsais_final_sorting_scan_left_to_right_32s(T, SA, induction_bucket, 0, n);
7581
17.0k
  }
7582
#if defined(LIBSAIS_OPENMP)
7583
  else {
7584
    fast_sint_t block_start, block_end;
7585
    for (block_start = 0; block_start < n; block_start = block_end) {
7586
      block_end =
7587
          block_start + (fast_sint_t)threads * LIBSAIS_PER_THREAD_CACHE_SIZE;
7588
      if (block_end > n) {
7589
        block_end = n;
7590
      }
7591
7592
      libsais_final_sorting_scan_left_to_right_32s_block_omp(
7593
          T, SA, induction_bucket, thread_state[0].state.cache, block_start,
7594
          block_end - block_start, threads);
7595
    }
7596
  }
7597
#else
7598
17.0k
  UNUSED(thread_state);
7599
17.0k
#endif
7600
17.0k
}
7601
7602
static sa_sint_t libsais_final_bwt_scan_right_to_left_8u(
7603
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA,
7604
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t omp_block_start,
7605
0
    fast_sint_t omp_block_size) {
7606
0
  const fast_sint_t prefetch_distance = 64;
7607
7608
0
  fast_sint_t i, j;
7609
0
  sa_sint_t index = -1;
7610
0
  for (i = omp_block_start + omp_block_size - 1,
7611
0
      j = omp_block_start + prefetch_distance + 1;
7612
0
       i >= j; i -= 2) {
7613
0
    libsais_prefetchw(&SA[i - 2 * prefetch_distance]);
7614
7615
0
    sa_sint_t s0 = SA[i - prefetch_distance - 0];
7616
0
    const uint8_t* Ts0 = &T[s0 > 0 ? s0 : 2];
7617
0
    libsais_prefetchr(Ts0 - 1);
7618
0
    libsais_prefetchr(Ts0 - 2);
7619
0
    sa_sint_t s1 = SA[i - prefetch_distance - 1];
7620
0
    const uint8_t* Ts1 = &T[s1 > 0 ? s1 : 2];
7621
0
    libsais_prefetchr(Ts1 - 1);
7622
0
    libsais_prefetchr(Ts1 - 2);
7623
7624
0
    sa_sint_t p0 = SA[i - 0];
7625
0
    index = (p0 == 0) ? (sa_sint_t)(i - 0) : index;
7626
0
    SA[i - 0] = p0 & SAINT_MAX;
7627
0
    if (p0 > 0) {
7628
0
      p0--;
7629
0
      uint8_t c0 = T[p0 - (p0 > 0)], c1 = T[p0];
7630
0
      SA[i - 0] = c1;
7631
0
      sa_sint_t t = c0 | SAINT_MIN;
7632
0
      SA[--induction_bucket[c1]] = (c0 <= c1) ? p0 : t;
7633
0
    }
7634
7635
0
    sa_sint_t p1 = SA[i - 1];
7636
0
    index = (p1 == 0) ? (sa_sint_t)(i - 1) : index;
7637
0
    SA[i - 1] = p1 & SAINT_MAX;
7638
0
    if (p1 > 0) {
7639
0
      p1--;
7640
0
      uint8_t c0 = T[p1 - (p1 > 0)], c1 = T[p1];
7641
0
      SA[i - 1] = c1;
7642
0
      sa_sint_t t = c0 | SAINT_MIN;
7643
0
      SA[--induction_bucket[c1]] = (c0 <= c1) ? p1 : t;
7644
0
    }
7645
0
  }
7646
7647
0
  for (j -= prefetch_distance + 1; i >= j; i -= 1) {
7648
0
    sa_sint_t p = SA[i];
7649
0
    index = (p == 0) ? (sa_sint_t)i : index;
7650
0
    SA[i] = p & SAINT_MAX;
7651
0
    if (p > 0) {
7652
0
      p--;
7653
0
      uint8_t c0 = T[p - (p > 0)], c1 = T[p];
7654
0
      SA[i] = c1;
7655
0
      sa_sint_t t = c0 | SAINT_MIN;
7656
0
      SA[--induction_bucket[c1]] = (c0 <= c1) ? p : t;
7657
0
    }
7658
0
  }
7659
7660
0
  return index;
7661
0
}
7662
7663
static void libsais_final_bwt_aux_scan_right_to_left_8u(
7664
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t rm,
7665
    sa_sint_t* RESTRICT I, sa_sint_t* RESTRICT induction_bucket,
7666
0
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
7667
0
  const fast_sint_t prefetch_distance = 64;
7668
7669
0
  fast_sint_t i, j;
7670
0
  for (i = omp_block_start + omp_block_size - 1,
7671
0
      j = omp_block_start + prefetch_distance + 1;
7672
0
       i >= j; i -= 2) {
7673
0
    libsais_prefetchw(&SA[i - 2 * prefetch_distance]);
7674
7675
0
    sa_sint_t s0 = SA[i - prefetch_distance - 0];
7676
0
    const uint8_t* Ts0 = &T[s0 > 0 ? s0 : 2];
7677
0
    libsais_prefetchr(Ts0 - 1);
7678
0
    libsais_prefetchr(Ts0 - 2);
7679
0
    sa_sint_t s1 = SA[i - prefetch_distance - 1];
7680
0
    const uint8_t* Ts1 = &T[s1 > 0 ? s1 : 2];
7681
0
    libsais_prefetchr(Ts1 - 1);
7682
0
    libsais_prefetchr(Ts1 - 2);
7683
7684
0
    sa_sint_t p0 = SA[i - 0];
7685
0
    SA[i - 0] = p0 & SAINT_MAX;
7686
0
    if (p0 > 0) {
7687
0
      p0--;
7688
0
      uint8_t c0 = T[p0 - (p0 > 0)], c1 = T[p0];
7689
0
      SA[i - 0] = c1;
7690
0
      sa_sint_t t = c0 | SAINT_MIN;
7691
0
      SA[--induction_bucket[c1]] = (c0 <= c1) ? p0 : t;
7692
0
      if ((p0 & rm) == 0) {
7693
0
        I[p0 / (rm + 1)] = induction_bucket[T[p0]] + 1;
7694
0
      }
7695
0
    }
7696
7697
0
    sa_sint_t p1 = SA[i - 1];
7698
0
    SA[i - 1] = p1 & SAINT_MAX;
7699
0
    if (p1 > 0) {
7700
0
      p1--;
7701
0
      uint8_t c0 = T[p1 - (p1 > 0)], c1 = T[p1];
7702
0
      SA[i - 1] = c1;
7703
0
      sa_sint_t t = c0 | SAINT_MIN;
7704
0
      SA[--induction_bucket[c1]] = (c0 <= c1) ? p1 : t;
7705
0
      if ((p1 & rm) == 0) {
7706
0
        I[p1 / (rm + 1)] = induction_bucket[T[p1]] + 1;
7707
0
      }
7708
0
    }
7709
0
  }
7710
7711
0
  for (j -= prefetch_distance + 1; i >= j; i -= 1) {
7712
0
    sa_sint_t p = SA[i];
7713
0
    SA[i] = p & SAINT_MAX;
7714
0
    if (p > 0) {
7715
0
      p--;
7716
0
      uint8_t c0 = T[p - (p > 0)], c1 = T[p];
7717
0
      SA[i] = c1;
7718
0
      sa_sint_t t = c0 | SAINT_MIN;
7719
0
      SA[--induction_bucket[c1]] = (c0 <= c1) ? p : t;
7720
0
      if ((p & rm) == 0) {
7721
0
        I[p / (rm + 1)] = induction_bucket[T[p]] + 1;
7722
0
      }
7723
0
    }
7724
0
  }
7725
0
}
7726
7727
static void libsais_final_sorting_scan_right_to_left_8u(
7728
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA,
7729
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t omp_block_start,
7730
8.54k
    fast_sint_t omp_block_size) {
7731
8.54k
  const fast_sint_t prefetch_distance = 64;
7732
7733
8.54k
  fast_sint_t i, j;
7734
8.54k
  for (i = omp_block_start + omp_block_size - 1,
7735
8.54k
      j = omp_block_start + prefetch_distance + 1;
7736
10.8M
       i >= j; i -= 2) {
7737
10.8M
    libsais_prefetchw(&SA[i - 2 * prefetch_distance]);
7738
7739
10.8M
    sa_sint_t s0 = SA[i - prefetch_distance - 0];
7740
10.8M
    const uint8_t* Ts0 = &T[s0 > 0 ? s0 : 2];
7741
10.8M
    libsais_prefetchr(Ts0 - 1);
7742
10.8M
    libsais_prefetchr(Ts0 - 2);
7743
10.8M
    sa_sint_t s1 = SA[i - prefetch_distance - 1];
7744
10.8M
    const uint8_t* Ts1 = &T[s1 > 0 ? s1 : 2];
7745
10.8M
    libsais_prefetchr(Ts1 - 1);
7746
10.8M
    libsais_prefetchr(Ts1 - 2);
7747
7748
10.8M
    sa_sint_t p0 = SA[i - 0];
7749
10.8M
    SA[i - 0] = p0 & SAINT_MAX;
7750
10.8M
    if (p0 > 0) {
7751
4.71M
      p0--;
7752
4.71M
      SA[--induction_bucket[T[p0]]] =
7753
4.71M
          p0 |
7754
4.71M
          (sa_sint_t)((sa_uint_t)(T[p0 - (p0 > 0)] > T[p0]) << (SAINT_BIT - 1));
7755
4.71M
    }
7756
10.8M
    sa_sint_t p1 = SA[i - 1];
7757
10.8M
    SA[i - 1] = p1 & SAINT_MAX;
7758
10.8M
    if (p1 > 0) {
7759
4.71M
      p1--;
7760
4.71M
      SA[--induction_bucket[T[p1]]] =
7761
4.71M
          p1 |
7762
4.71M
          (sa_sint_t)((sa_uint_t)(T[p1 - (p1 > 0)] > T[p1]) << (SAINT_BIT - 1));
7763
4.71M
    }
7764
10.8M
  }
7765
7766
494k
  for (j -= prefetch_distance + 1; i >= j; i -= 1) {
7767
485k
    sa_sint_t p = SA[i];
7768
485k
    SA[i] = p & SAINT_MAX;
7769
485k
    if (p > 0) {
7770
99.5k
      p--;
7771
99.5k
      SA[--induction_bucket[T[p]]] =
7772
99.5k
          p |
7773
99.5k
          (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] > T[p]) << (SAINT_BIT - 1));
7774
99.5k
    }
7775
485k
  }
7776
8.54k
}
7777
7778
static void libsais_final_gsa_scan_right_to_left_8u(
7779
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA,
7780
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t omp_block_start,
7781
0
    fast_sint_t omp_block_size) {
7782
0
  const fast_sint_t prefetch_distance = 64;
7783
7784
0
  fast_sint_t i, j;
7785
0
  for (i = omp_block_start + omp_block_size - 1,
7786
0
      j = omp_block_start + prefetch_distance + 1;
7787
0
       i >= j; i -= 2) {
7788
0
    libsais_prefetchw(&SA[i - 2 * prefetch_distance]);
7789
7790
0
    sa_sint_t s0 = SA[i - prefetch_distance - 0];
7791
0
    const uint8_t* Ts0 = &T[s0 > 0 ? s0 : 2];
7792
0
    libsais_prefetchr(Ts0 - 1);
7793
0
    libsais_prefetchr(Ts0 - 2);
7794
0
    sa_sint_t s1 = SA[i - prefetch_distance - 1];
7795
0
    const uint8_t* Ts1 = &T[s1 > 0 ? s1 : 2];
7796
0
    libsais_prefetchr(Ts1 - 1);
7797
0
    libsais_prefetchr(Ts1 - 2);
7798
7799
0
    sa_sint_t p0 = SA[i - 0];
7800
0
    SA[i - 0] = p0 & SAINT_MAX;
7801
0
    if (p0 > 0 && T[p0 - 1] > 0) {
7802
0
      p0--;
7803
0
      SA[--induction_bucket[T[p0]]] =
7804
0
          p0 |
7805
0
          (sa_sint_t)((sa_uint_t)(T[p0 - (p0 > 0)] > T[p0]) << (SAINT_BIT - 1));
7806
0
    }
7807
0
    sa_sint_t p1 = SA[i - 1];
7808
0
    SA[i - 1] = p1 & SAINT_MAX;
7809
0
    if (p1 > 0 && T[p1 - 1] > 0) {
7810
0
      p1--;
7811
0
      SA[--induction_bucket[T[p1]]] =
7812
0
          p1 |
7813
0
          (sa_sint_t)((sa_uint_t)(T[p1 - (p1 > 0)] > T[p1]) << (SAINT_BIT - 1));
7814
0
    }
7815
0
  }
7816
7817
0
  for (j -= prefetch_distance + 1; i >= j; i -= 1) {
7818
0
    sa_sint_t p = SA[i];
7819
0
    SA[i] = p & SAINT_MAX;
7820
0
    if (p > 0 && T[p - 1] > 0) {
7821
0
      p--;
7822
0
      SA[--induction_bucket[T[p]]] =
7823
0
          p |
7824
0
          (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] > T[p]) << (SAINT_BIT - 1));
7825
0
    }
7826
0
  }
7827
0
}
7828
7829
static void libsais_final_sorting_scan_right_to_left_32s(
7830
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
7831
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t omp_block_start,
7832
17.0k
    fast_sint_t omp_block_size) {
7833
17.0k
  const fast_sint_t prefetch_distance = 64;
7834
7835
17.0k
  fast_sint_t i, j;
7836
17.0k
  for (i = omp_block_start + omp_block_size - 1,
7837
17.0k
      j = omp_block_start + 2 * prefetch_distance + 1;
7838
5.50M
       i >= j; i -= 2) {
7839
5.48M
    libsais_prefetchw(&SA[i - 3 * prefetch_distance]);
7840
7841
5.48M
    sa_sint_t s0 = SA[i - 2 * prefetch_distance - 0];
7842
5.48M
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 : 1];
7843
5.48M
    libsais_prefetchr(Ts0 - 1);
7844
5.48M
    sa_sint_t s1 = SA[i - 2 * prefetch_distance - 1];
7845
5.48M
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 : 1];
7846
5.48M
    libsais_prefetchr(Ts1 - 1);
7847
5.48M
    sa_sint_t s2 = SA[i - 1 * prefetch_distance - 0];
7848
5.48M
    if (s2 > 0) {
7849
2.82M
      libsais_prefetchw(&induction_bucket[T[s2 - 1]]);
7850
2.82M
      libsais_prefetchr(&T[s2] - 2);
7851
2.82M
    }
7852
5.48M
    sa_sint_t s3 = SA[i - 1 * prefetch_distance - 1];
7853
5.48M
    if (s3 > 0) {
7854
2.82M
      libsais_prefetchw(&induction_bucket[T[s3 - 1]]);
7855
2.82M
      libsais_prefetchr(&T[s3] - 2);
7856
2.82M
    }
7857
7858
5.48M
    sa_sint_t p0 = SA[i - 0];
7859
5.48M
    SA[i - 0] = p0 & SAINT_MAX;
7860
5.48M
    if (p0 > 0) {
7861
3.05M
      p0--;
7862
3.05M
      SA[--induction_bucket[T[p0]]] =
7863
3.05M
          p0 |
7864
3.05M
          (sa_sint_t)((sa_uint_t)(T[p0 - (p0 > 0)] > T[p0]) << (SAINT_BIT - 1));
7865
3.05M
    }
7866
5.48M
    sa_sint_t p1 = SA[i - 1];
7867
5.48M
    SA[i - 1] = p1 & SAINT_MAX;
7868
5.48M
    if (p1 > 0) {
7869
3.04M
      p1--;
7870
3.04M
      SA[--induction_bucket[T[p1]]] =
7871
3.04M
          p1 |
7872
3.04M
          (sa_sint_t)((sa_uint_t)(T[p1 - (p1 > 0)] > T[p1]) << (SAINT_BIT - 1));
7873
3.04M
    }
7874
5.48M
  }
7875
7876
1.17M
  for (j -= 2 * prefetch_distance + 1; i >= j; i -= 1) {
7877
1.15M
    sa_sint_t p = SA[i];
7878
1.15M
    SA[i] = p & SAINT_MAX;
7879
1.15M
    if (p > 0) {
7880
337k
      p--;
7881
337k
      SA[--induction_bucket[T[p]]] =
7882
337k
          p |
7883
337k
          (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] > T[p]) << (SAINT_BIT - 1));
7884
337k
    }
7885
1.15M
  }
7886
17.0k
}
7887
7888
#if defined(LIBSAIS_OPENMP)
7889
7890
static fast_sint_t libsais_final_bwt_scan_right_to_left_8u_block_prepare(
7891
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
7892
    sa_sint_t* RESTRICT buckets, LIBSAIS_THREAD_CACHE* RESTRICT cache,
7893
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
7894
  const fast_sint_t prefetch_distance = 64;
7895
7896
  memset(buckets, 0, (size_t)k * sizeof(sa_sint_t));
7897
7898
  fast_sint_t i, j, count = 0;
7899
  for (i = omp_block_start + omp_block_size - 1,
7900
      j = omp_block_start + prefetch_distance + 1;
7901
       i >= j; i -= 2) {
7902
    libsais_prefetchw(&SA[i - 2 * prefetch_distance]);
7903
7904
    sa_sint_t s0 = SA[i - prefetch_distance - 0];
7905
    const uint8_t* Ts0 = &T[s0 > 0 ? s0 : 2];
7906
    libsais_prefetchr(Ts0 - 1);
7907
    libsais_prefetchr(Ts0 - 2);
7908
    sa_sint_t s1 = SA[i - prefetch_distance - 1];
7909
    const uint8_t* Ts1 = &T[s1 > 0 ? s1 : 2];
7910
    libsais_prefetchr(Ts1 - 1);
7911
    libsais_prefetchr(Ts1 - 2);
7912
7913
    sa_sint_t p0 = SA[i - 0];
7914
    SA[i - 0] = p0 & SAINT_MAX;
7915
    if (p0 > 0) {
7916
      p0--;
7917
      uint8_t c0 = T[p0 - (p0 > 0)], c1 = T[p0];
7918
      SA[i - 0] = c1;
7919
      sa_sint_t t = c0 | SAINT_MIN;
7920
      buckets[cache[count].symbol = c1]++;
7921
      cache[count++].index = (c0 <= c1) ? p0 : t;
7922
    }
7923
    sa_sint_t p1 = SA[i - 1];
7924
    SA[i - 1] = p1 & SAINT_MAX;
7925
    if (p1 > 0) {
7926
      p1--;
7927
      uint8_t c0 = T[p1 - (p1 > 0)], c1 = T[p1];
7928
      SA[i - 1] = c1;
7929
      sa_sint_t t = c0 | SAINT_MIN;
7930
      buckets[cache[count].symbol = c1]++;
7931
      cache[count++].index = (c0 <= c1) ? p1 : t;
7932
    }
7933
  }
7934
7935
  for (j -= prefetch_distance + 1; i >= j; i -= 1) {
7936
    sa_sint_t p = SA[i];
7937
    SA[i] = p & SAINT_MAX;
7938
    if (p > 0) {
7939
      p--;
7940
      uint8_t c0 = T[p - (p > 0)], c1 = T[p];
7941
      SA[i] = c1;
7942
      sa_sint_t t = c0 | SAINT_MIN;
7943
      buckets[cache[count].symbol = c1]++;
7944
      cache[count++].index = (c0 <= c1) ? p : t;
7945
    }
7946
  }
7947
7948
  return count;
7949
}
7950
7951
static fast_sint_t libsais_final_bwt_aux_scan_right_to_left_8u_block_prepare(
7952
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
7953
    sa_sint_t* RESTRICT buckets, LIBSAIS_THREAD_CACHE* RESTRICT cache,
7954
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
7955
  const fast_sint_t prefetch_distance = 64;
7956
7957
  memset(buckets, 0, (size_t)k * sizeof(sa_sint_t));
7958
7959
  fast_sint_t i, j, count = 0;
7960
  for (i = omp_block_start + omp_block_size - 1,
7961
      j = omp_block_start + prefetch_distance + 1;
7962
       i >= j; i -= 2) {
7963
    libsais_prefetchw(&SA[i - 2 * prefetch_distance]);
7964
7965
    sa_sint_t s0 = SA[i - prefetch_distance - 0];
7966
    const uint8_t* Ts0 = &T[s0 > 0 ? s0 : 2];
7967
    libsais_prefetchr(Ts0 - 1);
7968
    libsais_prefetchr(Ts0 - 2);
7969
    sa_sint_t s1 = SA[i - prefetch_distance - 1];
7970
    const uint8_t* Ts1 = &T[s1 > 0 ? s1 : 2];
7971
    libsais_prefetchr(Ts1 - 1);
7972
    libsais_prefetchr(Ts1 - 2);
7973
7974
    sa_sint_t p0 = SA[i - 0];
7975
    SA[i - 0] = p0 & SAINT_MAX;
7976
    if (p0 > 0) {
7977
      p0--;
7978
      uint8_t c0 = T[p0 - (p0 > 0)], c1 = T[p0];
7979
      SA[i - 0] = c1;
7980
      sa_sint_t t = c0 | SAINT_MIN;
7981
      buckets[cache[count].symbol = c1]++;
7982
      cache[count].index = (c0 <= c1) ? p0 : t;
7983
      cache[count + 1].index = p0;
7984
      count += 2;
7985
    }
7986
    sa_sint_t p1 = SA[i - 1];
7987
    SA[i - 1] = p1 & SAINT_MAX;
7988
    if (p1 > 0) {
7989
      p1--;
7990
      uint8_t c0 = T[p1 - (p1 > 0)], c1 = T[p1];
7991
      SA[i - 1] = c1;
7992
      sa_sint_t t = c0 | SAINT_MIN;
7993
      buckets[cache[count].symbol = c1]++;
7994
      cache[count].index = (c0 <= c1) ? p1 : t;
7995
      cache[count + 1].index = p1;
7996
      count += 2;
7997
    }
7998
  }
7999
8000
  for (j -= prefetch_distance + 1; i >= j; i -= 1) {
8001
    sa_sint_t p = SA[i];
8002
    SA[i] = p & SAINT_MAX;
8003
    if (p > 0) {
8004
      p--;
8005
      uint8_t c0 = T[p - (p > 0)], c1 = T[p];
8006
      SA[i] = c1;
8007
      sa_sint_t t = c0 | SAINT_MIN;
8008
      buckets[cache[count].symbol = c1]++;
8009
      cache[count].index = (c0 <= c1) ? p : t;
8010
      cache[count + 1].index = p;
8011
      count += 2;
8012
    }
8013
  }
8014
8015
  return count;
8016
}
8017
8018
static fast_sint_t libsais_final_sorting_scan_right_to_left_8u_block_prepare(
8019
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
8020
    sa_sint_t* RESTRICT buckets, LIBSAIS_THREAD_CACHE* RESTRICT cache,
8021
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
8022
  const fast_sint_t prefetch_distance = 64;
8023
8024
  memset(buckets, 0, (size_t)k * sizeof(sa_sint_t));
8025
8026
  fast_sint_t i, j, count = 0;
8027
  for (i = omp_block_start + omp_block_size - 1,
8028
      j = omp_block_start + prefetch_distance + 1;
8029
       i >= j; i -= 2) {
8030
    libsais_prefetchw(&SA[i - 2 * prefetch_distance]);
8031
8032
    sa_sint_t s0 = SA[i - prefetch_distance - 0];
8033
    const uint8_t* Ts0 = &T[s0 > 0 ? s0 : 2];
8034
    libsais_prefetchr(Ts0 - 1);
8035
    libsais_prefetchr(Ts0 - 2);
8036
    sa_sint_t s1 = SA[i - prefetch_distance - 1];
8037
    const uint8_t* Ts1 = &T[s1 > 0 ? s1 : 2];
8038
    libsais_prefetchr(Ts1 - 1);
8039
    libsais_prefetchr(Ts1 - 2);
8040
8041
    sa_sint_t p0 = SA[i - 0];
8042
    SA[i - 0] = p0 & SAINT_MAX;
8043
    if (p0 > 0) {
8044
      p0--;
8045
      buckets[cache[count].symbol = T[p0]]++;
8046
      cache[count++].index =
8047
          p0 |
8048
          (sa_sint_t)((sa_uint_t)(T[p0 - (p0 > 0)] > T[p0]) << (SAINT_BIT - 1));
8049
    }
8050
    sa_sint_t p1 = SA[i - 1];
8051
    SA[i - 1] = p1 & SAINT_MAX;
8052
    if (p1 > 0) {
8053
      p1--;
8054
      buckets[cache[count].symbol = T[p1]]++;
8055
      cache[count++].index =
8056
          p1 |
8057
          (sa_sint_t)((sa_uint_t)(T[p1 - (p1 > 0)] > T[p1]) << (SAINT_BIT - 1));
8058
    }
8059
  }
8060
8061
  for (j -= prefetch_distance + 1; i >= j; i -= 1) {
8062
    sa_sint_t p = SA[i];
8063
    SA[i] = p & SAINT_MAX;
8064
    if (p > 0) {
8065
      p--;
8066
      buckets[cache[count].symbol = T[p]]++;
8067
      cache[count++].index = p | (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] > T[p])
8068
                                             << (SAINT_BIT - 1));
8069
    }
8070
  }
8071
8072
  return count;
8073
}
8074
8075
static void libsais_final_order_scan_right_to_left_8u_block_place(
8076
    sa_sint_t* RESTRICT SA, sa_sint_t* RESTRICT buckets,
8077
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t count) {
8078
  const fast_sint_t prefetch_distance = 64;
8079
8080
  fast_sint_t i, j;
8081
  for (i = 0, j = count - 3; i < j; i += 4) {
8082
    libsais_prefetchr(&cache[i + prefetch_distance]);
8083
8084
    SA[--buckets[cache[i + 0].symbol]] = cache[i + 0].index;
8085
    SA[--buckets[cache[i + 1].symbol]] = cache[i + 1].index;
8086
    SA[--buckets[cache[i + 2].symbol]] = cache[i + 2].index;
8087
    SA[--buckets[cache[i + 3].symbol]] = cache[i + 3].index;
8088
  }
8089
8090
  for (j += 3; i < j; i += 1) {
8091
    SA[--buckets[cache[i].symbol]] = cache[i].index;
8092
  }
8093
}
8094
8095
static void libsais_final_gsa_scan_right_to_left_8u_block_place(
8096
    sa_sint_t* RESTRICT SA, sa_sint_t* RESTRICT buckets,
8097
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t count) {
8098
  const fast_sint_t prefetch_distance = 64;
8099
8100
  fast_sint_t i, j;
8101
  for (i = 0, j = count - 3; i < j; i += 4) {
8102
    libsais_prefetchr(&cache[i + prefetch_distance]);
8103
8104
    if (cache[i + 0].symbol > 0) {
8105
      SA[--buckets[cache[i + 0].symbol]] = cache[i + 0].index;
8106
    }
8107
    if (cache[i + 1].symbol > 0) {
8108
      SA[--buckets[cache[i + 1].symbol]] = cache[i + 1].index;
8109
    }
8110
    if (cache[i + 2].symbol > 0) {
8111
      SA[--buckets[cache[i + 2].symbol]] = cache[i + 2].index;
8112
    }
8113
    if (cache[i + 3].symbol > 0) {
8114
      SA[--buckets[cache[i + 3].symbol]] = cache[i + 3].index;
8115
    }
8116
  }
8117
8118
  for (j += 3; i < j; i += 1) {
8119
    if (cache[i].symbol > 0) {
8120
      SA[--buckets[cache[i].symbol]] = cache[i].index;
8121
    }
8122
  }
8123
}
8124
8125
static void libsais_final_bwt_aux_scan_right_to_left_8u_block_place(
8126
    sa_sint_t* RESTRICT SA, sa_sint_t rm, sa_sint_t* RESTRICT I,
8127
    sa_sint_t* RESTRICT buckets, LIBSAIS_THREAD_CACHE* RESTRICT cache,
8128
    fast_sint_t count) {
8129
  const fast_sint_t prefetch_distance = 64;
8130
8131
  fast_sint_t i, j;
8132
  for (i = 0, j = count - 6; i < j; i += 8) {
8133
    libsais_prefetchr(&cache[i + prefetch_distance]);
8134
8135
    SA[--buckets[cache[i + 0].symbol]] = cache[i + 0].index;
8136
    if ((cache[i + 1].index & rm) == 0) {
8137
      I[cache[i + 1].index / (rm + 1)] = buckets[cache[i + 0].symbol] + 1;
8138
    }
8139
    SA[--buckets[cache[i + 2].symbol]] = cache[i + 2].index;
8140
    if ((cache[i + 3].index & rm) == 0) {
8141
      I[cache[i + 3].index / (rm + 1)] = buckets[cache[i + 2].symbol] + 1;
8142
    }
8143
    SA[--buckets[cache[i + 4].symbol]] = cache[i + 4].index;
8144
    if ((cache[i + 5].index & rm) == 0) {
8145
      I[cache[i + 5].index / (rm + 1)] = buckets[cache[i + 4].symbol] + 1;
8146
    }
8147
    SA[--buckets[cache[i + 6].symbol]] = cache[i + 6].index;
8148
    if ((cache[i + 7].index & rm) == 0) {
8149
      I[cache[i + 7].index / (rm + 1)] = buckets[cache[i + 6].symbol] + 1;
8150
    }
8151
  }
8152
8153
  for (j += 6; i < j; i += 2) {
8154
    SA[--buckets[cache[i].symbol]] = cache[i].index;
8155
    if ((cache[i + 1].index & rm) == 0) {
8156
      I[(cache[i + 1].index & SAINT_MAX) / (rm + 1)] =
8157
          buckets[cache[i].symbol] + 1;
8158
    }
8159
  }
8160
}
8161
8162
static void libsais_final_sorting_scan_right_to_left_32s_block_gather(
8163
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
8164
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t omp_block_start,
8165
    fast_sint_t omp_block_size) {
8166
  const fast_sint_t prefetch_distance = 64;
8167
8168
  fast_sint_t i, j;
8169
  for (i = omp_block_start,
8170
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
8171
       i < j; i += 2) {
8172
    libsais_prefetchw(&SA[i + 2 * prefetch_distance]);
8173
8174
    sa_sint_t s0 = SA[i + prefetch_distance + 0];
8175
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 : 2];
8176
    libsais_prefetchr(Ts0 - 1);
8177
    libsais_prefetchr(Ts0 - 2);
8178
    sa_sint_t s1 = SA[i + prefetch_distance + 1];
8179
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 : 2];
8180
    libsais_prefetchr(Ts1 - 1);
8181
    libsais_prefetchr(Ts1 - 2);
8182
8183
    libsais_prefetchw(&cache[i + prefetch_distance]);
8184
8185
    sa_sint_t symbol0 = SAINT_MIN, p0 = SA[i + 0];
8186
    SA[i + 0] = p0 & SAINT_MAX;
8187
    if (p0 > 0) {
8188
      p0--;
8189
      cache[i + 0].index =
8190
          p0 |
8191
          (sa_sint_t)((sa_uint_t)(T[p0 - (p0 > 0)] > T[p0]) << (SAINT_BIT - 1));
8192
      symbol0 = T[p0];
8193
    }
8194
    cache[i + 0].symbol = symbol0;
8195
    sa_sint_t symbol1 = SAINT_MIN, p1 = SA[i + 1];
8196
    SA[i + 1] = p1 & SAINT_MAX;
8197
    if (p1 > 0) {
8198
      p1--;
8199
      cache[i + 1].index =
8200
          p1 |
8201
          (sa_sint_t)((sa_uint_t)(T[p1 - (p1 > 0)] > T[p1]) << (SAINT_BIT - 1));
8202
      symbol1 = T[p1];
8203
    }
8204
    cache[i + 1].symbol = symbol1;
8205
  }
8206
8207
  for (j += prefetch_distance + 1; i < j; i += 1) {
8208
    sa_sint_t symbol = SAINT_MIN, p = SA[i];
8209
    SA[i] = p & SAINT_MAX;
8210
    if (p > 0) {
8211
      p--;
8212
      cache[i].index = p | (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] > T[p])
8213
                                       << (SAINT_BIT - 1));
8214
      symbol = T[p];
8215
    }
8216
    cache[i].symbol = symbol;
8217
  }
8218
}
8219
8220
static void libsais_final_sorting_scan_right_to_left_32s_block_sort(
8221
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT induction_bucket,
8222
    LIBSAIS_THREAD_CACHE* RESTRICT cache, fast_sint_t omp_block_start,
8223
    fast_sint_t omp_block_size) {
8224
  const fast_sint_t prefetch_distance = 64;
8225
8226
  fast_sint_t i, j;
8227
  for (i = omp_block_start + omp_block_size - 1,
8228
      j = omp_block_start + prefetch_distance + 1;
8229
       i >= j; i -= 2) {
8230
    libsais_prefetchw(&cache[i - 2 * prefetch_distance]);
8231
8232
    sa_sint_t s0 = cache[i - prefetch_distance - 0].symbol;
8233
    const sa_sint_t* Is0 = &induction_bucket[s0 > 0 ? s0 : 0];
8234
    libsais_prefetchw(Is0);
8235
    sa_sint_t s1 = cache[i - prefetch_distance - 1].symbol;
8236
    const sa_sint_t* Is1 = &induction_bucket[s1 > 0 ? s1 : 0];
8237
    libsais_prefetchw(Is1);
8238
8239
    sa_sint_t v0 = cache[i - 0].symbol;
8240
    if (v0 >= 0) {
8241
      cache[i - 0].symbol = --induction_bucket[v0];
8242
      if (cache[i - 0].symbol >= omp_block_start) {
8243
        sa_sint_t ni = cache[i - 0].symbol, np = cache[i - 0].index;
8244
        cache[i - 0].index = np & SAINT_MAX;
8245
        if (np > 0) {
8246
          np--;
8247
          cache[ni].index =
8248
              np | (sa_sint_t)((sa_uint_t)(T[np - (np > 0)] > T[np])
8249
                               << (SAINT_BIT - 1));
8250
          cache[ni].symbol = T[np];
8251
        }
8252
      }
8253
    }
8254
8255
    sa_sint_t v1 = cache[i - 1].symbol;
8256
    if (v1 >= 0) {
8257
      cache[i - 1].symbol = --induction_bucket[v1];
8258
      if (cache[i - 1].symbol >= omp_block_start) {
8259
        sa_sint_t ni = cache[i - 1].symbol, np = cache[i - 1].index;
8260
        cache[i - 1].index = np & SAINT_MAX;
8261
        if (np > 0) {
8262
          np--;
8263
          cache[ni].index =
8264
              np | (sa_sint_t)((sa_uint_t)(T[np - (np > 0)] > T[np])
8265
                               << (SAINT_BIT - 1));
8266
          cache[ni].symbol = T[np];
8267
        }
8268
      }
8269
    }
8270
  }
8271
8272
  for (j -= prefetch_distance + 1; i >= j; i -= 1) {
8273
    sa_sint_t v = cache[i].symbol;
8274
    if (v >= 0) {
8275
      cache[i].symbol = --induction_bucket[v];
8276
      if (cache[i].symbol >= omp_block_start) {
8277
        sa_sint_t ni = cache[i].symbol, np = cache[i].index;
8278
        cache[i].index = np & SAINT_MAX;
8279
        if (np > 0) {
8280
          np--;
8281
          cache[ni].index =
8282
              np | (sa_sint_t)((sa_uint_t)(T[np - (np > 0)] > T[np])
8283
                               << (SAINT_BIT - 1));
8284
          cache[ni].symbol = T[np];
8285
        }
8286
      }
8287
    }
8288
  }
8289
}
8290
8291
static void libsais_final_bwt_scan_right_to_left_8u_block_omp(
8292
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
8293
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t block_start,
8294
    fast_sint_t block_size, sa_sint_t threads,
8295
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8296
#if defined(LIBSAIS_OPENMP)
8297
#pragma omp parallel num_threads(                                             \
8298
        threads) if (threads > 1 && block_size >= 64 * (k > 256 ? k : 256) && \
8299
                         omp_get_dynamic() == 0)
8300
#endif
8301
  {
8302
#if defined(LIBSAIS_OPENMP)
8303
    fast_sint_t omp_thread_num = omp_get_thread_num();
8304
    fast_sint_t omp_num_threads = omp_get_num_threads();
8305
#else
8306
    UNUSED(k);
8307
    UNUSED(threads);
8308
    UNUSED(thread_state);
8309
8310
    fast_sint_t omp_thread_num = 0;
8311
    fast_sint_t omp_num_threads = 1;
8312
#endif
8313
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
8314
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
8315
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
8316
                                     ? omp_block_stride
8317
                                     : block_size - omp_block_start;
8318
8319
    omp_block_start += block_start;
8320
8321
    if (omp_num_threads == 1) {
8322
      libsais_final_bwt_scan_right_to_left_8u(T, SA, induction_bucket,
8323
                                              omp_block_start, omp_block_size);
8324
    }
8325
#if defined(LIBSAIS_OPENMP)
8326
    else {
8327
      {
8328
        thread_state[omp_thread_num].state.count =
8329
            libsais_final_bwt_scan_right_to_left_8u_block_prepare(
8330
                T, SA, k, thread_state[omp_thread_num].state.buckets,
8331
                thread_state[omp_thread_num].state.cache, omp_block_start,
8332
                omp_block_size);
8333
      }
8334
8335
#pragma omp barrier
8336
8337
#pragma omp master
8338
      {
8339
        fast_sint_t t;
8340
        for (t = omp_num_threads - 1; t >= 0; --t) {
8341
          sa_sint_t* RESTRICT temp_bucket = thread_state[t].state.buckets;
8342
          fast_sint_t c;
8343
          for (c = 0; c < k; c += 1) {
8344
            sa_sint_t A = induction_bucket[c], B = temp_bucket[c];
8345
            induction_bucket[c] = A - B;
8346
            temp_bucket[c] = A;
8347
          }
8348
        }
8349
      }
8350
8351
#pragma omp barrier
8352
8353
      {
8354
        libsais_final_order_scan_right_to_left_8u_block_place(
8355
            SA, thread_state[omp_thread_num].state.buckets,
8356
            thread_state[omp_thread_num].state.cache,
8357
            thread_state[omp_thread_num].state.count);
8358
      }
8359
    }
8360
#endif
8361
  }
8362
}
8363
8364
static void libsais_final_bwt_aux_scan_right_to_left_8u_block_omp(
8365
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
8366
    sa_sint_t rm, sa_sint_t* RESTRICT I, sa_sint_t* RESTRICT induction_bucket,
8367
    fast_sint_t block_start, fast_sint_t block_size, sa_sint_t threads,
8368
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8369
#if defined(LIBSAIS_OPENMP)
8370
#pragma omp parallel num_threads(                                             \
8371
        threads) if (threads > 1 && block_size >= 64 * (k > 256 ? k : 256) && \
8372
                         omp_get_dynamic() == 0)
8373
#endif
8374
  {
8375
#if defined(LIBSAIS_OPENMP)
8376
    fast_sint_t omp_thread_num = omp_get_thread_num();
8377
    fast_sint_t omp_num_threads = omp_get_num_threads();
8378
#else
8379
    UNUSED(k);
8380
    UNUSED(threads);
8381
    UNUSED(thread_state);
8382
8383
    fast_sint_t omp_thread_num = 0;
8384
    fast_sint_t omp_num_threads = 1;
8385
#endif
8386
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
8387
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
8388
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
8389
                                     ? omp_block_stride
8390
                                     : block_size - omp_block_start;
8391
8392
    omp_block_start += block_start;
8393
8394
    if (omp_num_threads == 1) {
8395
      libsais_final_bwt_aux_scan_right_to_left_8u(
8396
          T, SA, rm, I, induction_bucket, omp_block_start, omp_block_size);
8397
    }
8398
#if defined(LIBSAIS_OPENMP)
8399
    else {
8400
      {
8401
        thread_state[omp_thread_num].state.count =
8402
            libsais_final_bwt_aux_scan_right_to_left_8u_block_prepare(
8403
                T, SA, k, thread_state[omp_thread_num].state.buckets,
8404
                thread_state[omp_thread_num].state.cache, omp_block_start,
8405
                omp_block_size);
8406
      }
8407
8408
#pragma omp barrier
8409
8410
#pragma omp master
8411
      {
8412
        fast_sint_t t;
8413
        for (t = omp_num_threads - 1; t >= 0; --t) {
8414
          sa_sint_t* RESTRICT temp_bucket = thread_state[t].state.buckets;
8415
          fast_sint_t c;
8416
          for (c = 0; c < k; c += 1) {
8417
            sa_sint_t A = induction_bucket[c], B = temp_bucket[c];
8418
            induction_bucket[c] = A - B;
8419
            temp_bucket[c] = A;
8420
          }
8421
        }
8422
      }
8423
8424
#pragma omp barrier
8425
8426
      {
8427
        libsais_final_bwt_aux_scan_right_to_left_8u_block_place(
8428
            SA, rm, I, thread_state[omp_thread_num].state.buckets,
8429
            thread_state[omp_thread_num].state.cache,
8430
            thread_state[omp_thread_num].state.count);
8431
      }
8432
    }
8433
#endif
8434
  }
8435
}
8436
8437
static void libsais_final_sorting_scan_right_to_left_8u_block_omp(
8438
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
8439
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t block_start,
8440
    fast_sint_t block_size, sa_sint_t threads,
8441
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8442
#if defined(LIBSAIS_OPENMP)
8443
#pragma omp parallel num_threads(                                             \
8444
        threads) if (threads > 1 && block_size >= 64 * (k > 256 ? k : 256) && \
8445
                         omp_get_dynamic() == 0)
8446
#endif
8447
  {
8448
#if defined(LIBSAIS_OPENMP)
8449
    fast_sint_t omp_thread_num = omp_get_thread_num();
8450
    fast_sint_t omp_num_threads = omp_get_num_threads();
8451
#else
8452
    UNUSED(k);
8453
    UNUSED(threads);
8454
    UNUSED(thread_state);
8455
8456
    fast_sint_t omp_thread_num = 0;
8457
    fast_sint_t omp_num_threads = 1;
8458
#endif
8459
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
8460
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
8461
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
8462
                                     ? omp_block_stride
8463
                                     : block_size - omp_block_start;
8464
8465
    omp_block_start += block_start;
8466
8467
    if (omp_num_threads == 1) {
8468
      libsais_final_sorting_scan_right_to_left_8u(
8469
          T, SA, induction_bucket, omp_block_start, omp_block_size);
8470
    }
8471
#if defined(LIBSAIS_OPENMP)
8472
    else {
8473
      {
8474
        thread_state[omp_thread_num].state.count =
8475
            libsais_final_sorting_scan_right_to_left_8u_block_prepare(
8476
                T, SA, k, thread_state[omp_thread_num].state.buckets,
8477
                thread_state[omp_thread_num].state.cache, omp_block_start,
8478
                omp_block_size);
8479
      }
8480
8481
#pragma omp barrier
8482
8483
#pragma omp master
8484
      {
8485
        fast_sint_t t;
8486
        for (t = omp_num_threads - 1; t >= 0; --t) {
8487
          sa_sint_t* RESTRICT temp_bucket = thread_state[t].state.buckets;
8488
          fast_sint_t c;
8489
          for (c = 0; c < k; c += 1) {
8490
            sa_sint_t A = induction_bucket[c], B = temp_bucket[c];
8491
            induction_bucket[c] = A - B;
8492
            temp_bucket[c] = A;
8493
          }
8494
        }
8495
      }
8496
8497
#pragma omp barrier
8498
8499
      {
8500
        libsais_final_order_scan_right_to_left_8u_block_place(
8501
            SA, thread_state[omp_thread_num].state.buckets,
8502
            thread_state[omp_thread_num].state.cache,
8503
            thread_state[omp_thread_num].state.count);
8504
      }
8505
    }
8506
#endif
8507
  }
8508
}
8509
8510
static void libsais_final_gsa_scan_right_to_left_8u_block_omp(
8511
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t k,
8512
    sa_sint_t* RESTRICT induction_bucket, fast_sint_t block_start,
8513
    fast_sint_t block_size, sa_sint_t threads,
8514
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8515
#if defined(LIBSAIS_OPENMP)
8516
#pragma omp parallel num_threads(                                             \
8517
        threads) if (threads > 1 && block_size >= 64 * (k > 256 ? k : 256) && \
8518
                         omp_get_dynamic() == 0)
8519
#endif
8520
  {
8521
#if defined(LIBSAIS_OPENMP)
8522
    fast_sint_t omp_thread_num = omp_get_thread_num();
8523
    fast_sint_t omp_num_threads = omp_get_num_threads();
8524
#else
8525
    UNUSED(k);
8526
    UNUSED(threads);
8527
    UNUSED(thread_state);
8528
8529
    fast_sint_t omp_thread_num = 0;
8530
    fast_sint_t omp_num_threads = 1;
8531
#endif
8532
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
8533
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
8534
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
8535
                                     ? omp_block_stride
8536
                                     : block_size - omp_block_start;
8537
8538
    omp_block_start += block_start;
8539
8540
    if (omp_num_threads == 1) {
8541
      libsais_final_gsa_scan_right_to_left_8u(T, SA, induction_bucket,
8542
                                              omp_block_start, omp_block_size);
8543
    }
8544
#if defined(LIBSAIS_OPENMP)
8545
    else {
8546
      {
8547
        thread_state[omp_thread_num].state.count =
8548
            libsais_final_sorting_scan_right_to_left_8u_block_prepare(
8549
                T, SA, k, thread_state[omp_thread_num].state.buckets,
8550
                thread_state[omp_thread_num].state.cache, omp_block_start,
8551
                omp_block_size);
8552
      }
8553
8554
#pragma omp barrier
8555
8556
#pragma omp master
8557
      {
8558
        fast_sint_t t;
8559
        for (t = omp_num_threads - 1; t >= 0; --t) {
8560
          sa_sint_t* RESTRICT temp_bucket = thread_state[t].state.buckets;
8561
          fast_sint_t c;
8562
          for (c = 0; c < k; c += 1) {
8563
            sa_sint_t A = induction_bucket[c], B = temp_bucket[c];
8564
            induction_bucket[c] = A - B;
8565
            temp_bucket[c] = A;
8566
          }
8567
        }
8568
      }
8569
8570
#pragma omp barrier
8571
8572
      {
8573
        libsais_final_gsa_scan_right_to_left_8u_block_place(
8574
            SA, thread_state[omp_thread_num].state.buckets,
8575
            thread_state[omp_thread_num].state.cache,
8576
            thread_state[omp_thread_num].state.count);
8577
      }
8578
    }
8579
#endif
8580
  }
8581
}
8582
8583
static void libsais_final_sorting_scan_right_to_left_32s_block_omp(
8584
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA,
8585
    sa_sint_t* RESTRICT buckets, LIBSAIS_THREAD_CACHE* RESTRICT cache,
8586
    fast_sint_t block_start, fast_sint_t block_size, sa_sint_t threads) {
8587
#if defined(LIBSAIS_OPENMP)
8588
#pragma omp parallel num_threads(threads) if (threads > 1 && \
8589
                                                  block_size >= 16384)
8590
#endif
8591
  {
8592
#if defined(LIBSAIS_OPENMP)
8593
    fast_sint_t omp_thread_num = omp_get_thread_num();
8594
    fast_sint_t omp_num_threads = omp_get_num_threads();
8595
#else
8596
    UNUSED(threads);
8597
    UNUSED(cache);
8598
8599
    fast_sint_t omp_thread_num = 0;
8600
    fast_sint_t omp_num_threads = 1;
8601
#endif
8602
    fast_sint_t omp_block_stride = (block_size / omp_num_threads) & (-16);
8603
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
8604
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
8605
                                     ? omp_block_stride
8606
                                     : block_size - omp_block_start;
8607
8608
    omp_block_start += block_start;
8609
8610
    if (omp_num_threads == 1) {
8611
      libsais_final_sorting_scan_right_to_left_32s(
8612
          T, SA, buckets, omp_block_start, omp_block_size);
8613
    }
8614
#if defined(LIBSAIS_OPENMP)
8615
    else {
8616
      {
8617
        libsais_final_sorting_scan_right_to_left_32s_block_gather(
8618
            T, SA, cache - block_start, omp_block_start, omp_block_size);
8619
      }
8620
8621
#pragma omp barrier
8622
8623
#pragma omp master
8624
      {
8625
        libsais_final_sorting_scan_right_to_left_32s_block_sort(
8626
            T, buckets, cache - block_start, block_start, block_size);
8627
      }
8628
8629
#pragma omp barrier
8630
8631
      {
8632
        libsais_compact_and_place_cached_suffixes(
8633
            SA, cache - block_start, omp_block_start, omp_block_size);
8634
      }
8635
    }
8636
#endif
8637
  }
8638
}
8639
8640
#endif
8641
8642
static sa_sint_t libsais_final_bwt_scan_right_to_left_8u_omp(
8643
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t k,
8644
    sa_sint_t* RESTRICT induction_bucket, sa_sint_t threads,
8645
0
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8646
0
  sa_sint_t index = -1;
8647
8648
0
  if (threads == 1 || n < 65536) {
8649
0
    index =
8650
0
        libsais_final_bwt_scan_right_to_left_8u(T, SA, induction_bucket, 0, n);
8651
0
  }
8652
#if defined(LIBSAIS_OPENMP)
8653
  else {
8654
    fast_sint_t block_start;
8655
    for (block_start = (fast_sint_t)n - 1; block_start >= 0;) {
8656
      if (SA[block_start] == 0) {
8657
        index = (sa_sint_t)block_start--;
8658
      } else {
8659
        fast_sint_t block_max_end =
8660
            block_start -
8661
            ((fast_sint_t)threads) *
8662
                (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * (fast_sint_t)threads);
8663
        if (block_max_end < 0) {
8664
          block_max_end = -1;
8665
        }
8666
        fast_sint_t block_end = block_start - 1;
8667
        while (block_end > block_max_end && SA[block_end] != 0) {
8668
          block_end--;
8669
        }
8670
        fast_sint_t block_size = block_start - block_end;
8671
8672
        if (block_size < 32) {
8673
          for (; block_start > block_end; block_start -= 1) {
8674
            sa_sint_t p = SA[block_start];
8675
            SA[block_start] = p & SAINT_MAX;
8676
            if (p > 0) {
8677
              p--;
8678
              uint8_t c0 = T[p - (p > 0)], c1 = T[p];
8679
              SA[block_start] = c1;
8680
              sa_sint_t t = c0 | SAINT_MIN;
8681
              SA[--induction_bucket[c1]] = (c0 <= c1) ? p : t;
8682
            }
8683
          }
8684
        } else {
8685
          libsais_final_bwt_scan_right_to_left_8u_block_omp(
8686
              T, SA, k, induction_bucket, block_end + 1, block_size, threads,
8687
              thread_state);
8688
          block_start = block_end;
8689
        }
8690
      }
8691
    }
8692
  }
8693
#else
8694
0
  UNUSED(k);
8695
0
  UNUSED(thread_state);
8696
0
#endif
8697
8698
0
  return index;
8699
0
}
8700
8701
static void libsais_final_bwt_aux_scan_right_to_left_8u_omp(
8702
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t k,
8703
    sa_sint_t rm, sa_sint_t* RESTRICT I, sa_sint_t* RESTRICT induction_bucket,
8704
0
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8705
0
  if (threads == 1 || n < 65536) {
8706
0
    libsais_final_bwt_aux_scan_right_to_left_8u(T, SA, rm, I, induction_bucket,
8707
0
                                                0, n);
8708
0
  }
8709
#if defined(LIBSAIS_OPENMP)
8710
  else {
8711
    fast_sint_t block_start;
8712
    for (block_start = (fast_sint_t)n - 1; block_start >= 0;) {
8713
      if (SA[block_start] == 0) {
8714
        block_start--;
8715
      } else {
8716
        fast_sint_t block_max_end =
8717
            block_start -
8718
            ((fast_sint_t)threads) *
8719
                ((LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * (fast_sint_t)threads) /
8720
                 2);
8721
        if (block_max_end < 0) {
8722
          block_max_end = -1;
8723
        }
8724
        fast_sint_t block_end = block_start - 1;
8725
        while (block_end > block_max_end && SA[block_end] != 0) {
8726
          block_end--;
8727
        }
8728
        fast_sint_t block_size = block_start - block_end;
8729
8730
        if (block_size < 32) {
8731
          for (; block_start > block_end; block_start -= 1) {
8732
            sa_sint_t p = SA[block_start];
8733
            SA[block_start] = p & SAINT_MAX;
8734
            if (p > 0) {
8735
              p--;
8736
              uint8_t c0 = T[p - (p > 0)], c1 = T[p];
8737
              SA[block_start] = c1;
8738
              sa_sint_t t = c0 | SAINT_MIN;
8739
              SA[--induction_bucket[c1]] = (c0 <= c1) ? p : t;
8740
              if ((p & rm) == 0) {
8741
                I[p / (rm + 1)] = induction_bucket[T[p]] + 1;
8742
              }
8743
            }
8744
          }
8745
        } else {
8746
          libsais_final_bwt_aux_scan_right_to_left_8u_block_omp(
8747
              T, SA, k, rm, I, induction_bucket, block_end + 1, block_size,
8748
              threads, thread_state);
8749
          block_start = block_end;
8750
        }
8751
      }
8752
    }
8753
  }
8754
#else
8755
0
  UNUSED(k);
8756
0
  UNUSED(thread_state);
8757
0
#endif
8758
0
}
8759
8760
static void libsais_final_sorting_scan_right_to_left_8u_omp(
8761
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA,
8762
    fast_sint_t omp_block_start, fast_sint_t omp_block_size, sa_sint_t k,
8763
    sa_sint_t* RESTRICT induction_bucket, sa_sint_t threads,
8764
8.54k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8765
8.54k
  if (threads == 1 || omp_block_size < 65536) {
8766
8.54k
    libsais_final_sorting_scan_right_to_left_8u(
8767
8.54k
        T, SA, induction_bucket, omp_block_start, omp_block_size);
8768
8.54k
  }
8769
#if defined(LIBSAIS_OPENMP)
8770
  else {
8771
    fast_sint_t block_start;
8772
    for (block_start = omp_block_start + omp_block_size - 1;
8773
         block_start >= omp_block_start;) {
8774
      if (SA[block_start] == 0) {
8775
        block_start--;
8776
      } else {
8777
        fast_sint_t block_max_end =
8778
            block_start -
8779
            ((fast_sint_t)threads) *
8780
                (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * (fast_sint_t)threads);
8781
        if (block_max_end < omp_block_start) {
8782
          block_max_end = omp_block_start - 1;
8783
        }
8784
        fast_sint_t block_end = block_start - 1;
8785
        while (block_end > block_max_end && SA[block_end] != 0) {
8786
          block_end--;
8787
        }
8788
        fast_sint_t block_size = block_start - block_end;
8789
8790
        if (block_size < 32) {
8791
          for (; block_start > block_end; block_start -= 1) {
8792
            sa_sint_t p = SA[block_start];
8793
            SA[block_start] = p & SAINT_MAX;
8794
            if (p > 0) {
8795
              p--;
8796
              SA[--induction_bucket[T[p]]] =
8797
                  p | (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] > T[p])
8798
                                  << (SAINT_BIT - 1));
8799
            }
8800
          }
8801
        } else {
8802
          libsais_final_sorting_scan_right_to_left_8u_block_omp(
8803
              T, SA, k, induction_bucket, block_end + 1, block_size, threads,
8804
              thread_state);
8805
          block_start = block_end;
8806
        }
8807
      }
8808
    }
8809
  }
8810
#else
8811
8.54k
  UNUSED(k);
8812
8.54k
  UNUSED(thread_state);
8813
8.54k
#endif
8814
8.54k
}
8815
8816
static void libsais_final_gsa_scan_right_to_left_8u_omp(
8817
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA,
8818
    fast_sint_t omp_block_start, fast_sint_t omp_block_size, sa_sint_t k,
8819
    sa_sint_t* RESTRICT induction_bucket, sa_sint_t threads,
8820
0
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8821
0
  if (threads == 1 || omp_block_size < 65536) {
8822
0
    libsais_final_gsa_scan_right_to_left_8u(T, SA, induction_bucket,
8823
0
                                            omp_block_start, omp_block_size);
8824
0
  }
8825
#if defined(LIBSAIS_OPENMP)
8826
  else {
8827
    fast_sint_t block_start;
8828
    for (block_start = omp_block_start + omp_block_size - 1;
8829
         block_start >= omp_block_start;) {
8830
      if (SA[block_start] == 0) {
8831
        block_start--;
8832
      } else {
8833
        fast_sint_t block_max_end =
8834
            block_start -
8835
            ((fast_sint_t)threads) *
8836
                (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * (fast_sint_t)threads);
8837
        if (block_max_end < omp_block_start) {
8838
          block_max_end = omp_block_start - 1;
8839
        }
8840
        fast_sint_t block_end = block_start - 1;
8841
        while (block_end > block_max_end && SA[block_end] != 0) {
8842
          block_end--;
8843
        }
8844
        fast_sint_t block_size = block_start - block_end;
8845
8846
        if (block_size < 32) {
8847
          for (; block_start > block_end; block_start -= 1) {
8848
            sa_sint_t p = SA[block_start];
8849
            SA[block_start] = p & SAINT_MAX;
8850
            if (p > 0 && T[p - 1] > 0) {
8851
              p--;
8852
              SA[--induction_bucket[T[p]]] =
8853
                  p | (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] > T[p])
8854
                                  << (SAINT_BIT - 1));
8855
            }
8856
          }
8857
        } else {
8858
          libsais_final_gsa_scan_right_to_left_8u_block_omp(
8859
              T, SA, k, induction_bucket, block_end + 1, block_size, threads,
8860
              thread_state);
8861
          block_start = block_end;
8862
        }
8863
      }
8864
    }
8865
  }
8866
#else
8867
0
  UNUSED(k);
8868
0
  UNUSED(thread_state);
8869
0
#endif
8870
0
}
8871
8872
static void libsais_final_sorting_scan_right_to_left_32s_omp(
8873
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
8874
    sa_sint_t* RESTRICT induction_bucket, sa_sint_t threads,
8875
17.0k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8876
17.0k
  if (threads == 1 || n < 65536) {
8877
17.0k
    libsais_final_sorting_scan_right_to_left_32s(T, SA, induction_bucket, 0, n);
8878
17.0k
  }
8879
#if defined(LIBSAIS_OPENMP)
8880
  else {
8881
    fast_sint_t block_start, block_end;
8882
    for (block_start = (fast_sint_t)n - 1; block_start >= 0;
8883
         block_start = block_end) {
8884
      block_end =
8885
          block_start - (fast_sint_t)threads * LIBSAIS_PER_THREAD_CACHE_SIZE;
8886
      if (block_end < 0) {
8887
        block_end = -1;
8888
      }
8889
8890
      libsais_final_sorting_scan_right_to_left_32s_block_omp(
8891
          T, SA, induction_bucket, thread_state[0].state.cache, block_end + 1,
8892
          block_start - block_end, threads);
8893
    }
8894
  }
8895
#else
8896
17.0k
  UNUSED(thread_state);
8897
17.0k
#endif
8898
17.0k
}
8899
8900
static void libsais_clear_lms_suffixes_omp(sa_sint_t* RESTRICT SA, sa_sint_t n,
8901
                                           sa_sint_t k,
8902
                                           sa_sint_t* RESTRICT bucket_start,
8903
                                           sa_sint_t* RESTRICT bucket_end,
8904
0
                                           sa_sint_t threads) {
8905
0
  fast_sint_t c;
8906
8907
#if defined(LIBSAIS_OPENMP)
8908
#pragma omp parallel for schedule(static, 1) \
8909
    num_threads(threads) if (threads > 1 && n >= 65536)
8910
#else
8911
0
  UNUSED(threads);
8912
0
  UNUSED(n);
8913
0
#endif
8914
0
  for (c = 0; c < k; ++c) {
8915
0
    if (bucket_end[c] > bucket_start[c]) {
8916
0
      memset(&SA[bucket_start[c]], 0,
8917
0
             ((size_t)bucket_end[c] - (size_t)bucket_start[c]) *
8918
0
                 sizeof(sa_sint_t));
8919
0
    }
8920
0
  }
8921
0
}
8922
8923
static sa_sint_t libsais_induce_final_order_8u_omp(
8924
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t k,
8925
    sa_sint_t flags, sa_sint_t r, sa_sint_t* RESTRICT I,
8926
    sa_sint_t* RESTRICT buckets, sa_sint_t threads,
8927
8.54k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8928
8.54k
  if ((flags & LIBSAIS_FLAGS_BWT) == 0) {
8929
8.54k
    if (flags & LIBSAIS_FLAGS_GSA) {
8930
0
      buckets[6 * ALPHABET_SIZE] = buckets[7 * ALPHABET_SIZE] - 1;
8931
0
    }
8932
8933
8.54k
    libsais_final_sorting_scan_left_to_right_8u_omp(
8934
8.54k
        T, SA, n, k, &buckets[6 * ALPHABET_SIZE], threads, thread_state);
8935
8.54k
    if (threads > 1 && n >= 65536) {
8936
0
      libsais_clear_lms_suffixes_omp(SA, n, ALPHABET_SIZE,
8937
0
                                     &buckets[6 * ALPHABET_SIZE],
8938
0
                                     &buckets[7 * ALPHABET_SIZE], threads);
8939
0
    }
8940
8941
8.54k
    if (flags & LIBSAIS_FLAGS_GSA) {
8942
0
      libsais_flip_suffix_markers_omp(SA, buckets[7 * ALPHABET_SIZE], threads);
8943
0
      libsais_final_gsa_scan_right_to_left_8u_omp(
8944
0
          T, SA, buckets[7 * ALPHABET_SIZE],
8945
0
          (fast_sint_t)n - buckets[7 * ALPHABET_SIZE], k,
8946
0
          &buckets[7 * ALPHABET_SIZE], threads, thread_state);
8947
8.54k
    } else {
8948
8.54k
      libsais_final_sorting_scan_right_to_left_8u_omp(
8949
8.54k
          T, SA, 0, n, k, &buckets[7 * ALPHABET_SIZE], threads, thread_state);
8950
8.54k
    }
8951
8952
8.54k
    return 0;
8953
8.54k
  } else if (I != NULL) {
8954
0
    libsais_final_bwt_aux_scan_left_to_right_8u_omp(T, SA, n, k, r - 1, I,
8955
0
                                                    &buckets[6 * ALPHABET_SIZE],
8956
0
                                                    threads, thread_state);
8957
0
    if (threads > 1 && n >= 65536) {
8958
0
      libsais_clear_lms_suffixes_omp(SA, n, ALPHABET_SIZE,
8959
0
                                     &buckets[6 * ALPHABET_SIZE],
8960
0
                                     &buckets[7 * ALPHABET_SIZE], threads);
8961
0
    }
8962
0
    libsais_final_bwt_aux_scan_right_to_left_8u_omp(T, SA, n, k, r - 1, I,
8963
0
                                                    &buckets[7 * ALPHABET_SIZE],
8964
0
                                                    threads, thread_state);
8965
0
    return 0;
8966
0
  } else {
8967
0
    libsais_final_bwt_scan_left_to_right_8u_omp(
8968
0
        T, SA, n, k, &buckets[6 * ALPHABET_SIZE], threads, thread_state);
8969
0
    if (threads > 1 && n >= 65536) {
8970
0
      libsais_clear_lms_suffixes_omp(SA, n, ALPHABET_SIZE,
8971
0
                                     &buckets[6 * ALPHABET_SIZE],
8972
0
                                     &buckets[7 * ALPHABET_SIZE], threads);
8973
0
    }
8974
0
    return libsais_final_bwt_scan_right_to_left_8u_omp(
8975
0
        T, SA, n, k, &buckets[7 * ALPHABET_SIZE], threads, thread_state);
8976
0
  }
8977
8.54k
}
8978
8979
static void libsais_induce_final_order_32s_6k(
8980
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
8981
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t threads,
8982
1.94k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8983
1.94k
  libsais_final_sorting_scan_left_to_right_32s_omp(
8984
1.94k
      T, SA, n, &buckets[4 * (fast_sint_t)k], threads, thread_state);
8985
1.94k
  libsais_final_sorting_scan_right_to_left_32s_omp(
8986
1.94k
      T, SA, n, &buckets[5 * (fast_sint_t)k], threads, thread_state);
8987
1.94k
}
8988
8989
static void libsais_induce_final_order_32s_4k(
8990
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
8991
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t threads,
8992
14.3k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8993
14.3k
  libsais_final_sorting_scan_left_to_right_32s_omp(
8994
14.3k
      T, SA, n, &buckets[2 * (fast_sint_t)k], threads, thread_state);
8995
14.3k
  libsais_final_sorting_scan_right_to_left_32s_omp(
8996
14.3k
      T, SA, n, &buckets[3 * (fast_sint_t)k], threads, thread_state);
8997
14.3k
}
8998
8999
static void libsais_induce_final_order_32s_2k(
9000
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
9001
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t threads,
9002
664
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9003
664
  libsais_final_sorting_scan_left_to_right_32s_omp(
9004
664
      T, SA, n, &buckets[1 * (fast_sint_t)k], threads, thread_state);
9005
664
  libsais_final_sorting_scan_right_to_left_32s_omp(
9006
664
      T, SA, n, &buckets[0 * (fast_sint_t)k], threads, thread_state);
9007
664
}
9008
9009
static void libsais_induce_final_order_32s_1k(
9010
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n,
9011
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t threads,
9012
178
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9013
178
  libsais_count_suffixes_32s(T, n, k, buckets);
9014
178
  libsais_initialize_buckets_start_32s_1k(k, buckets);
9015
178
  libsais_final_sorting_scan_left_to_right_32s_omp(T, SA, n, buckets, threads,
9016
178
                                                   thread_state);
9017
9018
178
  libsais_count_suffixes_32s(T, n, k, buckets);
9019
178
  libsais_initialize_buckets_end_32s_1k(k, buckets);
9020
178
  libsais_final_sorting_scan_right_to_left_32s_omp(T, SA, n, buckets, threads,
9021
178
                                                   thread_state);
9022
178
}
9023
9024
static sa_sint_t libsais_renumber_unique_and_nonunique_lms_suffixes_32s(
9025
    sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t m, sa_sint_t f,
9026
9.61k
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
9027
9.61k
  const fast_sint_t prefetch_distance = 64;
9028
9029
9.61k
  sa_sint_t* RESTRICT SAm = &SA[m];
9030
9031
9.61k
  sa_sint_t i, j;
9032
9.61k
  for (i = (sa_sint_t)omp_block_start, j = (sa_sint_t)omp_block_start +
9033
9.61k
                                           (sa_sint_t)omp_block_size -
9034
9.61k
                                           2 * (sa_sint_t)prefetch_distance - 3;
9035
876k
       i < j; i += 4) {
9036
867k
    libsais_prefetchr(&SA[i + 3 * prefetch_distance]);
9037
9038
867k
    libsais_prefetchw(
9039
867k
        &SAm[((sa_uint_t)SA[i + 2 * prefetch_distance + 0]) >> 1]);
9040
867k
    libsais_prefetchw(
9041
867k
        &SAm[((sa_uint_t)SA[i + 2 * prefetch_distance + 1]) >> 1]);
9042
867k
    libsais_prefetchw(
9043
867k
        &SAm[((sa_uint_t)SA[i + 2 * prefetch_distance + 2]) >> 1]);
9044
867k
    libsais_prefetchw(
9045
867k
        &SAm[((sa_uint_t)SA[i + 2 * prefetch_distance + 3]) >> 1]);
9046
9047
867k
    sa_uint_t q0 = (sa_uint_t)SA[i + prefetch_distance + 0];
9048
867k
    sa_sint_t* RESTRICT Tq0 = &T[q0];
9049
867k
    libsais_prefetchw(SAm[q0 >> 1] < 0 ? Tq0 : &SAm[q0 >> 1]);
9050
867k
    sa_uint_t q1 = (sa_uint_t)SA[i + prefetch_distance + 1];
9051
867k
    sa_sint_t* RESTRICT Tq1 = &T[q1];
9052
867k
    libsais_prefetchw(SAm[q1 >> 1] < 0 ? Tq1 : &SAm[q1 >> 1]);
9053
867k
    sa_uint_t q2 = (sa_uint_t)SA[i + prefetch_distance + 2];
9054
867k
    sa_sint_t* RESTRICT Tq2 = &T[q2];
9055
867k
    libsais_prefetchw(SAm[q2 >> 1] < 0 ? Tq2 : &SAm[q2 >> 1]);
9056
867k
    sa_uint_t q3 = (sa_uint_t)SA[i + prefetch_distance + 3];
9057
867k
    sa_sint_t* RESTRICT Tq3 = &T[q3];
9058
867k
    libsais_prefetchw(SAm[q3 >> 1] < 0 ? Tq3 : &SAm[q3 >> 1]);
9059
9060
867k
    sa_uint_t p0 = (sa_uint_t)SA[i + 0];
9061
867k
    sa_sint_t s0 = SAm[p0 >> 1];
9062
867k
    if (s0 < 0) {
9063
82.1k
      T[p0] |= SAINT_MIN;
9064
82.1k
      f++;
9065
82.1k
      s0 = i + 0 + SAINT_MIN + f;
9066
82.1k
    }
9067
867k
    SAm[p0 >> 1] = s0 - f;
9068
867k
    sa_uint_t p1 = (sa_uint_t)SA[i + 1];
9069
867k
    sa_sint_t s1 = SAm[p1 >> 1];
9070
867k
    if (s1 < 0) {
9071
83.3k
      T[p1] |= SAINT_MIN;
9072
83.3k
      f++;
9073
83.3k
      s1 = i + 1 + SAINT_MIN + f;
9074
83.3k
    }
9075
867k
    SAm[p1 >> 1] = s1 - f;
9076
867k
    sa_uint_t p2 = (sa_uint_t)SA[i + 2];
9077
867k
    sa_sint_t s2 = SAm[p2 >> 1];
9078
867k
    if (s2 < 0) {
9079
83.0k
      T[p2] |= SAINT_MIN;
9080
83.0k
      f++;
9081
83.0k
      s2 = i + 2 + SAINT_MIN + f;
9082
83.0k
    }
9083
867k
    SAm[p2 >> 1] = s2 - f;
9084
867k
    sa_uint_t p3 = (sa_uint_t)SA[i + 3];
9085
867k
    sa_sint_t s3 = SAm[p3 >> 1];
9086
867k
    if (s3 < 0) {
9087
82.7k
      T[p3] |= SAINT_MIN;
9088
82.7k
      f++;
9089
82.7k
      s3 = i + 3 + SAINT_MIN + f;
9090
82.7k
    }
9091
867k
    SAm[p3 >> 1] = s3 - f;
9092
867k
  }
9093
9094
668k
  for (j += 2 * (sa_sint_t)prefetch_distance + 3; i < j; i += 1) {
9095
659k
    sa_uint_t p = (sa_uint_t)SA[i];
9096
659k
    sa_sint_t s = SAm[p >> 1];
9097
659k
    if (s < 0) {
9098
161k
      T[p] |= SAINT_MIN;
9099
161k
      f++;
9100
161k
      s = i + SAINT_MIN + f;
9101
161k
    }
9102
659k
    SAm[p >> 1] = s - f;
9103
659k
  }
9104
9105
9.61k
  return f;
9106
9.61k
}
9107
9108
static void libsais_compact_unique_and_nonunique_lms_suffixes_32s(
9109
    sa_sint_t* RESTRICT SA, sa_sint_t m, fast_sint_t* pl, fast_sint_t* pr,
9110
9.61k
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
9111
9.61k
  const fast_sint_t prefetch_distance = 64;
9112
9113
9.61k
  sa_uint_t* RESTRICT SAl = (sa_uint_t*)&SA[0];
9114
9.61k
  sa_uint_t* RESTRICT SAr = (sa_uint_t*)&SA[0];
9115
9116
9.61k
  fast_sint_t i, j, l = *pl - 1, r = *pr - 1;
9117
9.61k
  for (i = (fast_sint_t)m + omp_block_start + omp_block_size - 1,
9118
9.61k
      j = (fast_sint_t)m + omp_block_start + 3;
9119
1.45M
       i >= j; i -= 4) {
9120
1.44M
    libsais_prefetchr(&SA[i - prefetch_distance]);
9121
9122
1.44M
    sa_uint_t p0 = (sa_uint_t)SA[i - 0];
9123
1.44M
    SAl[l] = p0 & SAINT_MAX;
9124
1.44M
    l -= (sa_sint_t)p0 < 0;
9125
1.44M
    SAr[r] = p0 - 1;
9126
1.44M
    r -= (sa_sint_t)p0 > 0;
9127
1.44M
    sa_uint_t p1 = (sa_uint_t)SA[i - 1];
9128
1.44M
    SAl[l] = p1 & SAINT_MAX;
9129
1.44M
    l -= (sa_sint_t)p1 < 0;
9130
1.44M
    SAr[r] = p1 - 1;
9131
1.44M
    r -= (sa_sint_t)p1 > 0;
9132
1.44M
    sa_uint_t p2 = (sa_uint_t)SA[i - 2];
9133
1.44M
    SAl[l] = p2 & SAINT_MAX;
9134
1.44M
    l -= (sa_sint_t)p2 < 0;
9135
1.44M
    SAr[r] = p2 - 1;
9136
1.44M
    r -= (sa_sint_t)p2 > 0;
9137
1.44M
    sa_uint_t p3 = (sa_uint_t)SA[i - 3];
9138
1.44M
    SAl[l] = p3 & SAINT_MAX;
9139
1.44M
    l -= (sa_sint_t)p3 < 0;
9140
1.44M
    SAr[r] = p3 - 1;
9141
1.44M
    r -= (sa_sint_t)p3 > 0;
9142
1.44M
  }
9143
9144
24.4k
  for (j -= 3; i >= j; i -= 1) {
9145
14.8k
    sa_uint_t p = (sa_uint_t)SA[i];
9146
14.8k
    SAl[l] = p & SAINT_MAX;
9147
14.8k
    l -= (sa_sint_t)p < 0;
9148
14.8k
    SAr[r] = p - 1;
9149
14.8k
    r -= (sa_sint_t)p > 0;
9150
14.8k
  }
9151
9152
9.61k
  *pl = l + 1;
9153
9.61k
  *pr = r + 1;
9154
9.61k
}
9155
9156
#if defined(LIBSAIS_OPENMP)
9157
9158
static sa_sint_t libsais_count_unique_suffixes(sa_sint_t* RESTRICT SA,
9159
                                               sa_sint_t m,
9160
                                               fast_sint_t omp_block_start,
9161
                                               fast_sint_t omp_block_size) {
9162
  const fast_sint_t prefetch_distance = 64;
9163
9164
  sa_sint_t* RESTRICT SAm = &SA[m];
9165
9166
  fast_sint_t i, j;
9167
  sa_sint_t f0 = 0, f1 = 0, f2 = 0, f3 = 0;
9168
  for (i = omp_block_start,
9169
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
9170
       i < j; i += 4) {
9171
    libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
9172
9173
    libsais_prefetchr(&SAm[((sa_uint_t)SA[i + prefetch_distance + 0]) >> 1]);
9174
    libsais_prefetchr(&SAm[((sa_uint_t)SA[i + prefetch_distance + 1]) >> 1]);
9175
    libsais_prefetchr(&SAm[((sa_uint_t)SA[i + prefetch_distance + 2]) >> 1]);
9176
    libsais_prefetchr(&SAm[((sa_uint_t)SA[i + prefetch_distance + 3]) >> 1]);
9177
9178
    f0 += SAm[((sa_uint_t)SA[i + 0]) >> 1] < 0;
9179
    f1 += SAm[((sa_uint_t)SA[i + 1]) >> 1] < 0;
9180
    f2 += SAm[((sa_uint_t)SA[i + 2]) >> 1] < 0;
9181
    f3 += SAm[((sa_uint_t)SA[i + 3]) >> 1] < 0;
9182
  }
9183
9184
  for (j += prefetch_distance + 3; i < j; i += 1) {
9185
    f0 += SAm[((sa_uint_t)SA[i]) >> 1] < 0;
9186
  }
9187
9188
  return f0 + f1 + f2 + f3;
9189
}
9190
9191
#endif
9192
9193
static sa_sint_t libsais_renumber_unique_and_nonunique_lms_suffixes_32s_omp(
9194
    sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t m,
9195
9.61k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9196
9.61k
  sa_sint_t f = 0;
9197
9198
#if defined(LIBSAIS_OPENMP)
9199
#pragma omp parallel num_threads(threads) if (threads > 1 && m >= 65536)
9200
#endif
9201
9.61k
  {
9202
#if defined(LIBSAIS_OPENMP)
9203
    fast_sint_t omp_thread_num = omp_get_thread_num();
9204
    fast_sint_t omp_num_threads = omp_get_num_threads();
9205
#else
9206
9.61k
    UNUSED(threads);
9207
9.61k
    UNUSED(thread_state);
9208
9209
9.61k
    fast_sint_t omp_thread_num = 0;
9210
9.61k
    fast_sint_t omp_num_threads = 1;
9211
9.61k
#endif
9212
9.61k
    fast_sint_t omp_block_stride = (m / omp_num_threads) & (-16);
9213
9.61k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
9214
9.61k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
9215
9.61k
                                     ? omp_block_stride
9216
9.61k
                                     : m - omp_block_start;
9217
9218
9.61k
    if (omp_num_threads == 1) {
9219
9.61k
      f = libsais_renumber_unique_and_nonunique_lms_suffixes_32s(
9220
9.61k
          T, SA, m, 0, omp_block_start, omp_block_size);
9221
9.61k
    }
9222
#if defined(LIBSAIS_OPENMP)
9223
    else {
9224
      {
9225
        thread_state[omp_thread_num].state.count =
9226
            libsais_count_unique_suffixes(SA, m, omp_block_start,
9227
                                          omp_block_size);
9228
      }
9229
9230
#pragma omp barrier
9231
9232
      {
9233
        fast_sint_t t, count = 0;
9234
        for (t = 0; t < omp_thread_num; ++t) {
9235
          count += thread_state[t].state.count;
9236
        }
9237
9238
        if (omp_thread_num == omp_num_threads - 1) {
9239
          f = (sa_sint_t)(count + thread_state[omp_thread_num].state.count);
9240
        }
9241
9242
        libsais_renumber_unique_and_nonunique_lms_suffixes_32s(
9243
            T, SA, m, (sa_sint_t)count, omp_block_start, omp_block_size);
9244
      }
9245
    }
9246
#endif
9247
9.61k
  }
9248
9249
9.61k
  return f;
9250
9.61k
}
9251
9252
static void libsais_compact_unique_and_nonunique_lms_suffixes_32s_omp(
9253
    sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t m, sa_sint_t fs, sa_sint_t f,
9254
9.61k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9255
#if defined(LIBSAIS_OPENMP)
9256
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 131072 && \
9257
                                                  m < fs)
9258
#endif
9259
9.61k
  {
9260
#if defined(LIBSAIS_OPENMP)
9261
    fast_sint_t omp_thread_num = omp_get_thread_num();
9262
    fast_sint_t omp_num_threads = omp_get_num_threads();
9263
#else
9264
9.61k
    UNUSED(threads);
9265
9.61k
    UNUSED(thread_state);
9266
9267
9.61k
    fast_sint_t omp_thread_num = 0;
9268
9.61k
    fast_sint_t omp_num_threads = 1;
9269
9.61k
#endif
9270
9.61k
    fast_sint_t omp_block_stride =
9271
9.61k
        (((fast_sint_t)n >> 1) / omp_num_threads) & (-16);
9272
9.61k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
9273
9.61k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
9274
9.61k
                                     ? omp_block_stride
9275
9.61k
                                     : ((fast_sint_t)n >> 1) - omp_block_start;
9276
9277
9.61k
    if (omp_num_threads == 1) {
9278
9.61k
      fast_sint_t l = m, r = (fast_sint_t)n + (fast_sint_t)fs;
9279
9.61k
      libsais_compact_unique_and_nonunique_lms_suffixes_32s(
9280
9.61k
          SA, m, &l, &r, omp_block_start, omp_block_size);
9281
9.61k
    }
9282
#if defined(LIBSAIS_OPENMP)
9283
    else {
9284
      {
9285
        thread_state[omp_thread_num].state.position =
9286
            (fast_sint_t)m + ((fast_sint_t)n >> 1) + omp_block_start +
9287
            omp_block_size;
9288
        thread_state[omp_thread_num].state.count =
9289
            (fast_sint_t)m + omp_block_start + omp_block_size;
9290
9291
        libsais_compact_unique_and_nonunique_lms_suffixes_32s(
9292
            SA, m, &thread_state[omp_thread_num].state.position,
9293
            &thread_state[omp_thread_num].state.count, omp_block_start,
9294
            omp_block_size);
9295
      }
9296
9297
#pragma omp barrier
9298
9299
#pragma omp master
9300
      {
9301
        fast_sint_t t, position;
9302
9303
        for (position = m, t = omp_num_threads - 1; t >= 0; --t) {
9304
          fast_sint_t omp_block_end = t < omp_num_threads - 1
9305
                                          ? omp_block_stride * (t + 1)
9306
                                          : ((fast_sint_t)n >> 1);
9307
          fast_sint_t count = ((fast_sint_t)m + ((fast_sint_t)n >> 1) +
9308
                               omp_block_end - thread_state[t].state.position);
9309
9310
          if (count > 0) {
9311
            position -= count;
9312
            memcpy(&SA[position], &SA[thread_state[t].state.position],
9313
                   (size_t)count * sizeof(sa_sint_t));
9314
          }
9315
        }
9316
9317
        for (position = (fast_sint_t)n + (fast_sint_t)fs,
9318
            t = omp_num_threads - 1;
9319
             t >= 0; --t) {
9320
          fast_sint_t omp_block_end = t < omp_num_threads - 1
9321
                                          ? omp_block_stride * (t + 1)
9322
                                          : ((fast_sint_t)n >> 1);
9323
          fast_sint_t count =
9324
              ((fast_sint_t)m + omp_block_end - thread_state[t].state.count);
9325
9326
          if (count > 0) {
9327
            position -= count;
9328
            memcpy(&SA[position], &SA[thread_state[t].state.count],
9329
                   (size_t)count * sizeof(sa_sint_t));
9330
          }
9331
        }
9332
      }
9333
    }
9334
#endif
9335
9.61k
  }
9336
9337
9.61k
  memcpy(&SA[(fast_sint_t)n + (fast_sint_t)fs - (fast_sint_t)m],
9338
9.61k
         &SA[(fast_sint_t)m - (fast_sint_t)f], (size_t)f * sizeof(sa_sint_t));
9339
9.61k
}
9340
9341
static sa_sint_t libsais_compact_lms_suffixes_32s_omp(
9342
    sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t m,
9343
    sa_sint_t fs, sa_sint_t threads,
9344
9.61k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9345
9.61k
  sa_sint_t f = libsais_renumber_unique_and_nonunique_lms_suffixes_32s_omp(
9346
9.61k
      T, SA, m, threads, thread_state);
9347
9.61k
  libsais_compact_unique_and_nonunique_lms_suffixes_32s_omp(
9348
9.61k
      SA, n, m, fs, f, threads, thread_state);
9349
9350
9.61k
  return f;
9351
9.61k
}
9352
9353
static void libsais_merge_unique_lms_suffixes_32s(
9354
    sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t m,
9355
7.18k
    fast_sint_t l, fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
9356
7.18k
  const fast_sint_t prefetch_distance = 64;
9357
9358
7.18k
  const sa_sint_t* RESTRICT SAnm = &SA[(fast_sint_t)n - (fast_sint_t)m - 1 + l];
9359
9360
7.18k
  sa_sint_t i, j;
9361
7.18k
  fast_sint_t tmp = *SAnm++;
9362
7.18k
  for (i = (sa_sint_t)omp_block_start,
9363
7.18k
      j = (sa_sint_t)omp_block_start + (sa_sint_t)omp_block_size - 6;
9364
2.39M
       i < j; i += 4) {
9365
2.38M
    libsais_prefetchr(&T[i + prefetch_distance]);
9366
9367
2.38M
    sa_sint_t c0 = T[i + 0];
9368
2.38M
    if (c0 < 0) {
9369
122k
      T[i + 0] = c0 & SAINT_MAX;
9370
122k
      SA[tmp] = i + 0;
9371
122k
      i++;
9372
122k
      tmp = *SAnm++;
9373
122k
    }
9374
2.38M
    sa_sint_t c1 = T[i + 1];
9375
2.38M
    if (c1 < 0) {
9376
122k
      T[i + 1] = c1 & SAINT_MAX;
9377
122k
      SA[tmp] = i + 1;
9378
122k
      i++;
9379
122k
      tmp = *SAnm++;
9380
122k
    }
9381
2.38M
    sa_sint_t c2 = T[i + 2];
9382
2.38M
    if (c2 < 0) {
9383
122k
      T[i + 2] = c2 & SAINT_MAX;
9384
122k
      SA[tmp] = i + 2;
9385
122k
      i++;
9386
122k
      tmp = *SAnm++;
9387
122k
    }
9388
2.38M
    sa_sint_t c3 = T[i + 3];
9389
2.38M
    if (c3 < 0) {
9390
123k
      T[i + 3] = c3 & SAINT_MAX;
9391
123k
      SA[tmp] = i + 3;
9392
123k
      i++;
9393
123k
      tmp = *SAnm++;
9394
123k
    }
9395
2.38M
  }
9396
9397
33.2k
  for (j += 6; i < j; i += 1) {
9398
26.0k
    sa_sint_t c = T[i];
9399
26.0k
    if (c < 0) {
9400
3.09k
      T[i] = c & SAINT_MAX;
9401
3.09k
      SA[tmp] = i;
9402
3.09k
      i++;
9403
3.09k
      tmp = *SAnm++;
9404
3.09k
    }
9405
26.0k
  }
9406
7.18k
}
9407
9408
static void libsais_merge_nonunique_lms_suffixes_32s(
9409
    sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t m, fast_sint_t l,
9410
7.18k
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
9411
7.18k
  const fast_sint_t prefetch_distance = 64;
9412
9413
7.18k
  const sa_sint_t* RESTRICT SAnm = &SA[(fast_sint_t)n - (fast_sint_t)m - 1 + l];
9414
9415
7.18k
  fast_sint_t i, j;
9416
7.18k
  sa_sint_t tmp = *SAnm++;
9417
884k
  for (i = omp_block_start, j = omp_block_start + omp_block_size - 3; i < j;
9418
877k
       i += 4) {
9419
877k
    libsais_prefetchr(&SA[i + prefetch_distance]);
9420
9421
877k
    if (SA[i + 0] == 0) {
9422
757k
      SA[i + 0] = tmp;
9423
757k
      tmp = *SAnm++;
9424
757k
    }
9425
877k
    if (SA[i + 1] == 0) {
9426
753k
      SA[i + 1] = tmp;
9427
753k
      tmp = *SAnm++;
9428
753k
    }
9429
877k
    if (SA[i + 2] == 0) {
9430
754k
      SA[i + 2] = tmp;
9431
754k
      tmp = *SAnm++;
9432
754k
    }
9433
877k
    if (SA[i + 3] == 0) {
9434
754k
      SA[i + 3] = tmp;
9435
754k
      tmp = *SAnm++;
9436
754k
    }
9437
877k
  }
9438
9439
17.7k
  for (j += 3; i < j; i += 1) {
9440
10.5k
    if (SA[i] == 0) {
9441
5.73k
      SA[i] = tmp;
9442
5.73k
      tmp = *SAnm++;
9443
5.73k
    }
9444
10.5k
  }
9445
7.18k
}
9446
9447
static void libsais_merge_unique_lms_suffixes_32s_omp(
9448
    sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t m,
9449
7.18k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9450
#if defined(LIBSAIS_OPENMP)
9451
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 65536)
9452
#endif
9453
7.18k
  {
9454
#if defined(LIBSAIS_OPENMP)
9455
    fast_sint_t omp_thread_num = omp_get_thread_num();
9456
    fast_sint_t omp_num_threads = omp_get_num_threads();
9457
#else
9458
7.18k
    UNUSED(threads);
9459
7.18k
    UNUSED(thread_state);
9460
9461
7.18k
    fast_sint_t omp_thread_num = 0;
9462
7.18k
    fast_sint_t omp_num_threads = 1;
9463
7.18k
#endif
9464
7.18k
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
9465
7.18k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
9466
7.18k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
9467
7.18k
                                     ? omp_block_stride
9468
7.18k
                                     : n - omp_block_start;
9469
9470
7.18k
    if (omp_num_threads == 1) {
9471
7.18k
      libsais_merge_unique_lms_suffixes_32s(T, SA, n, m, 0, omp_block_start,
9472
7.18k
                                            omp_block_size);
9473
7.18k
    }
9474
#if defined(LIBSAIS_OPENMP)
9475
    else {
9476
      {
9477
        thread_state[omp_thread_num].state.count =
9478
            libsais_count_negative_marked_suffixes(T, omp_block_start,
9479
                                                   omp_block_size);
9480
      }
9481
9482
#pragma omp barrier
9483
9484
      {
9485
        fast_sint_t t, count = 0;
9486
        for (t = 0; t < omp_thread_num; ++t) {
9487
          count += thread_state[t].state.count;
9488
        }
9489
9490
        libsais_merge_unique_lms_suffixes_32s(T, SA, n, m, count,
9491
                                              omp_block_start, omp_block_size);
9492
      }
9493
    }
9494
#endif
9495
7.18k
  }
9496
7.18k
}
9497
9498
static void libsais_merge_nonunique_lms_suffixes_32s_omp(
9499
    sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t m, sa_sint_t f,
9500
7.18k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9501
#if defined(LIBSAIS_OPENMP)
9502
#pragma omp parallel num_threads(threads) if (threads > 1 && m >= 65536)
9503
#endif
9504
7.18k
  {
9505
#if defined(LIBSAIS_OPENMP)
9506
    fast_sint_t omp_thread_num = omp_get_thread_num();
9507
    fast_sint_t omp_num_threads = omp_get_num_threads();
9508
#else
9509
7.18k
    UNUSED(threads);
9510
7.18k
    UNUSED(thread_state);
9511
9512
7.18k
    fast_sint_t omp_thread_num = 0;
9513
7.18k
    fast_sint_t omp_num_threads = 1;
9514
7.18k
#endif
9515
7.18k
    fast_sint_t omp_block_stride = (m / omp_num_threads) & (-16);
9516
7.18k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
9517
7.18k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
9518
7.18k
                                     ? omp_block_stride
9519
7.18k
                                     : m - omp_block_start;
9520
9521
7.18k
    if (omp_num_threads == 1) {
9522
7.18k
      libsais_merge_nonunique_lms_suffixes_32s(SA, n, m, f, omp_block_start,
9523
7.18k
                                               omp_block_size);
9524
7.18k
    }
9525
#if defined(LIBSAIS_OPENMP)
9526
    else {
9527
      {
9528
        thread_state[omp_thread_num].state.count =
9529
            libsais_count_zero_marked_suffixes(SA, omp_block_start,
9530
                                               omp_block_size);
9531
      }
9532
9533
#pragma omp barrier
9534
9535
      {
9536
        fast_sint_t t, count = f;
9537
        for (t = 0; t < omp_thread_num; ++t) {
9538
          count += thread_state[t].state.count;
9539
        }
9540
9541
        libsais_merge_nonunique_lms_suffixes_32s(
9542
            SA, n, m, count, omp_block_start, omp_block_size);
9543
      }
9544
    }
9545
#endif
9546
7.18k
  }
9547
7.18k
}
9548
9549
static void libsais_merge_compacted_lms_suffixes_32s_omp(
9550
    sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t m,
9551
    sa_sint_t f, sa_sint_t threads,
9552
7.18k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9553
7.18k
  libsais_merge_unique_lms_suffixes_32s_omp(T, SA, n, m, threads, thread_state);
9554
7.18k
  libsais_merge_nonunique_lms_suffixes_32s_omp(SA, n, m, f, threads,
9555
7.18k
                                               thread_state);
9556
7.18k
}
9557
9558
static void libsais_reconstruct_compacted_lms_suffixes_32s_2k_omp(
9559
    sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t k,
9560
    sa_sint_t m, sa_sint_t fs, sa_sint_t f, sa_sint_t* RESTRICT buckets,
9561
    sa_sint_t local_buckets, sa_sint_t threads,
9562
9.45k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9563
9.45k
  if (f > 0) {
9564
7.02k
    memmove(&SA[n - m - 1], &SA[n + fs - m], (size_t)f * sizeof(sa_sint_t));
9565
9566
7.02k
    libsais_count_and_gather_compacted_lms_suffixes_32s_2k_omp(
9567
7.02k
        T, SA, n, k, buckets, local_buckets, threads, thread_state);
9568
7.02k
    libsais_reconstruct_lms_suffixes_omp(SA, n, m - f, threads);
9569
9570
7.02k
    memcpy(&SA[n - m - 1 + f], &SA[0],
9571
7.02k
           ((size_t)m - (size_t)f) * sizeof(sa_sint_t));
9572
7.02k
    memset(&SA[0], 0, (size_t)m * sizeof(sa_sint_t));
9573
9574
7.02k
    libsais_merge_compacted_lms_suffixes_32s_omp(T, SA, n, m, f, threads,
9575
7.02k
                                                 thread_state);
9576
7.02k
  } else {
9577
2.42k
    libsais_count_and_gather_lms_suffixes_32s_2k(T, SA, n, k, buckets, 0, n);
9578
2.42k
    libsais_reconstruct_lms_suffixes_omp(SA, n, m, threads);
9579
2.42k
  }
9580
9.45k
}
9581
9582
static void libsais_reconstruct_compacted_lms_suffixes_32s_1k_omp(
9583
    sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t m,
9584
    sa_sint_t fs, sa_sint_t f, sa_sint_t threads,
9585
160
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9586
160
  if (f > 0) {
9587
160
    memmove(&SA[n - m - 1], &SA[n + fs - m], (size_t)f * sizeof(sa_sint_t));
9588
9589
160
    libsais_gather_compacted_lms_suffixes_32s(T, SA, n);
9590
160
    libsais_reconstruct_lms_suffixes_omp(SA, n, m - f, threads);
9591
9592
160
    memcpy(&SA[n - m - 1 + f], &SA[0],
9593
160
           ((size_t)m - (size_t)f) * sizeof(sa_sint_t));
9594
160
    memset(&SA[0], 0, (size_t)m * sizeof(sa_sint_t));
9595
9596
160
    libsais_merge_compacted_lms_suffixes_32s_omp(T, SA, n, m, f, threads,
9597
160
                                                 thread_state);
9598
160
  } else {
9599
0
    libsais_gather_lms_suffixes_32s(T, SA, n);
9600
0
    libsais_reconstruct_lms_suffixes_omp(SA, n, m, threads);
9601
0
  }
9602
160
}
9603
9604
static sa_sint_t libsais_main_32s_recursion(
9605
    sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t k,
9606
    sa_sint_t fs, sa_sint_t threads,
9607
    LIBSAIS_THREAD_STATE* RESTRICT thread_state,
9608
17.0k
    sa_sint_t* RESTRICT local_buffer) {
9609
17.0k
  fs = fs < (SAINT_MAX - n) ? fs : (SAINT_MAX - n);
9610
9611
17.0k
  if (k > 0 && ((fs / k >= 6) || (LIBSAIS_LOCAL_BUFFER_SIZE / k >= 6))) {
9612
15.8k
    sa_sint_t alignment =
9613
15.8k
        (fs - 1024) / k >= 6 ? (sa_sint_t)1024 : (sa_sint_t)16;
9614
15.8k
    sa_sint_t* RESTRICT buckets =
9615
15.8k
        (fs - alignment) / k >= 6
9616
15.8k
            ? (sa_sint_t*)libsais_align_up(
9617
9.66k
                  &SA[n + fs - 6 * (fast_sint_t)k - alignment],
9618
9.66k
                  (size_t)alignment * sizeof(sa_sint_t))
9619
15.8k
            : &SA[n + fs - 6 * (fast_sint_t)k];
9620
15.8k
    buckets = (LIBSAIS_LOCAL_BUFFER_SIZE > fs) ? local_buffer : buckets;
9621
9622
15.8k
    sa_sint_t m = libsais_count_and_gather_lms_suffixes_32s_4k_omp(
9623
15.8k
        T, SA, n, k, buckets, buckets == local_buffer, threads, thread_state);
9624
15.8k
    if (m > 1) {
9625
13.9k
      memset(SA, 0, ((size_t)n - (size_t)m) * sizeof(sa_sint_t));
9626
9627
13.9k
      sa_sint_t first_lms_suffix = SA[n - m];
9628
13.9k
      sa_sint_t left_suffixes_count =
9629
13.9k
          libsais_initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
9630
13.9k
              T, k, buckets, first_lms_suffix);
9631
9632
13.9k
      libsais_radix_sort_lms_suffixes_32s_6k_omp(
9633
13.9k
          T, SA, n, m, &buckets[4 * (fast_sint_t)k], threads, thread_state);
9634
9635
13.9k
      if ((n / 8192) < k) {
9636
13.9k
        libsais_radix_sort_set_markers_32s_6k_omp(
9637
13.9k
            SA, k, &buckets[4 * (fast_sint_t)k], threads);
9638
13.9k
      }
9639
13.9k
      if (threads > 1 && n >= 65536) {
9640
0
        memset(&SA[(fast_sint_t)n - (fast_sint_t)m], 0,
9641
0
               (size_t)m * sizeof(sa_sint_t));
9642
0
      }
9643
9644
13.9k
      libsais_initialize_buckets_for_partial_sorting_32s_6k(
9645
13.9k
          T, k, buckets, first_lms_suffix, left_suffixes_count);
9646
13.9k
      libsais_induce_partial_order_32s_6k_omp(
9647
13.9k
          T, SA, n, k, buckets, first_lms_suffix, left_suffixes_count, threads,
9648
13.9k
          thread_state);
9649
9650
13.9k
      sa_sint_t names =
9651
13.9k
          (n / 8192) < k
9652
13.9k
              ? libsais_renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
9653
13.9k
                    SA, n, m, threads, thread_state)
9654
13.9k
              : libsais_renumber_and_gather_lms_suffixes_omp(
9655
0
                    SA, n, m, fs, threads, thread_state);
9656
9657
13.9k
      if (names < m) {
9658
8.62k
        sa_sint_t f = (n / 8192) < k
9659
8.62k
                          ? libsais_compact_lms_suffixes_32s_omp(
9660
8.62k
                                T, SA, n, m, fs, threads, thread_state)
9661
8.62k
                          : 0;
9662
9663
8.62k
        if (libsais_main_32s_recursion(SA + n + fs - m + f, SA, m - f,
9664
8.62k
                                       names - f, fs + n - 2 * m + f, threads,
9665
8.62k
                                       thread_state, local_buffer) != 0) {
9666
0
          return -2;
9667
0
        }
9668
9669
8.62k
        libsais_reconstruct_compacted_lms_suffixes_32s_2k_omp(
9670
8.62k
            T, SA, n, k, m, fs, f, buckets, buckets == local_buffer, threads,
9671
8.62k
            thread_state);
9672
8.62k
      } else {
9673
5.32k
        libsais_count_lms_suffixes_32s_2k(T, n, k, buckets);
9674
5.32k
      }
9675
9676
13.9k
      libsais_initialize_buckets_start_and_end_32s_4k(k, buckets);
9677
13.9k
      libsais_place_lms_suffixes_histogram_32s_4k(SA, n, k, m, buckets);
9678
13.9k
      libsais_induce_final_order_32s_4k(T, SA, n, k, buckets, threads,
9679
13.9k
                                        thread_state);
9680
13.9k
    } else {
9681
1.94k
      SA[0] = SA[n - 1];
9682
9683
1.94k
      libsais_initialize_buckets_start_and_end_32s_6k(k, buckets);
9684
1.94k
      libsais_place_lms_suffixes_histogram_32s_6k(SA, n, k, m, buckets);
9685
1.94k
      libsais_induce_final_order_32s_6k(T, SA, n, k, buckets, threads,
9686
1.94k
                                        thread_state);
9687
1.94k
    }
9688
9689
15.8k
    return 0;
9690
15.8k
  } else if (k > 0 && (n <= SAINT_MAX / 2) &&
9691
1.19k
             ((fs / k >= 4) || (LIBSAIS_LOCAL_BUFFER_SIZE / k >= 4))) {
9692
355
    sa_sint_t alignment =
9693
355
        (fs - 1024) / k >= 4 ? (sa_sint_t)1024 : (sa_sint_t)16;
9694
355
    sa_sint_t* RESTRICT buckets =
9695
355
        (fs - alignment) / k >= 4
9696
355
            ? (sa_sint_t*)libsais_align_up(
9697
55
                  &SA[n + fs - 4 * (fast_sint_t)k - alignment],
9698
55
                  (size_t)alignment * sizeof(sa_sint_t))
9699
355
            : &SA[n + fs - 4 * (fast_sint_t)k];
9700
355
    buckets = (LIBSAIS_LOCAL_BUFFER_SIZE > fs) ? local_buffer : buckets;
9701
9702
355
    sa_sint_t m = libsais_count_and_gather_lms_suffixes_32s_2k_omp(
9703
355
        T, SA, n, k, buckets, buckets == local_buffer, threads, thread_state);
9704
355
    if (m > 1) {
9705
355
      libsais_initialize_buckets_for_radix_and_partial_sorting_32s_4k(
9706
355
          T, k, buckets, SA[n - m]);
9707
9708
355
      libsais_radix_sort_lms_suffixes_32s_2k_omp(T, SA, n, m, &buckets[1],
9709
355
                                                 threads, thread_state);
9710
355
      libsais_radix_sort_set_markers_32s_4k_omp(SA, k, &buckets[1], threads);
9711
9712
355
      libsais_place_lms_suffixes_interval_32s_4k(SA, n, k, m - 1, buckets);
9713
355
      libsais_induce_partial_order_32s_4k_omp(T, SA, n, k, buckets, threads,
9714
355
                                              thread_state);
9715
9716
355
      sa_sint_t names =
9717
355
          libsais_renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
9718
355
              SA, n, m, threads, thread_state);
9719
355
      if (names < m) {
9720
290
        sa_sint_t f = libsais_compact_lms_suffixes_32s_omp(
9721
290
            T, SA, n, m, fs, threads, thread_state);
9722
9723
290
        if (libsais_main_32s_recursion(SA + n + fs - m + f, SA, m - f,
9724
290
                                       names - f, fs + n - 2 * m + f, threads,
9725
290
                                       thread_state, local_buffer) != 0) {
9726
0
          return -2;
9727
0
        }
9728
9729
290
        libsais_reconstruct_compacted_lms_suffixes_32s_2k_omp(
9730
290
            T, SA, n, k, m, fs, f, buckets, buckets == local_buffer, threads,
9731
290
            thread_state);
9732
290
      } else {
9733
65
        libsais_count_lms_suffixes_32s_2k(T, n, k, buckets);
9734
65
      }
9735
355
    } else {
9736
0
      SA[0] = SA[n - 1];
9737
0
    }
9738
9739
355
    libsais_initialize_buckets_start_and_end_32s_4k(k, buckets);
9740
355
    libsais_place_lms_suffixes_histogram_32s_4k(SA, n, k, m, buckets);
9741
355
    libsais_induce_final_order_32s_4k(T, SA, n, k, buckets, threads,
9742
355
                                      thread_state);
9743
9744
355
    return 0;
9745
842
  } else if (k > 0 && ((fs / k >= 2) || (LIBSAIS_LOCAL_BUFFER_SIZE / k >= 2))) {
9746
664
    sa_sint_t alignment =
9747
664
        (fs - 1024) / k >= 2 ? (sa_sint_t)1024 : (sa_sint_t)16;
9748
664
    sa_sint_t* RESTRICT buckets =
9749
664
        (fs - alignment) / k >= 2
9750
664
            ? (sa_sint_t*)libsais_align_up(
9751
446
                  &SA[n + fs - 2 * (fast_sint_t)k - alignment],
9752
446
                  (size_t)alignment * sizeof(sa_sint_t))
9753
664
            : &SA[n + fs - 2 * (fast_sint_t)k];
9754
664
    buckets = (LIBSAIS_LOCAL_BUFFER_SIZE > fs) ? local_buffer : buckets;
9755
9756
664
    sa_sint_t m = libsais_count_and_gather_lms_suffixes_32s_2k_omp(
9757
664
        T, SA, n, k, buckets, buckets == local_buffer, threads, thread_state);
9758
664
    if (m > 1) {
9759
664
      libsais_initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
9760
664
          T, k, buckets, SA[n - m]);
9761
9762
664
      libsais_radix_sort_lms_suffixes_32s_2k_omp(T, SA, n, m, &buckets[1],
9763
664
                                                 threads, thread_state);
9764
664
      libsais_place_lms_suffixes_interval_32s_2k(SA, n, k, m - 1, buckets);
9765
9766
664
      libsais_initialize_buckets_start_and_end_32s_2k(k, buckets);
9767
664
      libsais_induce_partial_order_32s_2k_omp(T, SA, n, k, buckets, threads,
9768
664
                                              thread_state);
9769
9770
664
      sa_sint_t names =
9771
664
          libsais_renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(
9772
664
              T, SA, n, m, threads);
9773
664
      if (names < m) {
9774
535
        sa_sint_t f = libsais_compact_lms_suffixes_32s_omp(
9775
535
            T, SA, n, m, fs, threads, thread_state);
9776
9777
535
        if (libsais_main_32s_recursion(SA + n + fs - m + f, SA, m - f,
9778
535
                                       names - f, fs + n - 2 * m + f, threads,
9779
535
                                       thread_state, local_buffer) != 0) {
9780
0
          return -2;
9781
0
        }
9782
9783
535
        libsais_reconstruct_compacted_lms_suffixes_32s_2k_omp(
9784
535
            T, SA, n, k, m, fs, f, buckets, buckets == local_buffer, threads,
9785
535
            thread_state);
9786
535
      } else {
9787
129
        libsais_count_lms_suffixes_32s_2k(T, n, k, buckets);
9788
129
      }
9789
664
    } else {
9790
0
      SA[0] = SA[n - 1];
9791
0
    }
9792
9793
664
    libsais_initialize_buckets_end_32s_2k(k, buckets);
9794
664
    libsais_place_lms_suffixes_histogram_32s_2k(SA, n, k, m, buckets);
9795
9796
664
    libsais_initialize_buckets_start_and_end_32s_2k(k, buckets);
9797
664
    libsais_induce_final_order_32s_2k(T, SA, n, k, buckets, threads,
9798
664
                                      thread_state);
9799
9800
664
    return 0;
9801
664
  } else {
9802
178
    sa_sint_t* buffer = fs < k ? (sa_sint_t*)libsais_alloc_aligned(
9803
0
                                     (size_t)k * sizeof(sa_sint_t), 4096)
9804
178
                               : (sa_sint_t*)NULL;
9805
9806
178
    sa_sint_t alignment = fs - 1024 >= k ? (sa_sint_t)1024 : (sa_sint_t)16;
9807
178
    sa_sint_t* RESTRICT buckets =
9808
178
        fs - alignment >= k ? (sa_sint_t*)libsais_align_up(
9809
178
                                  &SA[n + fs - k - alignment],
9810
178
                                  (size_t)alignment * sizeof(sa_sint_t))
9811
178
        : fs >= k           ? &SA[n + fs - k]
9812
0
                            : buffer;
9813
9814
178
    if (buckets == NULL) {
9815
0
      return -2;
9816
0
    }
9817
9818
178
    memset(SA, 0, (size_t)n * sizeof(sa_sint_t));
9819
9820
178
    libsais_count_suffixes_32s(T, n, k, buckets);
9821
178
    libsais_initialize_buckets_end_32s_1k(k, buckets);
9822
9823
178
    sa_sint_t m = libsais_radix_sort_lms_suffixes_32s_1k(T, SA, n, buckets);
9824
178
    if (m > 1) {
9825
178
      libsais_induce_partial_order_32s_1k_omp(T, SA, n, k, buckets, threads,
9826
178
                                              thread_state);
9827
9828
178
      sa_sint_t names =
9829
178
          libsais_renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(
9830
178
              T, SA, n, m, threads);
9831
178
      if (names < m) {
9832
160
        if (buffer != NULL) {
9833
0
          libsais_free_aligned(buffer);
9834
0
          buckets = NULL;
9835
0
        }
9836
9837
160
        sa_sint_t f = libsais_compact_lms_suffixes_32s_omp(
9838
160
            T, SA, n, m, fs, threads, thread_state);
9839
9840
160
        if (libsais_main_32s_recursion(SA + n + fs - m + f, SA, m - f,
9841
160
                                       names - f, fs + n - 2 * m + f, threads,
9842
160
                                       thread_state, local_buffer) != 0) {
9843
0
          return -2;
9844
0
        }
9845
9846
160
        libsais_reconstruct_compacted_lms_suffixes_32s_1k_omp(
9847
160
            T, SA, n, m, fs, f, threads, thread_state);
9848
9849
160
        if (buckets == NULL) {
9850
0
          buckets = buffer = (sa_sint_t*)libsais_alloc_aligned(
9851
0
              (size_t)k * sizeof(sa_sint_t), 4096);
9852
0
        }
9853
160
        if (buckets == NULL) {
9854
0
          return -2;
9855
0
        }
9856
160
      }
9857
9858
178
      libsais_count_suffixes_32s(T, n, k, buckets);
9859
178
      libsais_initialize_buckets_end_32s_1k(k, buckets);
9860
178
      libsais_place_lms_suffixes_interval_32s_1k(T, SA, k, m, buckets);
9861
178
    }
9862
9863
178
    libsais_induce_final_order_32s_1k(T, SA, n, k, buckets, threads,
9864
178
                                      thread_state);
9865
178
    libsais_free_aligned(buffer);
9866
9867
178
    return 0;
9868
178
  }
9869
17.0k
}
9870
9871
static sa_sint_t libsais_main_32s_entry(
9872
    sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n, sa_sint_t k,
9873
    sa_sint_t fs, sa_sint_t threads,
9874
7.48k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9875
7.48k
  sa_sint_t local_buffer[2 * LIBSAIS_LOCAL_BUFFER_SIZE];
9876
9877
7.48k
  return libsais_main_32s_recursion(T, SA, n, k, fs, threads, thread_state,
9878
7.48k
                                    local_buffer + LIBSAIS_LOCAL_BUFFER_SIZE);
9879
7.48k
}
9880
9881
static sa_sint_t libsais_main_8u(const uint8_t* T, sa_sint_t* SA, sa_sint_t n,
9882
                                 sa_sint_t* RESTRICT buckets, sa_sint_t flags,
9883
                                 sa_sint_t r, sa_sint_t* RESTRICT I,
9884
                                 sa_sint_t fs, sa_sint_t* freq,
9885
                                 sa_sint_t threads,
9886
8.54k
                                 LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9887
8.54k
  fs = fs < (SAINT_MAX - n) ? fs : (SAINT_MAX - n);
9888
9889
8.54k
  sa_sint_t m = libsais_count_and_gather_lms_suffixes_8u_omp(
9890
8.54k
      T, SA, n, buckets, threads, thread_state);
9891
8.54k
  sa_sint_t k = libsais_initialize_buckets_start_and_end_8u(buckets, freq);
9892
9893
8.54k
  if ((flags & LIBSAIS_FLAGS_GSA) &&
9894
0
      (buckets[0] != 0 || buckets[2] != 0 || buckets[3] != 1)) {
9895
0
    return -1;
9896
0
  }
9897
9898
8.54k
  if (m > 0) {
9899
8.47k
    sa_sint_t first_lms_suffix = SA[n - m];
9900
8.47k
    sa_sint_t left_suffixes_count =
9901
8.47k
        libsais_initialize_buckets_for_lms_suffixes_radix_sort_8u(
9902
8.47k
            T, buckets, first_lms_suffix);
9903
9904
8.47k
    if (threads > 1 && n >= 65536) {
9905
0
      memset(SA, 0, ((size_t)n - (size_t)m) * sizeof(sa_sint_t));
9906
0
    }
9907
8.47k
    libsais_radix_sort_lms_suffixes_8u_omp(T, SA, n, m, flags, buckets, threads,
9908
8.47k
                                           thread_state);
9909
8.47k
    if (threads > 1 && n >= 65536) {
9910
0
      memset(&SA[(fast_sint_t)n - (fast_sint_t)m], 0,
9911
0
             (size_t)m * sizeof(sa_sint_t));
9912
0
    }
9913
9914
8.47k
    libsais_initialize_buckets_for_partial_sorting_8u(
9915
8.47k
        T, buckets, first_lms_suffix, left_suffixes_count);
9916
8.47k
    libsais_induce_partial_order_8u_omp(T, SA, n, k, flags, buckets,
9917
8.47k
                                        first_lms_suffix, left_suffixes_count,
9918
8.47k
                                        threads, thread_state);
9919
9920
8.47k
    sa_sint_t names = libsais_renumber_and_gather_lms_suffixes_omp(
9921
8.47k
        SA, n, m, fs, threads, thread_state);
9922
8.47k
    if (names < m) {
9923
7.48k
      if (libsais_main_32s_entry(SA + n + fs - m, SA, m, names, fs + n - 2 * m,
9924
7.48k
                                 threads, thread_state) != 0) {
9925
0
        return -2;
9926
0
      }
9927
9928
7.48k
      libsais_gather_lms_suffixes_8u_omp(T, SA, n, threads, thread_state);
9929
7.48k
      libsais_reconstruct_lms_suffixes_omp(SA, n, m, threads);
9930
7.48k
    }
9931
9932
8.47k
    libsais_place_lms_suffixes_interval_8u(SA, n, m, flags, buckets);
9933
8.47k
  } else {
9934
70
    memset(SA, 0, (size_t)n * sizeof(sa_sint_t));
9935
70
  }
9936
9937
8.54k
  return libsais_induce_final_order_8u_omp(T, SA, n, k, flags, r, I, buckets,
9938
8.54k
                                           threads, thread_state);
9939
8.54k
}
9940
9941
static sa_sint_t libsais_main(const uint8_t* T, sa_sint_t* SA, sa_sint_t n,
9942
                              sa_sint_t flags, sa_sint_t r, sa_sint_t* I,
9943
                              sa_sint_t fs, sa_sint_t* freq,
9944
8.54k
                              sa_sint_t threads) {
9945
8.54k
  LIBSAIS_THREAD_STATE* RESTRICT thread_state =
9946
8.54k
      threads > 1 ? libsais_alloc_thread_state(threads) : NULL;
9947
8.54k
  sa_sint_t* RESTRICT buckets = (sa_sint_t*)libsais_alloc_aligned(
9948
8.54k
      (size_t)8 * ALPHABET_SIZE * sizeof(sa_sint_t), 4096);
9949
9950
8.54k
  sa_sint_t index = buckets != NULL && (thread_state != NULL || threads == 1)
9951
8.54k
                        ? libsais_main_8u(T, SA, n, buckets, flags, r, I, fs,
9952
8.54k
                                          freq, threads, thread_state)
9953
8.54k
                        : -2;
9954
9955
8.54k
  libsais_free_aligned(buckets);
9956
8.54k
  libsais_free_thread_state(thread_state);
9957
9958
8.54k
  return index;
9959
8.54k
}
9960
9961
static sa_sint_t libsais_main_int(sa_sint_t* T, sa_sint_t* SA, sa_sint_t n,
9962
                                  sa_sint_t k, sa_sint_t fs,
9963
0
                                  sa_sint_t threads) {
9964
0
  LIBSAIS_THREAD_STATE* RESTRICT thread_state =
9965
0
      threads > 1 ? libsais_alloc_thread_state(threads) : NULL;
9966
9967
0
  sa_sint_t index =
9968
0
      thread_state != NULL || threads == 1
9969
0
          ? libsais_main_32s_entry(T, SA, n, k, fs, threads, thread_state)
9970
0
          : -2;
9971
9972
0
  libsais_free_thread_state(thread_state);
9973
9974
0
  return index;
9975
0
}
9976
9977
static sa_sint_t libsais_main_ctx(const LIBSAIS_CONTEXT* ctx, const uint8_t* T,
9978
                                  sa_sint_t* SA, sa_sint_t n, sa_sint_t flags,
9979
                                  sa_sint_t r, sa_sint_t* I, sa_sint_t fs,
9980
0
                                  sa_sint_t* freq) {
9981
0
  return ctx != NULL && (ctx->buckets != NULL &&
9982
0
                         (ctx->thread_state != NULL || ctx->threads == 1))
9983
0
             ? libsais_main_8u(T, SA, n, ctx->buckets, flags, r, I, fs, freq,
9984
0
                               (sa_sint_t)ctx->threads, ctx->thread_state)
9985
0
             : -2;
9986
0
}
9987
9988
static void libsais_bwt_copy_8u(uint8_t* RESTRICT U, sa_sint_t* RESTRICT A,
9989
0
                                sa_sint_t n) {
9990
0
  const fast_sint_t prefetch_distance = 64;
9991
9992
0
  fast_sint_t i, j;
9993
0
  for (i = 0, j = (fast_sint_t)n - 7; i < j; i += 8) {
9994
0
    libsais_prefetchr(&A[i + prefetch_distance]);
9995
9996
0
    U[i + 0] = (uint8_t)A[i + 0];
9997
0
    U[i + 1] = (uint8_t)A[i + 1];
9998
0
    U[i + 2] = (uint8_t)A[i + 2];
9999
0
    U[i + 3] = (uint8_t)A[i + 3];
10000
0
    U[i + 4] = (uint8_t)A[i + 4];
10001
0
    U[i + 5] = (uint8_t)A[i + 5];
10002
0
    U[i + 6] = (uint8_t)A[i + 6];
10003
0
    U[i + 7] = (uint8_t)A[i + 7];
10004
0
  }
10005
10006
0
  for (j += 7; i < j; i += 1) {
10007
0
    U[i] = (uint8_t)A[i];
10008
0
  }
10009
0
}
10010
10011
static void libsais_bwt_copy_8u_omp(uint8_t* RESTRICT U, sa_sint_t* RESTRICT A,
10012
0
                                    sa_sint_t n, sa_sint_t threads) {
10013
#if defined(LIBSAIS_OPENMP)
10014
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 65536)
10015
#endif
10016
0
  {
10017
#if defined(LIBSAIS_OPENMP)
10018
    fast_sint_t omp_thread_num = omp_get_thread_num();
10019
    fast_sint_t omp_num_threads = omp_get_num_threads();
10020
    fast_sint_t omp_block_stride = ((fast_sint_t)n / omp_num_threads) & (-16);
10021
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
10022
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
10023
                                     ? omp_block_stride
10024
                                     : (fast_sint_t)n - omp_block_start;
10025
#else
10026
0
    UNUSED(threads);
10027
10028
0
    fast_sint_t omp_block_start = 0;
10029
0
    fast_sint_t omp_block_size = (fast_sint_t)n;
10030
0
#endif
10031
10032
0
    libsais_bwt_copy_8u(U + omp_block_start, A + omp_block_start,
10033
0
                        (sa_sint_t)omp_block_size);
10034
0
  }
10035
0
}
10036
10037
0
void* libsais_create_ctx(void) { return (void*)libsais_create_ctx_main(1); }
10038
10039
0
void libsais_free_ctx(void* ctx) {
10040
0
  libsais_free_ctx_main((LIBSAIS_CONTEXT*)ctx);
10041
0
}
10042
10043
int32_t libsais(const uint8_t* T, int32_t* SA, int32_t n, int32_t fs,
10044
8.54k
                int32_t* freq) {
10045
8.54k
  if ((T == NULL) || (SA == NULL) || (n < 0) || (fs < 0)) {
10046
0
    return -1;
10047
8.54k
  } else if (n <= 1) {
10048
0
    if (freq != NULL) {
10049
0
      memset(freq, 0, ALPHABET_SIZE * sizeof(int32_t));
10050
0
    }
10051
0
    if (n == 1) {
10052
0
      SA[0] = 0;
10053
0
      if (freq != NULL) {
10054
0
        freq[T[0]]++;
10055
0
      }
10056
0
    }
10057
0
    return 0;
10058
0
  }
10059
10060
8.54k
  return libsais_main(T, SA, n, LIBSAIS_FLAGS_NONE, 0, NULL, fs, freq, 1);
10061
8.54k
}
10062
10063
int32_t libsais_gsa(const uint8_t* T, int32_t* SA, int32_t n, int32_t fs,
10064
0
                    int32_t* freq) {
10065
0
  if ((T == NULL) || (SA == NULL) || (n < 0) || (n > 0 && T[n - 1] != 0) ||
10066
0
      (fs < 0)) {
10067
0
    return -1;
10068
0
  } else if (n <= 1) {
10069
0
    if (freq != NULL) {
10070
0
      memset(freq, 0, ALPHABET_SIZE * sizeof(int32_t));
10071
0
    }
10072
0
    if (n == 1) {
10073
0
      SA[0] = 0;
10074
0
      if (freq != NULL) {
10075
0
        freq[T[0]]++;
10076
0
      }
10077
0
    }
10078
0
    return 0;
10079
0
  }
10080
10081
0
  return libsais_main(T, SA, n, LIBSAIS_FLAGS_GSA, 0, NULL, fs, freq, 1);
10082
0
}
10083
10084
0
int32_t libsais_int(int32_t* T, int32_t* SA, int32_t n, int32_t k, int32_t fs) {
10085
0
  if ((T == NULL) || (SA == NULL) || (n < 0) || (fs < 0)) {
10086
0
    return -1;
10087
0
  } else if (n <= 1) {
10088
0
    if (n == 1) {
10089
0
      SA[0] = 0;
10090
0
    }
10091
0
    return 0;
10092
0
  }
10093
10094
0
  return libsais_main_int(T, SA, n, k, fs, 1);
10095
0
}
10096
10097
int32_t libsais_ctx(const void* ctx, const uint8_t* T, int32_t* SA, int32_t n,
10098
0
                    int32_t fs, int32_t* freq) {
10099
0
  if ((ctx == NULL) || (T == NULL) || (SA == NULL) || (n < 0) || (fs < 0)) {
10100
0
    return -1;
10101
0
  } else if (n <= 1) {
10102
0
    if (freq != NULL) {
10103
0
      memset(freq, 0, ALPHABET_SIZE * sizeof(int32_t));
10104
0
    }
10105
0
    if (n == 1) {
10106
0
      SA[0] = 0;
10107
0
      if (freq != NULL) {
10108
0
        freq[T[0]]++;
10109
0
      }
10110
0
    }
10111
0
    return 0;
10112
0
  }
10113
10114
0
  return libsais_main_ctx((const LIBSAIS_CONTEXT*)ctx, T, SA, n,
10115
0
                          LIBSAIS_FLAGS_NONE, 0, NULL, fs, freq);
10116
0
}
10117
10118
int32_t libsais_gsa_ctx(const void* ctx, const uint8_t* T, int32_t* SA,
10119
0
                        int32_t n, int32_t fs, int32_t* freq) {
10120
0
  if ((ctx == NULL) || (T == NULL) || (SA == NULL) || (n < 0) ||
10121
0
      (n > 0 && T[n - 1] != 0) || (fs < 0)) {
10122
0
    return -1;
10123
0
  } else if (n <= 1) {
10124
0
    if (freq != NULL) {
10125
0
      memset(freq, 0, ALPHABET_SIZE * sizeof(int32_t));
10126
0
    }
10127
0
    if (n == 1) {
10128
0
      SA[0] = 0;
10129
0
      if (freq != NULL) {
10130
0
        freq[T[0]]++;
10131
0
      }
10132
0
    }
10133
0
    return 0;
10134
0
  }
10135
10136
0
  return libsais_main_ctx((const LIBSAIS_CONTEXT*)ctx, T, SA, n,
10137
0
                          LIBSAIS_FLAGS_GSA, 0, NULL, fs, freq);
10138
0
}
10139
10140
int32_t libsais_bwt(const uint8_t* T, uint8_t* U, int32_t* A, int32_t n,
10141
0
                    int32_t fs, int32_t* freq) {
10142
0
  if ((T == NULL) || (U == NULL) || (A == NULL) || (n < 0) || (fs < 0)) {
10143
0
    return -1;
10144
0
  } else if (n <= 1) {
10145
0
    if (freq != NULL) {
10146
0
      memset(freq, 0, ALPHABET_SIZE * sizeof(int32_t));
10147
0
    }
10148
0
    if (n == 1) {
10149
0
      U[0] = T[0];
10150
0
      if (freq != NULL) {
10151
0
        freq[T[0]]++;
10152
0
      }
10153
0
    }
10154
0
    return n;
10155
0
  }
10156
10157
0
  sa_sint_t index =
10158
0
      libsais_main(T, A, n, LIBSAIS_FLAGS_BWT, 0, NULL, fs, freq, 1);
10159
0
  if (index >= 0) {
10160
0
    index++;
10161
10162
0
    U[0] = T[n - 1];
10163
0
    libsais_bwt_copy_8u_omp(U + 1, A, index - 1, 1);
10164
0
    libsais_bwt_copy_8u_omp(U + index, A + index, n - index, 1);
10165
0
  }
10166
10167
0
  return index;
10168
0
}
10169
10170
int32_t libsais_bwt_aux(const uint8_t* T, uint8_t* U, int32_t* A, int32_t n,
10171
0
                        int32_t fs, int32_t* freq, int32_t r, int32_t* I) {
10172
0
  if ((T == NULL) || (U == NULL) || (A == NULL) || (n < 0) || (fs < 0) ||
10173
0
      (r < 2) || ((r & (r - 1)) != 0) || (I == NULL)) {
10174
0
    return -1;
10175
0
  } else if (n <= 1) {
10176
0
    if (freq != NULL) {
10177
0
      memset(freq, 0, ALPHABET_SIZE * sizeof(int32_t));
10178
0
    }
10179
0
    if (n == 1) {
10180
0
      U[0] = T[0];
10181
0
      if (freq != NULL) {
10182
0
        freq[T[0]]++;
10183
0
      }
10184
0
    }
10185
0
    I[0] = n;
10186
0
    return 0;
10187
0
  }
10188
10189
0
  sa_sint_t index = libsais_main(T, A, n, LIBSAIS_FLAGS_BWT, r, I, fs, freq, 1);
10190
0
  if (index == 0) {
10191
0
    U[0] = T[n - 1];
10192
0
    libsais_bwt_copy_8u_omp(U + 1, A, I[0] - 1, 1);
10193
0
    libsais_bwt_copy_8u_omp(U + I[0], A + I[0], n - I[0], 1);
10194
0
  }
10195
10196
0
  return index;
10197
0
}
10198
10199
int32_t libsais_bwt_ctx(const void* ctx, const uint8_t* T, uint8_t* U,
10200
0
                        int32_t* A, int32_t n, int32_t fs, int32_t* freq) {
10201
0
  if ((ctx == NULL) || (T == NULL) || (U == NULL) || (A == NULL) || (n < 0) ||
10202
0
      (fs < 0)) {
10203
0
    return -1;
10204
0
  } else if (n <= 1) {
10205
0
    if (freq != NULL) {
10206
0
      memset(freq, 0, ALPHABET_SIZE * sizeof(int32_t));
10207
0
    }
10208
0
    if (n == 1) {
10209
0
      U[0] = T[0];
10210
0
      if (freq != NULL) {
10211
0
        freq[T[0]]++;
10212
0
      }
10213
0
    }
10214
0
    return n;
10215
0
  }
10216
10217
0
  sa_sint_t index = libsais_main_ctx((const LIBSAIS_CONTEXT*)ctx, T, A, n,
10218
0
                                     LIBSAIS_FLAGS_BWT, 0, NULL, fs, freq);
10219
0
  if (index >= 0) {
10220
0
    index++;
10221
10222
0
    U[0] = T[n - 1];
10223
0
    libsais_bwt_copy_8u_omp(U + 1, A, index - 1,
10224
0
                            (sa_sint_t)((const LIBSAIS_CONTEXT*)ctx)->threads);
10225
0
    libsais_bwt_copy_8u_omp(U + index, A + index, n - index,
10226
0
                            (sa_sint_t)((const LIBSAIS_CONTEXT*)ctx)->threads);
10227
0
  }
10228
10229
0
  return index;
10230
0
}
10231
10232
int32_t libsais_bwt_aux_ctx(const void* ctx, const uint8_t* T, uint8_t* U,
10233
                            int32_t* A, int32_t n, int32_t fs, int32_t* freq,
10234
0
                            int32_t r, int32_t* I) {
10235
0
  if ((ctx == NULL) || (T == NULL) || (U == NULL) || (A == NULL) || (n < 0) ||
10236
0
      (fs < 0) || (r < 2) || ((r & (r - 1)) != 0) || (I == NULL)) {
10237
0
    return -1;
10238
0
  } else if (n <= 1) {
10239
0
    if (freq != NULL) {
10240
0
      memset(freq, 0, ALPHABET_SIZE * sizeof(int32_t));
10241
0
    }
10242
0
    if (n == 1) {
10243
0
      U[0] = T[0];
10244
0
      if (freq != NULL) {
10245
0
        freq[T[0]]++;
10246
0
      }
10247
0
    }
10248
0
    I[0] = n;
10249
0
    return 0;
10250
0
  }
10251
10252
0
  sa_sint_t index = libsais_main_ctx((const LIBSAIS_CONTEXT*)ctx, T, A, n,
10253
0
                                     LIBSAIS_FLAGS_BWT, r, I, fs, freq);
10254
0
  if (index == 0) {
10255
0
    U[0] = T[n - 1];
10256
0
    libsais_bwt_copy_8u_omp(U + 1, A, I[0] - 1,
10257
0
                            (sa_sint_t)((const LIBSAIS_CONTEXT*)ctx)->threads);
10258
0
    libsais_bwt_copy_8u_omp(U + I[0], A + I[0], n - I[0],
10259
0
                            (sa_sint_t)((const LIBSAIS_CONTEXT*)ctx)->threads);
10260
0
  }
10261
10262
0
  return index;
10263
0
}
10264
10265
#if defined(LIBSAIS_OPENMP)
10266
10267
void* libsais_create_ctx_omp(int32_t threads) {
10268
  if (threads < 0) {
10269
    return NULL;
10270
  }
10271
10272
  threads =
10273
      threads > 0 ? threads : (omp_get_max_threads() / omp_get_num_threads());
10274
  threads = threads > 0 ? threads : 1;
10275
10276
  return (void*)libsais_create_ctx_main(threads);
10277
}
10278
10279
int32_t libsais_omp(const uint8_t* T, int32_t* SA, int32_t n, int32_t fs,
10280
                    int32_t* freq, int32_t threads) {
10281
  if ((T == NULL) || (SA == NULL) || (n < 0) || (fs < 0) || (threads < 0)) {
10282
    return -1;
10283
  } else if (n <= 1) {
10284
    if (freq != NULL) {
10285
      memset(freq, 0, ALPHABET_SIZE * sizeof(int32_t));
10286
    }
10287
    if (n == 1) {
10288
      SA[0] = 0;
10289
      if (freq != NULL) {
10290
        freq[T[0]]++;
10291
      }
10292
    }
10293
    return 0;
10294
  }
10295
10296
  threads =
10297
      threads > 0 ? threads : (omp_get_max_threads() / omp_get_num_threads());
10298
  threads = threads > 0 ? threads : 1;
10299
10300
  return libsais_main(T, SA, n, LIBSAIS_FLAGS_NONE, 0, NULL, fs, freq, threads);
10301
}
10302
10303
int32_t libsais_gsa_omp(const uint8_t* T, int32_t* SA, int32_t n, int32_t fs,
10304
                        int32_t* freq, int32_t threads) {
10305
  if ((T == NULL) || (SA == NULL) || (n < 0) || (n > 0 && T[n - 1] != 0) ||
10306
      (fs < 0) || (threads < 0)) {
10307
    return -1;
10308
  } else if (n <= 1) {
10309
    if (freq != NULL) {
10310
      memset(freq, 0, ALPHABET_SIZE * sizeof(int32_t));
10311
    }
10312
    if (n == 1) {
10313
      SA[0] = 0;
10314
      if (freq != NULL) {
10315
        freq[T[0]]++;
10316
      }
10317
    }
10318
    return 0;
10319
  }
10320
10321
  threads =
10322
      threads > 0 ? threads : (omp_get_max_threads() / omp_get_num_threads());
10323
  threads = threads > 0 ? threads : 1;
10324
10325
  return libsais_main(T, SA, n, LIBSAIS_FLAGS_GSA, 0, NULL, fs, freq, threads);
10326
}
10327
10328
int32_t libsais_int_omp(int32_t* T, int32_t* SA, int32_t n, int32_t k,
10329
                        int32_t fs, int32_t threads) {
10330
  if ((T == NULL) || (SA == NULL) || (n < 0) || (fs < 0) || (threads < 0)) {
10331
    return -1;
10332
  } else if (n <= 1) {
10333
    if (n == 1) {
10334
      SA[0] = 0;
10335
    }
10336
    return 0;
10337
  }
10338
10339
  threads =
10340
      threads > 0 ? threads : (omp_get_max_threads() / omp_get_num_threads());
10341
  threads = threads > 0 ? threads : 1;
10342
10343
  return libsais_main_int(T, SA, n, k, fs, threads);
10344
}
10345
10346
int32_t libsais_bwt_omp(const uint8_t* T, uint8_t* U, int32_t* A, int32_t n,
10347
                        int32_t fs, int32_t* freq, int32_t threads) {
10348
  if ((T == NULL) || (U == NULL) || (A == NULL) || (n < 0) || (fs < 0) ||
10349
      (threads < 0)) {
10350
    return -1;
10351
  } else if (n <= 1) {
10352
    if (freq != NULL) {
10353
      memset(freq, 0, ALPHABET_SIZE * sizeof(int32_t));
10354
    }
10355
    if (n == 1) {
10356
      U[0] = T[0];
10357
      if (freq != NULL) {
10358
        freq[T[0]]++;
10359
      }
10360
    }
10361
    return n;
10362
  }
10363
10364
  threads =
10365
      threads > 0 ? threads : (omp_get_max_threads() / omp_get_num_threads());
10366
  threads = threads > 0 ? threads : 1;
10367
10368
  sa_sint_t index =
10369
      libsais_main(T, A, n, LIBSAIS_FLAGS_BWT, 0, NULL, fs, freq, threads);
10370
  if (index >= 0) {
10371
    index++;
10372
10373
    U[0] = T[n - 1];
10374
    libsais_bwt_copy_8u_omp(U + 1, A, index - 1, threads);
10375
    libsais_bwt_copy_8u_omp(U + index, A + index, n - index, threads);
10376
  }
10377
10378
  return index;
10379
}
10380
10381
int32_t libsais_bwt_aux_omp(const uint8_t* T, uint8_t* U, int32_t* A, int32_t n,
10382
                            int32_t fs, int32_t* freq, int32_t r, int32_t* I,
10383
                            int32_t threads) {
10384
  if ((T == NULL) || (U == NULL) || (A == NULL) || (n < 0) || (fs < 0) ||
10385
      (r < 2) || ((r & (r - 1)) != 0) || (I == NULL) || (threads < 0)) {
10386
    return -1;
10387
  } else if (n <= 1) {
10388
    if (freq != NULL) {
10389
      memset(freq, 0, ALPHABET_SIZE * sizeof(int32_t));
10390
    }
10391
    if (n == 1) {
10392
      U[0] = T[0];
10393
      if (freq != NULL) {
10394
        freq[T[0]]++;
10395
      }
10396
    }
10397
    I[0] = n;
10398
    return 0;
10399
  }
10400
10401
  threads =
10402
      threads > 0 ? threads : (omp_get_max_threads() / omp_get_num_threads());
10403
  threads = threads > 0 ? threads : 1;
10404
10405
  sa_sint_t index =
10406
      libsais_main(T, A, n, LIBSAIS_FLAGS_BWT, r, I, fs, freq, threads);
10407
  if (index == 0) {
10408
    U[0] = T[n - 1];
10409
    libsais_bwt_copy_8u_omp(U + 1, A, I[0] - 1, threads);
10410
    libsais_bwt_copy_8u_omp(U + I[0], A + I[0], n - I[0], threads);
10411
  }
10412
10413
  return index;
10414
}
10415
10416
#endif
10417
10418
0
static LIBSAIS_UNBWT_CONTEXT* libsais_unbwt_create_ctx_main(sa_sint_t threads) {
10419
0
  LIBSAIS_UNBWT_CONTEXT* RESTRICT ctx =
10420
0
      (LIBSAIS_UNBWT_CONTEXT*)libsais_alloc_aligned(
10421
0
          sizeof(LIBSAIS_UNBWT_CONTEXT), 64);
10422
0
  sa_uint_t* RESTRICT bucket2 = (sa_uint_t*)libsais_alloc_aligned(
10423
0
      ALPHABET_SIZE * ALPHABET_SIZE * sizeof(sa_uint_t), 4096);
10424
0
  uint16_t* RESTRICT fastbits = (uint16_t*)libsais_alloc_aligned(
10425
0
      (1 + (1 << UNBWT_FASTBITS)) * sizeof(uint16_t), 4096);
10426
0
  sa_uint_t* RESTRICT buckets =
10427
0
      threads > 1 ? (sa_uint_t*)libsais_alloc_aligned(
10428
0
                        (size_t)threads *
10429
0
                            (ALPHABET_SIZE + (ALPHABET_SIZE * ALPHABET_SIZE)) *
10430
0
                            sizeof(sa_uint_t),
10431
0
                        4096)
10432
0
                  : NULL;
10433
10434
0
  if (ctx != NULL && bucket2 != NULL && fastbits != NULL &&
10435
0
      (buckets != NULL || threads == 1)) {
10436
0
    ctx->bucket2 = bucket2;
10437
0
    ctx->fastbits = fastbits;
10438
0
    ctx->buckets = buckets;
10439
0
    ctx->threads = threads;
10440
10441
0
    return ctx;
10442
0
  }
10443
10444
0
  libsais_free_aligned(buckets);
10445
0
  libsais_free_aligned(fastbits);
10446
0
  libsais_free_aligned(bucket2);
10447
0
  libsais_free_aligned(ctx);
10448
10449
0
  return NULL;
10450
0
}
10451
10452
0
static void libsais_unbwt_free_ctx_main(LIBSAIS_UNBWT_CONTEXT* ctx) {
10453
0
  if (ctx != NULL) {
10454
0
    libsais_free_aligned(ctx->buckets);
10455
0
    libsais_free_aligned(ctx->fastbits);
10456
0
    libsais_free_aligned(ctx->bucket2);
10457
0
    libsais_free_aligned(ctx);
10458
0
  }
10459
0
}
10460
10461
static void libsais_unbwt_compute_histogram(const uint8_t* RESTRICT T,
10462
                                            fast_sint_t n,
10463
0
                                            sa_uint_t* RESTRICT count) {
10464
0
  const fast_sint_t prefetch_distance = 256;
10465
10466
0
  const uint8_t* RESTRICT T_p = T;
10467
10468
0
  if (n >= 1024) {
10469
0
    sa_uint_t copy[4 * (ALPHABET_SIZE + 16)];
10470
10471
0
    memset(copy, 0, (size_t)4 * (ALPHABET_SIZE + 16) * sizeof(sa_uint_t));
10472
10473
0
    sa_uint_t* RESTRICT copy0 = copy + 0 * (ALPHABET_SIZE + 16);
10474
0
    sa_uint_t* RESTRICT copy1 = copy + 1 * (ALPHABET_SIZE + 16);
10475
0
    sa_uint_t* RESTRICT copy2 = copy + 2 * (ALPHABET_SIZE + 16);
10476
0
    sa_uint_t* RESTRICT copy3 = copy + 3 * (ALPHABET_SIZE + 16);
10477
10478
0
    for (; T_p < (uint8_t*)((ptrdiff_t)(T + 63) & (-64)); T_p += 1) {
10479
0
      copy0[T_p[0]]++;
10480
0
    }
10481
10482
0
    fast_uint_t x = ((const uint32_t*)(const void*)T_p)[0],
10483
0
                y = ((const uint32_t*)(const void*)T_p)[1];
10484
10485
0
    for (; T_p < (uint8_t*)((ptrdiff_t)(T + n - 8) & (-64)); T_p += 64) {
10486
0
      libsais_prefetchr(&T_p[prefetch_distance]);
10487
10488
0
      fast_uint_t z = ((const uint32_t*)(const void*)T_p)[2],
10489
0
                  w = ((const uint32_t*)(const void*)T_p)[3];
10490
0
      copy0[(uint8_t)x]++;
10491
0
      x >>= 8;
10492
0
      copy1[(uint8_t)x]++;
10493
0
      x >>= 8;
10494
0
      copy2[(uint8_t)x]++;
10495
0
      x >>= 8;
10496
0
      copy3[x]++;
10497
0
      copy0[(uint8_t)y]++;
10498
0
      y >>= 8;
10499
0
      copy1[(uint8_t)y]++;
10500
0
      y >>= 8;
10501
0
      copy2[(uint8_t)y]++;
10502
0
      y >>= 8;
10503
0
      copy3[y]++;
10504
10505
0
      x = ((const uint32_t*)(const void*)T_p)[4];
10506
0
      y = ((const uint32_t*)(const void*)T_p)[5];
10507
0
      copy0[(uint8_t)z]++;
10508
0
      z >>= 8;
10509
0
      copy1[(uint8_t)z]++;
10510
0
      z >>= 8;
10511
0
      copy2[(uint8_t)z]++;
10512
0
      z >>= 8;
10513
0
      copy3[z]++;
10514
0
      copy0[(uint8_t)w]++;
10515
0
      w >>= 8;
10516
0
      copy1[(uint8_t)w]++;
10517
0
      w >>= 8;
10518
0
      copy2[(uint8_t)w]++;
10519
0
      w >>= 8;
10520
0
      copy3[w]++;
10521
10522
0
      z = ((const uint32_t*)(const void*)T_p)[6];
10523
0
      w = ((const uint32_t*)(const void*)T_p)[7];
10524
0
      copy0[(uint8_t)x]++;
10525
0
      x >>= 8;
10526
0
      copy1[(uint8_t)x]++;
10527
0
      x >>= 8;
10528
0
      copy2[(uint8_t)x]++;
10529
0
      x >>= 8;
10530
0
      copy3[x]++;
10531
0
      copy0[(uint8_t)y]++;
10532
0
      y >>= 8;
10533
0
      copy1[(uint8_t)y]++;
10534
0
      y >>= 8;
10535
0
      copy2[(uint8_t)y]++;
10536
0
      y >>= 8;
10537
0
      copy3[y]++;
10538
10539
0
      x = ((const uint32_t*)(const void*)T_p)[8];
10540
0
      y = ((const uint32_t*)(const void*)T_p)[9];
10541
0
      copy0[(uint8_t)z]++;
10542
0
      z >>= 8;
10543
0
      copy1[(uint8_t)z]++;
10544
0
      z >>= 8;
10545
0
      copy2[(uint8_t)z]++;
10546
0
      z >>= 8;
10547
0
      copy3[z]++;
10548
0
      copy0[(uint8_t)w]++;
10549
0
      w >>= 8;
10550
0
      copy1[(uint8_t)w]++;
10551
0
      w >>= 8;
10552
0
      copy2[(uint8_t)w]++;
10553
0
      w >>= 8;
10554
0
      copy3[w]++;
10555
10556
0
      z = ((const uint32_t*)(const void*)T_p)[10];
10557
0
      w = ((const uint32_t*)(const void*)T_p)[11];
10558
0
      copy0[(uint8_t)x]++;
10559
0
      x >>= 8;
10560
0
      copy1[(uint8_t)x]++;
10561
0
      x >>= 8;
10562
0
      copy2[(uint8_t)x]++;
10563
0
      x >>= 8;
10564
0
      copy3[x]++;
10565
0
      copy0[(uint8_t)y]++;
10566
0
      y >>= 8;
10567
0
      copy1[(uint8_t)y]++;
10568
0
      y >>= 8;
10569
0
      copy2[(uint8_t)y]++;
10570
0
      y >>= 8;
10571
0
      copy3[y]++;
10572
10573
0
      x = ((const uint32_t*)(const void*)T_p)[12];
10574
0
      y = ((const uint32_t*)(const void*)T_p)[13];
10575
0
      copy0[(uint8_t)z]++;
10576
0
      z >>= 8;
10577
0
      copy1[(uint8_t)z]++;
10578
0
      z >>= 8;
10579
0
      copy2[(uint8_t)z]++;
10580
0
      z >>= 8;
10581
0
      copy3[z]++;
10582
0
      copy0[(uint8_t)w]++;
10583
0
      w >>= 8;
10584
0
      copy1[(uint8_t)w]++;
10585
0
      w >>= 8;
10586
0
      copy2[(uint8_t)w]++;
10587
0
      w >>= 8;
10588
0
      copy3[w]++;
10589
10590
0
      z = ((const uint32_t*)(const void*)T_p)[14];
10591
0
      w = ((const uint32_t*)(const void*)T_p)[15];
10592
0
      copy0[(uint8_t)x]++;
10593
0
      x >>= 8;
10594
0
      copy1[(uint8_t)x]++;
10595
0
      x >>= 8;
10596
0
      copy2[(uint8_t)x]++;
10597
0
      x >>= 8;
10598
0
      copy3[x]++;
10599
0
      copy0[(uint8_t)y]++;
10600
0
      y >>= 8;
10601
0
      copy1[(uint8_t)y]++;
10602
0
      y >>= 8;
10603
0
      copy2[(uint8_t)y]++;
10604
0
      y >>= 8;
10605
0
      copy3[y]++;
10606
10607
0
      x = ((const uint32_t*)(const void*)T_p)[16];
10608
0
      y = ((const uint32_t*)(const void*)T_p)[17];
10609
0
      copy0[(uint8_t)z]++;
10610
0
      z >>= 8;
10611
0
      copy1[(uint8_t)z]++;
10612
0
      z >>= 8;
10613
0
      copy2[(uint8_t)z]++;
10614
0
      z >>= 8;
10615
0
      copy3[z]++;
10616
0
      copy0[(uint8_t)w]++;
10617
0
      w >>= 8;
10618
0
      copy1[(uint8_t)w]++;
10619
0
      w >>= 8;
10620
0
      copy2[(uint8_t)w]++;
10621
0
      w >>= 8;
10622
0
      copy3[w]++;
10623
0
    }
10624
10625
0
    copy0[(uint8_t)x]++;
10626
0
    x >>= 8;
10627
0
    copy1[(uint8_t)x]++;
10628
0
    x >>= 8;
10629
0
    copy2[(uint8_t)x]++;
10630
0
    x >>= 8;
10631
0
    copy3[x]++;
10632
0
    copy0[(uint8_t)y]++;
10633
0
    y >>= 8;
10634
0
    copy1[(uint8_t)y]++;
10635
0
    y >>= 8;
10636
0
    copy2[(uint8_t)y]++;
10637
0
    y >>= 8;
10638
0
    copy3[y]++;
10639
10640
0
    T_p += 8;
10641
10642
0
    fast_uint_t i;
10643
0
    for (i = 0; i < ALPHABET_SIZE; i++) {
10644
0
      count[i] += copy0[i] + copy1[i] + copy2[i] + copy3[i];
10645
0
    }
10646
0
  }
10647
10648
0
  for (; T_p < T + n; T_p += 1) {
10649
0
    count[T_p[0]]++;
10650
0
  }
10651
0
}
10652
10653
0
static void libsais_unbwt_transpose_bucket2(sa_uint_t* RESTRICT bucket2) {
10654
0
  fast_uint_t x, y, c, d;
10655
0
  for (x = 0; x != ALPHABET_SIZE; x += 16) {
10656
0
    for (c = x; c != x + 16; ++c) {
10657
0
      for (d = c + 1; d != x + 16; ++d) {
10658
0
        sa_uint_t tmp = bucket2[(d << 8) + c];
10659
0
        bucket2[(d << 8) + c] = bucket2[(c << 8) + d];
10660
0
        bucket2[(c << 8) + d] = tmp;
10661
0
      }
10662
0
    }
10663
10664
0
    for (y = x + 16; y != ALPHABET_SIZE; y += 16) {
10665
0
      for (c = x; c != x + 16; ++c) {
10666
0
        sa_uint_t* bucket2_yc = &bucket2[(y << 8) + c];
10667
0
        sa_uint_t* bucket2_cy = &bucket2[(c << 8) + y];
10668
10669
0
        sa_uint_t tmp00 = bucket2_yc[0 * 256];
10670
0
        bucket2_yc[0 * 256] = bucket2_cy[0];
10671
0
        bucket2_cy[0] = tmp00;
10672
0
        sa_uint_t tmp01 = bucket2_yc[1 * 256];
10673
0
        bucket2_yc[1 * 256] = bucket2_cy[1];
10674
0
        bucket2_cy[1] = tmp01;
10675
0
        sa_uint_t tmp02 = bucket2_yc[2 * 256];
10676
0
        bucket2_yc[2 * 256] = bucket2_cy[2];
10677
0
        bucket2_cy[2] = tmp02;
10678
0
        sa_uint_t tmp03 = bucket2_yc[3 * 256];
10679
0
        bucket2_yc[3 * 256] = bucket2_cy[3];
10680
0
        bucket2_cy[3] = tmp03;
10681
0
        sa_uint_t tmp04 = bucket2_yc[4 * 256];
10682
0
        bucket2_yc[4 * 256] = bucket2_cy[4];
10683
0
        bucket2_cy[4] = tmp04;
10684
0
        sa_uint_t tmp05 = bucket2_yc[5 * 256];
10685
0
        bucket2_yc[5 * 256] = bucket2_cy[5];
10686
0
        bucket2_cy[5] = tmp05;
10687
0
        sa_uint_t tmp06 = bucket2_yc[6 * 256];
10688
0
        bucket2_yc[6 * 256] = bucket2_cy[6];
10689
0
        bucket2_cy[6] = tmp06;
10690
0
        sa_uint_t tmp07 = bucket2_yc[7 * 256];
10691
0
        bucket2_yc[7 * 256] = bucket2_cy[7];
10692
0
        bucket2_cy[7] = tmp07;
10693
0
        sa_uint_t tmp08 = bucket2_yc[8 * 256];
10694
0
        bucket2_yc[8 * 256] = bucket2_cy[8];
10695
0
        bucket2_cy[8] = tmp08;
10696
0
        sa_uint_t tmp09 = bucket2_yc[9 * 256];
10697
0
        bucket2_yc[9 * 256] = bucket2_cy[9];
10698
0
        bucket2_cy[9] = tmp09;
10699
0
        sa_uint_t tmp10 = bucket2_yc[10 * 256];
10700
0
        bucket2_yc[10 * 256] = bucket2_cy[10];
10701
0
        bucket2_cy[10] = tmp10;
10702
0
        sa_uint_t tmp11 = bucket2_yc[11 * 256];
10703
0
        bucket2_yc[11 * 256] = bucket2_cy[11];
10704
0
        bucket2_cy[11] = tmp11;
10705
0
        sa_uint_t tmp12 = bucket2_yc[12 * 256];
10706
0
        bucket2_yc[12 * 256] = bucket2_cy[12];
10707
0
        bucket2_cy[12] = tmp12;
10708
0
        sa_uint_t tmp13 = bucket2_yc[13 * 256];
10709
0
        bucket2_yc[13 * 256] = bucket2_cy[13];
10710
0
        bucket2_cy[13] = tmp13;
10711
0
        sa_uint_t tmp14 = bucket2_yc[14 * 256];
10712
0
        bucket2_yc[14 * 256] = bucket2_cy[14];
10713
0
        bucket2_cy[14] = tmp14;
10714
0
        sa_uint_t tmp15 = bucket2_yc[15 * 256];
10715
0
        bucket2_yc[15 * 256] = bucket2_cy[15];
10716
0
        bucket2_cy[15] = tmp15;
10717
0
      }
10718
0
    }
10719
0
  }
10720
0
}
10721
10722
static void libsais_unbwt_compute_bigram_histogram_single(
10723
    const uint8_t* RESTRICT T, sa_uint_t* RESTRICT bucket1,
10724
0
    sa_uint_t* RESTRICT bucket2, fast_uint_t index) {
10725
0
  fast_uint_t sum, c;
10726
0
  for (sum = 1, c = 0; c < ALPHABET_SIZE; ++c) {
10727
0
    fast_uint_t prev = sum;
10728
0
    sum += bucket1[c];
10729
0
    bucket1[c] = (sa_uint_t)prev;
10730
0
    if (prev != sum) {
10731
0
      sa_uint_t* RESTRICT bucket2_p = &bucket2[c << 8];
10732
10733
0
      {
10734
0
        fast_uint_t hi = index;
10735
0
        if (sum < hi) {
10736
0
          hi = sum;
10737
0
        }
10738
0
        libsais_unbwt_compute_histogram(&T[prev], (fast_sint_t)(hi - prev),
10739
0
                                        bucket2_p);
10740
0
      }
10741
10742
0
      {
10743
0
        fast_uint_t lo = index + 1;
10744
0
        if (prev > lo) {
10745
0
          lo = prev;
10746
0
        }
10747
0
        libsais_unbwt_compute_histogram(&T[lo - 1], (fast_sint_t)(sum - lo),
10748
0
                                        bucket2_p);
10749
0
      }
10750
0
    }
10751
0
  }
10752
10753
0
  libsais_unbwt_transpose_bucket2(bucket2);
10754
0
}
10755
10756
static void libsais_unbwt_calculate_fastbits(sa_uint_t* RESTRICT bucket2,
10757
                                             uint16_t* RESTRICT fastbits,
10758
                                             fast_uint_t lastc,
10759
0
                                             fast_uint_t shift) {
10760
0
  fast_uint_t v, w, sum, c, d;
10761
0
  for (v = 0, w = 0, sum = 1, c = 0; c < ALPHABET_SIZE; ++c) {
10762
0
    if (c == lastc) {
10763
0
      sum += 1;
10764
0
    }
10765
10766
0
    for (d = 0; d < ALPHABET_SIZE; ++d, ++w) {
10767
0
      fast_uint_t prev = sum;
10768
0
      sum += bucket2[w];
10769
0
      bucket2[w] = (sa_uint_t)prev;
10770
0
      if (prev != sum) {
10771
0
        for (; v <= ((sum - 1) >> shift); ++v) {
10772
0
          fastbits[v] = (uint16_t)w;
10773
0
        }
10774
0
      }
10775
0
    }
10776
0
  }
10777
0
}
10778
10779
static void libsais_unbwt_calculate_biPSI(
10780
    const uint8_t* RESTRICT T, sa_uint_t* RESTRICT P,
10781
    sa_uint_t* RESTRICT bucket1, sa_uint_t* RESTRICT bucket2, fast_uint_t index,
10782
0
    fast_sint_t omp_block_start, fast_sint_t omp_block_end) {
10783
0
  {
10784
0
    fast_sint_t i = omp_block_start, j = (fast_sint_t)index;
10785
0
    if (omp_block_end < j) {
10786
0
      j = omp_block_end;
10787
0
    }
10788
0
    for (; i < j; ++i) {
10789
0
      fast_uint_t c = T[i];
10790
0
      fast_uint_t p = bucket1[c]++;
10791
0
      fast_sint_t t = (fast_sint_t)(index - p);
10792
10793
0
      if (t != 0) {
10794
0
        fast_uint_t w =
10795
0
            (((fast_uint_t)
10796
0
                  T[p + (fast_uint_t)(t >> ((sizeof(fast_sint_t) * 8) - 1))])
10797
0
             << 8) +
10798
0
            c;
10799
0
        P[bucket2[w]++] = (sa_uint_t)i;
10800
0
      }
10801
0
    }
10802
0
  }
10803
10804
0
  {
10805
0
    fast_sint_t i = (fast_sint_t)index, j = omp_block_end;
10806
0
    if (omp_block_start > i) {
10807
0
      i = omp_block_start;
10808
0
    }
10809
0
    for (i += 1; i <= j; ++i) {
10810
0
      fast_uint_t c = T[i - 1];
10811
0
      fast_uint_t p = bucket1[c]++;
10812
0
      fast_sint_t t = (fast_sint_t)(index - p);
10813
10814
0
      if (t != 0) {
10815
0
        fast_uint_t w =
10816
0
            (((fast_uint_t)
10817
0
                  T[p + (fast_uint_t)(t >> ((sizeof(fast_sint_t) * 8) - 1))])
10818
0
             << 8) +
10819
0
            c;
10820
0
        P[bucket2[w]++] = (sa_uint_t)i;
10821
0
      }
10822
0
    }
10823
0
  }
10824
0
}
10825
10826
static void libsais_unbwt_init_single(const uint8_t* RESTRICT T,
10827
                                      sa_uint_t* RESTRICT P, sa_sint_t n,
10828
                                      const sa_sint_t* freq,
10829
                                      const sa_uint_t* RESTRICT I,
10830
                                      sa_uint_t* RESTRICT bucket2,
10831
0
                                      uint16_t* RESTRICT fastbits) {
10832
0
  sa_uint_t bucket1[ALPHABET_SIZE];
10833
10834
0
  fast_uint_t index = I[0];
10835
0
  fast_uint_t lastc = T[0];
10836
0
  fast_uint_t shift = 0;
10837
0
  while ((n >> shift) > ((sa_sint_t)1 << UNBWT_FASTBITS)) {
10838
0
    shift++;
10839
0
  }
10840
10841
0
  if (freq != NULL) {
10842
0
    memcpy(bucket1, freq, ALPHABET_SIZE * sizeof(sa_uint_t));
10843
0
  } else {
10844
0
    memset(bucket1, 0, ALPHABET_SIZE * sizeof(sa_uint_t));
10845
0
    libsais_unbwt_compute_histogram(T, n, bucket1);
10846
0
  }
10847
10848
0
  memset(bucket2, 0, ALPHABET_SIZE * ALPHABET_SIZE * sizeof(sa_uint_t));
10849
0
  libsais_unbwt_compute_bigram_histogram_single(T, bucket1, bucket2, index);
10850
10851
0
  libsais_unbwt_calculate_fastbits(bucket2, fastbits, lastc, shift);
10852
0
  libsais_unbwt_calculate_biPSI(T, P, bucket1, bucket2, index, 0, n);
10853
0
}
10854
10855
#if defined(LIBSAIS_OPENMP)
10856
10857
static void libsais_unbwt_compute_bigram_histogram_parallel(
10858
    const uint8_t* RESTRICT T, fast_uint_t index, sa_uint_t* RESTRICT bucket1,
10859
    sa_uint_t* RESTRICT bucket2, fast_sint_t omp_block_start,
10860
    fast_sint_t omp_block_size) {
10861
  fast_sint_t i;
10862
  for (i = omp_block_start; i < omp_block_start + omp_block_size; ++i) {
10863
    fast_uint_t c = T[i];
10864
    fast_uint_t p = bucket1[c]++;
10865
    fast_sint_t t = (fast_sint_t)(index - p);
10866
10867
    if (t != 0) {
10868
      fast_uint_t w =
10869
          (((fast_uint_t)
10870
                T[p + (fast_uint_t)(t >> ((sizeof(fast_sint_t) * 8) - 1))])
10871
           << 8) +
10872
          c;
10873
      bucket2[w]++;
10874
    }
10875
  }
10876
}
10877
10878
static void libsais_unbwt_init_parallel(
10879
    const uint8_t* RESTRICT T, sa_uint_t* RESTRICT P, sa_sint_t n,
10880
    const sa_sint_t* freq, const sa_uint_t* RESTRICT I,
10881
    sa_uint_t* RESTRICT bucket2, uint16_t* RESTRICT fastbits,
10882
    sa_uint_t* RESTRICT buckets, sa_sint_t threads) {
10883
  sa_uint_t bucket1[ALPHABET_SIZE];
10884
10885
  fast_uint_t index = I[0];
10886
  fast_uint_t lastc = T[0];
10887
  fast_uint_t shift = 0;
10888
  while ((n >> shift) > ((sa_sint_t)1 << UNBWT_FASTBITS)) {
10889
    shift++;
10890
  }
10891
10892
  memset(bucket1, 0, ALPHABET_SIZE * sizeof(sa_uint_t));
10893
  memset(bucket2, 0, ALPHABET_SIZE * ALPHABET_SIZE * sizeof(sa_uint_t));
10894
10895
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 65536)
10896
  {
10897
    fast_sint_t omp_thread_num = omp_get_thread_num();
10898
    fast_sint_t omp_num_threads = omp_get_num_threads();
10899
10900
    if (omp_num_threads == 1) {
10901
      libsais_unbwt_init_single(T, P, n, freq, I, bucket2, fastbits);
10902
    } else {
10903
      sa_uint_t* RESTRICT bucket1_local =
10904
          buckets +
10905
          omp_thread_num * (ALPHABET_SIZE + (ALPHABET_SIZE * ALPHABET_SIZE));
10906
      sa_uint_t* RESTRICT bucket2_local = bucket1_local + ALPHABET_SIZE;
10907
10908
      fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
10909
      fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
10910
      fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
10911
                                       ? omp_block_stride
10912
                                       : n - omp_block_start;
10913
10914
      {
10915
        memset(bucket1_local, 0, ALPHABET_SIZE * sizeof(sa_uint_t));
10916
        libsais_unbwt_compute_histogram(T + omp_block_start, omp_block_size,
10917
                                        bucket1_local);
10918
      }
10919
10920
#pragma omp barrier
10921
10922
#pragma omp master
10923
      {
10924
        {
10925
          sa_uint_t* RESTRICT bucket1_temp = buckets;
10926
10927
          fast_sint_t t;
10928
          for (t = 0; t < omp_num_threads; ++t,
10929
              bucket1_temp += ALPHABET_SIZE + (ALPHABET_SIZE * ALPHABET_SIZE)) {
10930
            fast_sint_t c;
10931
            for (c = 0; c < ALPHABET_SIZE; c += 1) {
10932
              sa_uint_t A = bucket1[c], B = bucket1_temp[c];
10933
              bucket1[c] = A + B;
10934
              bucket1_temp[c] = A;
10935
            }
10936
          }
10937
        }
10938
10939
        {
10940
          fast_uint_t sum, c;
10941
          for (sum = 1, c = 0; c < ALPHABET_SIZE; ++c) {
10942
            fast_uint_t prev = sum;
10943
            sum += bucket1[c];
10944
            bucket1[c] = (sa_uint_t)prev;
10945
          }
10946
        }
10947
      }
10948
10949
#pragma omp barrier
10950
10951
      {
10952
        fast_sint_t c;
10953
        for (c = 0; c < ALPHABET_SIZE; c += 1) {
10954
          sa_uint_t A = bucket1[c], B = bucket1_local[c];
10955
          bucket1_local[c] = A + B;
10956
        }
10957
10958
        memset(bucket2_local, 0,
10959
               ALPHABET_SIZE * ALPHABET_SIZE * sizeof(sa_uint_t));
10960
        libsais_unbwt_compute_bigram_histogram_parallel(
10961
            T, index, bucket1_local, bucket2_local, omp_block_start,
10962
            omp_block_size);
10963
      }
10964
10965
#pragma omp barrier
10966
10967
      {
10968
        fast_sint_t omp_bucket2_stride =
10969
            ((ALPHABET_SIZE * ALPHABET_SIZE) / omp_num_threads) & (-16);
10970
        fast_sint_t omp_bucket2_start = omp_thread_num * omp_bucket2_stride;
10971
        fast_sint_t omp_bucket2_size =
10972
            omp_thread_num < omp_num_threads - 1
10973
                ? omp_bucket2_stride
10974
                : (ALPHABET_SIZE * ALPHABET_SIZE) - omp_bucket2_start;
10975
10976
        sa_uint_t* RESTRICT bucket2_temp = buckets + ALPHABET_SIZE;
10977
10978
        fast_sint_t t;
10979
        for (t = 0; t < omp_num_threads; ++t,
10980
            bucket2_temp += ALPHABET_SIZE + (ALPHABET_SIZE * ALPHABET_SIZE)) {
10981
          fast_sint_t c;
10982
          for (c = omp_bucket2_start; c < omp_bucket2_start + omp_bucket2_size;
10983
               c += 1) {
10984
            sa_uint_t A = bucket2[c], B = bucket2_temp[c];
10985
            bucket2[c] = A + B;
10986
            bucket2_temp[c] = A;
10987
          }
10988
        }
10989
      }
10990
10991
#pragma omp barrier
10992
10993
#pragma omp master
10994
      {
10995
        libsais_unbwt_calculate_fastbits(bucket2, fastbits, lastc, shift);
10996
10997
        {
10998
          fast_sint_t t;
10999
          for (t = omp_num_threads - 1; t >= 1; --t) {
11000
            sa_uint_t* RESTRICT dst_bucket1 =
11001
                buckets + t * (ALPHABET_SIZE + (ALPHABET_SIZE * ALPHABET_SIZE));
11002
            sa_uint_t* RESTRICT src_bucket1 =
11003
                dst_bucket1 - (ALPHABET_SIZE + (ALPHABET_SIZE * ALPHABET_SIZE));
11004
11005
            memcpy(dst_bucket1, src_bucket1, ALPHABET_SIZE * sizeof(sa_uint_t));
11006
          }
11007
11008
          memcpy(buckets, bucket1, ALPHABET_SIZE * sizeof(sa_uint_t));
11009
        }
11010
      }
11011
11012
#pragma omp barrier
11013
11014
      {
11015
        fast_sint_t c;
11016
        for (c = 0; c < ALPHABET_SIZE * ALPHABET_SIZE; c += 1) {
11017
          sa_uint_t A = bucket2[c], B = bucket2_local[c];
11018
          bucket2_local[c] = A + B;
11019
        }
11020
11021
        libsais_unbwt_calculate_biPSI(T, P, bucket1_local, bucket2_local, index,
11022
                                      omp_block_start,
11023
                                      omp_block_start + omp_block_size);
11024
      }
11025
11026
#pragma omp barrier
11027
11028
#pragma omp master
11029
      {
11030
        memcpy(bucket2,
11031
               buckets + ALPHABET_SIZE +
11032
                   (omp_num_threads - 1) *
11033
                       (ALPHABET_SIZE + (ALPHABET_SIZE * ALPHABET_SIZE)),
11034
               ALPHABET_SIZE * ALPHABET_SIZE * sizeof(sa_uint_t));
11035
      }
11036
    }
11037
  }
11038
}
11039
11040
#endif
11041
11042
static void libsais_unbwt_decode_1(uint8_t* RESTRICT U, sa_uint_t* RESTRICT P,
11043
                                   sa_uint_t* RESTRICT bucket2,
11044
                                   uint16_t* RESTRICT fastbits,
11045
                                   fast_uint_t shift, fast_uint_t* i0,
11046
0
                                   fast_uint_t k) {
11047
0
  uint16_t* RESTRICT U0 = (uint16_t*)(void*)U;
11048
11049
0
  fast_uint_t i, p0 = *i0;
11050
11051
0
  for (i = 0; i != k; ++i) {
11052
0
    uint16_t c0 = fastbits[p0 >> shift];
11053
0
    if (bucket2[c0] <= p0) {
11054
0
      do {
11055
0
        c0++;
11056
0
      } while (bucket2[c0] <= p0);
11057
0
    }
11058
0
    p0 = P[p0];
11059
0
    U0[i] = libsais_bswap16(c0);
11060
0
  }
11061
11062
0
  *i0 = p0;
11063
0
}
11064
11065
static void libsais_unbwt_decode_2(uint8_t* RESTRICT U, sa_uint_t* RESTRICT P,
11066
                                   sa_uint_t* RESTRICT bucket2,
11067
                                   uint16_t* RESTRICT fastbits,
11068
                                   fast_uint_t shift, fast_uint_t r,
11069
                                   fast_uint_t* i0, fast_uint_t* i1,
11070
0
                                   fast_uint_t k) {
11071
0
  uint16_t* RESTRICT U0 = (uint16_t*)(void*)U;
11072
0
  uint16_t* RESTRICT U1 = (uint16_t*)(void*)(((uint8_t*)U0) + r);
11073
11074
0
  fast_uint_t i, p0 = *i0, p1 = *i1;
11075
11076
0
  for (i = 0; i != k; ++i) {
11077
0
    uint16_t c0 = fastbits[p0 >> shift];
11078
0
    if (bucket2[c0] <= p0) {
11079
0
      do {
11080
0
        c0++;
11081
0
      } while (bucket2[c0] <= p0);
11082
0
    }
11083
0
    p0 = P[p0];
11084
0
    U0[i] = libsais_bswap16(c0);
11085
0
    uint16_t c1 = fastbits[p1 >> shift];
11086
0
    if (bucket2[c1] <= p1) {
11087
0
      do {
11088
0
        c1++;
11089
0
      } while (bucket2[c1] <= p1);
11090
0
    }
11091
0
    p1 = P[p1];
11092
0
    U1[i] = libsais_bswap16(c1);
11093
0
  }
11094
11095
0
  *i0 = p0;
11096
0
  *i1 = p1;
11097
0
}
11098
11099
static void libsais_unbwt_decode_3(uint8_t* RESTRICT U, sa_uint_t* RESTRICT P,
11100
                                   sa_uint_t* RESTRICT bucket2,
11101
                                   uint16_t* RESTRICT fastbits,
11102
                                   fast_uint_t shift, fast_uint_t r,
11103
                                   fast_uint_t* i0, fast_uint_t* i1,
11104
0
                                   fast_uint_t* i2, fast_uint_t k) {
11105
0
  uint16_t* RESTRICT U0 = (uint16_t*)(void*)U;
11106
0
  uint16_t* RESTRICT U1 = (uint16_t*)(void*)(((uint8_t*)U0) + r);
11107
0
  uint16_t* RESTRICT U2 = (uint16_t*)(void*)(((uint8_t*)U1) + r);
11108
11109
0
  fast_uint_t i, p0 = *i0, p1 = *i1, p2 = *i2;
11110
11111
0
  for (i = 0; i != k; ++i) {
11112
0
    uint16_t c0 = fastbits[p0 >> shift];
11113
0
    if (bucket2[c0] <= p0) {
11114
0
      do {
11115
0
        c0++;
11116
0
      } while (bucket2[c0] <= p0);
11117
0
    }
11118
0
    p0 = P[p0];
11119
0
    U0[i] = libsais_bswap16(c0);
11120
0
    uint16_t c1 = fastbits[p1 >> shift];
11121
0
    if (bucket2[c1] <= p1) {
11122
0
      do {
11123
0
        c1++;
11124
0
      } while (bucket2[c1] <= p1);
11125
0
    }
11126
0
    p1 = P[p1];
11127
0
    U1[i] = libsais_bswap16(c1);
11128
0
    uint16_t c2 = fastbits[p2 >> shift];
11129
0
    if (bucket2[c2] <= p2) {
11130
0
      do {
11131
0
        c2++;
11132
0
      } while (bucket2[c2] <= p2);
11133
0
    }
11134
0
    p2 = P[p2];
11135
0
    U2[i] = libsais_bswap16(c2);
11136
0
  }
11137
11138
0
  *i0 = p0;
11139
0
  *i1 = p1;
11140
0
  *i2 = p2;
11141
0
}
11142
11143
static void libsais_unbwt_decode_4(uint8_t* RESTRICT U, sa_uint_t* RESTRICT P,
11144
                                   sa_uint_t* RESTRICT bucket2,
11145
                                   uint16_t* RESTRICT fastbits,
11146
                                   fast_uint_t shift, fast_uint_t r,
11147
                                   fast_uint_t* i0, fast_uint_t* i1,
11148
                                   fast_uint_t* i2, fast_uint_t* i3,
11149
0
                                   fast_uint_t k) {
11150
0
  uint16_t* RESTRICT U0 = (uint16_t*)(void*)U;
11151
0
  uint16_t* RESTRICT U1 = (uint16_t*)(void*)(((uint8_t*)U0) + r);
11152
0
  uint16_t* RESTRICT U2 = (uint16_t*)(void*)(((uint8_t*)U1) + r);
11153
0
  uint16_t* RESTRICT U3 = (uint16_t*)(void*)(((uint8_t*)U2) + r);
11154
11155
0
  fast_uint_t i, p0 = *i0, p1 = *i1, p2 = *i2, p3 = *i3;
11156
11157
0
  for (i = 0; i != k; ++i) {
11158
0
    uint16_t c0 = fastbits[p0 >> shift];
11159
0
    if (bucket2[c0] <= p0) {
11160
0
      do {
11161
0
        c0++;
11162
0
      } while (bucket2[c0] <= p0);
11163
0
    }
11164
0
    p0 = P[p0];
11165
0
    U0[i] = libsais_bswap16(c0);
11166
0
    uint16_t c1 = fastbits[p1 >> shift];
11167
0
    if (bucket2[c1] <= p1) {
11168
0
      do {
11169
0
        c1++;
11170
0
      } while (bucket2[c1] <= p1);
11171
0
    }
11172
0
    p1 = P[p1];
11173
0
    U1[i] = libsais_bswap16(c1);
11174
0
    uint16_t c2 = fastbits[p2 >> shift];
11175
0
    if (bucket2[c2] <= p2) {
11176
0
      do {
11177
0
        c2++;
11178
0
      } while (bucket2[c2] <= p2);
11179
0
    }
11180
0
    p2 = P[p2];
11181
0
    U2[i] = libsais_bswap16(c2);
11182
0
    uint16_t c3 = fastbits[p3 >> shift];
11183
0
    if (bucket2[c3] <= p3) {
11184
0
      do {
11185
0
        c3++;
11186
0
      } while (bucket2[c3] <= p3);
11187
0
    }
11188
0
    p3 = P[p3];
11189
0
    U3[i] = libsais_bswap16(c3);
11190
0
  }
11191
11192
0
  *i0 = p0;
11193
0
  *i1 = p1;
11194
0
  *i2 = p2;
11195
0
  *i3 = p3;
11196
0
}
11197
11198
static void libsais_unbwt_decode_5(uint8_t* RESTRICT U, sa_uint_t* RESTRICT P,
11199
                                   sa_uint_t* RESTRICT bucket2,
11200
                                   uint16_t* RESTRICT fastbits,
11201
                                   fast_uint_t shift, fast_uint_t r,
11202
                                   fast_uint_t* i0, fast_uint_t* i1,
11203
                                   fast_uint_t* i2, fast_uint_t* i3,
11204
0
                                   fast_uint_t* i4, fast_uint_t k) {
11205
0
  uint16_t* RESTRICT U0 = (uint16_t*)(void*)U;
11206
0
  uint16_t* RESTRICT U1 = (uint16_t*)(void*)(((uint8_t*)U0) + r);
11207
0
  uint16_t* RESTRICT U2 = (uint16_t*)(void*)(((uint8_t*)U1) + r);
11208
0
  uint16_t* RESTRICT U3 = (uint16_t*)(void*)(((uint8_t*)U2) + r);
11209
0
  uint16_t* RESTRICT U4 = (uint16_t*)(void*)(((uint8_t*)U3) + r);
11210
11211
0
  fast_uint_t i, p0 = *i0, p1 = *i1, p2 = *i2, p3 = *i3, p4 = *i4;
11212
11213
0
  for (i = 0; i != k; ++i) {
11214
0
    uint16_t c0 = fastbits[p0 >> shift];
11215
0
    if (bucket2[c0] <= p0) {
11216
0
      do {
11217
0
        c0++;
11218
0
      } while (bucket2[c0] <= p0);
11219
0
    }
11220
0
    p0 = P[p0];
11221
0
    U0[i] = libsais_bswap16(c0);
11222
0
    uint16_t c1 = fastbits[p1 >> shift];
11223
0
    if (bucket2[c1] <= p1) {
11224
0
      do {
11225
0
        c1++;
11226
0
      } while (bucket2[c1] <= p1);
11227
0
    }
11228
0
    p1 = P[p1];
11229
0
    U1[i] = libsais_bswap16(c1);
11230
0
    uint16_t c2 = fastbits[p2 >> shift];
11231
0
    if (bucket2[c2] <= p2) {
11232
0
      do {
11233
0
        c2++;
11234
0
      } while (bucket2[c2] <= p2);
11235
0
    }
11236
0
    p2 = P[p2];
11237
0
    U2[i] = libsais_bswap16(c2);
11238
0
    uint16_t c3 = fastbits[p3 >> shift];
11239
0
    if (bucket2[c3] <= p3) {
11240
0
      do {
11241
0
        c3++;
11242
0
      } while (bucket2[c3] <= p3);
11243
0
    }
11244
0
    p3 = P[p3];
11245
0
    U3[i] = libsais_bswap16(c3);
11246
0
    uint16_t c4 = fastbits[p4 >> shift];
11247
0
    if (bucket2[c4] <= p4) {
11248
0
      do {
11249
0
        c4++;
11250
0
      } while (bucket2[c4] <= p4);
11251
0
    }
11252
0
    p4 = P[p4];
11253
0
    U4[i] = libsais_bswap16(c4);
11254
0
  }
11255
11256
0
  *i0 = p0;
11257
0
  *i1 = p1;
11258
0
  *i2 = p2;
11259
0
  *i3 = p3;
11260
0
  *i4 = p4;
11261
0
}
11262
11263
static void libsais_unbwt_decode_6(
11264
    uint8_t* RESTRICT U, sa_uint_t* RESTRICT P, sa_uint_t* RESTRICT bucket2,
11265
    uint16_t* RESTRICT fastbits, fast_uint_t shift, fast_uint_t r,
11266
    fast_uint_t* i0, fast_uint_t* i1, fast_uint_t* i2, fast_uint_t* i3,
11267
0
    fast_uint_t* i4, fast_uint_t* i5, fast_uint_t k) {
11268
0
  uint16_t* RESTRICT U0 = (uint16_t*)(void*)U;
11269
0
  uint16_t* RESTRICT U1 = (uint16_t*)(void*)(((uint8_t*)U0) + r);
11270
0
  uint16_t* RESTRICT U2 = (uint16_t*)(void*)(((uint8_t*)U1) + r);
11271
0
  uint16_t* RESTRICT U3 = (uint16_t*)(void*)(((uint8_t*)U2) + r);
11272
0
  uint16_t* RESTRICT U4 = (uint16_t*)(void*)(((uint8_t*)U3) + r);
11273
0
  uint16_t* RESTRICT U5 = (uint16_t*)(void*)(((uint8_t*)U4) + r);
11274
11275
0
  fast_uint_t i, p0 = *i0, p1 = *i1, p2 = *i2, p3 = *i3, p4 = *i4, p5 = *i5;
11276
11277
0
  for (i = 0; i != k; ++i) {
11278
0
    uint16_t c0 = fastbits[p0 >> shift];
11279
0
    if (bucket2[c0] <= p0) {
11280
0
      do {
11281
0
        c0++;
11282
0
      } while (bucket2[c0] <= p0);
11283
0
    }
11284
0
    p0 = P[p0];
11285
0
    U0[i] = libsais_bswap16(c0);
11286
0
    uint16_t c1 = fastbits[p1 >> shift];
11287
0
    if (bucket2[c1] <= p1) {
11288
0
      do {
11289
0
        c1++;
11290
0
      } while (bucket2[c1] <= p1);
11291
0
    }
11292
0
    p1 = P[p1];
11293
0
    U1[i] = libsais_bswap16(c1);
11294
0
    uint16_t c2 = fastbits[p2 >> shift];
11295
0
    if (bucket2[c2] <= p2) {
11296
0
      do {
11297
0
        c2++;
11298
0
      } while (bucket2[c2] <= p2);
11299
0
    }
11300
0
    p2 = P[p2];
11301
0
    U2[i] = libsais_bswap16(c2);
11302
0
    uint16_t c3 = fastbits[p3 >> shift];
11303
0
    if (bucket2[c3] <= p3) {
11304
0
      do {
11305
0
        c3++;
11306
0
      } while (bucket2[c3] <= p3);
11307
0
    }
11308
0
    p3 = P[p3];
11309
0
    U3[i] = libsais_bswap16(c3);
11310
0
    uint16_t c4 = fastbits[p4 >> shift];
11311
0
    if (bucket2[c4] <= p4) {
11312
0
      do {
11313
0
        c4++;
11314
0
      } while (bucket2[c4] <= p4);
11315
0
    }
11316
0
    p4 = P[p4];
11317
0
    U4[i] = libsais_bswap16(c4);
11318
0
    uint16_t c5 = fastbits[p5 >> shift];
11319
0
    if (bucket2[c5] <= p5) {
11320
0
      do {
11321
0
        c5++;
11322
0
      } while (bucket2[c5] <= p5);
11323
0
    }
11324
0
    p5 = P[p5];
11325
0
    U5[i] = libsais_bswap16(c5);
11326
0
  }
11327
11328
0
  *i0 = p0;
11329
0
  *i1 = p1;
11330
0
  *i2 = p2;
11331
0
  *i3 = p3;
11332
0
  *i4 = p4;
11333
0
  *i5 = p5;
11334
0
}
11335
11336
static void libsais_unbwt_decode_7(
11337
    uint8_t* RESTRICT U, sa_uint_t* RESTRICT P, sa_uint_t* RESTRICT bucket2,
11338
    uint16_t* RESTRICT fastbits, fast_uint_t shift, fast_uint_t r,
11339
    fast_uint_t* i0, fast_uint_t* i1, fast_uint_t* i2, fast_uint_t* i3,
11340
0
    fast_uint_t* i4, fast_uint_t* i5, fast_uint_t* i6, fast_uint_t k) {
11341
0
  uint16_t* RESTRICT U0 = (uint16_t*)(void*)U;
11342
0
  uint16_t* RESTRICT U1 = (uint16_t*)(void*)(((uint8_t*)U0) + r);
11343
0
  uint16_t* RESTRICT U2 = (uint16_t*)(void*)(((uint8_t*)U1) + r);
11344
0
  uint16_t* RESTRICT U3 = (uint16_t*)(void*)(((uint8_t*)U2) + r);
11345
0
  uint16_t* RESTRICT U4 = (uint16_t*)(void*)(((uint8_t*)U3) + r);
11346
0
  uint16_t* RESTRICT U5 = (uint16_t*)(void*)(((uint8_t*)U4) + r);
11347
0
  uint16_t* RESTRICT U6 = (uint16_t*)(void*)(((uint8_t*)U5) + r);
11348
11349
0
  fast_uint_t i, p0 = *i0, p1 = *i1, p2 = *i2, p3 = *i3, p4 = *i4, p5 = *i5,
11350
0
                 p6 = *i6;
11351
11352
0
  for (i = 0; i != k; ++i) {
11353
0
    uint16_t c0 = fastbits[p0 >> shift];
11354
0
    if (bucket2[c0] <= p0) {
11355
0
      do {
11356
0
        c0++;
11357
0
      } while (bucket2[c0] <= p0);
11358
0
    }
11359
0
    p0 = P[p0];
11360
0
    U0[i] = libsais_bswap16(c0);
11361
0
    uint16_t c1 = fastbits[p1 >> shift];
11362
0
    if (bucket2[c1] <= p1) {
11363
0
      do {
11364
0
        c1++;
11365
0
      } while (bucket2[c1] <= p1);
11366
0
    }
11367
0
    p1 = P[p1];
11368
0
    U1[i] = libsais_bswap16(c1);
11369
0
    uint16_t c2 = fastbits[p2 >> shift];
11370
0
    if (bucket2[c2] <= p2) {
11371
0
      do {
11372
0
        c2++;
11373
0
      } while (bucket2[c2] <= p2);
11374
0
    }
11375
0
    p2 = P[p2];
11376
0
    U2[i] = libsais_bswap16(c2);
11377
0
    uint16_t c3 = fastbits[p3 >> shift];
11378
0
    if (bucket2[c3] <= p3) {
11379
0
      do {
11380
0
        c3++;
11381
0
      } while (bucket2[c3] <= p3);
11382
0
    }
11383
0
    p3 = P[p3];
11384
0
    U3[i] = libsais_bswap16(c3);
11385
0
    uint16_t c4 = fastbits[p4 >> shift];
11386
0
    if (bucket2[c4] <= p4) {
11387
0
      do {
11388
0
        c4++;
11389
0
      } while (bucket2[c4] <= p4);
11390
0
    }
11391
0
    p4 = P[p4];
11392
0
    U4[i] = libsais_bswap16(c4);
11393
0
    uint16_t c5 = fastbits[p5 >> shift];
11394
0
    if (bucket2[c5] <= p5) {
11395
0
      do {
11396
0
        c5++;
11397
0
      } while (bucket2[c5] <= p5);
11398
0
    }
11399
0
    p5 = P[p5];
11400
0
    U5[i] = libsais_bswap16(c5);
11401
0
    uint16_t c6 = fastbits[p6 >> shift];
11402
0
    if (bucket2[c6] <= p6) {
11403
0
      do {
11404
0
        c6++;
11405
0
      } while (bucket2[c6] <= p6);
11406
0
    }
11407
0
    p6 = P[p6];
11408
0
    U6[i] = libsais_bswap16(c6);
11409
0
  }
11410
11411
0
  *i0 = p0;
11412
0
  *i1 = p1;
11413
0
  *i2 = p2;
11414
0
  *i3 = p3;
11415
0
  *i4 = p4;
11416
0
  *i5 = p5;
11417
0
  *i6 = p6;
11418
0
}
11419
11420
static void libsais_unbwt_decode_8(
11421
    uint8_t* RESTRICT U, sa_uint_t* RESTRICT P, sa_uint_t* RESTRICT bucket2,
11422
    uint16_t* RESTRICT fastbits, fast_uint_t shift, fast_uint_t r,
11423
    fast_uint_t* i0, fast_uint_t* i1, fast_uint_t* i2, fast_uint_t* i3,
11424
    fast_uint_t* i4, fast_uint_t* i5, fast_uint_t* i6, fast_uint_t* i7,
11425
0
    fast_uint_t k) {
11426
0
  uint16_t* RESTRICT U0 = (uint16_t*)(void*)U;
11427
0
  uint16_t* RESTRICT U1 = (uint16_t*)(void*)(((uint8_t*)U0) + r);
11428
0
  uint16_t* RESTRICT U2 = (uint16_t*)(void*)(((uint8_t*)U1) + r);
11429
0
  uint16_t* RESTRICT U3 = (uint16_t*)(void*)(((uint8_t*)U2) + r);
11430
0
  uint16_t* RESTRICT U4 = (uint16_t*)(void*)(((uint8_t*)U3) + r);
11431
0
  uint16_t* RESTRICT U5 = (uint16_t*)(void*)(((uint8_t*)U4) + r);
11432
0
  uint16_t* RESTRICT U6 = (uint16_t*)(void*)(((uint8_t*)U5) + r);
11433
0
  uint16_t* RESTRICT U7 = (uint16_t*)(void*)(((uint8_t*)U6) + r);
11434
11435
0
  fast_uint_t i, p0 = *i0, p1 = *i1, p2 = *i2, p3 = *i3, p4 = *i4, p5 = *i5,
11436
0
                 p6 = *i6, p7 = *i7;
11437
11438
0
  for (i = 0; i != k; ++i) {
11439
0
    uint16_t c0 = fastbits[p0 >> shift];
11440
0
    if (bucket2[c0] <= p0) {
11441
0
      do {
11442
0
        c0++;
11443
0
      } while (bucket2[c0] <= p0);
11444
0
    }
11445
0
    p0 = P[p0];
11446
0
    U0[i] = libsais_bswap16(c0);
11447
0
    uint16_t c1 = fastbits[p1 >> shift];
11448
0
    if (bucket2[c1] <= p1) {
11449
0
      do {
11450
0
        c1++;
11451
0
      } while (bucket2[c1] <= p1);
11452
0
    }
11453
0
    p1 = P[p1];
11454
0
    U1[i] = libsais_bswap16(c1);
11455
0
    uint16_t c2 = fastbits[p2 >> shift];
11456
0
    if (bucket2[c2] <= p2) {
11457
0
      do {
11458
0
        c2++;
11459
0
      } while (bucket2[c2] <= p2);
11460
0
    }
11461
0
    p2 = P[p2];
11462
0
    U2[i] = libsais_bswap16(c2);
11463
0
    uint16_t c3 = fastbits[p3 >> shift];
11464
0
    if (bucket2[c3] <= p3) {
11465
0
      do {
11466
0
        c3++;
11467
0
      } while (bucket2[c3] <= p3);
11468
0
    }
11469
0
    p3 = P[p3];
11470
0
    U3[i] = libsais_bswap16(c3);
11471
0
    uint16_t c4 = fastbits[p4 >> shift];
11472
0
    if (bucket2[c4] <= p4) {
11473
0
      do {
11474
0
        c4++;
11475
0
      } while (bucket2[c4] <= p4);
11476
0
    }
11477
0
    p4 = P[p4];
11478
0
    U4[i] = libsais_bswap16(c4);
11479
0
    uint16_t c5 = fastbits[p5 >> shift];
11480
0
    if (bucket2[c5] <= p5) {
11481
0
      do {
11482
0
        c5++;
11483
0
      } while (bucket2[c5] <= p5);
11484
0
    }
11485
0
    p5 = P[p5];
11486
0
    U5[i] = libsais_bswap16(c5);
11487
0
    uint16_t c6 = fastbits[p6 >> shift];
11488
0
    if (bucket2[c6] <= p6) {
11489
0
      do {
11490
0
        c6++;
11491
0
      } while (bucket2[c6] <= p6);
11492
0
    }
11493
0
    p6 = P[p6];
11494
0
    U6[i] = libsais_bswap16(c6);
11495
0
    uint16_t c7 = fastbits[p7 >> shift];
11496
0
    if (bucket2[c7] <= p7) {
11497
0
      do {
11498
0
        c7++;
11499
0
      } while (bucket2[c7] <= p7);
11500
0
    }
11501
0
    p7 = P[p7];
11502
0
    U7[i] = libsais_bswap16(c7);
11503
0
  }
11504
11505
0
  *i0 = p0;
11506
0
  *i1 = p1;
11507
0
  *i2 = p2;
11508
0
  *i3 = p3;
11509
0
  *i4 = p4;
11510
0
  *i5 = p5;
11511
0
  *i6 = p6;
11512
0
  *i7 = p7;
11513
0
}
11514
11515
static void libsais_unbwt_decode(uint8_t* RESTRICT U, sa_uint_t* RESTRICT P,
11516
                                 sa_sint_t n, sa_sint_t r,
11517
                                 const sa_uint_t* RESTRICT I,
11518
                                 sa_uint_t* RESTRICT bucket2,
11519
                                 uint16_t* RESTRICT fastbits,
11520
0
                                 fast_sint_t blocks, fast_uint_t remainder) {
11521
0
  fast_uint_t shift = 0;
11522
0
  while ((n >> shift) > ((sa_sint_t)1 << UNBWT_FASTBITS)) {
11523
0
    shift++;
11524
0
  }
11525
0
  fast_uint_t offset = 0;
11526
11527
0
  while (blocks > 8) {
11528
0
    fast_uint_t i0 = I[0], i1 = I[1], i2 = I[2], i3 = I[3], i4 = I[4],
11529
0
                i5 = I[5], i6 = I[6], i7 = I[7];
11530
0
    libsais_unbwt_decode_8(U + offset, P, bucket2, fastbits, shift,
11531
0
                           (fast_uint_t)r, &i0, &i1, &i2, &i3, &i4, &i5, &i6,
11532
0
                           &i7, (fast_uint_t)r >> 1);
11533
0
    I += 8;
11534
0
    blocks -= 8;
11535
0
    offset += 8 * (fast_uint_t)r;
11536
0
  }
11537
11538
0
  if (blocks == 1) {
11539
0
    fast_uint_t i0 = I[0];
11540
0
    libsais_unbwt_decode_1(U + offset, P, bucket2, fastbits, shift, &i0,
11541
0
                           remainder >> 1);
11542
0
  } else if (blocks == 2) {
11543
0
    fast_uint_t i0 = I[0], i1 = I[1];
11544
0
    libsais_unbwt_decode_2(U + offset, P, bucket2, fastbits, shift,
11545
0
                           (fast_uint_t)r, &i0, &i1, remainder >> 1);
11546
0
    libsais_unbwt_decode_1(U + offset + 2 * (remainder >> 1), P, bucket2,
11547
0
                           fastbits, shift, &i0,
11548
0
                           ((fast_uint_t)r >> 1) - (remainder >> 1));
11549
0
  } else if (blocks == 3) {
11550
0
    fast_uint_t i0 = I[0], i1 = I[1], i2 = I[2];
11551
0
    libsais_unbwt_decode_3(U + offset, P, bucket2, fastbits, shift,
11552
0
                           (fast_uint_t)r, &i0, &i1, &i2, remainder >> 1);
11553
0
    libsais_unbwt_decode_2(U + offset + 2 * (remainder >> 1), P, bucket2,
11554
0
                           fastbits, shift, (fast_uint_t)r, &i0, &i1,
11555
0
                           ((fast_uint_t)r >> 1) - (remainder >> 1));
11556
0
  } else if (blocks == 4) {
11557
0
    fast_uint_t i0 = I[0], i1 = I[1], i2 = I[2], i3 = I[3];
11558
0
    libsais_unbwt_decode_4(U + offset, P, bucket2, fastbits, shift,
11559
0
                           (fast_uint_t)r, &i0, &i1, &i2, &i3, remainder >> 1);
11560
0
    libsais_unbwt_decode_3(U + offset + 2 * (remainder >> 1), P, bucket2,
11561
0
                           fastbits, shift, (fast_uint_t)r, &i0, &i1, &i2,
11562
0
                           ((fast_uint_t)r >> 1) - (remainder >> 1));
11563
0
  } else if (blocks == 5) {
11564
0
    fast_uint_t i0 = I[0], i1 = I[1], i2 = I[2], i3 = I[3], i4 = I[4];
11565
0
    libsais_unbwt_decode_5(U + offset, P, bucket2, fastbits, shift,
11566
0
                           (fast_uint_t)r, &i0, &i1, &i2, &i3, &i4,
11567
0
                           remainder >> 1);
11568
0
    libsais_unbwt_decode_4(U + offset + 2 * (remainder >> 1), P, bucket2,
11569
0
                           fastbits, shift, (fast_uint_t)r, &i0, &i1, &i2, &i3,
11570
0
                           ((fast_uint_t)r >> 1) - (remainder >> 1));
11571
0
  } else if (blocks == 6) {
11572
0
    fast_uint_t i0 = I[0], i1 = I[1], i2 = I[2], i3 = I[3], i4 = I[4],
11573
0
                i5 = I[5];
11574
0
    libsais_unbwt_decode_6(U + offset, P, bucket2, fastbits, shift,
11575
0
                           (fast_uint_t)r, &i0, &i1, &i2, &i3, &i4, &i5,
11576
0
                           remainder >> 1);
11577
0
    libsais_unbwt_decode_5(U + offset + 2 * (remainder >> 1), P, bucket2,
11578
0
                           fastbits, shift, (fast_uint_t)r, &i0, &i1, &i2, &i3,
11579
0
                           &i4, ((fast_uint_t)r >> 1) - (remainder >> 1));
11580
0
  } else if (blocks == 7) {
11581
0
    fast_uint_t i0 = I[0], i1 = I[1], i2 = I[2], i3 = I[3], i4 = I[4],
11582
0
                i5 = I[5], i6 = I[6];
11583
0
    libsais_unbwt_decode_7(U + offset, P, bucket2, fastbits, shift,
11584
0
                           (fast_uint_t)r, &i0, &i1, &i2, &i3, &i4, &i5, &i6,
11585
0
                           remainder >> 1);
11586
0
    libsais_unbwt_decode_6(U + offset + 2 * (remainder >> 1), P, bucket2,
11587
0
                           fastbits, shift, (fast_uint_t)r, &i0, &i1, &i2, &i3,
11588
0
                           &i4, &i5, ((fast_uint_t)r >> 1) - (remainder >> 1));
11589
0
  } else {
11590
0
    fast_uint_t i0 = I[0], i1 = I[1], i2 = I[2], i3 = I[3], i4 = I[4],
11591
0
                i5 = I[5], i6 = I[6], i7 = I[7];
11592
0
    libsais_unbwt_decode_8(U + offset, P, bucket2, fastbits, shift,
11593
0
                           (fast_uint_t)r, &i0, &i1, &i2, &i3, &i4, &i5, &i6,
11594
0
                           &i7, remainder >> 1);
11595
0
    libsais_unbwt_decode_7(U + offset + 2 * (remainder >> 1), P, bucket2,
11596
0
                           fastbits, shift, (fast_uint_t)r, &i0, &i1, &i2, &i3,
11597
0
                           &i4, &i5, &i6,
11598
0
                           ((fast_uint_t)r >> 1) - (remainder >> 1));
11599
0
  }
11600
0
}
11601
11602
static void libsais_unbwt_decode_omp(const uint8_t* RESTRICT T,
11603
                                     uint8_t* RESTRICT U, sa_uint_t* RESTRICT P,
11604
                                     sa_sint_t n, sa_sint_t r,
11605
                                     const sa_uint_t* RESTRICT I,
11606
                                     sa_uint_t* RESTRICT bucket2,
11607
                                     uint16_t* RESTRICT fastbits,
11608
0
                                     sa_sint_t threads) {
11609
0
  fast_uint_t lastc = T[0];
11610
0
  fast_sint_t blocks = 1 + (((fast_sint_t)n - 1) / (fast_sint_t)r);
11611
0
  fast_uint_t remainder =
11612
0
      (fast_uint_t)n - ((fast_uint_t)r * ((fast_uint_t)blocks - 1));
11613
11614
#if defined(LIBSAIS_OPENMP)
11615
  fast_sint_t max_threads = blocks < threads ? blocks : threads;
11616
#pragma omp parallel num_threads(max_threads) if (max_threads > 1 && n >= 65536)
11617
#endif
11618
0
  {
11619
#if defined(LIBSAIS_OPENMP)
11620
    fast_sint_t omp_thread_num = omp_get_thread_num();
11621
    fast_sint_t omp_num_threads = omp_get_num_threads();
11622
#else
11623
0
    UNUSED(threads);
11624
11625
0
    fast_sint_t omp_thread_num = 0;
11626
0
    fast_sint_t omp_num_threads = 1;
11627
0
#endif
11628
11629
0
    fast_sint_t omp_block_stride = blocks / omp_num_threads;
11630
0
    fast_sint_t omp_block_remainder = blocks % omp_num_threads;
11631
0
    fast_sint_t omp_block_size =
11632
0
        omp_block_stride + (omp_thread_num < omp_block_remainder);
11633
0
    fast_sint_t omp_block_start =
11634
0
        omp_block_stride * omp_thread_num +
11635
0
        (omp_thread_num < omp_block_remainder ? omp_thread_num
11636
0
                                              : omp_block_remainder);
11637
11638
0
    libsais_unbwt_decode(
11639
0
        U + r * omp_block_start, P, n, r, I + omp_block_start, bucket2,
11640
0
        fastbits, omp_block_size,
11641
0
        omp_thread_num < omp_num_threads - 1 ? (fast_uint_t)r : remainder);
11642
0
  }
11643
11644
0
  U[n - 1] = (uint8_t)lastc;
11645
0
}
11646
11647
static sa_sint_t libsais_unbwt_core(const uint8_t* RESTRICT T,
11648
                                    uint8_t* RESTRICT U, sa_uint_t* RESTRICT P,
11649
                                    sa_sint_t n, const sa_sint_t* freq,
11650
                                    sa_sint_t r, const sa_uint_t* RESTRICT I,
11651
                                    sa_uint_t* RESTRICT bucket2,
11652
                                    uint16_t* RESTRICT fastbits,
11653
                                    sa_uint_t* RESTRICT buckets,
11654
0
                                    sa_sint_t threads) {
11655
#if defined(LIBSAIS_OPENMP)
11656
  if (threads > 1 && n >= 262144) {
11657
    libsais_unbwt_init_parallel(T, P, n, freq, I, bucket2, fastbits, buckets,
11658
                                threads);
11659
  } else
11660
#else
11661
0
  UNUSED(buckets);
11662
0
#endif
11663
0
  {
11664
0
    libsais_unbwt_init_single(T, P, n, freq, I, bucket2, fastbits);
11665
0
  }
11666
11667
0
  libsais_unbwt_decode_omp(T, U, P, n, r, I, bucket2, fastbits, threads);
11668
0
  return 0;
11669
0
}
11670
11671
static sa_sint_t libsais_unbwt_main(const uint8_t* T, uint8_t* U, sa_uint_t* P,
11672
                                    sa_sint_t n, const sa_sint_t* freq,
11673
                                    sa_sint_t r, const sa_uint_t* I,
11674
0
                                    sa_sint_t threads) {
11675
0
  fast_uint_t shift = 0;
11676
0
  while ((n >> shift) > ((sa_sint_t)1 << UNBWT_FASTBITS)) {
11677
0
    shift++;
11678
0
  }
11679
11680
0
  sa_uint_t* RESTRICT bucket2 = (sa_uint_t*)libsais_alloc_aligned(
11681
0
      ALPHABET_SIZE * ALPHABET_SIZE * sizeof(sa_uint_t), 4096);
11682
0
  uint16_t* RESTRICT fastbits = (uint16_t*)libsais_alloc_aligned(
11683
0
      ((size_t)1 + (size_t)(n >> shift)) * sizeof(uint16_t), 4096);
11684
0
  sa_uint_t* RESTRICT buckets =
11685
0
      threads > 1 && n >= 262144
11686
0
          ? (sa_uint_t*)libsais_alloc_aligned(
11687
0
                (size_t)threads *
11688
0
                    (ALPHABET_SIZE + (ALPHABET_SIZE * ALPHABET_SIZE)) *
11689
0
                    sizeof(sa_uint_t),
11690
0
                4096)
11691
0
          : NULL;
11692
11693
0
  sa_sint_t index = bucket2 != NULL && fastbits != NULL &&
11694
0
                            (buckets != NULL || threads == 1 || n < 262144)
11695
0
                        ? libsais_unbwt_core(T, U, P, n, freq, r, I, bucket2,
11696
0
                                             fastbits, buckets, threads)
11697
0
                        : -2;
11698
11699
0
  libsais_free_aligned(buckets);
11700
0
  libsais_free_aligned(fastbits);
11701
0
  libsais_free_aligned(bucket2);
11702
11703
0
  return index;
11704
0
}
11705
11706
static sa_sint_t libsais_unbwt_main_ctx(const LIBSAIS_UNBWT_CONTEXT* ctx,
11707
                                        const uint8_t* T, uint8_t* U,
11708
                                        sa_uint_t* P, sa_sint_t n,
11709
                                        const sa_sint_t* freq, sa_sint_t r,
11710
0
                                        const sa_uint_t* I) {
11711
0
  return ctx != NULL && ctx->bucket2 != NULL && ctx->fastbits != NULL &&
11712
0
                 (ctx->buckets != NULL || ctx->threads == 1)
11713
0
             ? libsais_unbwt_core(T, U, P, n, freq, r, I, ctx->bucket2,
11714
0
                                  ctx->fastbits, ctx->buckets,
11715
0
                                  (sa_sint_t)ctx->threads)
11716
0
             : -2;
11717
0
}
11718
11719
0
void* libsais_unbwt_create_ctx(void) {
11720
0
  return (void*)libsais_unbwt_create_ctx_main(1);
11721
0
}
11722
11723
0
void libsais_unbwt_free_ctx(void* ctx) {
11724
0
  libsais_unbwt_free_ctx_main((LIBSAIS_UNBWT_CONTEXT*)ctx);
11725
0
}
11726
11727
int32_t libsais_unbwt(const uint8_t* T, uint8_t* U, int32_t* A, int32_t n,
11728
0
                      const int32_t* freq, int32_t i) {
11729
0
  return libsais_unbwt_aux(T, U, A, n, freq, n, &i);
11730
0
}
11731
11732
int32_t libsais_unbwt_ctx(const void* ctx, const uint8_t* T, uint8_t* U,
11733
                          int32_t* A, int32_t n, const int32_t* freq,
11734
0
                          int32_t i) {
11735
0
  return libsais_unbwt_aux_ctx(ctx, T, U, A, n, freq, n, &i);
11736
0
}
11737
11738
int32_t libsais_unbwt_aux(const uint8_t* T, uint8_t* U, int32_t* A, int32_t n,
11739
0
                          const int32_t* freq, int32_t r, const int32_t* I) {
11740
0
  if ((T == NULL) || (U == NULL) || (A == NULL) || (n < 0) ||
11741
0
      ((r != n) && ((r < 2) || ((r & (r - 1)) != 0))) || (I == NULL)) {
11742
0
    return -1;
11743
0
  } else if (n <= 1) {
11744
0
    if (I[0] != n) {
11745
0
      return -1;
11746
0
    }
11747
0
    if (n == 1) {
11748
0
      U[0] = T[0];
11749
0
    }
11750
0
    return 0;
11751
0
  }
11752
11753
0
  fast_sint_t t;
11754
0
  for (t = 0; t <= (n - 1) / r; ++t) {
11755
0
    if (I[t] <= 0 || I[t] > n) {
11756
0
      return -1;
11757
0
    }
11758
0
  }
11759
11760
0
  return libsais_unbwt_main(T, U, (sa_uint_t*)A, n, freq, r,
11761
0
                            (const sa_uint_t*)I, 1);
11762
0
}
11763
11764
int32_t libsais_unbwt_aux_ctx(const void* ctx, const uint8_t* T, uint8_t* U,
11765
                              int32_t* A, int32_t n, const int32_t* freq,
11766
0
                              int32_t r, const int32_t* I) {
11767
0
  if ((T == NULL) || (U == NULL) || (A == NULL) || (n < 0) ||
11768
0
      ((r != n) && ((r < 2) || ((r & (r - 1)) != 0))) || (I == NULL)) {
11769
0
    return -1;
11770
0
  } else if (n <= 1) {
11771
0
    if (I[0] != n) {
11772
0
      return -1;
11773
0
    }
11774
0
    if (n == 1) {
11775
0
      U[0] = T[0];
11776
0
    }
11777
0
    return 0;
11778
0
  }
11779
11780
0
  fast_sint_t t;
11781
0
  for (t = 0; t <= (n - 1) / r; ++t) {
11782
0
    if (I[t] <= 0 || I[t] > n) {
11783
0
      return -1;
11784
0
    }
11785
0
  }
11786
11787
0
  return libsais_unbwt_main_ctx((const LIBSAIS_UNBWT_CONTEXT*)ctx, T, U,
11788
0
                                (sa_uint_t*)A, n, freq, r, (const sa_uint_t*)I);
11789
0
}
11790
11791
#if defined(LIBSAIS_OPENMP)
11792
11793
void* libsais_unbwt_create_ctx_omp(int32_t threads) {
11794
  if (threads < 0) {
11795
    return NULL;
11796
  }
11797
11798
  threads =
11799
      threads > 0 ? threads : (omp_get_max_threads() / omp_get_num_threads());
11800
  threads = threads > 0 ? threads : 1;
11801
11802
  return (void*)libsais_unbwt_create_ctx_main(threads);
11803
}
11804
11805
int32_t libsais_unbwt_omp(const uint8_t* T, uint8_t* U, int32_t* A, int32_t n,
11806
                          const int32_t* freq, int32_t i, int32_t threads) {
11807
  return libsais_unbwt_aux_omp(T, U, A, n, freq, n, &i, threads);
11808
}
11809
11810
int32_t libsais_unbwt_aux_omp(const uint8_t* T, uint8_t* U, int32_t* A,
11811
                              int32_t n, const int32_t* freq, int32_t r,
11812
                              const int32_t* I, int32_t threads) {
11813
  if ((T == NULL) || (U == NULL) || (A == NULL) || (n < 0) ||
11814
      ((r != n) && ((r < 2) || ((r & (r - 1)) != 0))) || (I == NULL) ||
11815
      (threads < 0)) {
11816
    return -1;
11817
  } else if (n <= 1) {
11818
    if (I[0] != n) {
11819
      return -1;
11820
    }
11821
    if (n == 1) {
11822
      U[0] = T[0];
11823
    }
11824
    return 0;
11825
  }
11826
11827
  fast_sint_t t;
11828
  for (t = 0; t <= (n - 1) / r; ++t) {
11829
    if (I[t] <= 0 || I[t] > n) {
11830
      return -1;
11831
    }
11832
  }
11833
11834
  threads =
11835
      threads > 0 ? threads : (omp_get_max_threads() / omp_get_num_threads());
11836
  threads = threads > 0 ? threads : 1;
11837
11838
  return libsais_unbwt_main(T, U, (sa_uint_t*)A, n, freq, r,
11839
                            (const sa_uint_t*)I, threads);
11840
}
11841
11842
#endif
11843
11844
static void libsais_compute_phi(const sa_sint_t* RESTRICT SA,
11845
                                sa_sint_t* RESTRICT PLCP, sa_sint_t n,
11846
                                fast_sint_t omp_block_start,
11847
8.54k
                                fast_sint_t omp_block_size) {
11848
8.54k
  const fast_sint_t prefetch_distance = 64;
11849
11850
8.54k
  fast_sint_t i, j;
11851
8.54k
  sa_sint_t k = omp_block_start > 0 ? SA[omp_block_start - 1] : n;
11852
8.54k
  for (i = omp_block_start,
11853
8.54k
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
11854
5.41M
       i < j; i += 4) {
11855
5.41M
    libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
11856
11857
5.41M
    libsais_prefetchw(&PLCP[SA[i + prefetch_distance + 0]]);
11858
5.41M
    libsais_prefetchw(&PLCP[SA[i + prefetch_distance + 1]]);
11859
11860
5.41M
    PLCP[SA[i + 0]] = k;
11861
5.41M
    k = SA[i + 0];
11862
5.41M
    PLCP[SA[i + 1]] = k;
11863
5.41M
    k = SA[i + 1];
11864
11865
5.41M
    libsais_prefetchw(&PLCP[SA[i + prefetch_distance + 2]]);
11866
5.41M
    libsais_prefetchw(&PLCP[SA[i + prefetch_distance + 3]]);
11867
11868
5.41M
    PLCP[SA[i + 2]] = k;
11869
5.41M
    k = SA[i + 2];
11870
5.41M
    PLCP[SA[i + 3]] = k;
11871
5.41M
    k = SA[i + 3];
11872
5.41M
  }
11873
11874
500k
  for (j += prefetch_distance + 3; i < j; i += 1) {
11875
492k
    PLCP[SA[i]] = k;
11876
492k
    k = SA[i];
11877
492k
  }
11878
8.54k
}
11879
11880
static void libsais_compute_phi_omp(const sa_sint_t* RESTRICT SA,
11881
                                    sa_sint_t* RESTRICT PLCP, sa_sint_t n,
11882
8.54k
                                    sa_sint_t threads) {
11883
#if defined(LIBSAIS_OPENMP)
11884
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 65536)
11885
#endif
11886
8.54k
  {
11887
#if defined(LIBSAIS_OPENMP)
11888
    fast_sint_t omp_thread_num = omp_get_thread_num();
11889
    fast_sint_t omp_num_threads = omp_get_num_threads();
11890
#else
11891
8.54k
    UNUSED(threads);
11892
11893
8.54k
    fast_sint_t omp_thread_num = 0;
11894
8.54k
    fast_sint_t omp_num_threads = 1;
11895
8.54k
#endif
11896
8.54k
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
11897
8.54k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
11898
8.54k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
11899
8.54k
                                     ? omp_block_stride
11900
8.54k
                                     : n - omp_block_start;
11901
11902
8.54k
    libsais_compute_phi(SA, PLCP, n, omp_block_start, omp_block_size);
11903
8.54k
  }
11904
8.54k
}
11905
11906
static void libsais_compute_plcp(const uint8_t* RESTRICT T,
11907
                                 sa_sint_t* RESTRICT PLCP, fast_sint_t n,
11908
                                 fast_sint_t omp_block_start,
11909
8.54k
                                 fast_sint_t omp_block_size) {
11910
8.54k
  const fast_sint_t prefetch_distance = 64;
11911
11912
8.54k
  fast_sint_t i, j, l = 0;
11913
8.54k
  for (i = omp_block_start,
11914
8.54k
      j = omp_block_start + omp_block_size - prefetch_distance;
11915
21.6M
       i < j; i += 1) {
11916
21.6M
    libsais_prefetchw(&PLCP[i + 2 * prefetch_distance]);
11917
21.6M
    libsais_prefetchr(&T[PLCP[i + prefetch_distance] + l]);
11918
11919
21.6M
    fast_sint_t k = PLCP[i], m = n - (i > k ? i : k);
11920
43.1M
    while (l < m && T[i + l] == T[k + l]) {
11921
21.5M
      l++;
11922
21.5M
    }
11923
11924
21.6M
    PLCP[i] = (sa_sint_t)l;
11925
21.6M
    l -= (l != 0);
11926
21.6M
  }
11927
11928
490k
  for (j += prefetch_distance; i < j; i += 1) {
11929
482k
    fast_sint_t k = PLCP[i], m = n - (i > k ? i : k);
11930
776k
    while (l < m && T[i + l] == T[k + l]) {
11931
294k
      l++;
11932
294k
    }
11933
11934
482k
    PLCP[i] = (sa_sint_t)l;
11935
482k
    l -= (l != 0);
11936
482k
  }
11937
8.54k
}
11938
11939
static void libsais_compute_plcp_omp(const uint8_t* RESTRICT T,
11940
                                     sa_sint_t* RESTRICT PLCP, sa_sint_t n,
11941
8.54k
                                     sa_sint_t threads) {
11942
#if defined(LIBSAIS_OPENMP)
11943
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 65536)
11944
#endif
11945
8.54k
  {
11946
#if defined(LIBSAIS_OPENMP)
11947
    fast_sint_t omp_thread_num = omp_get_thread_num();
11948
    fast_sint_t omp_num_threads = omp_get_num_threads();
11949
#else
11950
8.54k
    UNUSED(threads);
11951
11952
8.54k
    fast_sint_t omp_thread_num = 0;
11953
8.54k
    fast_sint_t omp_num_threads = 1;
11954
8.54k
#endif
11955
8.54k
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
11956
8.54k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
11957
8.54k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
11958
8.54k
                                     ? omp_block_stride
11959
8.54k
                                     : n - omp_block_start;
11960
11961
8.54k
    libsais_compute_plcp(T, PLCP, n, omp_block_start, omp_block_size);
11962
8.54k
  }
11963
8.54k
}
11964
11965
static void libsais_compute_plcp_gsa(const uint8_t* RESTRICT T,
11966
                                     sa_sint_t* RESTRICT PLCP,
11967
                                     fast_sint_t omp_block_start,
11968
0
                                     fast_sint_t omp_block_size) {
11969
0
  const fast_sint_t prefetch_distance = 64;
11970
11971
0
  fast_sint_t i, j, l = 0;
11972
0
  for (i = omp_block_start,
11973
0
      j = omp_block_start + omp_block_size - prefetch_distance;
11974
0
       i < j; i += 1) {
11975
0
    libsais_prefetchw(&PLCP[i + 2 * prefetch_distance]);
11976
0
    libsais_prefetchr(&T[PLCP[i + prefetch_distance] + l]);
11977
11978
0
    fast_sint_t k = PLCP[i];
11979
0
    while (T[i + l] > 0 && T[i + l] == T[k + l]) {
11980
0
      l++;
11981
0
    }
11982
11983
0
    PLCP[i] = (sa_sint_t)l;
11984
0
    l -= (l != 0);
11985
0
  }
11986
11987
0
  for (j += prefetch_distance; i < j; i += 1) {
11988
0
    fast_sint_t k = PLCP[i];
11989
0
    while (T[i + l] > 0 && T[i + l] == T[k + l]) {
11990
0
      l++;
11991
0
    }
11992
11993
0
    PLCP[i] = (sa_sint_t)l;
11994
0
    l -= (l != 0);
11995
0
  }
11996
0
}
11997
11998
static void libsais_compute_plcp_gsa_omp(const uint8_t* RESTRICT T,
11999
                                         sa_sint_t* RESTRICT PLCP, sa_sint_t n,
12000
0
                                         sa_sint_t threads) {
12001
#if defined(LIBSAIS_OPENMP)
12002
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 65536)
12003
#endif
12004
0
  {
12005
#if defined(LIBSAIS_OPENMP)
12006
    fast_sint_t omp_thread_num = omp_get_thread_num();
12007
    fast_sint_t omp_num_threads = omp_get_num_threads();
12008
#else
12009
0
    UNUSED(threads);
12010
12011
0
    fast_sint_t omp_thread_num = 0;
12012
0
    fast_sint_t omp_num_threads = 1;
12013
0
#endif
12014
0
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
12015
0
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
12016
0
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
12017
0
                                     ? omp_block_stride
12018
0
                                     : n - omp_block_start;
12019
12020
0
    libsais_compute_plcp_gsa(T, PLCP, omp_block_start, omp_block_size);
12021
0
  }
12022
0
}
12023
12024
static void libsais_compute_plcp_int(const int32_t* RESTRICT T,
12025
                                     sa_sint_t* RESTRICT PLCP, fast_sint_t n,
12026
                                     fast_sint_t omp_block_start,
12027
0
                                     fast_sint_t omp_block_size) {
12028
0
  const fast_sint_t prefetch_distance = 64;
12029
12030
0
  fast_sint_t i, j, l = 0;
12031
0
  for (i = omp_block_start,
12032
0
      j = omp_block_start + omp_block_size - prefetch_distance;
12033
0
       i < j; i += 1) {
12034
0
    libsais_prefetchw(&PLCP[i + 2 * prefetch_distance]);
12035
0
    libsais_prefetchr(&T[PLCP[i + prefetch_distance] + l]);
12036
12037
0
    fast_sint_t k = PLCP[i], m = n - (i > k ? i : k);
12038
0
    while (l < m && T[i + l] == T[k + l]) {
12039
0
      l++;
12040
0
    }
12041
12042
0
    PLCP[i] = (sa_sint_t)l;
12043
0
    l -= (l != 0);
12044
0
  }
12045
12046
0
  for (j += prefetch_distance; i < j; i += 1) {
12047
0
    fast_sint_t k = PLCP[i], m = n - (i > k ? i : k);
12048
0
    while (l < m && T[i + l] == T[k + l]) {
12049
0
      l++;
12050
0
    }
12051
12052
0
    PLCP[i] = (sa_sint_t)l;
12053
0
    l -= (l != 0);
12054
0
  }
12055
0
}
12056
12057
static void libsais_compute_plcp_int_omp(const int32_t* RESTRICT T,
12058
                                         sa_sint_t* RESTRICT PLCP, sa_sint_t n,
12059
0
                                         sa_sint_t threads) {
12060
#if defined(LIBSAIS_OPENMP)
12061
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 65536)
12062
#endif
12063
0
  {
12064
#if defined(LIBSAIS_OPENMP)
12065
    fast_sint_t omp_thread_num = omp_get_thread_num();
12066
    fast_sint_t omp_num_threads = omp_get_num_threads();
12067
#else
12068
0
    UNUSED(threads);
12069
12070
0
    fast_sint_t omp_thread_num = 0;
12071
0
    fast_sint_t omp_num_threads = 1;
12072
0
#endif
12073
0
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
12074
0
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
12075
0
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
12076
0
                                     ? omp_block_stride
12077
0
                                     : n - omp_block_start;
12078
12079
0
    libsais_compute_plcp_int(T, PLCP, n, omp_block_start, omp_block_size);
12080
0
  }
12081
0
}
12082
12083
static void libsais_compute_lcp(const sa_sint_t* RESTRICT PLCP,
12084
                                const sa_sint_t* RESTRICT SA,
12085
                                sa_sint_t* RESTRICT LCP,
12086
                                fast_sint_t omp_block_start,
12087
8.54k
                                fast_sint_t omp_block_size) {
12088
8.54k
  const fast_sint_t prefetch_distance = 64;
12089
12090
8.54k
  fast_sint_t i, j;
12091
8.54k
  for (i = omp_block_start,
12092
8.54k
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
12093
5.41M
       i < j; i += 4) {
12094
5.41M
    libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
12095
5.41M
    libsais_prefetchw(&LCP[i + prefetch_distance]);
12096
12097
5.41M
    libsais_prefetchr(&PLCP[SA[i + prefetch_distance + 0]]);
12098
5.41M
    libsais_prefetchr(&PLCP[SA[i + prefetch_distance + 1]]);
12099
12100
5.41M
    LCP[i + 0] = PLCP[SA[i + 0]];
12101
5.41M
    LCP[i + 1] = PLCP[SA[i + 1]];
12102
12103
5.41M
    libsais_prefetchr(&PLCP[SA[i + prefetch_distance + 2]]);
12104
5.41M
    libsais_prefetchr(&PLCP[SA[i + prefetch_distance + 3]]);
12105
12106
5.41M
    LCP[i + 2] = PLCP[SA[i + 2]];
12107
5.41M
    LCP[i + 3] = PLCP[SA[i + 3]];
12108
5.41M
  }
12109
12110
500k
  for (j += prefetch_distance + 3; i < j; i += 1) {
12111
492k
    LCP[i] = PLCP[SA[i]];
12112
492k
  }
12113
8.54k
}
12114
12115
static void libsais_compute_lcp_omp(const sa_sint_t* RESTRICT PLCP,
12116
                                    const sa_sint_t* RESTRICT SA,
12117
                                    sa_sint_t* RESTRICT LCP, sa_sint_t n,
12118
8.54k
                                    sa_sint_t threads) {
12119
#if defined(LIBSAIS_OPENMP)
12120
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 65536)
12121
#endif
12122
8.54k
  {
12123
#if defined(LIBSAIS_OPENMP)
12124
    fast_sint_t omp_thread_num = omp_get_thread_num();
12125
    fast_sint_t omp_num_threads = omp_get_num_threads();
12126
#else
12127
8.54k
    UNUSED(threads);
12128
12129
8.54k
    fast_sint_t omp_thread_num = 0;
12130
8.54k
    fast_sint_t omp_num_threads = 1;
12131
8.54k
#endif
12132
8.54k
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
12133
8.54k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
12134
8.54k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
12135
8.54k
                                     ? omp_block_stride
12136
8.54k
                                     : n - omp_block_start;
12137
12138
8.54k
    libsais_compute_lcp(PLCP, SA, LCP, omp_block_start, omp_block_size);
12139
8.54k
  }
12140
8.54k
}
12141
12142
int32_t libsais_plcp(const uint8_t* T, const int32_t* SA, int32_t* PLCP,
12143
8.54k
                     int32_t n) {
12144
8.54k
  if ((T == NULL) || (SA == NULL) || (PLCP == NULL) || (n < 0)) {
12145
0
    return -1;
12146
8.54k
  } else if (n <= 1) {
12147
0
    if (n == 1) {
12148
0
      PLCP[0] = 0;
12149
0
    }
12150
0
    return 0;
12151
0
  }
12152
12153
8.54k
  libsais_compute_phi_omp(SA, PLCP, n, 1);
12154
8.54k
  libsais_compute_plcp_omp(T, PLCP, n, 1);
12155
12156
8.54k
  return 0;
12157
8.54k
}
12158
12159
int32_t libsais_plcp_gsa(const uint8_t* T, const int32_t* SA, int32_t* PLCP,
12160
0
                         int32_t n) {
12161
0
  if ((T == NULL) || (SA == NULL) || (PLCP == NULL) || (n < 0) ||
12162
0
      (n > 0 && T[n - 1] != 0)) {
12163
0
    return -1;
12164
0
  } else if (n <= 1) {
12165
0
    if (n == 1) {
12166
0
      PLCP[0] = 0;
12167
0
    }
12168
0
    return 0;
12169
0
  }
12170
12171
0
  libsais_compute_phi_omp(SA, PLCP, n, 1);
12172
0
  libsais_compute_plcp_gsa_omp(T, PLCP, n, 1);
12173
12174
0
  return 0;
12175
0
}
12176
12177
int32_t libsais_plcp_int(const int32_t* T, const int32_t* SA, int32_t* PLCP,
12178
0
                         int32_t n) {
12179
0
  if ((T == NULL) || (SA == NULL) || (PLCP == NULL) || (n < 0)) {
12180
0
    return -1;
12181
0
  } else if (n <= 1) {
12182
0
    if (n == 1) {
12183
0
      PLCP[0] = 0;
12184
0
    }
12185
0
    return 0;
12186
0
  }
12187
12188
0
  libsais_compute_phi_omp(SA, PLCP, n, 1);
12189
0
  libsais_compute_plcp_int_omp(T, PLCP, n, 1);
12190
12191
0
  return 0;
12192
0
}
12193
12194
int32_t libsais_lcp(const int32_t* PLCP, const int32_t* SA, int32_t* LCP,
12195
8.54k
                    int32_t n) {
12196
8.54k
  if ((PLCP == NULL) || (SA == NULL) || (LCP == NULL) || (n < 0)) {
12197
0
    return -1;
12198
8.54k
  } else if (n <= 1) {
12199
0
    if (n == 1) {
12200
0
      LCP[0] = PLCP[SA[0]];
12201
0
    }
12202
0
    return 0;
12203
0
  }
12204
12205
8.54k
  libsais_compute_lcp_omp(PLCP, SA, LCP, n, 1);
12206
12207
8.54k
  return 0;
12208
8.54k
}
12209
12210
#if defined(LIBSAIS_OPENMP)
12211
12212
int32_t libsais_plcp_omp(const uint8_t* T, const int32_t* SA, int32_t* PLCP,
12213
                         int32_t n, int32_t threads) {
12214
  if ((T == NULL) || (SA == NULL) || (PLCP == NULL) || (n < 0) ||
12215
      (threads < 0)) {
12216
    return -1;
12217
  } else if (n <= 1) {
12218
    if (n == 1) {
12219
      PLCP[0] = 0;
12220
    }
12221
    return 0;
12222
  }
12223
12224
  threads =
12225
      threads > 0 ? threads : (omp_get_max_threads() / omp_get_num_threads());
12226
  threads = threads > 0 ? threads : 1;
12227
12228
  libsais_compute_phi_omp(SA, PLCP, n, threads);
12229
  libsais_compute_plcp_omp(T, PLCP, n, threads);
12230
12231
  return 0;
12232
}
12233
12234
int32_t libsais_plcp_gsa_omp(const uint8_t* T, const int32_t* SA, int32_t* PLCP,
12235
                             int32_t n, int32_t threads) {
12236
  if ((T == NULL) || (SA == NULL) || (PLCP == NULL) || (n < 0) ||
12237
      (n > 0 && T[n - 1] != 0) || (threads < 0)) {
12238
    return -1;
12239
  } else if (n <= 1) {
12240
    if (n == 1) {
12241
      PLCP[0] = 0;
12242
    }
12243
    return 0;
12244
  }
12245
12246
  threads =
12247
      threads > 0 ? threads : (omp_get_max_threads() / omp_get_num_threads());
12248
  threads = threads > 0 ? threads : 1;
12249
12250
  libsais_compute_phi_omp(SA, PLCP, n, threads);
12251
  libsais_compute_plcp_gsa_omp(T, PLCP, n, threads);
12252
12253
  return 0;
12254
}
12255
12256
int32_t libsais_plcp_int_omp(const int32_t* T, const int32_t* SA, int32_t* PLCP,
12257
                             int32_t n, int32_t threads) {
12258
  if ((T == NULL) || (SA == NULL) || (PLCP == NULL) || (n < 0) ||
12259
      (threads < 0)) {
12260
    return -1;
12261
  } else if (n <= 1) {
12262
    if (n == 1) {
12263
      PLCP[0] = 0;
12264
    }
12265
    return 0;
12266
  }
12267
12268
  threads =
12269
      threads > 0 ? threads : (omp_get_max_threads() / omp_get_num_threads());
12270
  threads = threads > 0 ? threads : 1;
12271
12272
  libsais_compute_phi_omp(SA, PLCP, n, threads);
12273
  libsais_compute_plcp_int_omp(T, PLCP, n, threads);
12274
12275
  return 0;
12276
}
12277
12278
int32_t libsais_lcp_omp(const int32_t* PLCP, const int32_t* SA, int32_t* LCP,
12279
                        int32_t n, int32_t threads) {
12280
  if ((PLCP == NULL) || (SA == NULL) || (LCP == NULL) || (n < 0) ||
12281
      (threads < 0)) {
12282
    return -1;
12283
  } else if (n <= 1) {
12284
    if (n == 1) {
12285
      LCP[0] = PLCP[SA[0]];
12286
    }
12287
    return 0;
12288
  }
12289
12290
  threads =
12291
      threads > 0 ? threads : (omp_get_max_threads() / omp_get_num_threads());
12292
  threads = threads > 0 ? threads : 1;
12293
12294
  libsais_compute_lcp_omp(PLCP, SA, LCP, n, threads);
12295
12296
  return 0;
12297
}
12298
12299
#endif