Coverage Report

Created: 2026-10-17 06:38

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
469k
#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
65.7M
#define SAINT_BIT (32)
44
109M
#define SAINT_MAX INT32_MAX
45
56.7M
#define SAINT_MIN INT32_MIN
46
47
161k
#define ALPHABET_SIZE (1 << CHAR_BIT)
48
0
#define UNBWT_FASTBITS (17)
49
50
5.02M
#define SUFFIX_GROUP_BIT (SAINT_BIT - 1)
51
3.33M
#define SUFFIX_GROUP_MARKER (((sa_sint_t)1) << (SUFFIX_GROUP_BIT - 1))
52
53
86.6M
#define BUCKETS_INDEX2(_c, _s) (((_c) << 1) + (_s))
54
83.8M
#define BUCKETS_INDEX4(_c, _s) (((_c) << 2) + (_s))
55
56
30.6k
#define LIBSAIS_LOCAL_BUFFER_SIZE (2000)
57
0
#define LIBSAIS_PER_THREAD_CACHE_SIZE (24576)
58
59
8.12k
#define LIBSAIS_FLAGS_NONE (0)
60
8.12k
#define LIBSAIS_FLAGS_BWT (1)
61
64.6k
#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
274M
  __builtin_prefetch((const void*)(address), 0, 3)
125
#define libsais_prefetchw(address) \
126
138M
  __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
17.9k
static void* libsais_align_up(const void* address, size_t alignment) {
185
17.9k
  return (void*)((((ptrdiff_t)address) + ((ptrdiff_t)alignment) - 1) &
186
17.9k
                 (-((ptrdiff_t)alignment)));
187
17.9k
}
188
189
8.12k
static void* libsais_alloc_aligned(size_t size, size_t alignment) {
190
8.12k
  void* address = malloc(size + sizeof(short) + alignment - 1);
191
8.12k
  if (address != NULL) {
192
8.12k
    void* aligned_address = libsais_align_up(
193
8.12k
        (void*)((ptrdiff_t)address + (ptrdiff_t)(sizeof(short))), alignment);
194
8.12k
    ((short*)aligned_address)[-1] =
195
8.12k
        (short)((ptrdiff_t)aligned_address - (ptrdiff_t)address);
196
197
8.12k
    return aligned_address;
198
8.12k
  }
199
200
0
  return NULL;
201
8.12k
}
202
203
8.29k
static void libsais_free_aligned(void* aligned_address) {
204
8.29k
  if (aligned_address != NULL) {
205
8.12k
    free((void*)((ptrdiff_t)aligned_address - ((short*)aligned_address)[-1]));
206
8.12k
  }
207
8.29k
}
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.12k
static void libsais_free_thread_state(LIBSAIS_THREAD_STATE* thread_state) {
240
8.12k
  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.12k
}
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.10k
                                           fast_sint_t omp_block_size) {
552
7.10k
  if (omp_block_size > 0) {
553
7.10k
    const fast_sint_t prefetch_distance = 256;
554
555
7.10k
    fast_sint_t i, j = omp_block_start + omp_block_size,
556
7.10k
                   c0 = T[omp_block_start + omp_block_size - 1], c1 = -1;
557
558
7.10k
    while (j < n && (c1 = T[j]) == c0) {
559
0
      ++j;
560
0
    }
561
562
7.10k
    fast_uint_t f0 = c0 >= c1, f1 = 0;
563
564
7.10k
    for (i = omp_block_start + omp_block_size - 2, j = omp_block_start + 3;
565
4.98M
         i >= j; i -= 4) {
566
4.98M
      libsais_prefetchr(&T[i - prefetch_distance]);
567
568
4.98M
      c1 = T[i - 0];
569
4.98M
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
570
4.98M
      SA[m] = (sa_sint_t)(i + 1);
571
4.98M
      m -= (f1 & ~f0);
572
4.98M
      c0 = T[i - 1];
573
4.98M
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
574
4.98M
      SA[m] = (sa_sint_t)(i - 0);
575
4.98M
      m -= (f0 & ~f1);
576
4.98M
      c1 = T[i - 2];
577
4.98M
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
578
4.98M
      SA[m] = (sa_sint_t)(i - 1);
579
4.98M
      m -= (f1 & ~f0);
580
4.98M
      c0 = T[i - 3];
581
4.98M
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
582
4.98M
      SA[m] = (sa_sint_t)(i - 2);
583
4.98M
      m -= (f0 & ~f1);
584
4.98M
    }
585
586
17.8k
    for (j -= 3; i >= j; i -= 1) {
587
10.7k
      c1 = c0;
588
10.7k
      c0 = T[i];
589
10.7k
      f1 = f0;
590
10.7k
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
591
10.7k
      SA[m] = (sa_sint_t)(i + 1);
592
10.7k
      m -= (f0 & ~f1);
593
10.7k
    }
594
595
7.10k
    SA[m] = (sa_sint_t)(i + 1);
596
7.10k
  }
597
7.10k
}
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.10k
    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.10k
  {
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.10k
    UNUSED(threads);
612
7.10k
    UNUSED(thread_state);
613
614
7.10k
    fast_sint_t omp_thread_num = 0;
615
7.10k
    fast_sint_t omp_num_threads = 1;
616
7.10k
#endif
617
7.10k
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
618
7.10k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
619
7.10k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
620
7.10k
                                     ? omp_block_stride
621
7.10k
                                     : n - omp_block_start;
622
623
7.10k
    if (omp_num_threads == 1) {
624
7.10k
      libsais_gather_lms_suffixes_8u(T, SA, n, (fast_sint_t)n - 1,
625
7.10k
                                     omp_block_start, omp_block_size);
626
7.10k
    }
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.10k
  }
646
7.10k
}
647
648
static sa_sint_t libsais_gather_lms_suffixes_32s(const sa_sint_t* RESTRICT T,
649
                                                 sa_sint_t* RESTRICT SA,
650
802
                                                 sa_sint_t n) {
651
802
  const fast_sint_t prefetch_distance = 64;
652
653
802
  sa_sint_t i = n - 2;
654
802
  sa_sint_t m = n - 1;
655
802
  fast_uint_t f0 = 1;
656
802
  fast_uint_t f1 = 0;
657
802
  fast_sint_t c0 = T[n - 1];
658
802
  fast_sint_t c1 = 0;
659
660
394k
  for (; i >= 3; i -= 4) {
661
394k
    libsais_prefetchr(&T[i - prefetch_distance]);
662
663
394k
    c1 = T[i - 0];
664
394k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
665
394k
    SA[m] = i + 1;
666
394k
    m -= (sa_sint_t)(f1 & ~f0);
667
394k
    c0 = T[i - 1];
668
394k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
669
394k
    SA[m] = i - 0;
670
394k
    m -= (sa_sint_t)(f0 & ~f1);
671
394k
    c1 = T[i - 2];
672
394k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
673
394k
    SA[m] = i - 1;
674
394k
    m -= (sa_sint_t)(f1 & ~f0);
675
394k
    c0 = T[i - 3];
676
394k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
677
394k
    SA[m] = i - 2;
678
394k
    m -= (sa_sint_t)(f0 & ~f1);
679
394k
  }
680
681
1.99k
  for (; i >= 0; i -= 1) {
682
1.19k
    c1 = c0;
683
1.19k
    c0 = T[i];
684
1.19k
    f1 = f0;
685
1.19k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
686
1.19k
    SA[m] = i + 1;
687
1.19k
    m -= (sa_sint_t)(f0 & ~f1);
688
1.19k
  }
689
690
802
  return n - 1 - m;
691
802
}
692
693
static sa_sint_t libsais_gather_compacted_lms_suffixes_32s(
694
149
    const sa_sint_t* RESTRICT T, sa_sint_t* RESTRICT SA, sa_sint_t n) {
695
149
  const fast_sint_t prefetch_distance = 64;
696
697
149
  sa_sint_t i = n - 2;
698
149
  sa_sint_t m = n - 1;
699
149
  fast_uint_t f0 = 1;
700
149
  fast_uint_t f1 = 0;
701
149
  fast_sint_t c0 = T[n - 1];
702
149
  fast_sint_t c1 = 0;
703
704
71.6k
  for (; i >= 3; i -= 4) {
705
71.4k
    libsais_prefetchr(&T[i - prefetch_distance]);
706
707
71.4k
    c1 = T[i - 0];
708
71.4k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
709
71.4k
    SA[m] = i + 1;
710
71.4k
    m -= (sa_sint_t)(f1 & ~f0 & (c0 >= 0));
711
71.4k
    c0 = T[i - 1];
712
71.4k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
713
71.4k
    SA[m] = i - 0;
714
71.4k
    m -= (sa_sint_t)(f0 & ~f1 & (c1 >= 0));
715
71.4k
    c1 = T[i - 2];
716
71.4k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
717
71.4k
    SA[m] = i - 1;
718
71.4k
    m -= (sa_sint_t)(f1 & ~f0 & (c0 >= 0));
719
71.4k
    c0 = T[i - 3];
720
71.4k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
721
71.4k
    SA[m] = i - 2;
722
71.4k
    m -= (sa_sint_t)(f0 & ~f1 & (c1 >= 0));
723
71.4k
  }
724
725
400
  for (; i >= 0; i -= 1) {
726
251
    c1 = c0;
727
251
    c0 = T[i];
728
251
    f1 = f0;
729
251
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
730
251
    SA[m] = i + 1;
731
251
    m -= (sa_sint_t)(f0 & ~f1 & (c1 >= 0));
732
251
  }
733
734
149
  return n - 1 - m;
735
149
}
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.28k
                                              sa_sint_t* RESTRICT buckets) {
797
5.28k
  const fast_sint_t prefetch_distance = 64;
798
799
5.28k
  memset(buckets, 0, 2 * (size_t)k * sizeof(sa_sint_t));
800
801
5.28k
  sa_sint_t i = n - 2;
802
5.28k
  fast_uint_t f0 = 1;
803
5.28k
  fast_uint_t f1 = 0;
804
5.28k
  fast_sint_t c0 = T[n - 1];
805
5.28k
  fast_sint_t c1 = 0;
806
807
68.8k
  for (; i >= prefetch_distance + 3; i -= 4) {
808
63.5k
    libsais_prefetchr(&T[i - 2 * prefetch_distance]);
809
810
63.5k
    libsais_prefetchw(
811
63.5k
        &buckets[BUCKETS_INDEX2(T[i - prefetch_distance - 0], 0)]);
812
63.5k
    libsais_prefetchw(
813
63.5k
        &buckets[BUCKETS_INDEX2(T[i - prefetch_distance - 1], 0)]);
814
63.5k
    libsais_prefetchw(
815
63.5k
        &buckets[BUCKETS_INDEX2(T[i - prefetch_distance - 2], 0)]);
816
63.5k
    libsais_prefetchw(
817
63.5k
        &buckets[BUCKETS_INDEX2(T[i - prefetch_distance - 3], 0)]);
818
819
63.5k
    c1 = T[i - 0];
820
63.5k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
821
63.5k
    buckets[BUCKETS_INDEX2((fast_uint_t)c0, (f1 & ~f0))]++;
822
823
63.5k
    c0 = T[i - 1];
824
63.5k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
825
63.5k
    buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
826
827
63.5k
    c1 = T[i - 2];
828
63.5k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
829
63.5k
    buckets[BUCKETS_INDEX2((fast_uint_t)c0, (f1 & ~f0))]++;
830
831
63.5k
    c0 = T[i - 3];
832
63.5k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
833
63.5k
    buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
834
63.5k
  }
835
836
165k
  for (; i >= 0; i -= 1) {
837
160k
    c1 = c0;
838
160k
    c0 = T[i];
839
160k
    f1 = f0;
840
160k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
841
160k
    buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
842
160k
  }
843
844
5.28k
  buckets[BUCKETS_INDEX2((fast_uint_t)c0, 0)]++;
845
5.28k
}
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.12k
    fast_sint_t omp_block_size) {
914
8.12k
  memset(buckets, 0, (size_t)4 * ALPHABET_SIZE * sizeof(sa_sint_t));
915
916
8.12k
  fast_sint_t m = omp_block_start + omp_block_size - 1;
917
918
8.12k
  if (omp_block_size > 0) {
919
8.12k
    const fast_sint_t prefetch_distance = 256;
920
921
8.12k
    fast_sint_t i, j = m + 1, c0 = T[m], c1 = -1;
922
923
8.12k
    while (j < n && (c1 = T[j]) == c0) {
924
0
      ++j;
925
0
    }
926
927
8.12k
    fast_uint_t f0 = c0 >= c1, f1 = 0;
928
929
4.99M
    for (i = m - 1, j = omp_block_start + 3; i >= j; i -= 4) {
930
4.99M
      libsais_prefetchr(&T[i - prefetch_distance]);
931
932
4.99M
      c1 = T[i - 0];
933
4.99M
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
934
4.99M
      SA[m] = (sa_sint_t)(i + 1);
935
4.99M
      m -= (f1 & ~f0);
936
4.99M
      buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0 + f1)]++;
937
938
4.99M
      c0 = T[i - 1];
939
4.99M
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
940
4.99M
      SA[m] = (sa_sint_t)(i - 0);
941
4.99M
      m -= (f0 & ~f1);
942
4.99M
      buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]++;
943
944
4.99M
      c1 = T[i - 2];
945
4.99M
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
946
4.99M
      SA[m] = (sa_sint_t)(i - 1);
947
4.99M
      m -= (f1 & ~f0);
948
4.99M
      buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0 + f1)]++;
949
950
4.99M
      c0 = T[i - 3];
951
4.99M
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
952
4.99M
      SA[m] = (sa_sint_t)(i - 2);
953
4.99M
      m -= (f0 & ~f1);
954
4.99M
      buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]++;
955
4.99M
    }
956
957
20.3k
    for (j -= 3; i >= j; i -= 1) {
958
12.2k
      c1 = c0;
959
12.2k
      c0 = T[i];
960
12.2k
      f1 = f0;
961
12.2k
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
962
12.2k
      SA[m] = (sa_sint_t)(i + 1);
963
12.2k
      m -= (f0 & ~f1);
964
12.2k
      buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]++;
965
12.2k
    }
966
967
8.12k
    c1 = (i >= 0) ? T[i] : -1;
968
8.12k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
969
8.12k
    SA[m] = (sa_sint_t)(i + 1);
970
8.12k
    m -= (f1 & ~f0);
971
8.12k
    buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0 + f1)]++;
972
8.12k
  }
973
974
8.12k
  return (sa_sint_t)(omp_block_start + omp_block_size - 1 - m);
975
8.12k
}
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.12k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
981
8.12k
  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.12k
  {
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.12k
    UNUSED(threads);
993
8.12k
    UNUSED(thread_state);
994
995
8.12k
    fast_sint_t omp_thread_num = 0;
996
8.12k
    fast_sint_t omp_num_threads = 1;
997
8.12k
#endif
998
8.12k
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
999
8.12k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
1000
8.12k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
1001
8.12k
                                     ? omp_block_stride
1002
8.12k
                                     : n - omp_block_start;
1003
1004
8.12k
    if (omp_num_threads == 1) {
1005
8.12k
      m = libsais_count_and_gather_lms_suffixes_8u(
1006
8.12k
          T, SA, n, buckets, omp_block_start, omp_block_size);
1007
8.12k
    }
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.12k
  }
1055
1056
8.12k
  return m;
1057
8.12k
}
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
14.9k
    fast_sint_t omp_block_size) {
1063
14.9k
  memset(buckets, 0, 4 * (size_t)k * sizeof(sa_sint_t));
1064
1065
14.9k
  fast_sint_t m = omp_block_start + omp_block_size - 1;
1066
1067
14.9k
  if (omp_block_size > 0) {
1068
14.9k
    const fast_sint_t prefetch_distance = 64;
1069
1070
14.9k
    fast_sint_t i, j = m + 1, c0 = T[m], c1 = -1;
1071
1072
14.9k
    while (j < n && (c1 = T[j]) == c0) {
1073
0
      ++j;
1074
0
    }
1075
1076
14.9k
    fast_uint_t f0 = c0 >= c1, f1 = 0;
1077
1078
1.93M
    for (i = m - 1, j = omp_block_start + prefetch_distance + 3; i >= j;
1079
1.92M
         i -= 4) {
1080
1.92M
      libsais_prefetchr(&T[i - 2 * prefetch_distance]);
1081
1082
1.92M
      libsais_prefetchw(
1083
1.92M
          &buckets[BUCKETS_INDEX4(T[i - prefetch_distance - 0], 0)]);
1084
1.92M
      libsais_prefetchw(
1085
1.92M
          &buckets[BUCKETS_INDEX4(T[i - prefetch_distance - 1], 0)]);
1086
1.92M
      libsais_prefetchw(
1087
1.92M
          &buckets[BUCKETS_INDEX4(T[i - prefetch_distance - 2], 0)]);
1088
1.92M
      libsais_prefetchw(
1089
1.92M
          &buckets[BUCKETS_INDEX4(T[i - prefetch_distance - 3], 0)]);
1090
1091
1.92M
      c1 = T[i - 0];
1092
1.92M
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
1093
1.92M
      SA[m] = (sa_sint_t)(i + 1);
1094
1.92M
      m -= (f1 & ~f0);
1095
1.92M
      buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0 + f1)]++;
1096
1097
1.92M
      c0 = T[i - 1];
1098
1.92M
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1099
1.92M
      SA[m] = (sa_sint_t)(i - 0);
1100
1.92M
      m -= (f0 & ~f1);
1101
1.92M
      buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]++;
1102
1103
1.92M
      c1 = T[i - 2];
1104
1.92M
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
1105
1.92M
      SA[m] = (sa_sint_t)(i - 1);
1106
1.92M
      m -= (f1 & ~f0);
1107
1.92M
      buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0 + f1)]++;
1108
1109
1.92M
      c0 = T[i - 3];
1110
1.92M
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1111
1.92M
      SA[m] = (sa_sint_t)(i - 2);
1112
1.92M
      m -= (f0 & ~f1);
1113
1.92M
      buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]++;
1114
1.92M
    }
1115
1116
614k
    for (j -= prefetch_distance + 3; i >= j; i -= 1) {
1117
599k
      c1 = c0;
1118
599k
      c0 = T[i];
1119
599k
      f1 = f0;
1120
599k
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1121
599k
      SA[m] = (sa_sint_t)(i + 1);
1122
599k
      m -= (f0 & ~f1);
1123
599k
      buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]++;
1124
599k
    }
1125
1126
14.9k
    c1 = (i >= 0) ? T[i] : -1;
1127
14.9k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
1128
14.9k
    SA[m] = (sa_sint_t)(i + 1);
1129
14.9k
    m -= (f1 & ~f0);
1130
14.9k
    buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0 + f1)]++;
1131
14.9k
  }
1132
1133
14.9k
  return (sa_sint_t)(omp_block_start + omp_block_size - 1 - m);
1134
14.9k
}
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.15k
    fast_sint_t omp_block_size) {
1140
3.15k
  memset(buckets, 0, 2 * (size_t)k * sizeof(sa_sint_t));
1141
1142
3.15k
  fast_sint_t m = omp_block_start + omp_block_size - 1;
1143
1144
3.15k
  if (omp_block_size > 0) {
1145
3.15k
    const fast_sint_t prefetch_distance = 64;
1146
1147
3.15k
    fast_sint_t i, j = m + 1, c0 = T[m], c1 = -1;
1148
1149
3.15k
    while (j < n && (c1 = T[j]) == c0) {
1150
0
      ++j;
1151
0
    }
1152
1153
3.15k
    fast_uint_t f0 = c0 >= c1, f1 = 0;
1154
1155
808k
    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
161k
    for (j -= prefetch_distance + 3; i >= j; i -= 1) {
1194
158k
      c1 = c0;
1195
158k
      c0 = T[i];
1196
158k
      f1 = f0;
1197
158k
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1198
158k
      SA[m] = (sa_sint_t)(i + 1);
1199
158k
      m -= (f0 & ~f1);
1200
158k
      buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
1201
158k
    }
1202
1203
3.15k
    c1 = (i >= 0) ? T[i] : -1;
1204
3.15k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
1205
3.15k
    SA[m] = (sa_sint_t)(i + 1);
1206
3.15k
    m -= (f1 & ~f0);
1207
3.15k
    buckets[BUCKETS_INDEX2((fast_uint_t)c0, (f1 & ~f0))]++;
1208
3.15k
  }
1209
1210
3.15k
  return (sa_sint_t)(omp_block_start + omp_block_size - 1 - m);
1211
3.15k
}
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
6.66k
    fast_sint_t omp_block_size) {
1217
6.66k
  memset(buckets, 0, 2 * (size_t)k * sizeof(sa_sint_t));
1218
1219
6.66k
  fast_sint_t m = omp_block_start + omp_block_size - 1;
1220
1221
6.66k
  if (omp_block_size > 0) {
1222
6.66k
    const fast_sint_t prefetch_distance = 64;
1223
1224
6.66k
    fast_sint_t i, j = m + 1, c0 = T[m], c1 = -1;
1225
1226
6.66k
    while (j < n && (c1 = T[j]) == c0) {
1227
0
      ++j;
1228
0
    }
1229
1230
6.66k
    fast_uint_t f0 = c0 >= c1, f1 = 0;
1231
1232
1.93M
    for (i = m - 1, j = omp_block_start + prefetch_distance + 3; i >= j;
1233
1.93M
         i -= 4) {
1234
1.93M
      libsais_prefetchr(&T[i - 2 * prefetch_distance]);
1235
1236
1.93M
      libsais_prefetchw(&buckets[BUCKETS_INDEX2(
1237
1.93M
          T[i - prefetch_distance - 0] & SAINT_MAX, 0)]);
1238
1.93M
      libsais_prefetchw(&buckets[BUCKETS_INDEX2(
1239
1.93M
          T[i - prefetch_distance - 1] & SAINT_MAX, 0)]);
1240
1.93M
      libsais_prefetchw(&buckets[BUCKETS_INDEX2(
1241
1.93M
          T[i - prefetch_distance - 2] & SAINT_MAX, 0)]);
1242
1.93M
      libsais_prefetchw(&buckets[BUCKETS_INDEX2(
1243
1.93M
          T[i - prefetch_distance - 3] & SAINT_MAX, 0)]);
1244
1245
1.93M
      c1 = T[i - 0];
1246
1.93M
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
1247
1.93M
      SA[m] = (sa_sint_t)(i + 1);
1248
1.93M
      m -= (f1 & ~f0 & (c0 >= 0));
1249
1.93M
      c0 &= SAINT_MAX;
1250
1.93M
      buckets[BUCKETS_INDEX2((fast_uint_t)c0, (f1 & ~f0))]++;
1251
1252
1.93M
      c0 = T[i - 1];
1253
1.93M
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1254
1.93M
      SA[m] = (sa_sint_t)(i - 0);
1255
1.93M
      m -= (f0 & ~f1 & (c1 >= 0));
1256
1.93M
      c1 &= SAINT_MAX;
1257
1.93M
      buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
1258
1259
1.93M
      c1 = T[i - 2];
1260
1.93M
      f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
1261
1.93M
      SA[m] = (sa_sint_t)(i - 1);
1262
1.93M
      m -= (f1 & ~f0 & (c0 >= 0));
1263
1.93M
      c0 &= SAINT_MAX;
1264
1.93M
      buckets[BUCKETS_INDEX2((fast_uint_t)c0, (f1 & ~f0))]++;
1265
1266
1.93M
      c0 = T[i - 3];
1267
1.93M
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1268
1.93M
      SA[m] = (sa_sint_t)(i - 2);
1269
1.93M
      m -= (f0 & ~f1 & (c1 >= 0));
1270
1.93M
      c1 &= SAINT_MAX;
1271
1.93M
      buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
1272
1.93M
    }
1273
1274
400k
    for (j -= prefetch_distance + 3; i >= j; i -= 1) {
1275
394k
      c1 = c0;
1276
394k
      c0 = T[i];
1277
394k
      f1 = f0;
1278
394k
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1279
394k
      SA[m] = (sa_sint_t)(i + 1);
1280
394k
      m -= (f0 & ~f1 & (c1 >= 0));
1281
394k
      c1 &= SAINT_MAX;
1282
394k
      buckets[BUCKETS_INDEX2((fast_uint_t)c1, (f0 & ~f1))]++;
1283
394k
    }
1284
1285
6.66k
    c1 = (i >= 0) ? T[i] : -1;
1286
6.66k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
1287
6.66k
    SA[m] = (sa_sint_t)(i + 1);
1288
6.66k
    m -= (f1 & ~f0 & (c0 >= 0));
1289
6.66k
    c0 &= SAINT_MAX;
1290
6.66k
    buckets[BUCKETS_INDEX2((fast_uint_t)c0, (f1 & ~f0))]++;
1291
6.66k
  }
1292
1293
6.66k
  return (sa_sint_t)(omp_block_start + omp_block_size - 1 - m);
1294
6.66k
}
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
14.9k
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t threads) {
1557
14.9k
  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
14.9k
  {
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
14.9k
    UNUSED(threads);
1568
1569
14.9k
    fast_sint_t omp_num_threads = 1;
1570
14.9k
#endif
1571
14.9k
    if (omp_num_threads == 1) {
1572
14.9k
      m = libsais_count_and_gather_lms_suffixes_32s_4k(T, SA, n, k, buckets, 0,
1573
14.9k
                                                       n);
1574
14.9k
    }
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
14.9k
  }
1583
1584
14.9k
  return m;
1585
14.9k
}
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
988
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t threads) {
1590
988
  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
988
  {
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
988
    UNUSED(threads);
1601
1602
988
    fast_sint_t omp_num_threads = 1;
1603
988
#endif
1604
988
    if (omp_num_threads == 1) {
1605
988
      m = libsais_count_and_gather_lms_suffixes_32s_2k(T, SA, n, k, buckets, 0,
1606
988
                                                       n);
1607
988
    }
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
988
  }
1616
1617
988
  return m;
1618
988
}
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
6.66k
    sa_sint_t k, sa_sint_t* RESTRICT buckets, sa_sint_t threads) {
1624
6.66k
  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
6.66k
  {
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
6.66k
    UNUSED(threads);
1635
1636
6.66k
    fast_sint_t omp_num_threads = 1;
1637
6.66k
#endif
1638
6.66k
    if (omp_num_threads == 1) {
1639
6.66k
      m = libsais_count_and_gather_compacted_lms_suffixes_32s_2k(T, SA, n, k,
1640
6.66k
                                                                 buckets, 0, n);
1641
6.66k
    }
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
6.66k
  }
1650
1651
6.66k
  return m;
1652
6.66k
}
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
14.9k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
1658
14.9k
  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
14.9k
  UNUSED(local_buckets);
1679
14.9k
  UNUSED(thread_state);
1680
14.9k
#endif
1681
14.9k
  {
1682
14.9k
    m = libsais_count_and_gather_lms_suffixes_32s_4k_nofs_omp(T, SA, n, k,
1683
14.9k
                                                              buckets, threads);
1684
14.9k
  }
1685
1686
14.9k
  return m;
1687
14.9k
}
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
988
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
1693
988
  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
988
  UNUSED(local_buckets);
1714
988
  UNUSED(thread_state);
1715
988
#endif
1716
988
  {
1717
988
    m = libsais_count_and_gather_lms_suffixes_32s_2k_nofs_omp(T, SA, n, k,
1718
988
                                                              buckets, threads);
1719
988
  }
1720
1721
988
  return m;
1722
988
}
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
6.66k
    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
6.66k
  UNUSED(local_buckets);
1748
6.66k
  UNUSED(thread_state);
1749
6.66k
#endif
1750
6.66k
  {
1751
6.66k
    libsais_count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(
1752
6.66k
        T, SA, n, k, buckets, threads);
1753
6.66k
  }
1754
6.66k
}
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.02k
                                       sa_sint_t* RESTRICT buckets) {
1759
1.02k
  const fast_sint_t prefetch_distance = 64;
1760
1761
1.02k
  memset(buckets, 0, (size_t)k * sizeof(sa_sint_t));
1762
1763
1.02k
  fast_sint_t i, j;
1764
239k
  for (i = 0, j = (fast_sint_t)n - 7; i < j; i += 8) {
1765
238k
    libsais_prefetchr(&T[i + prefetch_distance]);
1766
1767
238k
    buckets[T[i + 0]]++;
1768
238k
    buckets[T[i + 1]]++;
1769
238k
    buckets[T[i + 2]]++;
1770
238k
    buckets[T[i + 3]]++;
1771
238k
    buckets[T[i + 4]]++;
1772
238k
    buckets[T[i + 5]]++;
1773
238k
    buckets[T[i + 6]]++;
1774
238k
    buckets[T[i + 7]]++;
1775
238k
  }
1776
1777
4.24k
  for (j += 7; i < j; i += 1) {
1778
3.22k
    buckets[T[i]]++;
1779
3.22k
  }
1780
1.02k
}
1781
1782
static sa_sint_t libsais_initialize_buckets_start_and_end_8u(
1783
8.12k
    sa_sint_t* RESTRICT buckets, sa_sint_t* RESTRICT freq) {
1784
8.12k
  sa_sint_t* RESTRICT bucket_start = &buckets[6 * ALPHABET_SIZE];
1785
8.12k
  sa_sint_t* RESTRICT bucket_end = &buckets[7 * ALPHABET_SIZE];
1786
1787
8.12k
  fast_sint_t k = -1;
1788
1789
8.12k
  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.12k
  } else {
1807
8.12k
    fast_sint_t i, j;
1808
8.12k
    sa_sint_t sum = 0;
1809
8.12k
    for (i = BUCKETS_INDEX4(0, 0), j = 0;
1810
2.08M
         i <= BUCKETS_INDEX4(ALPHABET_SIZE - 1, 0);
1811
2.07M
         i += BUCKETS_INDEX4(1, 0), j += 1) {
1812
2.07M
      sa_sint_t total = buckets[i + BUCKETS_INDEX4(0, 0)] +
1813
2.07M
                        buckets[i + BUCKETS_INDEX4(0, 1)] +
1814
2.07M
                        buckets[i + BUCKETS_INDEX4(0, 2)] +
1815
2.07M
                        buckets[i + BUCKETS_INDEX4(0, 3)];
1816
1817
2.07M
      bucket_start[j] = sum;
1818
2.07M
      sum += total;
1819
2.07M
      bucket_end[j] = sum;
1820
2.07M
      k = total > 0 ? j : k;
1821
2.07M
    }
1822
8.12k
  }
1823
1824
8.12k
  return (sa_sint_t)(k + 1);
1825
8.12k
}
1826
1827
static void libsais_initialize_buckets_start_and_end_32s_6k(
1828
1.79k
    sa_sint_t k, sa_sint_t* RESTRICT buckets) {
1829
1.79k
  sa_sint_t* RESTRICT bucket_start = &buckets[4 * (fast_sint_t)k];
1830
1.79k
  sa_sint_t* RESTRICT bucket_end = &buckets[5 * (fast_sint_t)k];
1831
1832
1.79k
  fast_sint_t i, j;
1833
1.79k
  sa_sint_t sum = 0;
1834
1.79k
  for (i = BUCKETS_INDEX4(0, 0), j = 0;
1835
8.23k
       i <= BUCKETS_INDEX4((fast_sint_t)k - 1, 0);
1836
6.43k
       i += BUCKETS_INDEX4(1, 0), j += 1) {
1837
6.43k
    bucket_start[j] = sum;
1838
6.43k
    sum +=
1839
6.43k
        buckets[i + BUCKETS_INDEX4(0, 0)] + buckets[i + BUCKETS_INDEX4(0, 1)] +
1840
6.43k
        buckets[i + BUCKETS_INDEX4(0, 2)] + buckets[i + BUCKETS_INDEX4(0, 3)];
1841
6.43k
    bucket_end[j] = sum;
1842
6.43k
  }
1843
1.79k
}
1844
1845
static void libsais_initialize_buckets_start_and_end_32s_4k(
1846
13.4k
    sa_sint_t k, sa_sint_t* RESTRICT buckets) {
1847
13.4k
  sa_sint_t* RESTRICT bucket_start = &buckets[2 * (fast_sint_t)k];
1848
13.4k
  sa_sint_t* RESTRICT bucket_end = &buckets[3 * (fast_sint_t)k];
1849
1850
13.4k
  fast_sint_t i, j;
1851
13.4k
  sa_sint_t sum = 0;
1852
13.4k
  for (i = BUCKETS_INDEX2(0, 0), j = 0;
1853
716k
       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
13.4k
}
1861
1862
static void libsais_initialize_buckets_end_32s_2k(sa_sint_t k,
1863
632
                                                  sa_sint_t* RESTRICT buckets) {
1864
632
  fast_sint_t i;
1865
632
  sa_sint_t sum0 = 0;
1866
520k
  for (i = BUCKETS_INDEX2(0, 0); i <= BUCKETS_INDEX2((fast_sint_t)k - 1, 0);
1867
519k
       i += BUCKETS_INDEX2(1, 0)) {
1868
519k
    sum0 +=
1869
519k
        buckets[i + BUCKETS_INDEX2(0, 0)] + buckets[i + BUCKETS_INDEX2(0, 1)];
1870
519k
    buckets[i + BUCKETS_INDEX2(0, 0)] = sum0;
1871
519k
  }
1872
632
}
1873
1874
static void libsais_initialize_buckets_start_and_end_32s_2k(
1875
1.26k
    sa_sint_t k, sa_sint_t* RESTRICT buckets) {
1876
1.26k
  fast_sint_t i, j;
1877
1.26k
  for (i = BUCKETS_INDEX2(0, 0), j = 0;
1878
1.04M
       i <= BUCKETS_INDEX2((fast_sint_t)k - 1, 0);
1879
1.03M
       i += BUCKETS_INDEX2(1, 0), j += 1) {
1880
1.03M
    buckets[j] = buckets[i];
1881
1.03M
  }
1882
1883
1.26k
  buckets[k] = 0;
1884
1.26k
  memcpy(&buckets[k + 1], buckets, ((size_t)k - 1) * sizeof(sa_sint_t));
1885
1.26k
}
1886
1887
static void libsais_initialize_buckets_start_32s_1k(
1888
340
    sa_sint_t k, sa_sint_t* RESTRICT buckets) {
1889
340
  fast_sint_t i;
1890
340
  sa_sint_t sum = 0;
1891
353k
  for (i = 0; i <= (fast_sint_t)k - 1; i += 1) {
1892
352k
    sa_sint_t tmp = buckets[i];
1893
352k
    buckets[i] = sum;
1894
352k
    sum += tmp;
1895
352k
  }
1896
340
}
1897
1898
static void libsais_initialize_buckets_end_32s_1k(sa_sint_t k,
1899
680
                                                  sa_sint_t* RESTRICT buckets) {
1900
680
  fast_sint_t i;
1901
680
  sa_sint_t sum = 0;
1902
706k
  for (i = 0; i <= (fast_sint_t)k - 1; i += 1) {
1903
705k
    sum += buckets[i];
1904
705k
    buckets[i] = sum;
1905
705k
  }
1906
680
}
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.05k
    sa_sint_t first_lms_suffix) {
1911
8.05k
  {
1912
8.05k
    fast_uint_t f0 = 0;
1913
8.05k
    fast_uint_t f1 = 0;
1914
8.05k
    fast_sint_t c0 = T[first_lms_suffix];
1915
8.05k
    fast_sint_t c1 = 0;
1916
1917
32.4k
    for (; --first_lms_suffix >= 0;) {
1918
24.3k
      c1 = c0;
1919
24.3k
      c0 = T[first_lms_suffix];
1920
24.3k
      f1 = f0;
1921
24.3k
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1922
24.3k
      buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]--;
1923
24.3k
    }
1924
1925
8.05k
    buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0)]--;
1926
8.05k
  }
1927
1928
8.05k
  {
1929
8.05k
    sa_sint_t* RESTRICT temp_bucket = &buckets[4 * ALPHABET_SIZE];
1930
1931
8.05k
    fast_sint_t i, j;
1932
8.05k
    sa_sint_t sum = 0;
1933
8.05k
    for (i = BUCKETS_INDEX4(0, 0), j = BUCKETS_INDEX2(0, 0);
1934
2.07M
         i <= BUCKETS_INDEX4(ALPHABET_SIZE - 1, 0);
1935
2.06M
         i += BUCKETS_INDEX4(1, 0), j += BUCKETS_INDEX2(1, 0)) {
1936
2.06M
      temp_bucket[j + BUCKETS_INDEX2(0, 1)] = sum;
1937
2.06M
      sum +=
1938
2.06M
          buckets[i + BUCKETS_INDEX4(0, 1)] + buckets[i + BUCKETS_INDEX4(0, 3)];
1939
2.06M
      temp_bucket[j] = sum;
1940
2.06M
    }
1941
1942
8.05k
    return sum;
1943
8.05k
  }
1944
8.05k
}
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
632
    sa_sint_t first_lms_suffix) {
1949
632
  buckets[BUCKETS_INDEX2(T[first_lms_suffix], 0)]++;
1950
632
  buckets[BUCKETS_INDEX2(T[first_lms_suffix], 1)]--;
1951
1952
632
  fast_sint_t i;
1953
632
  sa_sint_t sum0 = 0, sum1 = 0;
1954
520k
  for (i = BUCKETS_INDEX2(0, 0); i <= BUCKETS_INDEX2((fast_sint_t)k - 1, 0);
1955
519k
       i += BUCKETS_INDEX2(1, 0)) {
1956
519k
    sum0 +=
1957
519k
        buckets[i + BUCKETS_INDEX2(0, 0)] + buckets[i + BUCKETS_INDEX2(0, 1)];
1958
519k
    sum1 += buckets[i + BUCKETS_INDEX2(0, 1)];
1959
1960
519k
    buckets[i + BUCKETS_INDEX2(0, 0)] = sum0;
1961
519k
    buckets[i + BUCKETS_INDEX2(0, 1)] = sum1;
1962
519k
  }
1963
632
}
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.1k
    sa_sint_t first_lms_suffix) {
1968
13.1k
  {
1969
13.1k
    fast_uint_t f0 = 0;
1970
13.1k
    fast_uint_t f1 = 0;
1971
13.1k
    fast_sint_t c0 = T[first_lms_suffix];
1972
13.1k
    fast_sint_t c1 = 0;
1973
1974
52.9k
    for (; --first_lms_suffix >= 0;) {
1975
39.8k
      c1 = c0;
1976
39.8k
      c0 = T[first_lms_suffix];
1977
39.8k
      f1 = f0;
1978
39.8k
      f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
1979
39.8k
      buckets[BUCKETS_INDEX4((fast_uint_t)c1, f1 + f1 + f0)]--;
1980
39.8k
    }
1981
1982
13.1k
    buckets[BUCKETS_INDEX4((fast_uint_t)c0, f0 + f0)]--;
1983
13.1k
  }
1984
1985
13.1k
  {
1986
13.1k
    sa_sint_t* RESTRICT temp_bucket = &buckets[4 * (fast_sint_t)k];
1987
1988
13.1k
    fast_sint_t i, j;
1989
13.1k
    sa_sint_t sum = 0;
1990
13.1k
    for (i = BUCKETS_INDEX4(0, 0), j = 0;
1991
548k
         i <= BUCKETS_INDEX4((fast_sint_t)k - 1, 0);
1992
535k
         i += BUCKETS_INDEX4(1, 0), j += 1) {
1993
535k
      sum +=
1994
535k
          buckets[i + BUCKETS_INDEX4(0, 1)] + buckets[i + BUCKETS_INDEX4(0, 3)];
1995
535k
      temp_bucket[j] = sum;
1996
535k
    }
1997
1998
13.1k
    return sum;
1999
13.1k
  }
2000
13.1k
}
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
356
    sa_sint_t first_lms_suffix) {
2005
356
  sa_sint_t* RESTRICT bucket_start = &buckets[2 * (fast_sint_t)k];
2006
356
  sa_sint_t* RESTRICT bucket_end = &buckets[3 * (fast_sint_t)k];
2007
2008
356
  buckets[BUCKETS_INDEX2(T[first_lms_suffix], 0)]++;
2009
356
  buckets[BUCKETS_INDEX2(T[first_lms_suffix], 1)]--;
2010
2011
356
  fast_sint_t i, j;
2012
356
  sa_sint_t sum0 = 0, sum1 = 0;
2013
356
  for (i = BUCKETS_INDEX2(0, 0), j = 0;
2014
168k
       i <= BUCKETS_INDEX2((fast_sint_t)k - 1, 0);
2015
167k
       i += BUCKETS_INDEX2(1, 0), j += 1) {
2016
167k
    bucket_start[j] = sum1;
2017
2018
167k
    sum0 += buckets[i + BUCKETS_INDEX2(0, 1)];
2019
167k
    sum1 +=
2020
167k
        buckets[i + BUCKETS_INDEX2(0, 0)] + buckets[i + BUCKETS_INDEX2(0, 1)];
2021
167k
    buckets[i + BUCKETS_INDEX2(0, 1)] = sum0;
2022
2023
167k
    bucket_end[j] = sum1;
2024
167k
  }
2025
356
}
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.05k
    fast_sint_t omp_block_size) {
2031
8.05k
  const fast_sint_t prefetch_distance = 64;
2032
2033
8.05k
  fast_sint_t i, j;
2034
8.05k
  for (i = omp_block_start + omp_block_size - 1,
2035
8.05k
      j = omp_block_start + prefetch_distance + 3;
2036
1.77M
       i >= j; i -= 4) {
2037
1.76M
    libsais_prefetchr(&SA[i - 2 * prefetch_distance]);
2038
2039
1.76M
    libsais_prefetchr(&T[SA[i - prefetch_distance - 0]]);
2040
1.76M
    libsais_prefetchr(&T[SA[i - prefetch_distance - 1]]);
2041
1.76M
    libsais_prefetchr(&T[SA[i - prefetch_distance - 2]]);
2042
1.76M
    libsais_prefetchr(&T[SA[i - prefetch_distance - 3]]);
2043
2044
1.76M
    sa_sint_t p0 = SA[i - 0];
2045
1.76M
    SA[--induction_bucket[BUCKETS_INDEX2(T[p0], 0)]] = p0;
2046
1.76M
    sa_sint_t p1 = SA[i - 1];
2047
1.76M
    SA[--induction_bucket[BUCKETS_INDEX2(T[p1], 0)]] = p1;
2048
1.76M
    sa_sint_t p2 = SA[i - 2];
2049
1.76M
    SA[--induction_bucket[BUCKETS_INDEX2(T[p2], 0)]] = p2;
2050
1.76M
    sa_sint_t p3 = SA[i - 3];
2051
1.76M
    SA[--induction_bucket[BUCKETS_INDEX2(T[p3], 0)]] = p3;
2052
1.76M
  }
2053
2054
371k
  for (j -= prefetch_distance + 3; i >= j; i -= 1) {
2055
363k
    sa_sint_t p = SA[i];
2056
363k
    SA[--induction_bucket[BUCKETS_INDEX2(T[p], 0)]] = p;
2057
363k
  }
2058
8.05k
}
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.05k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
2064
8.05k
  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.05k
  {
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.05k
    UNUSED(threads);
2079
8.05k
    UNUSED(thread_state);
2080
2081
8.05k
    fast_sint_t omp_num_threads = 1;
2082
8.05k
#endif
2083
8.05k
    if (omp_num_threads == 1) {
2084
8.05k
      libsais_radix_sort_lms_suffixes_8u(T, SA, &buckets[4 * ALPHABET_SIZE],
2085
8.05k
                                         (fast_sint_t)n - (fast_sint_t)m + 1,
2086
8.05k
                                         (fast_sint_t)m - 1);
2087
8.05k
    }
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.05k
  }
2121
8.05k
}
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.1k
    fast_sint_t omp_block_size) {
2127
13.1k
  const fast_sint_t prefetch_distance = 64;
2128
2129
13.1k
  fast_sint_t i, j;
2130
13.1k
  for (i = omp_block_start + omp_block_size - 1,
2131
13.1k
      j = omp_block_start + 2 * prefetch_distance + 3;
2132
605k
       i >= j; i -= 4) {
2133
592k
    libsais_prefetchr(&SA[i - 3 * prefetch_distance]);
2134
2135
592k
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 0]]);
2136
592k
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 1]]);
2137
592k
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 2]]);
2138
592k
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 3]]);
2139
2140
592k
    libsais_prefetchw(&induction_bucket[T[SA[i - prefetch_distance - 0]]]);
2141
592k
    libsais_prefetchw(&induction_bucket[T[SA[i - prefetch_distance - 1]]]);
2142
592k
    libsais_prefetchw(&induction_bucket[T[SA[i - prefetch_distance - 2]]]);
2143
592k
    libsais_prefetchw(&induction_bucket[T[SA[i - prefetch_distance - 3]]]);
2144
2145
592k
    sa_sint_t p0 = SA[i - 0];
2146
592k
    SA[--induction_bucket[T[p0]]] = p0;
2147
592k
    sa_sint_t p1 = SA[i - 1];
2148
592k
    SA[--induction_bucket[T[p1]]] = p1;
2149
592k
    sa_sint_t p2 = SA[i - 2];
2150
592k
    SA[--induction_bucket[T[p2]]] = p2;
2151
592k
    sa_sint_t p3 = SA[i - 3];
2152
592k
    SA[--induction_bucket[T[p3]]] = p3;
2153
592k
  }
2154
2155
547k
  for (j -= 2 * prefetch_distance + 3; i >= j; i -= 1) {
2156
534k
    sa_sint_t p = SA[i];
2157
534k
    SA[--induction_bucket[T[p]]] = p;
2158
534k
  }
2159
13.1k
}
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
988
    fast_sint_t omp_block_size) {
2165
988
  const fast_sint_t prefetch_distance = 64;
2166
2167
988
  fast_sint_t i, j;
2168
988
  for (i = omp_block_start + omp_block_size - 1,
2169
988
      j = omp_block_start + 2 * prefetch_distance + 3;
2170
117k
       i >= j; i -= 4) {
2171
116k
    libsais_prefetchr(&SA[i - 3 * prefetch_distance]);
2172
2173
116k
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 0]]);
2174
116k
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 1]]);
2175
116k
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 2]]);
2176
116k
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 3]]);
2177
2178
116k
    libsais_prefetchw(
2179
116k
        &induction_bucket[BUCKETS_INDEX2(T[SA[i - prefetch_distance - 0]], 0)]);
2180
116k
    libsais_prefetchw(
2181
116k
        &induction_bucket[BUCKETS_INDEX2(T[SA[i - prefetch_distance - 1]], 0)]);
2182
116k
    libsais_prefetchw(
2183
116k
        &induction_bucket[BUCKETS_INDEX2(T[SA[i - prefetch_distance - 2]], 0)]);
2184
116k
    libsais_prefetchw(
2185
116k
        &induction_bucket[BUCKETS_INDEX2(T[SA[i - prefetch_distance - 3]], 0)]);
2186
2187
116k
    sa_sint_t p0 = SA[i - 0];
2188
116k
    SA[--induction_bucket[BUCKETS_INDEX2(T[p0], 0)]] = p0;
2189
116k
    sa_sint_t p1 = SA[i - 1];
2190
116k
    SA[--induction_bucket[BUCKETS_INDEX2(T[p1], 0)]] = p1;
2191
116k
    sa_sint_t p2 = SA[i - 2];
2192
116k
    SA[--induction_bucket[BUCKETS_INDEX2(T[p2], 0)]] = p2;
2193
116k
    sa_sint_t p3 = SA[i - 3];
2194
116k
    SA[--induction_bucket[BUCKETS_INDEX2(T[p3], 0)]] = p3;
2195
116k
  }
2196
2197
128k
  for (j -= 2 * prefetch_distance + 3; i >= j; i -= 1) {
2198
127k
    sa_sint_t p = SA[i];
2199
127k
    SA[--induction_bucket[BUCKETS_INDEX2(T[p], 0)]] = p;
2200
127k
  }
2201
988
}
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.1k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
2421
13.1k
  if (threads == 1 || m < 65536) {
2422
13.1k
    libsais_radix_sort_lms_suffixes_32s_6k(T, SA, induction_bucket,
2423
13.1k
                                           (fast_sint_t)n - (fast_sint_t)m + 1,
2424
13.1k
                                           (fast_sint_t)m - 1);
2425
13.1k
  }
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.1k
  UNUSED(thread_state);
2444
13.1k
#endif
2445
13.1k
}
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
988
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
2451
988
  if (threads == 1 || m < 65536) {
2452
988
    libsais_radix_sort_lms_suffixes_32s_2k(T, SA, induction_bucket,
2453
988
                                           (fast_sint_t)n - (fast_sint_t)m + 1,
2454
988
                                           (fast_sint_t)m - 1);
2455
988
  }
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
988
  UNUSED(thread_state);
2474
988
#endif
2475
988
}
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
170
    sa_sint_t* RESTRICT buckets) {
2480
170
  const fast_sint_t prefetch_distance = 64;
2481
2482
170
  sa_sint_t i = n - 2;
2483
170
  sa_sint_t m = 0;
2484
170
  fast_uint_t f0 = 1;
2485
170
  fast_uint_t f1 = 0;
2486
170
  fast_sint_t c0 = T[n - 1];
2487
170
  fast_sint_t c1 = 0;
2488
170
  fast_sint_t c2 = 0;
2489
2490
76.8k
  for (; i >= prefetch_distance + 3; i -= 4) {
2491
76.6k
    libsais_prefetchr(&T[i - 2 * prefetch_distance]);
2492
2493
76.6k
    libsais_prefetchw(&buckets[T[i - prefetch_distance - 0]]);
2494
76.6k
    libsais_prefetchw(&buckets[T[i - prefetch_distance - 1]]);
2495
76.6k
    libsais_prefetchw(&buckets[T[i - prefetch_distance - 2]]);
2496
76.6k
    libsais_prefetchw(&buckets[T[i - prefetch_distance - 3]]);
2497
2498
76.6k
    c1 = T[i - 0];
2499
76.6k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
2500
76.6k
    if (f1 & ~f0) {
2501
25.2k
      SA[--buckets[c2 = c0]] = i + 1;
2502
25.2k
      m++;
2503
25.2k
    }
2504
2505
76.6k
    c0 = T[i - 1];
2506
76.6k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
2507
76.6k
    if (f0 & ~f1) {
2508
25.4k
      SA[--buckets[c2 = c1]] = i - 0;
2509
25.4k
      m++;
2510
25.4k
    }
2511
2512
76.6k
    c1 = T[i - 2];
2513
76.6k
    f1 = (fast_uint_t)(c1 > (c0 - (fast_sint_t)(f0)));
2514
76.6k
    if (f1 & ~f0) {
2515
25.2k
      SA[--buckets[c2 = c0]] = i - 1;
2516
25.2k
      m++;
2517
25.2k
    }
2518
2519
76.6k
    c0 = T[i - 3];
2520
76.6k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
2521
76.6k
    if (f0 & ~f1) {
2522
25.4k
      SA[--buckets[c2 = c1]] = i - 2;
2523
25.4k
      m++;
2524
25.4k
    }
2525
76.6k
  }
2526
2527
11.3k
  for (; i >= 0; i -= 1) {
2528
11.1k
    c1 = c0;
2529
11.1k
    c0 = T[i];
2530
11.1k
    f1 = f0;
2531
11.1k
    f0 = (fast_uint_t)(c0 > (c1 - (fast_sint_t)(f1)));
2532
11.1k
    if (f0 & ~f1) {
2533
3.49k
      SA[--buckets[c2 = c1]] = i + 1;
2534
3.49k
      m++;
2535
3.49k
    }
2536
11.1k
  }
2537
2538
170
  if (m > 1) {
2539
170
    SA[buckets[c2]] = 0;
2540
170
  }
2541
2542
170
  return m;
2543
170
}
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.1k
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
2548
13.1k
  const fast_sint_t prefetch_distance = 64;
2549
2550
13.1k
  fast_sint_t i, j;
2551
13.1k
  for (i = omp_block_start,
2552
13.1k
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
2553
61.2k
       i < j; i += 4) {
2554
48.0k
    libsais_prefetchr(&induction_bucket[i + 2 * prefetch_distance]);
2555
2556
48.0k
    libsais_prefetchw(&SA[induction_bucket[i + prefetch_distance + 0]]);
2557
48.0k
    libsais_prefetchw(&SA[induction_bucket[i + prefetch_distance + 1]]);
2558
48.0k
    libsais_prefetchw(&SA[induction_bucket[i + prefetch_distance + 2]]);
2559
48.0k
    libsais_prefetchw(&SA[induction_bucket[i + prefetch_distance + 3]]);
2560
2561
48.0k
    SA[induction_bucket[i + 0]] |= SAINT_MIN;
2562
48.0k
    SA[induction_bucket[i + 1]] |= SAINT_MIN;
2563
48.0k
    SA[induction_bucket[i + 2]] |= SAINT_MIN;
2564
48.0k
    SA[induction_bucket[i + 3]] |= SAINT_MIN;
2565
48.0k
  }
2566
2567
342k
  for (j += prefetch_distance + 3; i < j; i += 1) {
2568
329k
    SA[induction_bucket[i]] |= SAINT_MIN;
2569
329k
  }
2570
13.1k
}
2571
2572
static void libsais_radix_sort_set_markers_32s_4k(
2573
    sa_sint_t* RESTRICT SA, sa_sint_t* RESTRICT induction_bucket,
2574
356
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
2575
356
  const fast_sint_t prefetch_distance = 64;
2576
2577
356
  fast_sint_t i, j;
2578
356
  for (i = omp_block_start,
2579
356
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
2580
36.3k
       i < j; i += 4) {
2581
36.0k
    libsais_prefetchr(
2582
36.0k
        &induction_bucket[BUCKETS_INDEX2(i + 2 * prefetch_distance, 0)]);
2583
2584
36.0k
    libsais_prefetchw(
2585
36.0k
        &SA[induction_bucket[BUCKETS_INDEX2(i + prefetch_distance + 0, 0)]]);
2586
36.0k
    libsais_prefetchw(
2587
36.0k
        &SA[induction_bucket[BUCKETS_INDEX2(i + prefetch_distance + 1, 0)]]);
2588
36.0k
    libsais_prefetchw(
2589
36.0k
        &SA[induction_bucket[BUCKETS_INDEX2(i + prefetch_distance + 2, 0)]]);
2590
36.0k
    libsais_prefetchw(
2591
36.0k
        &SA[induction_bucket[BUCKETS_INDEX2(i + prefetch_distance + 3, 0)]]);
2592
2593
36.0k
    SA[induction_bucket[BUCKETS_INDEX2(i + 0, 0)]] |= SUFFIX_GROUP_MARKER;
2594
36.0k
    SA[induction_bucket[BUCKETS_INDEX2(i + 1, 0)]] |= SUFFIX_GROUP_MARKER;
2595
36.0k
    SA[induction_bucket[BUCKETS_INDEX2(i + 2, 0)]] |= SUFFIX_GROUP_MARKER;
2596
36.0k
    SA[induction_bucket[BUCKETS_INDEX2(i + 3, 0)]] |= SUFFIX_GROUP_MARKER;
2597
36.0k
  }
2598
2599
23.6k
  for (j += prefetch_distance + 3; i < j; i += 1) {
2600
23.3k
    SA[induction_bucket[BUCKETS_INDEX2(i, 0)]] |= SUFFIX_GROUP_MARKER;
2601
23.3k
  }
2602
356
}
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.1k
    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.1k
  {
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.1k
    UNUSED(threads);
2622
2623
13.1k
    fast_sint_t omp_block_start = 0;
2624
13.1k
    fast_sint_t omp_block_size = (fast_sint_t)k - 1;
2625
13.1k
#endif
2626
2627
13.1k
    libsais_radix_sort_set_markers_32s_6k(SA, induction_bucket, omp_block_start,
2628
13.1k
                                          omp_block_size);
2629
13.1k
  }
2630
13.1k
}
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
356
    sa_sint_t threads) {
2635
#if defined(LIBSAIS_OPENMP)
2636
#pragma omp parallel num_threads(threads) if (threads > 1 && k >= 65536)
2637
#endif
2638
356
  {
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
356
    UNUSED(threads);
2650
2651
356
    fast_sint_t omp_block_start = 0;
2652
356
    fast_sint_t omp_block_size = (fast_sint_t)k - 1;
2653
356
#endif
2654
2655
356
    libsais_radix_sort_set_markers_32s_4k(SA, induction_bucket, omp_block_start,
2656
356
                                          omp_block_size);
2657
356
  }
2658
356
}
2659
2660
static void libsais_initialize_buckets_for_partial_sorting_8u(
2661
    const uint8_t* RESTRICT T, sa_sint_t* RESTRICT buckets,
2662
8.05k
    sa_sint_t first_lms_suffix, sa_sint_t left_suffixes_count) {
2663
8.05k
  sa_sint_t* RESTRICT temp_bucket = &buckets[4 * ALPHABET_SIZE];
2664
2665
8.05k
  buckets[BUCKETS_INDEX4((fast_uint_t)T[first_lms_suffix], 1)]++;
2666
2667
8.05k
  fast_sint_t i, j;
2668
8.05k
  sa_sint_t sum0 = left_suffixes_count + 1, sum1 = 0;
2669
8.05k
  for (i = BUCKETS_INDEX4(0, 0), j = BUCKETS_INDEX2(0, 0);
2670
2.07M
       i <= BUCKETS_INDEX4(ALPHABET_SIZE - 1, 0);
2671
2.06M
       i += BUCKETS_INDEX4(1, 0), j += BUCKETS_INDEX2(1, 0)) {
2672
2.06M
    temp_bucket[j + BUCKETS_INDEX2(0, 0)] = sum0;
2673
2674
2.06M
    sum0 +=
2675
2.06M
        buckets[i + BUCKETS_INDEX4(0, 0)] + buckets[i + BUCKETS_INDEX4(0, 2)];
2676
2.06M
    sum1 += buckets[i + BUCKETS_INDEX4(0, 1)];
2677
2678
2.06M
    buckets[j + BUCKETS_INDEX2(0, 0)] = sum0;
2679
2.06M
    buckets[j + BUCKETS_INDEX2(0, 1)] = sum1;
2680
2.06M
  }
2681
8.05k
}
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.1k
    sa_sint_t first_lms_suffix, sa_sint_t left_suffixes_count) {
2686
13.1k
  sa_sint_t* RESTRICT temp_bucket = &buckets[4 * (fast_sint_t)k];
2687
2688
13.1k
  fast_sint_t i, j;
2689
13.1k
  sa_sint_t sum0 = left_suffixes_count + 1, sum1 = 0, sum2 = 0;
2690
13.1k
  for (first_lms_suffix = T[first_lms_suffix], i = BUCKETS_INDEX4(0, 0),
2691
13.1k
      j = BUCKETS_INDEX2(0, 0);
2692
208k
       i != BUCKETS_INDEX4((fast_sint_t)first_lms_suffix, 0);
2693
195k
       i += BUCKETS_INDEX4(1, 0), j += BUCKETS_INDEX2(1, 0)) {
2694
195k
    sa_sint_t SS = buckets[i + BUCKETS_INDEX4(0, 0)];
2695
195k
    sa_sint_t LS = buckets[i + BUCKETS_INDEX4(0, 1)];
2696
195k
    sa_sint_t SL = buckets[i + BUCKETS_INDEX4(0, 2)];
2697
195k
    sa_sint_t LL = buckets[i + BUCKETS_INDEX4(0, 3)];
2698
2699
195k
    buckets[i + BUCKETS_INDEX4(0, 0)] = sum0;
2700
195k
    buckets[i + BUCKETS_INDEX4(0, 1)] = sum2;
2701
195k
    buckets[i + BUCKETS_INDEX4(0, 2)] = 0;
2702
195k
    buckets[i + BUCKETS_INDEX4(0, 3)] = 0;
2703
2704
195k
    sum0 += SS + SL;
2705
195k
    sum1 += LS;
2706
195k
    sum2 += LS + LL;
2707
2708
195k
    temp_bucket[j + BUCKETS_INDEX2(0, 0)] = sum0;
2709
195k
    temp_bucket[j + BUCKETS_INDEX2(0, 1)] = sum1;
2710
195k
  }
2711
2712
352k
  for (sum1 += 1; i <= BUCKETS_INDEX4((fast_sint_t)k - 1, 0);
2713
339k
       i += BUCKETS_INDEX4(1, 0), j += BUCKETS_INDEX2(1, 0)) {
2714
339k
    sa_sint_t SS = buckets[i + BUCKETS_INDEX4(0, 0)];
2715
339k
    sa_sint_t LS = buckets[i + BUCKETS_INDEX4(0, 1)];
2716
339k
    sa_sint_t SL = buckets[i + BUCKETS_INDEX4(0, 2)];
2717
339k
    sa_sint_t LL = buckets[i + BUCKETS_INDEX4(0, 3)];
2718
2719
339k
    buckets[i + BUCKETS_INDEX4(0, 0)] = sum0;
2720
339k
    buckets[i + BUCKETS_INDEX4(0, 1)] = sum2;
2721
339k
    buckets[i + BUCKETS_INDEX4(0, 2)] = 0;
2722
339k
    buckets[i + BUCKETS_INDEX4(0, 3)] = 0;
2723
2724
339k
    sum0 += SS + SL;
2725
339k
    sum1 += LS;
2726
339k
    sum2 += LS + LL;
2727
2728
339k
    temp_bucket[j + BUCKETS_INDEX2(0, 0)] = sum0;
2729
339k
    temp_bucket[j + BUCKETS_INDEX2(0, 1)] = sum1;
2730
339k
  }
2731
13.1k
}
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.05k
    fast_sint_t omp_block_size) {
2737
8.05k
  const fast_sint_t prefetch_distance = 64;
2738
2739
8.05k
  sa_sint_t* RESTRICT induction_bucket = &buckets[4 * ALPHABET_SIZE];
2740
8.05k
  sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
2741
2742
8.05k
  fast_sint_t i, j;
2743
8.05k
  for (i = omp_block_start,
2744
8.05k
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
2745
5.53M
       i < j; i += 2) {
2746
5.53M
    libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
2747
2748
5.53M
    libsais_prefetchr(&T[SA[i + prefetch_distance + 0] & SAINT_MAX] - 1);
2749
5.53M
    libsais_prefetchr(&T[SA[i + prefetch_distance + 0] & SAINT_MAX] - 2);
2750
5.53M
    libsais_prefetchr(&T[SA[i + prefetch_distance + 1] & SAINT_MAX] - 1);
2751
5.53M
    libsais_prefetchr(&T[SA[i + prefetch_distance + 1] & SAINT_MAX] - 2);
2752
2753
5.53M
    sa_sint_t p0 = SA[i + 0];
2754
5.53M
    d += (p0 < 0);
2755
5.53M
    p0 &= SAINT_MAX;
2756
5.53M
    sa_sint_t v0 = BUCKETS_INDEX2(T[p0 - 1], T[p0 - 2] >= T[p0 - 1]);
2757
5.53M
    SA[induction_bucket[v0]++] =
2758
5.53M
        (p0 - 1) |
2759
5.53M
        (sa_sint_t)((sa_uint_t)(distinct_names[v0] != d) << (SAINT_BIT - 1));
2760
5.53M
    distinct_names[v0] = d;
2761
2762
5.53M
    sa_sint_t p1 = SA[i + 1];
2763
5.53M
    d += (p1 < 0);
2764
5.53M
    p1 &= SAINT_MAX;
2765
5.53M
    sa_sint_t v1 = BUCKETS_INDEX2(T[p1 - 1], T[p1 - 2] >= T[p1 - 1]);
2766
5.53M
    SA[induction_bucket[v1]++] =
2767
5.53M
        (p1 - 1) |
2768
5.53M
        (sa_sint_t)((sa_uint_t)(distinct_names[v1] != d) << (SAINT_BIT - 1));
2769
5.53M
    distinct_names[v1] = d;
2770
5.53M
  }
2771
2772
419k
  for (j += prefetch_distance + 1; i < j; i += 1) {
2773
411k
    sa_sint_t p = SA[i];
2774
411k
    d += (p < 0);
2775
411k
    p &= SAINT_MAX;
2776
411k
    sa_sint_t v = BUCKETS_INDEX2(T[p - 1], T[p - 2] >= T[p - 1]);
2777
411k
    SA[induction_bucket[v]++] =
2778
411k
        (p - 1) |
2779
411k
        (sa_sint_t)((sa_uint_t)(distinct_names[v] != d) << (SAINT_BIT - 1));
2780
411k
    distinct_names[v] = d;
2781
411k
  }
2782
2783
8.05k
  return d;
2784
8.05k
}
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.05k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
2981
8.05k
  sa_sint_t* RESTRICT induction_bucket = &buckets[4 * ALPHABET_SIZE];
2982
8.05k
  sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
2983
2984
8.05k
  SA[induction_bucket[BUCKETS_INDEX2(T[n - 1], T[n - 2] >= T[n - 1])]++] =
2985
8.05k
      (n - 1) | SAINT_MIN;
2986
8.05k
  distinct_names[BUCKETS_INDEX2(T[n - 1], T[n - 2] >= T[n - 1])] = ++d;
2987
2988
8.05k
  if (threads == 1 || left_suffixes_count < 65536) {
2989
8.05k
    d = libsais_partial_sorting_scan_left_to_right_8u(T, SA, buckets, d, 0,
2990
8.05k
                                                      left_suffixes_count);
2991
8.05k
  }
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.05k
  UNUSED(k);
3034
8.05k
  UNUSED(thread_state);
3035
8.05k
#endif
3036
3037
8.05k
  return d;
3038
8.05k
}
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.1k
    fast_sint_t omp_block_size) {
3044
13.1k
  const fast_sint_t prefetch_distance = 64;
3045
3046
13.1k
  fast_sint_t i, j;
3047
13.1k
  for (i = omp_block_start,
3048
13.1k
      j = omp_block_start + omp_block_size - 2 * prefetch_distance - 1;
3049
1.60M
       i < j; i += 2) {
3050
1.58M
    libsais_prefetchr(&SA[i + 3 * prefetch_distance]);
3051
3052
1.58M
    libsais_prefetchr(&T[SA[i + 2 * prefetch_distance + 0] & SAINT_MAX] - 1);
3053
1.58M
    libsais_prefetchr(&T[SA[i + 2 * prefetch_distance + 0] & SAINT_MAX] - 2);
3054
1.58M
    libsais_prefetchr(&T[SA[i + 2 * prefetch_distance + 1] & SAINT_MAX] - 1);
3055
1.58M
    libsais_prefetchr(&T[SA[i + 2 * prefetch_distance + 1] & SAINT_MAX] - 2);
3056
3057
1.58M
    sa_sint_t p0 = SA[i + prefetch_distance + 0] & SAINT_MAX;
3058
1.58M
    sa_sint_t v0 = BUCKETS_INDEX4(T[p0 - (p0 > 0)], 0);
3059
1.58M
    libsais_prefetchw(&buckets[v0]);
3060
1.58M
    sa_sint_t p1 = SA[i + prefetch_distance + 1] & SAINT_MAX;
3061
1.58M
    sa_sint_t v1 = BUCKETS_INDEX4(T[p1 - (p1 > 0)], 0);
3062
1.58M
    libsais_prefetchw(&buckets[v1]);
3063
3064
1.58M
    sa_sint_t p2 = SA[i + 0];
3065
1.58M
    d += (p2 < 0);
3066
1.58M
    p2 &= SAINT_MAX;
3067
1.58M
    sa_sint_t v2 = BUCKETS_INDEX4(T[p2 - 1], T[p2 - 2] >= T[p2 - 1]);
3068
1.58M
    SA[buckets[v2]++] = (p2 - 1) | (sa_sint_t)((sa_uint_t)(buckets[2 + v2] != d)
3069
1.58M
                                               << (SAINT_BIT - 1));
3070
1.58M
    buckets[2 + v2] = d;
3071
3072
1.58M
    sa_sint_t p3 = SA[i + 1];
3073
1.58M
    d += (p3 < 0);
3074
1.58M
    p3 &= SAINT_MAX;
3075
1.58M
    sa_sint_t v3 = BUCKETS_INDEX4(T[p3 - 1], T[p3 - 2] >= T[p3 - 1]);
3076
1.58M
    SA[buckets[v3]++] = (p3 - 1) | (sa_sint_t)((sa_uint_t)(buckets[2 + v3] != d)
3077
1.58M
                                               << (SAINT_BIT - 1));
3078
1.58M
    buckets[2 + v3] = d;
3079
1.58M
  }
3080
3081
663k
  for (j += 2 * prefetch_distance + 1; i < j; i += 1) {
3082
650k
    sa_sint_t p = SA[i];
3083
650k
    d += (p < 0);
3084
650k
    p &= SAINT_MAX;
3085
650k
    sa_sint_t v = BUCKETS_INDEX4(T[p - 1], T[p - 2] >= T[p - 1]);
3086
650k
    SA[buckets[v]++] = (p - 1) | (sa_sint_t)((sa_uint_t)(buckets[2 + v] != d)
3087
650k
                                             << (SAINT_BIT - 1));
3088
650k
    buckets[2 + v] = d;
3089
650k
  }
3090
3091
13.1k
  return d;
3092
13.1k
}
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
356
    fast_sint_t omp_block_size) {
3098
356
  const fast_sint_t prefetch_distance = 64;
3099
3100
356
  sa_sint_t* RESTRICT induction_bucket = &buckets[2 * (fast_sint_t)k];
3101
356
  sa_sint_t* RESTRICT distinct_names = &buckets[0 * (fast_sint_t)k];
3102
3103
356
  fast_sint_t i, j;
3104
356
  for (i = omp_block_start,
3105
356
      j = omp_block_start + omp_block_size - 2 * prefetch_distance - 1;
3106
259k
       i < j; i += 2) {
3107
259k
    libsais_prefetchw(&SA[i + 3 * prefetch_distance]);
3108
3109
259k
    sa_sint_t s0 = SA[i + 2 * prefetch_distance + 0];
3110
259k
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 & ~SUFFIX_GROUP_MARKER : 2];
3111
259k
    libsais_prefetchr(Ts0 - 1);
3112
259k
    libsais_prefetchr(Ts0 - 2);
3113
259k
    sa_sint_t s1 = SA[i + 2 * prefetch_distance + 1];
3114
259k
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 & ~SUFFIX_GROUP_MARKER : 2];
3115
259k
    libsais_prefetchr(Ts1 - 1);
3116
259k
    libsais_prefetchr(Ts1 - 2);
3117
259k
    sa_sint_t s2 = SA[i + 1 * prefetch_distance + 0];
3118
259k
    if (s2 > 0) {
3119
114k
      const fast_sint_t Ts2 = T[(s2 & ~SUFFIX_GROUP_MARKER) - 1];
3120
114k
      libsais_prefetchw(&induction_bucket[Ts2]);
3121
114k
      libsais_prefetchw(&distinct_names[BUCKETS_INDEX2(Ts2, 0)]);
3122
114k
    }
3123
259k
    sa_sint_t s3 = SA[i + 1 * prefetch_distance + 1];
3124
259k
    if (s3 > 0) {
3125
114k
      const fast_sint_t Ts3 = T[(s3 & ~SUFFIX_GROUP_MARKER) - 1];
3126
114k
      libsais_prefetchw(&induction_bucket[Ts3]);
3127
114k
      libsais_prefetchw(&distinct_names[BUCKETS_INDEX2(Ts3, 0)]);
3128
114k
    }
3129
3130
259k
    sa_sint_t p0 = SA[i + 0];
3131
259k
    SA[i + 0] = p0 & SAINT_MAX;
3132
259k
    if (p0 > 0) {
3133
134k
      SA[i + 0] = 0;
3134
134k
      d += (p0 >> (SUFFIX_GROUP_BIT - 1));
3135
134k
      p0 &= ~SUFFIX_GROUP_MARKER;
3136
134k
      sa_sint_t v0 = BUCKETS_INDEX2(T[p0 - 1], T[p0 - 2] < T[p0 - 1]);
3137
134k
      SA[induction_bucket[T[p0 - 1]]++] =
3138
134k
          (p0 - 1) |
3139
134k
          (sa_sint_t)((sa_uint_t)(T[p0 - 2] < T[p0 - 1]) << (SAINT_BIT - 1)) |
3140
134k
          ((sa_sint_t)(distinct_names[v0] != d) << (SUFFIX_GROUP_BIT - 1));
3141
134k
      distinct_names[v0] = d;
3142
134k
    }
3143
3144
259k
    sa_sint_t p1 = SA[i + 1];
3145
259k
    SA[i + 1] = p1 & SAINT_MAX;
3146
259k
    if (p1 > 0) {
3147
134k
      SA[i + 1] = 0;
3148
134k
      d += (p1 >> (SUFFIX_GROUP_BIT - 1));
3149
134k
      p1 &= ~SUFFIX_GROUP_MARKER;
3150
134k
      sa_sint_t v1 = BUCKETS_INDEX2(T[p1 - 1], T[p1 - 2] < T[p1 - 1]);
3151
134k
      SA[induction_bucket[T[p1 - 1]]++] =
3152
134k
          (p1 - 1) |
3153
134k
          (sa_sint_t)((sa_uint_t)(T[p1 - 2] < T[p1 - 1]) << (SAINT_BIT - 1)) |
3154
134k
          ((sa_sint_t)(distinct_names[v1] != d) << (SUFFIX_GROUP_BIT - 1));
3155
134k
      distinct_names[v1] = d;
3156
134k
    }
3157
259k
  }
3158
3159
46.1k
  for (j += 2 * prefetch_distance + 1; i < j; i += 1) {
3160
45.7k
    sa_sint_t p = SA[i];
3161
45.7k
    SA[i] = p & SAINT_MAX;
3162
45.7k
    if (p > 0) {
3163
20.9k
      SA[i] = 0;
3164
20.9k
      d += (p >> (SUFFIX_GROUP_BIT - 1));
3165
20.9k
      p &= ~SUFFIX_GROUP_MARKER;
3166
20.9k
      sa_sint_t v = BUCKETS_INDEX2(T[p - 1], T[p - 2] < T[p - 1]);
3167
20.9k
      SA[induction_bucket[T[p - 1]]++] =
3168
20.9k
          (p - 1) |
3169
20.9k
          (sa_sint_t)((sa_uint_t)(T[p - 2] < T[p - 1]) << (SAINT_BIT - 1)) |
3170
20.9k
          ((sa_sint_t)(distinct_names[v] != d) << (SUFFIX_GROUP_BIT - 1));
3171
20.9k
      distinct_names[v] = d;
3172
20.9k
    }
3173
45.7k
  }
3174
3175
356
  return d;
3176
356
}
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
802
    fast_sint_t omp_block_size) {
3182
802
  const fast_sint_t prefetch_distance = 64;
3183
3184
802
  fast_sint_t i, j;
3185
802
  for (i = omp_block_start,
3186
802
      j = omp_block_start + omp_block_size - 2 * prefetch_distance - 1;
3187
738k
       i < j; i += 2) {
3188
737k
    libsais_prefetchw(&SA[i + 3 * prefetch_distance]);
3189
3190
737k
    sa_sint_t s0 = SA[i + 2 * prefetch_distance + 0];
3191
737k
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 : 1];
3192
737k
    libsais_prefetchr(Ts0 - 1);
3193
737k
    sa_sint_t s1 = SA[i + 2 * prefetch_distance + 1];
3194
737k
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 : 1];
3195
737k
    libsais_prefetchr(Ts1 - 1);
3196
737k
    sa_sint_t s2 = SA[i + 1 * prefetch_distance + 0];
3197
737k
    if (s2 > 0) {
3198
329k
      libsais_prefetchw(&induction_bucket[T[s2 - 1]]);
3199
329k
      libsais_prefetchr(&T[s2] - 2);
3200
329k
    }
3201
737k
    sa_sint_t s3 = SA[i + 1 * prefetch_distance + 1];
3202
737k
    if (s3 > 0) {
3203
329k
      libsais_prefetchw(&induction_bucket[T[s3 - 1]]);
3204
329k
      libsais_prefetchr(&T[s3] - 2);
3205
329k
    }
3206
3207
737k
    sa_sint_t p0 = SA[i + 0];
3208
737k
    SA[i + 0] = p0 & SAINT_MAX;
3209
737k
    if (p0 > 0) {
3210
377k
      SA[i + 0] = 0;
3211
377k
      SA[induction_bucket[T[p0 - 1]]++] =
3212
377k
          (p0 - 1) |
3213
377k
          (sa_sint_t)((sa_uint_t)(T[p0 - 2] < T[p0 - 1]) << (SAINT_BIT - 1));
3214
377k
    }
3215
737k
    sa_sint_t p1 = SA[i + 1];
3216
737k
    SA[i + 1] = p1 & SAINT_MAX;
3217
737k
    if (p1 > 0) {
3218
377k
      SA[i + 1] = 0;
3219
377k
      SA[induction_bucket[T[p1 - 1]]++] =
3220
377k
          (p1 - 1) |
3221
377k
          (sa_sint_t)((sa_uint_t)(T[p1 - 2] < T[p1 - 1]) << (SAINT_BIT - 1));
3222
377k
    }
3223
737k
  }
3224
3225
103k
  for (j += 2 * prefetch_distance + 1; i < j; i += 1) {
3226
103k
    sa_sint_t p = SA[i];
3227
103k
    SA[i] = p & SAINT_MAX;
3228
103k
    if (p > 0) {
3229
49.1k
      SA[i] = 0;
3230
49.1k
      SA[induction_bucket[T[p - 1]]++] =
3231
49.1k
          (p - 1) |
3232
49.1k
          (sa_sint_t)((sa_uint_t)(T[p - 2] < T[p - 1]) << (SAINT_BIT - 1));
3233
49.1k
    }
3234
103k
  }
3235
802
}
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.1k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
3804
13.1k
  SA[buckets[BUCKETS_INDEX4(T[n - 1], T[n - 2] >= T[n - 1])]++] =
3805
13.1k
      (n - 1) | SAINT_MIN;
3806
13.1k
  buckets[2 + BUCKETS_INDEX4(T[n - 1], T[n - 2] >= T[n - 1])] = ++d;
3807
3808
13.1k
  if (threads == 1 || left_suffixes_count < 65536) {
3809
13.1k
    d = libsais_partial_sorting_scan_left_to_right_32s_6k(T, SA, buckets, d, 0,
3810
13.1k
                                                          left_suffixes_count);
3811
13.1k
  }
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.1k
  UNUSED(thread_state);
3830
13.1k
#endif
3831
3832
13.1k
  return d;
3833
13.1k
}
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
356
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
3839
356
  sa_sint_t* RESTRICT induction_bucket = &buckets[2 * (fast_sint_t)k];
3840
356
  sa_sint_t* RESTRICT distinct_names = &buckets[0 * (fast_sint_t)k];
3841
3842
356
  SA[induction_bucket[T[n - 1]]++] =
3843
356
      (n - 1) |
3844
356
      (sa_sint_t)((sa_uint_t)(T[n - 2] < T[n - 1]) << (SAINT_BIT - 1)) |
3845
356
      SUFFIX_GROUP_MARKER;
3846
356
  distinct_names[BUCKETS_INDEX2(T[n - 1], T[n - 2] < T[n - 1])] = ++d;
3847
3848
356
  if (threads == 1 || n < 65536) {
3849
356
    d = libsais_partial_sorting_scan_left_to_right_32s_4k(T, SA, k, buckets, d,
3850
356
                                                          0, n);
3851
356
  }
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
356
  UNUSED(thread_state);
3869
356
#endif
3870
3871
356
  return d;
3872
356
}
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
802
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
3878
802
  SA[buckets[T[n - 1]]++] =
3879
802
      (n - 1) |
3880
802
      (sa_sint_t)((sa_uint_t)(T[n - 2] < T[n - 1]) << (SAINT_BIT - 1));
3881
3882
802
  if (threads == 1 || n < 65536) {
3883
802
    libsais_partial_sorting_scan_left_to_right_32s_1k(T, SA, buckets, 0, n);
3884
802
  }
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
802
  UNUSED(thread_state);
3902
802
#endif
3903
802
}
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.05k
    sa_sint_t threads) {
3908
8.05k
  const fast_sint_t prefetch_distance = 64;
3909
3910
8.05k
  const sa_sint_t* RESTRICT temp_bucket = &buckets[4 * ALPHABET_SIZE];
3911
3912
8.05k
  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.05k
  UNUSED(threads);
3919
8.05k
  UNUSED(n);
3920
8.05k
#endif
3921
2.06M
  for (c = BUCKETS_INDEX2(ALPHABET_SIZE - 1, 0); c >= BUCKETS_INDEX2(1, 0);
3922
2.05M
       c -= BUCKETS_INDEX2(1, 0)) {
3923
2.05M
    fast_sint_t i, j;
3924
2.05M
    sa_sint_t s = SAINT_MIN;
3925
2.05M
    for (i = (fast_sint_t)temp_bucket[c] - 1,
3926
2.05M
        j = (fast_sint_t)buckets[c - BUCKETS_INDEX2(1, 0)] + 3;
3927
3.88M
         i >= j; i -= 4) {
3928
1.83M
      libsais_prefetchw(&SA[i - prefetch_distance]);
3929
3930
1.83M
      sa_sint_t p0 = SA[i - 0], q0 = (p0 & SAINT_MIN) ^ s;
3931
1.83M
      s = s ^ q0;
3932
1.83M
      SA[i - 0] = p0 ^ q0;
3933
1.83M
      sa_sint_t p1 = SA[i - 1], q1 = (p1 & SAINT_MIN) ^ s;
3934
1.83M
      s = s ^ q1;
3935
1.83M
      SA[i - 1] = p1 ^ q1;
3936
1.83M
      sa_sint_t p2 = SA[i - 2], q2 = (p2 & SAINT_MIN) ^ s;
3937
1.83M
      s = s ^ q2;
3938
1.83M
      SA[i - 2] = p2 ^ q2;
3939
1.83M
      sa_sint_t p3 = SA[i - 3], q3 = (p3 & SAINT_MIN) ^ s;
3940
1.83M
      s = s ^ q3;
3941
1.83M
      SA[i - 3] = p3 ^ q3;
3942
1.83M
    }
3943
3944
2.17M
    for (j -= 3; i >= j; i -= 1) {
3945
125k
      sa_sint_t p = SA[i], q = (p & SAINT_MIN) ^ s;
3946
125k
      s = s ^ q;
3947
125k
      SA[i] = p ^ q;
3948
125k
    }
3949
2.05M
  }
3950
8.05k
}
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.1k
    sa_sint_t threads) {
3955
13.1k
  const fast_sint_t prefetch_distance = 64;
3956
3957
13.1k
  const sa_sint_t* RESTRICT temp_bucket = &buckets[4 * (fast_sint_t)k];
3958
3959
13.1k
  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.1k
  UNUSED(threads);
3966
13.1k
#endif
3967
535k
  for (c = (fast_sint_t)k - 1; c >= 1; c -= 1) {
3968
522k
    fast_sint_t i, j;
3969
522k
    sa_sint_t s = SAINT_MIN;
3970
522k
    for (i = (fast_sint_t)buckets[BUCKETS_INDEX4(c, 0)] - 1,
3971
522k
        j = (fast_sint_t)temp_bucket[BUCKETS_INDEX2(c - 1, 0)] + 3;
3972
1.19M
         i >= j; i -= 4) {
3973
669k
      libsais_prefetchw(&SA[i - prefetch_distance]);
3974
3975
669k
      sa_sint_t p0 = SA[i - 0], q0 = (p0 & SAINT_MIN) ^ s;
3976
669k
      s = s ^ q0;
3977
669k
      SA[i - 0] = p0 ^ q0;
3978
669k
      sa_sint_t p1 = SA[i - 1], q1 = (p1 & SAINT_MIN) ^ s;
3979
669k
      s = s ^ q1;
3980
669k
      SA[i - 1] = p1 ^ q1;
3981
669k
      sa_sint_t p2 = SA[i - 2], q2 = (p2 & SAINT_MIN) ^ s;
3982
669k
      s = s ^ q2;
3983
669k
      SA[i - 2] = p2 ^ q2;
3984
669k
      sa_sint_t p3 = SA[i - 3], q3 = (p3 & SAINT_MIN) ^ s;
3985
669k
      s = s ^ q3;
3986
669k
      SA[i - 3] = p3 ^ q3;
3987
669k
    }
3988
3989
761k
    for (j -= 3; i >= j; i -= 1) {
3990
239k
      sa_sint_t p = SA[i], q = (p & SAINT_MIN) ^ s;
3991
239k
      s = s ^ q;
3992
239k
      SA[i] = p ^ q;
3993
239k
    }
3994
522k
  }
3995
13.1k
}
3996
3997
static void libsais_partial_sorting_shift_markers_32s_4k(sa_sint_t* RESTRICT SA,
3998
356
                                                         sa_sint_t n) {
3999
356
  const fast_sint_t prefetch_distance = 64;
4000
4001
356
  fast_sint_t i;
4002
356
  sa_sint_t s = SUFFIX_GROUP_MARKER;
4003
141k
  for (i = (fast_sint_t)n - 1; i >= 3; i -= 4) {
4004
140k
    libsais_prefetchw(&SA[i - prefetch_distance]);
4005
4006
140k
    sa_sint_t p0 = SA[i - 0],
4007
140k
              q0 = ((p0 & SUFFIX_GROUP_MARKER) ^ s) &
4008
140k
                   ((sa_sint_t)(p0 > 0) << ((SUFFIX_GROUP_BIT - 1)));
4009
140k
    s = s ^ q0;
4010
140k
    SA[i - 0] = p0 ^ q0;
4011
140k
    sa_sint_t p1 = SA[i - 1],
4012
140k
              q1 = ((p1 & SUFFIX_GROUP_MARKER) ^ s) &
4013
140k
                   ((sa_sint_t)(p1 > 0) << ((SUFFIX_GROUP_BIT - 1)));
4014
140k
    s = s ^ q1;
4015
140k
    SA[i - 1] = p1 ^ q1;
4016
140k
    sa_sint_t p2 = SA[i - 2],
4017
140k
              q2 = ((p2 & SUFFIX_GROUP_MARKER) ^ s) &
4018
140k
                   ((sa_sint_t)(p2 > 0) << ((SUFFIX_GROUP_BIT - 1)));
4019
140k
    s = s ^ q2;
4020
140k
    SA[i - 2] = p2 ^ q2;
4021
140k
    sa_sint_t p3 = SA[i - 3],
4022
140k
              q3 = ((p3 & SUFFIX_GROUP_MARKER) ^ s) &
4023
140k
                   ((sa_sint_t)(p3 > 0) << ((SUFFIX_GROUP_BIT - 1)));
4024
140k
    s = s ^ q3;
4025
140k
    SA[i - 3] = p3 ^ q3;
4026
140k
  }
4027
4028
877
  for (; i >= 0; i -= 1) {
4029
521
    sa_sint_t p = SA[i], q = ((p & SUFFIX_GROUP_MARKER) ^ s) &
4030
521
                             ((sa_sint_t)(p > 0) << ((SUFFIX_GROUP_BIT - 1)));
4031
521
    s = s ^ q;
4032
521
    SA[i] = p ^ q;
4033
521
  }
4034
356
}
4035
4036
static void libsais_partial_sorting_shift_buckets_32s_6k(
4037
13.1k
    sa_sint_t k, sa_sint_t* RESTRICT buckets) {
4038
13.1k
  sa_sint_t* RESTRICT temp_bucket = &buckets[4 * (fast_sint_t)k];
4039
4040
13.1k
  fast_sint_t i;
4041
548k
  for (i = BUCKETS_INDEX2(0, 0); i <= BUCKETS_INDEX2((fast_sint_t)k - 1, 0);
4042
535k
       i += BUCKETS_INDEX2(1, 0)) {
4043
535k
    buckets[2 * i + BUCKETS_INDEX4(0, 0)] =
4044
535k
        temp_bucket[i + BUCKETS_INDEX2(0, 0)];
4045
535k
    buckets[2 * i + BUCKETS_INDEX4(0, 1)] =
4046
535k
        temp_bucket[i + BUCKETS_INDEX2(0, 1)];
4047
535k
  }
4048
13.1k
}
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.05k
    fast_sint_t omp_block_size) {
4054
8.05k
  const fast_sint_t prefetch_distance = 64;
4055
4056
8.05k
  sa_sint_t* RESTRICT induction_bucket = &buckets[0 * ALPHABET_SIZE];
4057
8.05k
  sa_sint_t* RESTRICT distinct_names = &buckets[2 * ALPHABET_SIZE];
4058
4059
8.05k
  fast_sint_t i, j;
4060
8.05k
  for (i = omp_block_start + omp_block_size - 1,
4061
8.05k
      j = omp_block_start + prefetch_distance + 1;
4062
4.05M
       i >= j; i -= 2) {
4063
4.04M
    libsais_prefetchr(&SA[i - 2 * prefetch_distance]);
4064
4065
4.04M
    libsais_prefetchr(&T[SA[i - prefetch_distance - 0] & SAINT_MAX] - 1);
4066
4.04M
    libsais_prefetchr(&T[SA[i - prefetch_distance - 0] & SAINT_MAX] - 2);
4067
4.04M
    libsais_prefetchr(&T[SA[i - prefetch_distance - 1] & SAINT_MAX] - 1);
4068
4.04M
    libsais_prefetchr(&T[SA[i - prefetch_distance - 1] & SAINT_MAX] - 2);
4069
4070
4.04M
    sa_sint_t p0 = SA[i - 0];
4071
4.04M
    d += (p0 < 0);
4072
4.04M
    p0 &= SAINT_MAX;
4073
4.04M
    sa_sint_t v0 = BUCKETS_INDEX2(T[p0 - 1], T[p0 - 2] > T[p0 - 1]);
4074
4.04M
    SA[--induction_bucket[v0]] =
4075
4.04M
        (p0 - 1) |
4076
4.04M
        (sa_sint_t)((sa_uint_t)(distinct_names[v0] != d) << (SAINT_BIT - 1));
4077
4.04M
    distinct_names[v0] = d;
4078
4079
4.04M
    sa_sint_t p1 = SA[i - 1];
4080
4.04M
    d += (p1 < 0);
4081
4.04M
    p1 &= SAINT_MAX;
4082
4.04M
    sa_sint_t v1 = BUCKETS_INDEX2(T[p1 - 1], T[p1 - 2] > T[p1 - 1]);
4083
4.04M
    SA[--induction_bucket[v1]] =
4084
4.04M
        (p1 - 1) |
4085
4.04M
        (sa_sint_t)((sa_uint_t)(distinct_names[v1] != d) << (SAINT_BIT - 1));
4086
4.04M
    distinct_names[v1] = d;
4087
4.04M
  }
4088
4089
397k
  for (j -= prefetch_distance + 1; i >= j; i -= 1) {
4090
389k
    sa_sint_t p = SA[i];
4091
389k
    d += (p < 0);
4092
389k
    p &= SAINT_MAX;
4093
389k
    sa_sint_t v = BUCKETS_INDEX2(T[p - 1], T[p - 2] > T[p - 1]);
4094
389k
    SA[--induction_bucket[v]] =
4095
389k
        (p - 1) |
4096
389k
        (sa_sint_t)((sa_uint_t)(distinct_names[v] != d) << (SAINT_BIT - 1));
4097
389k
    distinct_names[v] = d;
4098
389k
  }
4099
4100
8.05k
  return d;
4101
8.05k
}
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.05k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
4496
8.05k
  fast_sint_t scan_start = (fast_sint_t)left_suffixes_count + 1;
4497
8.05k
  fast_sint_t scan_end = (fast_sint_t)n - (fast_sint_t)first_lms_suffix;
4498
4499
8.05k
  if (threads == 1 || (scan_end - scan_start) < 65536) {
4500
8.05k
    libsais_partial_sorting_scan_right_to_left_8u(T, SA, buckets, d, scan_start,
4501
8.05k
                                                  scan_end - scan_start);
4502
8.05k
  }
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.05k
  UNUSED(k);
4548
8.05k
  UNUSED(thread_state);
4549
8.05k
#endif
4550
8.05k
}
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.1k
    fast_sint_t omp_block_size) {
4619
13.1k
  const fast_sint_t prefetch_distance = 64;
4620
4621
13.1k
  fast_sint_t i, j;
4622
13.1k
  for (i = omp_block_start + omp_block_size - 1,
4623
13.1k
      j = omp_block_start + 2 * prefetch_distance + 1;
4624
1.87M
       i >= j; i -= 2) {
4625
1.86M
    libsais_prefetchr(&SA[i - 3 * prefetch_distance]);
4626
4627
1.86M
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 0] & SAINT_MAX] - 1);
4628
1.86M
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 0] & SAINT_MAX] - 2);
4629
1.86M
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 1] & SAINT_MAX] - 1);
4630
1.86M
    libsais_prefetchr(&T[SA[i - 2 * prefetch_distance - 1] & SAINT_MAX] - 2);
4631
4632
1.86M
    sa_sint_t p0 = SA[i - prefetch_distance - 0] & SAINT_MAX;
4633
1.86M
    sa_sint_t v0 = BUCKETS_INDEX4(T[p0 - (p0 > 0)], 0);
4634
1.86M
    libsais_prefetchw(&buckets[v0]);
4635
1.86M
    sa_sint_t p1 = SA[i - prefetch_distance - 1] & SAINT_MAX;
4636
1.86M
    sa_sint_t v1 = BUCKETS_INDEX4(T[p1 - (p1 > 0)], 0);
4637
1.86M
    libsais_prefetchw(&buckets[v1]);
4638
4639
1.86M
    sa_sint_t p2 = SA[i - 0];
4640
1.86M
    d += (p2 < 0);
4641
1.86M
    p2 &= SAINT_MAX;
4642
1.86M
    sa_sint_t v2 = BUCKETS_INDEX4(T[p2 - 1], T[p2 - 2] > T[p2 - 1]);
4643
1.86M
    SA[--buckets[v2]] = (p2 - 1) | (sa_sint_t)((sa_uint_t)(buckets[2 + v2] != d)
4644
1.86M
                                               << (SAINT_BIT - 1));
4645
1.86M
    buckets[2 + v2] = d;
4646
4647
1.86M
    sa_sint_t p3 = SA[i - 1];
4648
1.86M
    d += (p3 < 0);
4649
1.86M
    p3 &= SAINT_MAX;
4650
1.86M
    sa_sint_t v3 = BUCKETS_INDEX4(T[p3 - 1], T[p3 - 2] > T[p3 - 1]);
4651
1.86M
    SA[--buckets[v3]] = (p3 - 1) | (sa_sint_t)((sa_uint_t)(buckets[2 + v3] != d)
4652
1.86M
                                               << (SAINT_BIT - 1));
4653
1.86M
    buckets[2 + v3] = d;
4654
1.86M
  }
4655
4656
690k
  for (j -= 2 * prefetch_distance + 1; i >= j; i -= 1) {
4657
677k
    sa_sint_t p = SA[i];
4658
677k
    d += (p < 0);
4659
677k
    p &= SAINT_MAX;
4660
677k
    sa_sint_t v = BUCKETS_INDEX4(T[p - 1], T[p - 2] > T[p - 1]);
4661
677k
    SA[--buckets[v]] = (p - 1) | (sa_sint_t)((sa_uint_t)(buckets[2 + v] != d)
4662
677k
                                             << (SAINT_BIT - 1));
4663
677k
    buckets[2 + v] = d;
4664
677k
  }
4665
4666
13.1k
  return d;
4667
13.1k
}
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
356
    fast_sint_t omp_block_size) {
4673
356
  const fast_sint_t prefetch_distance = 64;
4674
4675
356
  sa_sint_t* RESTRICT induction_bucket = &buckets[3 * (fast_sint_t)k];
4676
356
  sa_sint_t* RESTRICT distinct_names = &buckets[0 * (fast_sint_t)k];
4677
4678
356
  fast_sint_t i, j;
4679
356
  for (i = omp_block_start + omp_block_size - 1,
4680
356
      j = omp_block_start + 2 * prefetch_distance + 1;
4681
259k
       i >= j; i -= 2) {
4682
259k
    libsais_prefetchw(&SA[i - 3 * prefetch_distance]);
4683
4684
259k
    sa_sint_t s0 = SA[i - 2 * prefetch_distance - 0];
4685
259k
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 & ~SUFFIX_GROUP_MARKER : 2];
4686
259k
    libsais_prefetchr(Ts0 - 1);
4687
259k
    libsais_prefetchr(Ts0 - 2);
4688
259k
    sa_sint_t s1 = SA[i - 2 * prefetch_distance - 1];
4689
259k
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 & ~SUFFIX_GROUP_MARKER : 2];
4690
259k
    libsais_prefetchr(Ts1 - 1);
4691
259k
    libsais_prefetchr(Ts1 - 2);
4692
259k
    sa_sint_t s2 = SA[i - 1 * prefetch_distance - 0];
4693
259k
    if (s2 > 0) {
4694
123k
      const fast_sint_t Ts2 = T[(s2 & ~SUFFIX_GROUP_MARKER) - 1];
4695
123k
      libsais_prefetchw(&induction_bucket[Ts2]);
4696
123k
      libsais_prefetchw(&distinct_names[BUCKETS_INDEX2(Ts2, 0)]);
4697
123k
    }
4698
259k
    sa_sint_t s3 = SA[i - 1 * prefetch_distance - 1];
4699
259k
    if (s3 > 0) {
4700
123k
      const fast_sint_t Ts3 = T[(s3 & ~SUFFIX_GROUP_MARKER) - 1];
4701
123k
      libsais_prefetchw(&induction_bucket[Ts3]);
4702
123k
      libsais_prefetchw(&distinct_names[BUCKETS_INDEX2(Ts3, 0)]);
4703
123k
    }
4704
4705
259k
    sa_sint_t p0 = SA[i - 0];
4706
259k
    if (p0 > 0) {
4707
132k
      SA[i - 0] = 0;
4708
132k
      d += (p0 >> (SUFFIX_GROUP_BIT - 1));
4709
132k
      p0 &= ~SUFFIX_GROUP_MARKER;
4710
132k
      sa_sint_t v0 = BUCKETS_INDEX2(T[p0 - 1], T[p0 - 2] > T[p0 - 1]);
4711
132k
      SA[--induction_bucket[T[p0 - 1]]] =
4712
132k
          (p0 - 1) |
4713
132k
          (sa_sint_t)((sa_uint_t)(T[p0 - 2] > T[p0 - 1]) << (SAINT_BIT - 1)) |
4714
132k
          ((sa_sint_t)(distinct_names[v0] != d) << (SUFFIX_GROUP_BIT - 1));
4715
132k
      distinct_names[v0] = d;
4716
132k
    }
4717
4718
259k
    sa_sint_t p1 = SA[i - 1];
4719
259k
    if (p1 > 0) {
4720
132k
      SA[i - 1] = 0;
4721
132k
      d += (p1 >> (SUFFIX_GROUP_BIT - 1));
4722
132k
      p1 &= ~SUFFIX_GROUP_MARKER;
4723
132k
      sa_sint_t v1 = BUCKETS_INDEX2(T[p1 - 1], T[p1 - 2] > T[p1 - 1]);
4724
132k
      SA[--induction_bucket[T[p1 - 1]]] =
4725
132k
          (p1 - 1) |
4726
132k
          (sa_sint_t)((sa_uint_t)(T[p1 - 2] > T[p1 - 1]) << (SAINT_BIT - 1)) |
4727
132k
          ((sa_sint_t)(distinct_names[v1] != d) << (SUFFIX_GROUP_BIT - 1));
4728
132k
      distinct_names[v1] = d;
4729
132k
    }
4730
259k
  }
4731
4732
46.1k
  for (j -= 2 * prefetch_distance + 1; i >= j; i -= 1) {
4733
45.7k
    sa_sint_t p = SA[i];
4734
45.7k
    if (p > 0) {
4735
7.94k
      SA[i] = 0;
4736
7.94k
      d += (p >> (SUFFIX_GROUP_BIT - 1));
4737
7.94k
      p &= ~SUFFIX_GROUP_MARKER;
4738
7.94k
      sa_sint_t v = BUCKETS_INDEX2(T[p - 1], T[p - 2] > T[p - 1]);
4739
7.94k
      SA[--induction_bucket[T[p - 1]]] =
4740
7.94k
          (p - 1) |
4741
7.94k
          (sa_sint_t)((sa_uint_t)(T[p - 2] > T[p - 1]) << (SAINT_BIT - 1)) |
4742
7.94k
          ((sa_sint_t)(distinct_names[v] != d) << (SUFFIX_GROUP_BIT - 1));
4743
7.94k
      distinct_names[v] = d;
4744
7.94k
    }
4745
45.7k
  }
4746
4747
356
  return d;
4748
356
}
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
802
    fast_sint_t omp_block_size) {
4754
802
  const fast_sint_t prefetch_distance = 64;
4755
4756
802
  fast_sint_t i, j;
4757
802
  for (i = omp_block_start + omp_block_size - 1,
4758
802
      j = omp_block_start + 2 * prefetch_distance + 1;
4759
738k
       i >= j; i -= 2) {
4760
737k
    libsais_prefetchw(&SA[i - 3 * prefetch_distance]);
4761
4762
737k
    sa_sint_t s0 = SA[i - 2 * prefetch_distance - 0];
4763
737k
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 : 1];
4764
737k
    libsais_prefetchr(Ts0 - 1);
4765
737k
    sa_sint_t s1 = SA[i - 2 * prefetch_distance - 1];
4766
737k
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 : 1];
4767
737k
    libsais_prefetchr(Ts1 - 1);
4768
737k
    sa_sint_t s2 = SA[i - 1 * prefetch_distance - 0];
4769
737k
    if (s2 > 0) {
4770
353k
      libsais_prefetchw(&induction_bucket[T[s2 - 1]]);
4771
353k
      libsais_prefetchr(&T[s2] - 2);
4772
353k
    }
4773
737k
    sa_sint_t s3 = SA[i - 1 * prefetch_distance - 1];
4774
737k
    if (s3 > 0) {
4775
353k
      libsais_prefetchw(&induction_bucket[T[s3 - 1]]);
4776
353k
      libsais_prefetchr(&T[s3] - 2);
4777
353k
    }
4778
4779
737k
    sa_sint_t p0 = SA[i - 0];
4780
737k
    if (p0 > 0) {
4781
378k
      SA[i - 0] = 0;
4782
378k
      SA[--induction_bucket[T[p0 - 1]]] =
4783
378k
          (p0 - 1) |
4784
378k
          (sa_sint_t)((sa_uint_t)(T[p0 - 2] > T[p0 - 1]) << (SAINT_BIT - 1));
4785
378k
    }
4786
737k
    sa_sint_t p1 = SA[i - 1];
4787
737k
    if (p1 > 0) {
4788
378k
      SA[i - 1] = 0;
4789
378k
      SA[--induction_bucket[T[p1 - 1]]] =
4790
378k
          (p1 - 1) |
4791
378k
          (sa_sint_t)((sa_uint_t)(T[p1 - 2] > T[p1 - 1]) << (SAINT_BIT - 1));
4792
378k
    }
4793
737k
  }
4794
4795
103k
  for (j -= 2 * prefetch_distance + 1; i >= j; i -= 1) {
4796
103k
    sa_sint_t p = SA[i];
4797
103k
    if (p > 0) {
4798
12.0k
      SA[i] = 0;
4799
12.0k
      SA[--induction_bucket[T[p - 1]]] =
4800
12.0k
          (p - 1) |
4801
12.0k
          (sa_sint_t)((sa_uint_t)(T[p - 2] > T[p - 1]) << (SAINT_BIT - 1));
4802
12.0k
    }
4803
103k
  }
4804
802
}
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.1k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5365
13.1k
  fast_sint_t scan_start = (fast_sint_t)left_suffixes_count + 1;
5366
13.1k
  fast_sint_t scan_end = (fast_sint_t)n - (fast_sint_t)first_lms_suffix;
5367
5368
13.1k
  if (threads == 1 || (scan_end - scan_start) < 65536) {
5369
13.1k
    d = libsais_partial_sorting_scan_right_to_left_32s_6k(
5370
13.1k
        T, SA, buckets, d, scan_start, scan_end - scan_start);
5371
13.1k
  }
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.1k
  UNUSED(thread_state);
5390
13.1k
#endif
5391
5392
13.1k
  return d;
5393
13.1k
}
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
356
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5399
356
  if (threads == 1 || n < 65536) {
5400
356
    d = libsais_partial_sorting_scan_right_to_left_32s_4k(T, SA, k, buckets, d,
5401
356
                                                          0, n);
5402
356
  }
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
356
  UNUSED(thread_state);
5421
356
#endif
5422
5423
356
  return d;
5424
356
}
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
802
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5430
802
  if (threads == 1 || n < 65536) {
5431
802
    libsais_partial_sorting_scan_right_to_left_32s_1k(T, SA, buckets, 0, n);
5432
802
  }
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
802
  UNUSED(thread_state);
5451
802
#endif
5452
802
}
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
356
    fast_sint_t omp_block_size) {
5457
356
  const fast_sint_t prefetch_distance = 64;
5458
5459
356
  fast_sint_t i, j, l;
5460
356
  for (i = omp_block_start, j = omp_block_start + omp_block_size - 3,
5461
356
      l = omp_block_start;
5462
141k
       i < j; i += 4) {
5463
140k
    libsais_prefetchr(&SA[i + prefetch_distance]);
5464
5465
140k
    sa_uint_t s0 = (sa_uint_t)SA[i + 0];
5466
140k
    SA[l] = (sa_sint_t)((s0 - (sa_uint_t)SUFFIX_GROUP_MARKER) &
5467
140k
                        (sa_uint_t)(~SUFFIX_GROUP_MARKER));
5468
140k
    l += ((sa_sint_t)s0 < 0);
5469
140k
    sa_uint_t s1 = (sa_uint_t)SA[i + 1];
5470
140k
    SA[l] = (sa_sint_t)((s1 - (sa_uint_t)SUFFIX_GROUP_MARKER) &
5471
140k
                        (sa_uint_t)(~SUFFIX_GROUP_MARKER));
5472
140k
    l += ((sa_sint_t)s1 < 0);
5473
140k
    sa_uint_t s2 = (sa_uint_t)SA[i + 2];
5474
140k
    SA[l] = (sa_sint_t)((s2 - (sa_uint_t)SUFFIX_GROUP_MARKER) &
5475
140k
                        (sa_uint_t)(~SUFFIX_GROUP_MARKER));
5476
140k
    l += ((sa_sint_t)s2 < 0);
5477
140k
    sa_uint_t s3 = (sa_uint_t)SA[i + 3];
5478
140k
    SA[l] = (sa_sint_t)((s3 - (sa_uint_t)SUFFIX_GROUP_MARKER) &
5479
140k
                        (sa_uint_t)(~SUFFIX_GROUP_MARKER));
5480
140k
    l += ((sa_sint_t)s3 < 0);
5481
140k
  }
5482
5483
877
  for (j += 3; i < j; i += 1) {
5484
521
    sa_uint_t s = (sa_uint_t)SA[i];
5485
521
    SA[l] = (sa_sint_t)((s - (sa_uint_t)SUFFIX_GROUP_MARKER) &
5486
521
                        (sa_uint_t)(~SUFFIX_GROUP_MARKER));
5487
521
    l += ((sa_sint_t)s < 0);
5488
521
  }
5489
5490
356
  return l;
5491
356
}
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
802
    fast_sint_t omp_block_size) {
5496
802
  const fast_sint_t prefetch_distance = 64;
5497
5498
802
  fast_sint_t i, j, l;
5499
802
  for (i = omp_block_start, j = omp_block_start + omp_block_size - 3,
5500
802
      l = omp_block_start;
5501
395k
       i < j; i += 4) {
5502
394k
    libsais_prefetchr(&SA[i + prefetch_distance]);
5503
5504
394k
    sa_sint_t s0 = SA[i + 0];
5505
394k
    SA[l] = s0 & SAINT_MAX;
5506
394k
    l += (s0 < 0);
5507
394k
    sa_sint_t s1 = SA[i + 1];
5508
394k
    SA[l] = s1 & SAINT_MAX;
5509
394k
    l += (s1 < 0);
5510
394k
    sa_sint_t s2 = SA[i + 2];
5511
394k
    SA[l] = s2 & SAINT_MAX;
5512
394k
    l += (s2 < 0);
5513
394k
    sa_sint_t s3 = SA[i + 3];
5514
394k
    SA[l] = s3 & SAINT_MAX;
5515
394k
    l += (s3 < 0);
5516
394k
  }
5517
5518
1.92k
  for (j += 3; i < j; i += 1) {
5519
1.12k
    sa_sint_t s = SA[i];
5520
1.12k
    SA[l] = s & SAINT_MAX;
5521
1.12k
    l += (s < 0);
5522
1.12k
  }
5523
5524
802
  return l;
5525
802
}
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
356
    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
356
  {
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
356
    UNUSED(threads);
5539
356
    UNUSED(thread_state);
5540
5541
356
    fast_sint_t omp_thread_num = 0;
5542
356
    fast_sint_t omp_num_threads = 1;
5543
356
#endif
5544
356
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
5545
356
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
5546
356
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
5547
356
                                     ? omp_block_stride
5548
356
                                     : n - omp_block_start;
5549
5550
356
    if (omp_num_threads == 1) {
5551
356
      libsais_partial_sorting_gather_lms_suffixes_32s_4k(SA, omp_block_start,
5552
356
                                                         omp_block_size);
5553
356
    }
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
356
  }
5581
356
}
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
802
    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
802
  {
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
802
    UNUSED(threads);
5595
802
    UNUSED(thread_state);
5596
5597
802
    fast_sint_t omp_thread_num = 0;
5598
802
    fast_sint_t omp_num_threads = 1;
5599
802
#endif
5600
802
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
5601
802
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
5602
802
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
5603
802
                                     ? omp_block_stride
5604
802
                                     : n - omp_block_start;
5605
5606
802
    if (omp_num_threads == 1) {
5607
802
      libsais_partial_sorting_gather_lms_suffixes_32s_1k(SA, omp_block_start,
5608
802
                                                         omp_block_size);
5609
802
    }
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
802
  }
5637
802
}
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.05k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5644
8.05k
  memset(&buckets[2 * ALPHABET_SIZE], 0,
5645
8.05k
         (size_t)2 * ALPHABET_SIZE * sizeof(sa_sint_t));
5646
5647
8.05k
  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.05k
  sa_sint_t d = libsais_partial_sorting_scan_left_to_right_8u_omp(
5655
8.05k
      T, SA, n, k, buckets, left_suffixes_count, 0, threads, thread_state);
5656
8.05k
  libsais_partial_sorting_shift_markers_8u_omp(SA, n, buckets, threads);
5657
5658
8.05k
  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.05k
  } else {
5671
8.05k
    libsais_partial_sorting_scan_right_to_left_8u_omp(
5672
8.05k
        T, SA, n, k, buckets, first_lms_suffix, left_suffixes_count, d, threads,
5673
8.05k
        thread_state);
5674
8.05k
  }
5675
8.05k
}
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.1k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5682
13.1k
  sa_sint_t d = libsais_partial_sorting_scan_left_to_right_32s_6k_omp(
5683
13.1k
      T, SA, n, buckets, left_suffixes_count, 0, threads, thread_state);
5684
13.1k
  libsais_partial_sorting_shift_markers_32s_6k_omp(SA, k, buckets, threads);
5685
13.1k
  libsais_partial_sorting_shift_buckets_32s_6k(k, buckets);
5686
13.1k
  libsais_partial_sorting_scan_right_to_left_32s_6k_omp(
5687
13.1k
      T, SA, n, buckets, first_lms_suffix, left_suffixes_count, d, threads,
5688
13.1k
      thread_state);
5689
13.1k
}
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
356
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5695
356
  memset(buckets, 0, 2 * (size_t)k * sizeof(sa_sint_t));
5696
5697
356
  sa_sint_t d = libsais_partial_sorting_scan_left_to_right_32s_4k_omp(
5698
356
      T, SA, n, k, buckets, 0, threads, thread_state);
5699
356
  libsais_partial_sorting_shift_markers_32s_4k(SA, n);
5700
356
  libsais_partial_sorting_scan_right_to_left_32s_4k_omp(T, SA, n, k, buckets, d,
5701
356
                                                        threads, thread_state);
5702
356
  libsais_partial_sorting_gather_lms_suffixes_32s_4k_omp(SA, n, threads,
5703
356
                                                         thread_state);
5704
356
}
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
632
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5710
632
  libsais_partial_sorting_scan_left_to_right_32s_1k_omp(
5711
632
      T, SA, n, &buckets[1 * (fast_sint_t)k], threads, thread_state);
5712
632
  libsais_partial_sorting_scan_right_to_left_32s_1k_omp(
5713
632
      T, SA, n, &buckets[0 * (fast_sint_t)k], threads, thread_state);
5714
632
  libsais_partial_sorting_gather_lms_suffixes_32s_1k_omp(SA, n, threads,
5715
632
                                                         thread_state);
5716
632
}
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
170
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5722
170
  libsais_count_suffixes_32s(T, n, k, buckets);
5723
170
  libsais_initialize_buckets_start_32s_1k(k, buckets);
5724
170
  libsais_partial_sorting_scan_left_to_right_32s_1k_omp(T, SA, n, buckets,
5725
170
                                                        threads, thread_state);
5726
5727
170
  libsais_count_suffixes_32s(T, n, k, buckets);
5728
170
  libsais_initialize_buckets_end_32s_1k(k, buckets);
5729
170
  libsais_partial_sorting_scan_right_to_left_32s_1k_omp(T, SA, n, buckets,
5730
170
                                                        threads, thread_state);
5731
5732
170
  libsais_partial_sorting_gather_lms_suffixes_32s_1k_omp(SA, n, threads,
5733
170
                                                         thread_state);
5734
170
}
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.05k
                                                  fast_sint_t omp_block_size) {
5740
8.05k
  const fast_sint_t prefetch_distance = 64;
5741
5742
8.05k
  sa_sint_t* RESTRICT SAm = &SA[m];
5743
5744
8.05k
  fast_sint_t i, j;
5745
8.05k
  for (i = omp_block_start,
5746
8.05k
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
5747
1.77M
       i < j; i += 4) {
5748
1.76M
    libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
5749
5750
1.76M
    libsais_prefetchw(&SAm[(SA[i + prefetch_distance + 0] & SAINT_MAX) >> 1]);
5751
1.76M
    libsais_prefetchw(&SAm[(SA[i + prefetch_distance + 1] & SAINT_MAX) >> 1]);
5752
1.76M
    libsais_prefetchw(&SAm[(SA[i + prefetch_distance + 2] & SAINT_MAX) >> 1]);
5753
1.76M
    libsais_prefetchw(&SAm[(SA[i + prefetch_distance + 3] & SAINT_MAX) >> 1]);
5754
5755
1.76M
    sa_sint_t p0 = SA[i + 0];
5756
1.76M
    SAm[(p0 & SAINT_MAX) >> 1] = name | SAINT_MIN;
5757
1.76M
    name += p0 < 0;
5758
1.76M
    sa_sint_t p1 = SA[i + 1];
5759
1.76M
    SAm[(p1 & SAINT_MAX) >> 1] = name | SAINT_MIN;
5760
1.76M
    name += p1 < 0;
5761
1.76M
    sa_sint_t p2 = SA[i + 2];
5762
1.76M
    SAm[(p2 & SAINT_MAX) >> 1] = name | SAINT_MIN;
5763
1.76M
    name += p2 < 0;
5764
1.76M
    sa_sint_t p3 = SA[i + 3];
5765
1.76M
    SAm[(p3 & SAINT_MAX) >> 1] = name | SAINT_MIN;
5766
1.76M
    name += p3 < 0;
5767
1.76M
  }
5768
5769
375k
  for (j += prefetch_distance + 3; i < j; i += 1) {
5770
367k
    sa_sint_t p = SA[i];
5771
367k
    SAm[(p & SAINT_MAX) >> 1] = name | SAINT_MIN;
5772
367k
    name += p < 0;
5773
367k
  }
5774
5775
8.05k
  return name;
5776
8.05k
}
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.10k
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
5781
7.10k
  const fast_sint_t prefetch_distance = 64;
5782
5783
7.10k
  l -= 1;
5784
5785
7.10k
  fast_sint_t i, j;
5786
7.10k
  for (i = (fast_sint_t)m + omp_block_start + omp_block_size - 1,
5787
7.10k
      j = (fast_sint_t)m + omp_block_start + 3;
5788
2.49M
       i >= j; i -= 4) {
5789
2.49M
    libsais_prefetchr(&SA[i - prefetch_distance]);
5790
5791
2.49M
    sa_sint_t s0 = SA[i - 0];
5792
2.49M
    SA[l] = s0 & SAINT_MAX;
5793
2.49M
    l -= s0 < 0;
5794
2.49M
    sa_sint_t s1 = SA[i - 1];
5795
2.49M
    SA[l] = s1 & SAINT_MAX;
5796
2.49M
    l -= s1 < 0;
5797
2.49M
    sa_sint_t s2 = SA[i - 2];
5798
2.49M
    SA[l] = s2 & SAINT_MAX;
5799
2.49M
    l -= s2 < 0;
5800
2.49M
    sa_sint_t s3 = SA[i - 3];
5801
2.49M
    SA[l] = s3 & SAINT_MAX;
5802
2.49M
    l -= s3 < 0;
5803
2.49M
  }
5804
5805
17.9k
  for (j -= 3; i >= j; i -= 1) {
5806
10.8k
    sa_sint_t s = SA[i];
5807
10.8k
    SA[l] = s & SAINT_MAX;
5808
10.8k
    l -= s < 0;
5809
10.8k
  }
5810
5811
7.10k
  l += 1;
5812
5813
7.10k
  return l;
5814
7.10k
}
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.05k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5819
8.05k
  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.05k
  {
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.05k
    UNUSED(threads);
5830
8.05k
    UNUSED(thread_state);
5831
5832
8.05k
    fast_sint_t omp_thread_num = 0;
5833
8.05k
    fast_sint_t omp_num_threads = 1;
5834
8.05k
#endif
5835
8.05k
    fast_sint_t omp_block_stride = (m / omp_num_threads) & (-16);
5836
8.05k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
5837
8.05k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
5838
8.05k
                                     ? omp_block_stride
5839
8.05k
                                     : m - omp_block_start;
5840
5841
8.05k
    if (omp_num_threads == 1) {
5842
8.05k
      name = libsais_renumber_lms_suffixes_8u(SA, m, 0, omp_block_start,
5843
8.05k
                                              omp_block_size);
5844
8.05k
    }
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.05k
  }
5871
5872
8.05k
  return name;
5873
8.05k
}
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.10k
    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.10k
  {
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.10k
    UNUSED(threads);
5887
7.10k
    UNUSED(thread_state);
5888
5889
7.10k
    fast_sint_t omp_thread_num = 0;
5890
7.10k
    fast_sint_t omp_num_threads = 1;
5891
7.10k
#endif
5892
7.10k
    fast_sint_t omp_block_stride =
5893
7.10k
        (((fast_sint_t)n >> 1) / omp_num_threads) & (-16);
5894
7.10k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
5895
7.10k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
5896
7.10k
                                     ? omp_block_stride
5897
7.10k
                                     : ((fast_sint_t)n >> 1) - omp_block_start;
5898
5899
7.10k
    if (omp_num_threads == 1) {
5900
7.10k
      libsais_gather_marked_lms_suffixes(SA, m,
5901
7.10k
                                         (fast_sint_t)n + (fast_sint_t)fs,
5902
7.10k
                                         omp_block_start, omp_block_size);
5903
7.10k
    }
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.10k
  }
5943
7.10k
}
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.05k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
5948
8.05k
  memset(&SA[m], 0, ((size_t)n >> 1) * sizeof(sa_sint_t));
5949
5950
8.05k
  sa_sint_t name =
5951
8.05k
      libsais_renumber_lms_suffixes_8u_omp(SA, m, threads, thread_state);
5952
8.05k
  if (name < m) {
5953
7.10k
    libsais_gather_marked_lms_suffixes_omp(SA, n, m, fs, threads, thread_state);
5954
7.10k
  } else {
5955
948
    fast_sint_t i;
5956
8.19k
    for (i = 0; i < m; i += 1) {
5957
7.24k
      SA[i] &= SAINT_MAX;
5958
7.24k
    }
5959
948
  }
5960
5961
8.05k
  return name;
5962
8.05k
}
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
13.4k
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
5967
13.4k
  const fast_sint_t prefetch_distance = 64;
5968
5969
13.4k
  sa_sint_t* RESTRICT SAm = &SA[m];
5970
5971
13.4k
  fast_sint_t i, j;
5972
13.4k
  sa_sint_t p0, p1, p2, p3 = 0;
5973
13.4k
  for (i = omp_block_start,
5974
13.4k
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
5975
688k
       i < j; i += 4) {
5976
675k
    libsais_prefetchw(&SA[i + 2 * prefetch_distance]);
5977
5978
675k
    libsais_prefetchw(&SAm[(SA[i + prefetch_distance + 0] & SAINT_MAX) >> 1]);
5979
675k
    libsais_prefetchw(&SAm[(SA[i + prefetch_distance + 1] & SAINT_MAX) >> 1]);
5980
675k
    libsais_prefetchw(&SAm[(SA[i + prefetch_distance + 2] & SAINT_MAX) >> 1]);
5981
675k
    libsais_prefetchw(&SAm[(SA[i + prefetch_distance + 3] & SAINT_MAX) >> 1]);
5982
5983
675k
    p0 = SA[i + 0];
5984
675k
    SAm[(SA[i + 0] = p0 & SAINT_MAX) >> 1] = name | (p0 & p3 & SAINT_MIN);
5985
675k
    name += p0 < 0;
5986
675k
    p1 = SA[i + 1];
5987
675k
    SAm[(SA[i + 1] = p1 & SAINT_MAX) >> 1] = name | (p1 & p0 & SAINT_MIN);
5988
675k
    name += p1 < 0;
5989
675k
    p2 = SA[i + 2];
5990
675k
    SAm[(SA[i + 2] = p2 & SAINT_MAX) >> 1] = name | (p2 & p1 & SAINT_MIN);
5991
675k
    name += p2 < 0;
5992
675k
    p3 = SA[i + 3];
5993
675k
    SAm[(SA[i + 3] = p3 & SAINT_MAX) >> 1] = name | (p3 & p2 & SAINT_MIN);
5994
675k
    name += p3 < 0;
5995
675k
  }
5996
5997
415k
  for (j += prefetch_distance + 3; i < j; i += 1) {
5998
401k
    p2 = p3;
5999
401k
    p3 = SA[i];
6000
401k
    SAm[(SA[i] = p3 & SAINT_MAX) >> 1] = name | (p3 & p2 & SAINT_MIN);
6001
401k
    name += p3 < 0;
6002
401k
  }
6003
6004
13.4k
  return name;
6005
13.4k
}
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
8.97k
                                                   fast_sint_t omp_block_size) {
6011
8.97k
  const fast_sint_t prefetch_distance = 64;
6012
6013
8.97k
  fast_sint_t i, j;
6014
8.97k
  sa_sint_t p0, p1, p2, p3 = 0;
6015
8.97k
  for (i = (fast_sint_t)m + omp_block_start,
6016
8.97k
      j = (fast_sint_t)m + omp_block_start + omp_block_size - 3;
6017
1.25M
       i < j; i += 4) {
6018
1.24M
    libsais_prefetchw(&SA[i + prefetch_distance]);
6019
6020
1.24M
    p0 = SA[i + 0];
6021
1.24M
    SA[i + 0] = p0 & (p3 | SAINT_MAX);
6022
1.24M
    p0 = (p0 == 0) ? p3 : p0;
6023
1.24M
    p1 = SA[i + 1];
6024
1.24M
    SA[i + 1] = p1 & (p0 | SAINT_MAX);
6025
1.24M
    p1 = (p1 == 0) ? p0 : p1;
6026
1.24M
    p2 = SA[i + 2];
6027
1.24M
    SA[i + 2] = p2 & (p1 | SAINT_MAX);
6028
1.24M
    p2 = (p2 == 0) ? p1 : p2;
6029
1.24M
    p3 = SA[i + 3];
6030
1.24M
    SA[i + 3] = p3 & (p2 | SAINT_MAX);
6031
1.24M
    p3 = (p3 == 0) ? p2 : p3;
6032
1.24M
  }
6033
6034
22.6k
  for (j += 3; i < j; i += 1) {
6035
13.7k
    p2 = p3;
6036
13.7k
    p3 = SA[i];
6037
13.7k
    SA[i] = p3 & (p2 | SAINT_MAX);
6038
13.7k
    p3 = (p3 == 0) ? p2 : p3;
6039
13.7k
  }
6040
8.97k
}
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
802
                                                  fast_sint_t omp_block_size) {
6046
802
  const fast_sint_t prefetch_distance = 64;
6047
6048
802
  sa_sint_t* RESTRICT SAm = &SA[m];
6049
6050
802
  fast_sint_t i, j;
6051
197k
  for (i = omp_block_start, j = omp_block_start + omp_block_size - 3; i < j;
6052
196k
       i += 4) {
6053
196k
    libsais_prefetchw(&SAm[i + prefetch_distance]);
6054
6055
196k
    SAm[i + 0] = (SAm[i + 0] < 0 ? SAm[i + 0] : 0) & SAINT_MAX;
6056
196k
    SAm[i + 1] = (SAm[i + 1] < 0 ? SAm[i + 1] : 0) & SAINT_MAX;
6057
196k
    SAm[i + 2] = (SAm[i + 2] < 0 ? SAm[i + 2] : 0) & SAINT_MAX;
6058
196k
    SAm[i + 3] = (SAm[i + 3] < 0 ? SAm[i + 3] : 0) & SAINT_MAX;
6059
196k
  }
6060
6061
1.93k
  for (j += 3; i < j; i += 1) {
6062
1.12k
    SAm[i] = (SAm[i] < 0 ? SAm[i] : 0) & SAINT_MAX;
6063
1.12k
  }
6064
802
}
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
13.4k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
6069
13.4k
  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
13.4k
  {
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
13.4k
    UNUSED(threads);
6080
13.4k
    UNUSED(thread_state);
6081
6082
13.4k
    fast_sint_t omp_thread_num = 0;
6083
13.4k
    fast_sint_t omp_num_threads = 1;
6084
13.4k
#endif
6085
13.4k
    fast_sint_t omp_block_stride = (m / omp_num_threads) & (-16);
6086
13.4k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
6087
13.4k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
6088
13.4k
                                     ? omp_block_stride
6089
13.4k
                                     : m - omp_block_start;
6090
6091
13.4k
    if (omp_num_threads == 1) {
6092
13.4k
      name = libsais_renumber_distinct_lms_suffixes_32s_4k(
6093
13.4k
          SA, m, 1, omp_block_start, omp_block_size);
6094
13.4k
    }
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
13.4k
  }
6121
6122
13.4k
  return name - 1;
6123
13.4k
}
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
8.97k
                                                       sa_sint_t threads) {
6128
#if defined(LIBSAIS_OPENMP)
6129
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 131072)
6130
#endif
6131
8.97k
  {
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
8.97k
    UNUSED(threads);
6143
6144
8.97k
    fast_sint_t omp_block_start = 0;
6145
8.97k
    fast_sint_t omp_block_size = (fast_sint_t)n >> 1;
6146
8.97k
#endif
6147
8.97k
    libsais_mark_distinct_lms_suffixes_32s(SA, m, omp_block_start,
6148
8.97k
                                           omp_block_size);
6149
8.97k
  }
6150
8.97k
}
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
802
                                                      sa_sint_t threads) {
6155
#if defined(LIBSAIS_OPENMP)
6156
#pragma omp parallel num_threads(threads) if (threads > 1 && n >= 131072)
6157
#endif
6158
802
  {
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
802
    UNUSED(threads);
6170
6171
802
    fast_sint_t omp_block_start = 0;
6172
802
    fast_sint_t omp_block_size = (fast_sint_t)n >> 1;
6173
802
#endif
6174
802
    libsais_clamp_lms_suffixes_length_32s(SA, m, omp_block_start,
6175
802
                                          omp_block_size);
6176
802
  }
6177
802
}
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
13.4k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
6182
13.4k
  memset(&SA[m], 0, ((size_t)n >> 1) * sizeof(sa_sint_t));
6183
6184
13.4k
  sa_sint_t name = libsais_renumber_distinct_lms_suffixes_32s_4k_omp(
6185
13.4k
      SA, m, threads, thread_state);
6186
13.4k
  if (name < m) {
6187
8.29k
    libsais_mark_distinct_lms_suffixes_32s_omp(SA, n, m, threads);
6188
8.29k
  }
6189
6190
13.4k
  return name;
6191
13.4k
}
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
802
    sa_sint_t threads) {
6196
802
  const fast_sint_t prefetch_distance = 64;
6197
6198
802
  sa_sint_t* RESTRICT SAm = &SA[m];
6199
6200
802
  {
6201
802
    libsais_gather_lms_suffixes_32s(T, SA, n);
6202
6203
802
    memset(&SA[m], 0, ((size_t)n - (size_t)m - (size_t)m) * sizeof(sa_sint_t));
6204
6205
802
    fast_sint_t i, j;
6206
802
    for (i = (fast_sint_t)n - (fast_sint_t)m,
6207
802
        j = (fast_sint_t)n - 1 - prefetch_distance - 3;
6208
116k
         i < j; i += 4) {
6209
115k
      libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
6210
6211
115k
      libsais_prefetchw(&SAm[((sa_uint_t)SA[i + prefetch_distance + 0]) >> 1]);
6212
115k
      libsais_prefetchw(&SAm[((sa_uint_t)SA[i + prefetch_distance + 1]) >> 1]);
6213
115k
      libsais_prefetchw(&SAm[((sa_uint_t)SA[i + prefetch_distance + 2]) >> 1]);
6214
115k
      libsais_prefetchw(&SAm[((sa_uint_t)SA[i + prefetch_distance + 3]) >> 1]);
6215
6216
115k
      SAm[((sa_uint_t)SA[i + 0]) >> 1] = SA[i + 1] - SA[i + 0] + 1 + SAINT_MIN;
6217
115k
      SAm[((sa_uint_t)SA[i + 1]) >> 1] = SA[i + 2] - SA[i + 1] + 1 + SAINT_MIN;
6218
115k
      SAm[((sa_uint_t)SA[i + 2]) >> 1] = SA[i + 3] - SA[i + 2] + 1 + SAINT_MIN;
6219
115k
      SAm[((sa_uint_t)SA[i + 3]) >> 1] = SA[i + 4] - SA[i + 3] + 1 + SAINT_MIN;
6220
115k
    }
6221
6222
53.3k
    for (j += prefetch_distance + 3; i < j; i += 1) {
6223
52.5k
      SAm[((sa_uint_t)SA[i]) >> 1] = SA[i + 1] - SA[i] + 1 + SAINT_MIN;
6224
52.5k
    }
6225
6226
802
    SAm[((sa_uint_t)SA[n - 1]) >> 1] = 1 + SAINT_MIN;
6227
802
  }
6228
6229
802
  {
6230
802
    libsais_clamp_lms_suffixes_length_32s_omp(SA, n, m, threads);
6231
802
  }
6232
6233
802
  sa_sint_t name = 1;
6234
6235
802
  {
6236
802
    fast_sint_t i, j, p = SA[0], plen = SAm[p >> 1];
6237
802
    sa_sint_t pdiff = SAINT_MIN;
6238
232k
    for (i = 1, j = m - prefetch_distance - 1; i < j; i += 2) {
6239
231k
      libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
6240
6241
231k
      libsais_prefetchw(&SAm[((sa_uint_t)SA[i + prefetch_distance + 0]) >> 1]);
6242
231k
      libsais_prefetchr(&T[((sa_uint_t)SA[i + prefetch_distance + 0])]);
6243
231k
      libsais_prefetchw(&SAm[((sa_uint_t)SA[i + prefetch_distance + 1]) >> 1]);
6244
231k
      libsais_prefetchr(&T[((sa_uint_t)SA[i + prefetch_distance + 1])]);
6245
6246
231k
      fast_sint_t q = SA[i + 0], qlen = SAm[q >> 1];
6247
231k
      sa_sint_t qdiff = SAINT_MIN;
6248
231k
      if (plen == qlen) {
6249
129k
        fast_sint_t l = 0;
6250
372k
        do {
6251
372k
          if (T[p + l] != T[q + l]) {
6252
41.1k
            break;
6253
41.1k
          }
6254
372k
        } while (++l < qlen);
6255
129k
        qdiff = (sa_sint_t)(l - qlen) & SAINT_MIN;
6256
129k
      }
6257
231k
      SAm[p >> 1] = name | (pdiff & qdiff);
6258
231k
      name += (qdiff < 0);
6259
6260
231k
      p = SA[i + 1];
6261
231k
      plen = SAm[p >> 1];
6262
231k
      pdiff = SAINT_MIN;
6263
231k
      if (qlen == plen) {
6264
129k
        fast_sint_t l = 0;
6265
371k
        do {
6266
371k
          if (T[q + l] != T[p + l]) {
6267
41.4k
            break;
6268
41.4k
          }
6269
371k
        } while (++l < plen);
6270
129k
        pdiff = (sa_sint_t)(l - plen) & SAINT_MIN;
6271
129k
      }
6272
231k
      SAm[q >> 1] = name | (qdiff & pdiff);
6273
231k
      name += (pdiff < 0);
6274
231k
    }
6275
6276
52.5k
    for (j += prefetch_distance + 1; i < j; i += 1) {
6277
51.7k
      fast_sint_t q = SA[i], qlen = SAm[q >> 1];
6278
51.7k
      sa_sint_t qdiff = SAINT_MIN;
6279
51.7k
      if (plen == qlen) {
6280
32.0k
        fast_sint_t l = 0;
6281
100k
        do {
6282
100k
          if (T[p + l] != T[q + l]) {
6283
9.09k
            break;
6284
9.09k
          }
6285
100k
        } while (++l < plen);
6286
32.0k
        qdiff = (sa_sint_t)(l - plen) & SAINT_MIN;
6287
32.0k
      }
6288
51.7k
      SAm[p >> 1] = name | (pdiff & qdiff);
6289
51.7k
      name += (qdiff < 0);
6290
6291
51.7k
      p = q;
6292
51.7k
      plen = qlen;
6293
51.7k
      pdiff = qdiff;
6294
51.7k
    }
6295
6296
802
    SAm[p >> 1] = name | pdiff;
6297
802
    name++;
6298
802
  }
6299
6300
802
  if (name <= m) {
6301
682
    libsais_mark_distinct_lms_suffixes_32s_omp(SA, n, m, threads);
6302
682
  }
6303
6304
802
  return name - 1;
6305
802
}
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
16.0k
                                             fast_sint_t omp_block_size) {
6311
16.0k
  const fast_sint_t prefetch_distance = 64;
6312
6313
16.0k
  const sa_sint_t* RESTRICT SAnm = &SA[n - m];
6314
6315
16.0k
  fast_sint_t i, j;
6316
16.0k
  for (i = omp_block_start,
6317
16.0k
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
6318
2.45M
       i < j; i += 4) {
6319
2.44M
    libsais_prefetchw(&SA[i + 2 * prefetch_distance]);
6320
6321
2.44M
    libsais_prefetchr(&SAnm[SA[i + prefetch_distance + 0]]);
6322
2.44M
    libsais_prefetchr(&SAnm[SA[i + prefetch_distance + 1]]);
6323
2.44M
    libsais_prefetchr(&SAnm[SA[i + prefetch_distance + 2]]);
6324
2.44M
    libsais_prefetchr(&SAnm[SA[i + prefetch_distance + 3]]);
6325
6326
2.44M
    SA[i + 0] = SAnm[SA[i + 0]];
6327
2.44M
    SA[i + 1] = SAnm[SA[i + 1]];
6328
2.44M
    SA[i + 2] = SAnm[SA[i + 2]];
6329
2.44M
    SA[i + 3] = SAnm[SA[i + 3]];
6330
2.44M
  }
6331
6332
700k
  for (j += prefetch_distance + 3; i < j; i += 1) {
6333
684k
    SA[i] = SAnm[SA[i]];
6334
684k
  }
6335
16.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
16.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
16.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
16.0k
    UNUSED(threads);
6354
6355
16.0k
    fast_sint_t omp_block_start = 0;
6356
16.0k
    fast_sint_t omp_block_size = m;
6357
16.0k
#endif
6358
6359
16.0k
    libsais_reconstruct_lms_suffixes(SA, n, m, omp_block_start, omp_block_size);
6360
16.0k
  }
6361
16.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.05k
    sa_sint_t* RESTRICT buckets) {
6366
8.05k
  if (flags & LIBSAIS_FLAGS_GSA) {
6367
0
    buckets[7 * ALPHABET_SIZE]--;
6368
0
  }
6369
6370
8.05k
  {
6371
8.05k
    const sa_sint_t* RESTRICT bucket_end = &buckets[7 * ALPHABET_SIZE];
6372
6373
8.05k
    fast_sint_t c, j = n;
6374
2.06M
    for (c = ALPHABET_SIZE - 2; c >= 0; --c) {
6375
2.05M
      fast_sint_t l =
6376
2.05M
          (fast_sint_t)buckets[BUCKETS_INDEX2(c, 1) + BUCKETS_INDEX2(1, 0)] -
6377
2.05M
          (fast_sint_t)buckets[BUCKETS_INDEX2(c, 1)];
6378
2.05M
      if (l > 0) {
6379
165k
        fast_sint_t i = bucket_end[c];
6380
165k
        if (j - i > 0) {
6381
100k
          memset(&SA[i], 0, (size_t)(j - i) * sizeof(sa_sint_t));
6382
100k
        }
6383
6384
165k
        memmove(&SA[j = (i - l)], &SA[m -= (sa_sint_t)l],
6385
165k
                (size_t)l * sizeof(sa_sint_t));
6386
165k
      }
6387
2.05M
    }
6388
6389
8.05k
    memset(&SA[0], 0, (size_t)j * sizeof(sa_sint_t));
6390
8.05k
  }
6391
6392
8.05k
  if (flags & LIBSAIS_FLAGS_GSA) {
6393
0
    buckets[7 * ALPHABET_SIZE]++;
6394
0
  }
6395
8.05k
}
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
356
    const sa_sint_t* RESTRICT buckets) {
6400
356
  const sa_sint_t* RESTRICT bucket_end = &buckets[3 * (fast_sint_t)k];
6401
6402
356
  fast_sint_t c, j = n;
6403
167k
  for (c = (fast_sint_t)k - 2; c >= 0; --c) {
6404
167k
    fast_sint_t l =
6405
167k
        (fast_sint_t)buckets[BUCKETS_INDEX2(c, 1) + BUCKETS_INDEX2(1, 0)] -
6406
167k
        (fast_sint_t)buckets[BUCKETS_INDEX2(c, 1)];
6407
167k
    if (l > 0) {
6408
61.0k
      fast_sint_t i = bucket_end[c];
6409
61.0k
      if (j - i > 0) {
6410
28.0k
        memset(&SA[i], 0, (size_t)(j - i) * sizeof(sa_sint_t));
6411
28.0k
      }
6412
6413
61.0k
      memmove(&SA[j = (i - l)], &SA[m -= (sa_sint_t)l],
6414
61.0k
              (size_t)l * sizeof(sa_sint_t));
6415
61.0k
    }
6416
167k
  }
6417
6418
356
  memset(&SA[0], 0, (size_t)j * sizeof(sa_sint_t));
6419
356
}
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
632
    const sa_sint_t* RESTRICT buckets) {
6424
632
  fast_sint_t j = n;
6425
6426
632
  if (k > 1) {
6427
632
    fast_sint_t c;
6428
519k
    for (c = BUCKETS_INDEX2((fast_sint_t)k - 2, 0); c >= BUCKETS_INDEX2(0, 0);
6429
519k
         c -= BUCKETS_INDEX2(1, 0)) {
6430
519k
      fast_sint_t l = (fast_sint_t)buckets[c + BUCKETS_INDEX2(1, 1)] -
6431
519k
                      (fast_sint_t)buckets[c + BUCKETS_INDEX2(0, 1)];
6432
519k
      if (l > 0) {
6433
187k
        fast_sint_t i = buckets[c];
6434
187k
        if (j - i > 0) {
6435
86.2k
          memset(&SA[i], 0, (size_t)(j - i) * sizeof(sa_sint_t));
6436
86.2k
        }
6437
6438
187k
        memmove(&SA[j = (i - l)], &SA[m -= (sa_sint_t)l],
6439
187k
                (size_t)l * sizeof(sa_sint_t));
6440
187k
      }
6441
519k
    }
6442
632
  }
6443
6444
632
  memset(&SA[0], 0, (size_t)j * sizeof(sa_sint_t));
6445
632
}
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
170
    sa_sint_t m, sa_sint_t* RESTRICT buckets) {
6450
170
  const fast_sint_t prefetch_distance = 64;
6451
6452
170
  sa_sint_t c = k - 1;
6453
170
  fast_sint_t i, l = buckets[c];
6454
23.6k
  for (i = (fast_sint_t)m - 1; i >= prefetch_distance + 3; i -= 4) {
6455
23.4k
    libsais_prefetchr(&SA[i - 2 * prefetch_distance]);
6456
6457
23.4k
    libsais_prefetchr(&T[SA[i - prefetch_distance - 0]]);
6458
23.4k
    libsais_prefetchr(&T[SA[i - prefetch_distance - 1]]);
6459
23.4k
    libsais_prefetchr(&T[SA[i - prefetch_distance - 2]]);
6460
23.4k
    libsais_prefetchr(&T[SA[i - prefetch_distance - 3]]);
6461
6462
23.4k
    sa_sint_t p0 = SA[i - 0];
6463
23.4k
    if (T[p0] != c) {
6464
13.4k
      c = T[p0];
6465
13.4k
      memset(&SA[buckets[c]], 0, (size_t)(l - buckets[c]) * sizeof(sa_sint_t));
6466
13.4k
      l = buckets[c];
6467
13.4k
    }
6468
23.4k
    SA[--l] = p0;
6469
23.4k
    sa_sint_t p1 = SA[i - 1];
6470
23.4k
    if (T[p1] != c) {
6471
13.2k
      c = T[p1];
6472
13.2k
      memset(&SA[buckets[c]], 0, (size_t)(l - buckets[c]) * sizeof(sa_sint_t));
6473
13.2k
      l = buckets[c];
6474
13.2k
    }
6475
23.4k
    SA[--l] = p1;
6476
23.4k
    sa_sint_t p2 = SA[i - 2];
6477
23.4k
    if (T[p2] != c) {
6478
13.3k
      c = T[p2];
6479
13.3k
      memset(&SA[buckets[c]], 0, (size_t)(l - buckets[c]) * sizeof(sa_sint_t));
6480
13.3k
      l = buckets[c];
6481
13.3k
    }
6482
23.4k
    SA[--l] = p2;
6483
23.4k
    sa_sint_t p3 = SA[i - 3];
6484
23.4k
    if (T[p3] != c) {
6485
13.3k
      c = T[p3];
6486
13.3k
      memset(&SA[buckets[c]], 0, (size_t)(l - buckets[c]) * sizeof(sa_sint_t));
6487
13.3k
      l = buckets[c];
6488
13.3k
    }
6489
23.4k
    SA[--l] = p3;
6490
23.4k
  }
6491
6492
11.3k
  for (; i >= 0; i -= 1) {
6493
11.1k
    sa_sint_t p = SA[i];
6494
11.1k
    if (T[p] != c) {
6495
9.94k
      c = T[p];
6496
9.94k
      memset(&SA[buckets[c]], 0, (size_t)(l - buckets[c]) * sizeof(sa_sint_t));
6497
9.94k
      l = buckets[c];
6498
9.94k
    }
6499
11.1k
    SA[--l] = p;
6500
11.1k
  }
6501
6502
170
  memset(&SA[0], 0, (size_t)l * sizeof(sa_sint_t));
6503
170
}
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.79k
    const sa_sint_t* RESTRICT buckets) {
6508
1.79k
  const sa_sint_t* RESTRICT bucket_end = &buckets[5 * (fast_sint_t)k];
6509
6510
1.79k
  fast_sint_t c, j = n;
6511
6.43k
  for (c = (fast_sint_t)k - 2; c >= 0; --c) {
6512
4.63k
    fast_sint_t l = (fast_sint_t)buckets[BUCKETS_INDEX4(c, 1)];
6513
4.63k
    if (l > 0) {
6514
1.18k
      fast_sint_t i = bucket_end[c];
6515
1.18k
      if (j - i > 0) {
6516
1.18k
        memset(&SA[i], 0, (size_t)(j - i) * sizeof(sa_sint_t));
6517
1.18k
      }
6518
6519
1.18k
      memmove(&SA[j = (i - l)], &SA[m -= (sa_sint_t)l],
6520
1.18k
              (size_t)l * sizeof(sa_sint_t));
6521
1.18k
    }
6522
4.63k
  }
6523
6524
1.79k
  memset(&SA[0], 0, (size_t)j * sizeof(sa_sint_t));
6525
1.79k
}
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
13.4k
    const sa_sint_t* RESTRICT buckets) {
6530
13.4k
  const sa_sint_t* RESTRICT bucket_end = &buckets[3 * (fast_sint_t)k];
6531
6532
13.4k
  fast_sint_t c, j = n;
6533
703k
  for (c = (fast_sint_t)k - 2; c >= 0; --c) {
6534
689k
    fast_sint_t l = (fast_sint_t)buckets[BUCKETS_INDEX2(c, 1)];
6535
689k
    if (l > 0) {
6536
253k
      fast_sint_t i = bucket_end[c];
6537
253k
      if (j - i > 0) {
6538
118k
        memset(&SA[i], 0, (size_t)(j - i) * sizeof(sa_sint_t));
6539
118k
      }
6540
6541
253k
      memmove(&SA[j = (i - l)], &SA[m -= (sa_sint_t)l],
6542
253k
              (size_t)l * sizeof(sa_sint_t));
6543
253k
    }
6544
689k
  }
6545
6546
13.4k
  memset(&SA[0], 0, (size_t)j * sizeof(sa_sint_t));
6547
13.4k
}
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
632
    const sa_sint_t* RESTRICT buckets) {
6552
632
  fast_sint_t j = n;
6553
6554
632
  if (k > 1) {
6555
632
    fast_sint_t c;
6556
519k
    for (c = BUCKETS_INDEX2((fast_sint_t)k - 2, 0); c >= BUCKETS_INDEX2(0, 0);
6557
519k
         c -= BUCKETS_INDEX2(1, 0)) {
6558
519k
      fast_sint_t l = (fast_sint_t)buckets[c + BUCKETS_INDEX2(0, 1)];
6559
519k
      if (l > 0) {
6560
188k
        fast_sint_t i = buckets[c];
6561
188k
        if (j - i > 0) {
6562
86.3k
          memset(&SA[i], 0, (size_t)(j - i) * sizeof(sa_sint_t));
6563
86.3k
        }
6564
6565
188k
        memmove(&SA[j = (i - l)], &SA[m -= (sa_sint_t)l],
6566
188k
                (size_t)l * sizeof(sa_sint_t));
6567
188k
      }
6568
519k
    }
6569
632
  }
6570
6571
632
  memset(&SA[0], 0, (size_t)j * sizeof(sa_sint_t));
6572
632
}
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.12k
    fast_sint_t omp_block_size) {
6695
8.12k
  const fast_sint_t prefetch_distance = 64;
6696
6697
8.12k
  fast_sint_t i, j;
6698
8.12k
  for (i = omp_block_start,
6699
8.12k
      j = omp_block_start + omp_block_size - prefetch_distance - 1;
6700
9.76M
       i < j; i += 2) {
6701
9.75M
    libsais_prefetchw(&SA[i + 2 * prefetch_distance]);
6702
6703
9.75M
    sa_sint_t s0 = SA[i + prefetch_distance + 0];
6704
9.75M
    const uint8_t* Ts0 = &T[s0 > 0 ? s0 : 2];
6705
9.75M
    libsais_prefetchr(Ts0 - 1);
6706
9.75M
    libsais_prefetchr(Ts0 - 2);
6707
9.75M
    sa_sint_t s1 = SA[i + prefetch_distance + 1];
6708
9.75M
    const uint8_t* Ts1 = &T[s1 > 0 ? s1 : 2];
6709
9.75M
    libsais_prefetchr(Ts1 - 1);
6710
9.75M
    libsais_prefetchr(Ts1 - 2);
6711
6712
9.75M
    sa_sint_t p0 = SA[i + 0];
6713
9.75M
    SA[i + 0] = p0 ^ SAINT_MIN;
6714
9.75M
    if (p0 > 0) {
6715
5.69M
      p0--;
6716
5.69M
      SA[induction_bucket[T[p0]]++] =
6717
5.69M
          p0 |
6718
5.69M
          (sa_sint_t)((sa_uint_t)(T[p0 - (p0 > 0)] < T[p0]) << (SAINT_BIT - 1));
6719
5.69M
    }
6720
9.75M
    sa_sint_t p1 = SA[i + 1];
6721
9.75M
    SA[i + 1] = p1 ^ SAINT_MIN;
6722
9.75M
    if (p1 > 0) {
6723
5.69M
      p1--;
6724
5.69M
      SA[induction_bucket[T[p1]]++] =
6725
5.69M
          p1 |
6726
5.69M
          (sa_sint_t)((sa_uint_t)(T[p1 - (p1 > 0)] < T[p1]) << (SAINT_BIT - 1));
6727
5.69M
    }
6728
9.75M
  }
6729
6730
470k
  for (j += prefetch_distance + 1; i < j; i += 1) {
6731
462k
    sa_sint_t p = SA[i];
6732
462k
    SA[i] = p ^ SAINT_MIN;
6733
462k
    if (p > 0) {
6734
101k
      p--;
6735
101k
      SA[induction_bucket[T[p]]++] =
6736
101k
          p |
6737
101k
          (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] < T[p]) << (SAINT_BIT - 1));
6738
101k
    }
6739
462k
  }
6740
8.12k
}
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
16.0k
    fast_sint_t omp_block_size) {
6746
16.0k
  const fast_sint_t prefetch_distance = 64;
6747
6748
16.0k
  fast_sint_t i, j;
6749
16.0k
  for (i = omp_block_start,
6750
16.0k
      j = omp_block_start + omp_block_size - 2 * prefetch_distance - 1;
6751
4.69M
       i < j; i += 2) {
6752
4.68M
    libsais_prefetchw(&SA[i + 3 * prefetch_distance]);
6753
6754
4.68M
    sa_sint_t s0 = SA[i + 2 * prefetch_distance + 0];
6755
4.68M
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 : 1];
6756
4.68M
    libsais_prefetchr(Ts0 - 1);
6757
4.68M
    sa_sint_t s1 = SA[i + 2 * prefetch_distance + 1];
6758
4.68M
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 : 1];
6759
4.68M
    libsais_prefetchr(Ts1 - 1);
6760
4.68M
    sa_sint_t s2 = SA[i + 1 * prefetch_distance + 0];
6761
4.68M
    if (s2 > 0) {
6762
2.03M
      libsais_prefetchw(&induction_bucket[T[s2 - 1]]);
6763
2.03M
      libsais_prefetchr(&T[s2] - 2);
6764
2.03M
    }
6765
4.68M
    sa_sint_t s3 = SA[i + 1 * prefetch_distance + 1];
6766
4.68M
    if (s3 > 0) {
6767
2.02M
      libsais_prefetchw(&induction_bucket[T[s3 - 1]]);
6768
2.02M
      libsais_prefetchr(&T[s3] - 2);
6769
2.02M
    }
6770
6771
4.68M
    sa_sint_t p0 = SA[i + 0];
6772
4.68M
    SA[i + 0] = p0 ^ SAINT_MIN;
6773
4.68M
    if (p0 > 0) {
6774
2.27M
      p0--;
6775
2.27M
      SA[induction_bucket[T[p0]]++] =
6776
2.27M
          p0 |
6777
2.27M
          (sa_sint_t)((sa_uint_t)(T[p0 - (p0 > 0)] < T[p0]) << (SAINT_BIT - 1));
6778
2.27M
    }
6779
4.68M
    sa_sint_t p1 = SA[i + 1];
6780
4.68M
    SA[i + 1] = p1 ^ SAINT_MIN;
6781
4.68M
    if (p1 > 0) {
6782
2.27M
      p1--;
6783
2.27M
      SA[induction_bucket[T[p1]]++] =
6784
2.27M
          p1 |
6785
2.27M
          (sa_sint_t)((sa_uint_t)(T[p1 - (p1 > 0)] < T[p1]) << (SAINT_BIT - 1));
6786
2.27M
    }
6787
4.68M
  }
6788
6789
1.10M
  for (j += 2 * prefetch_distance + 1; i < j; i += 1) {
6790
1.08M
    sa_sint_t p = SA[i];
6791
1.08M
    SA[i] = p ^ SAINT_MIN;
6792
1.08M
    if (p > 0) {
6793
423k
      p--;
6794
423k
      SA[induction_bucket[T[p]]++] =
6795
423k
          p |
6796
423k
          (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] < T[p]) << (SAINT_BIT - 1));
6797
423k
    }
6798
1.08M
  }
6799
16.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.12k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
7517
8.12k
  SA[induction_bucket[T[(sa_sint_t)n - 1]]++] =
7518
8.12k
      ((sa_sint_t)n - 1) |
7519
8.12k
      (sa_sint_t)((sa_uint_t)(T[(sa_sint_t)n - 2] < T[(sa_sint_t)n - 1])
7520
8.12k
                  << (SAINT_BIT - 1));
7521
7522
8.12k
  if (threads == 1 || n < 65536) {
7523
8.12k
    libsais_final_sorting_scan_left_to_right_8u(T, SA, induction_bucket, 0, n);
7524
8.12k
  }
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.12k
  UNUSED(k);
7567
8.12k
  UNUSED(thread_state);
7568
8.12k
#endif
7569
8.12k
}
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
16.0k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
7575
16.0k
  SA[induction_bucket[T[n - 1]]++] =
7576
16.0k
      (n - 1) |
7577
16.0k
      (sa_sint_t)((sa_uint_t)(T[n - 2] < T[n - 1]) << (SAINT_BIT - 1));
7578
7579
16.0k
  if (threads == 1 || n < 65536) {
7580
16.0k
    libsais_final_sorting_scan_left_to_right_32s(T, SA, induction_bucket, 0, n);
7581
16.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
16.0k
  UNUSED(thread_state);
7599
16.0k
#endif
7600
16.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.12k
    fast_sint_t omp_block_size) {
7731
8.12k
  const fast_sint_t prefetch_distance = 64;
7732
7733
8.12k
  fast_sint_t i, j;
7734
8.12k
  for (i = omp_block_start + omp_block_size - 1,
7735
8.12k
      j = omp_block_start + prefetch_distance + 1;
7736
9.76M
       i >= j; i -= 2) {
7737
9.75M
    libsais_prefetchw(&SA[i - 2 * prefetch_distance]);
7738
7739
9.75M
    sa_sint_t s0 = SA[i - prefetch_distance - 0];
7740
9.75M
    const uint8_t* Ts0 = &T[s0 > 0 ? s0 : 2];
7741
9.75M
    libsais_prefetchr(Ts0 - 1);
7742
9.75M
    libsais_prefetchr(Ts0 - 2);
7743
9.75M
    sa_sint_t s1 = SA[i - prefetch_distance - 1];
7744
9.75M
    const uint8_t* Ts1 = &T[s1 > 0 ? s1 : 2];
7745
9.75M
    libsais_prefetchr(Ts1 - 1);
7746
9.75M
    libsais_prefetchr(Ts1 - 2);
7747
7748
9.75M
    sa_sint_t p0 = SA[i - 0];
7749
9.75M
    SA[i - 0] = p0 & SAINT_MAX;
7750
9.75M
    if (p0 > 0) {
7751
4.19M
      p0--;
7752
4.19M
      SA[--induction_bucket[T[p0]]] =
7753
4.19M
          p0 |
7754
4.19M
          (sa_sint_t)((sa_uint_t)(T[p0 - (p0 > 0)] > T[p0]) << (SAINT_BIT - 1));
7755
4.19M
    }
7756
9.75M
    sa_sint_t p1 = SA[i - 1];
7757
9.75M
    SA[i - 1] = p1 & SAINT_MAX;
7758
9.75M
    if (p1 > 0) {
7759
4.18M
      p1--;
7760
4.18M
      SA[--induction_bucket[T[p1]]] =
7761
4.18M
          p1 |
7762
4.18M
          (sa_sint_t)((sa_uint_t)(T[p1 - (p1 > 0)] > T[p1]) << (SAINT_BIT - 1));
7763
4.18M
    }
7764
9.75M
  }
7765
7766
470k
  for (j -= prefetch_distance + 1; i >= j; i -= 1) {
7767
462k
    sa_sint_t p = SA[i];
7768
462k
    SA[i] = p & SAINT_MAX;
7769
462k
    if (p > 0) {
7770
95.6k
      p--;
7771
95.6k
      SA[--induction_bucket[T[p]]] =
7772
95.6k
          p |
7773
95.6k
          (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] > T[p]) << (SAINT_BIT - 1));
7774
95.6k
    }
7775
462k
  }
7776
8.12k
}
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
16.0k
    fast_sint_t omp_block_size) {
7833
16.0k
  const fast_sint_t prefetch_distance = 64;
7834
7835
16.0k
  fast_sint_t i, j;
7836
16.0k
  for (i = omp_block_start + omp_block_size - 1,
7837
16.0k
      j = omp_block_start + 2 * prefetch_distance + 1;
7838
4.69M
       i >= j; i -= 2) {
7839
4.68M
    libsais_prefetchw(&SA[i - 3 * prefetch_distance]);
7840
7841
4.68M
    sa_sint_t s0 = SA[i - 2 * prefetch_distance - 0];
7842
4.68M
    const sa_sint_t* Ts0 = &T[s0 > 0 ? s0 : 1];
7843
4.68M
    libsais_prefetchr(Ts0 - 1);
7844
4.68M
    sa_sint_t s1 = SA[i - 2 * prefetch_distance - 1];
7845
4.68M
    const sa_sint_t* Ts1 = &T[s1 > 0 ? s1 : 1];
7846
4.68M
    libsais_prefetchr(Ts1 - 1);
7847
4.68M
    sa_sint_t s2 = SA[i - 1 * prefetch_distance - 0];
7848
4.68M
    if (s2 > 0) {
7849
2.38M
      libsais_prefetchw(&induction_bucket[T[s2 - 1]]);
7850
2.38M
      libsais_prefetchr(&T[s2] - 2);
7851
2.38M
    }
7852
4.68M
    sa_sint_t s3 = SA[i - 1 * prefetch_distance - 1];
7853
4.68M
    if (s3 > 0) {
7854
2.38M
      libsais_prefetchw(&induction_bucket[T[s3 - 1]]);
7855
2.38M
      libsais_prefetchr(&T[s3] - 2);
7856
2.38M
    }
7857
7858
4.68M
    sa_sint_t p0 = SA[i - 0];
7859
4.68M
    SA[i - 0] = p0 & SAINT_MAX;
7860
4.68M
    if (p0 > 0) {
7861
2.57M
      p0--;
7862
2.57M
      SA[--induction_bucket[T[p0]]] =
7863
2.57M
          p0 |
7864
2.57M
          (sa_sint_t)((sa_uint_t)(T[p0 - (p0 > 0)] > T[p0]) << (SAINT_BIT - 1));
7865
2.57M
    }
7866
4.68M
    sa_sint_t p1 = SA[i - 1];
7867
4.68M
    SA[i - 1] = p1 & SAINT_MAX;
7868
4.68M
    if (p1 > 0) {
7869
2.56M
      p1--;
7870
2.56M
      SA[--induction_bucket[T[p1]]] =
7871
2.56M
          p1 |
7872
2.56M
          (sa_sint_t)((sa_uint_t)(T[p1 - (p1 > 0)] > T[p1]) << (SAINT_BIT - 1));
7873
2.56M
    }
7874
4.68M
  }
7875
7876
1.10M
  for (j -= 2 * prefetch_distance + 1; i >= j; i -= 1) {
7877
1.08M
    sa_sint_t p = SA[i];
7878
1.08M
    SA[i] = p & SAINT_MAX;
7879
1.08M
    if (p > 0) {
7880
325k
      p--;
7881
325k
      SA[--induction_bucket[T[p]]] =
7882
325k
          p |
7883
325k
          (sa_sint_t)((sa_uint_t)(T[p - (p > 0)] > T[p]) << (SAINT_BIT - 1));
7884
325k
    }
7885
1.08M
  }
7886
16.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.12k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8765
8.12k
  if (threads == 1 || omp_block_size < 65536) {
8766
8.12k
    libsais_final_sorting_scan_right_to_left_8u(
8767
8.12k
        T, SA, induction_bucket, omp_block_start, omp_block_size);
8768
8.12k
  }
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.12k
  UNUSED(k);
8812
8.12k
  UNUSED(thread_state);
8813
8.12k
#endif
8814
8.12k
}
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
16.0k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8876
16.0k
  if (threads == 1 || n < 65536) {
8877
16.0k
    libsais_final_sorting_scan_right_to_left_32s(T, SA, induction_bucket, 0, n);
8878
16.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
16.0k
  UNUSED(thread_state);
8897
16.0k
#endif
8898
16.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.12k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8928
8.12k
  if ((flags & LIBSAIS_FLAGS_BWT) == 0) {
8929
8.12k
    if (flags & LIBSAIS_FLAGS_GSA) {
8930
0
      buckets[6 * ALPHABET_SIZE] = buckets[7 * ALPHABET_SIZE] - 1;
8931
0
    }
8932
8933
8.12k
    libsais_final_sorting_scan_left_to_right_8u_omp(
8934
8.12k
        T, SA, n, k, &buckets[6 * ALPHABET_SIZE], threads, thread_state);
8935
8.12k
    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.12k
    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.12k
    } else {
8948
8.12k
      libsais_final_sorting_scan_right_to_left_8u_omp(
8949
8.12k
          T, SA, 0, n, k, &buckets[7 * ALPHABET_SIZE], threads, thread_state);
8950
8.12k
    }
8951
8952
8.12k
    return 0;
8953
8.12k
  } 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.12k
}
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.79k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8983
1.79k
  libsais_final_sorting_scan_left_to_right_32s_omp(
8984
1.79k
      T, SA, n, &buckets[4 * (fast_sint_t)k], threads, thread_state);
8985
1.79k
  libsais_final_sorting_scan_right_to_left_32s_omp(
8986
1.79k
      T, SA, n, &buckets[5 * (fast_sint_t)k], threads, thread_state);
8987
1.79k
}
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
13.4k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
8993
13.4k
  libsais_final_sorting_scan_left_to_right_32s_omp(
8994
13.4k
      T, SA, n, &buckets[2 * (fast_sint_t)k], threads, thread_state);
8995
13.4k
  libsais_final_sorting_scan_right_to_left_32s_omp(
8996
13.4k
      T, SA, n, &buckets[3 * (fast_sint_t)k], threads, thread_state);
8997
13.4k
}
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
632
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9003
632
  libsais_final_sorting_scan_left_to_right_32s_omp(
9004
632
      T, SA, n, &buckets[1 * (fast_sint_t)k], threads, thread_state);
9005
632
  libsais_final_sorting_scan_right_to_left_32s_omp(
9006
632
      T, SA, n, &buckets[0 * (fast_sint_t)k], threads, thread_state);
9007
632
}
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
170
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9013
170
  libsais_count_suffixes_32s(T, n, k, buckets);
9014
170
  libsais_initialize_buckets_start_32s_1k(k, buckets);
9015
170
  libsais_final_sorting_scan_left_to_right_32s_omp(T, SA, n, buckets, threads,
9016
170
                                                   thread_state);
9017
9018
170
  libsais_count_suffixes_32s(T, n, k, buckets);
9019
170
  libsais_initialize_buckets_end_32s_1k(k, buckets);
9020
170
  libsais_final_sorting_scan_right_to_left_32s_omp(T, SA, n, buckets, threads,
9021
170
                                                   thread_state);
9022
170
}
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
8.97k
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
9027
8.97k
  const fast_sint_t prefetch_distance = 64;
9028
9029
8.97k
  sa_sint_t* RESTRICT SAm = &SA[m];
9030
9031
8.97k
  sa_sint_t i, j;
9032
8.97k
  for (i = (sa_sint_t)omp_block_start, j = (sa_sint_t)omp_block_start +
9033
8.97k
                                           (sa_sint_t)omp_block_size -
9034
8.97k
                                           2 * (sa_sint_t)prefetch_distance - 3;
9035
729k
       i < j; i += 4) {
9036
720k
    libsais_prefetchr(&SA[i + 3 * prefetch_distance]);
9037
9038
720k
    libsais_prefetchw(
9039
720k
        &SAm[((sa_uint_t)SA[i + 2 * prefetch_distance + 0]) >> 1]);
9040
720k
    libsais_prefetchw(
9041
720k
        &SAm[((sa_uint_t)SA[i + 2 * prefetch_distance + 1]) >> 1]);
9042
720k
    libsais_prefetchw(
9043
720k
        &SAm[((sa_uint_t)SA[i + 2 * prefetch_distance + 2]) >> 1]);
9044
720k
    libsais_prefetchw(
9045
720k
        &SAm[((sa_uint_t)SA[i + 2 * prefetch_distance + 3]) >> 1]);
9046
9047
720k
    sa_uint_t q0 = (sa_uint_t)SA[i + prefetch_distance + 0];
9048
720k
    sa_sint_t* RESTRICT Tq0 = &T[q0];
9049
720k
    libsais_prefetchw(SAm[q0 >> 1] < 0 ? Tq0 : &SAm[q0 >> 1]);
9050
720k
    sa_uint_t q1 = (sa_uint_t)SA[i + prefetch_distance + 1];
9051
720k
    sa_sint_t* RESTRICT Tq1 = &T[q1];
9052
720k
    libsais_prefetchw(SAm[q1 >> 1] < 0 ? Tq1 : &SAm[q1 >> 1]);
9053
720k
    sa_uint_t q2 = (sa_uint_t)SA[i + prefetch_distance + 2];
9054
720k
    sa_sint_t* RESTRICT Tq2 = &T[q2];
9055
720k
    libsais_prefetchw(SAm[q2 >> 1] < 0 ? Tq2 : &SAm[q2 >> 1]);
9056
720k
    sa_uint_t q3 = (sa_uint_t)SA[i + prefetch_distance + 3];
9057
720k
    sa_sint_t* RESTRICT Tq3 = &T[q3];
9058
720k
    libsais_prefetchw(SAm[q3 >> 1] < 0 ? Tq3 : &SAm[q3 >> 1]);
9059
9060
720k
    sa_uint_t p0 = (sa_uint_t)SA[i + 0];
9061
720k
    sa_sint_t s0 = SAm[p0 >> 1];
9062
720k
    if (s0 < 0) {
9063
81.6k
      T[p0] |= SAINT_MIN;
9064
81.6k
      f++;
9065
81.6k
      s0 = i + 0 + SAINT_MIN + f;
9066
81.6k
    }
9067
720k
    SAm[p0 >> 1] = s0 - f;
9068
720k
    sa_uint_t p1 = (sa_uint_t)SA[i + 1];
9069
720k
    sa_sint_t s1 = SAm[p1 >> 1];
9070
720k
    if (s1 < 0) {
9071
82.7k
      T[p1] |= SAINT_MIN;
9072
82.7k
      f++;
9073
82.7k
      s1 = i + 1 + SAINT_MIN + f;
9074
82.7k
    }
9075
720k
    SAm[p1 >> 1] = s1 - f;
9076
720k
    sa_uint_t p2 = (sa_uint_t)SA[i + 2];
9077
720k
    sa_sint_t s2 = SAm[p2 >> 1];
9078
720k
    if (s2 < 0) {
9079
82.5k
      T[p2] |= SAINT_MIN;
9080
82.5k
      f++;
9081
82.5k
      s2 = i + 2 + SAINT_MIN + f;
9082
82.5k
    }
9083
720k
    SAm[p2 >> 1] = s2 - f;
9084
720k
    sa_uint_t p3 = (sa_uint_t)SA[i + 3];
9085
720k
    sa_sint_t s3 = SAm[p3 >> 1];
9086
720k
    if (s3 < 0) {
9087
82.0k
      T[p3] |= SAINT_MIN;
9088
82.0k
      f++;
9089
82.0k
      s3 = i + 3 + SAINT_MIN + f;
9090
82.0k
    }
9091
720k
    SAm[p3 >> 1] = s3 - f;
9092
720k
  }
9093
9094
624k
  for (j += 2 * (sa_sint_t)prefetch_distance + 3; i < j; i += 1) {
9095
615k
    sa_uint_t p = (sa_uint_t)SA[i];
9096
615k
    sa_sint_t s = SAm[p >> 1];
9097
615k
    if (s < 0) {
9098
159k
      T[p] |= SAINT_MIN;
9099
159k
      f++;
9100
159k
      s = i + SAINT_MIN + f;
9101
159k
    }
9102
615k
    SAm[p >> 1] = s - f;
9103
615k
  }
9104
9105
8.97k
  return f;
9106
8.97k
}
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
8.97k
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
9111
8.97k
  const fast_sint_t prefetch_distance = 64;
9112
9113
8.97k
  sa_uint_t* RESTRICT SAl = (sa_uint_t*)&SA[0];
9114
8.97k
  sa_uint_t* RESTRICT SAr = (sa_uint_t*)&SA[0];
9115
9116
8.97k
  fast_sint_t i, j, l = *pl - 1, r = *pr - 1;
9117
8.97k
  for (i = (fast_sint_t)m + omp_block_start + omp_block_size - 1,
9118
8.97k
      j = (fast_sint_t)m + omp_block_start + 3;
9119
1.25M
       i >= j; i -= 4) {
9120
1.24M
    libsais_prefetchr(&SA[i - prefetch_distance]);
9121
9122
1.24M
    sa_uint_t p0 = (sa_uint_t)SA[i - 0];
9123
1.24M
    SAl[l] = p0 & SAINT_MAX;
9124
1.24M
    l -= (sa_sint_t)p0 < 0;
9125
1.24M
    SAr[r] = p0 - 1;
9126
1.24M
    r -= (sa_sint_t)p0 > 0;
9127
1.24M
    sa_uint_t p1 = (sa_uint_t)SA[i - 1];
9128
1.24M
    SAl[l] = p1 & SAINT_MAX;
9129
1.24M
    l -= (sa_sint_t)p1 < 0;
9130
1.24M
    SAr[r] = p1 - 1;
9131
1.24M
    r -= (sa_sint_t)p1 > 0;
9132
1.24M
    sa_uint_t p2 = (sa_uint_t)SA[i - 2];
9133
1.24M
    SAl[l] = p2 & SAINT_MAX;
9134
1.24M
    l -= (sa_sint_t)p2 < 0;
9135
1.24M
    SAr[r] = p2 - 1;
9136
1.24M
    r -= (sa_sint_t)p2 > 0;
9137
1.24M
    sa_uint_t p3 = (sa_uint_t)SA[i - 3];
9138
1.24M
    SAl[l] = p3 & SAINT_MAX;
9139
1.24M
    l -= (sa_sint_t)p3 < 0;
9140
1.24M
    SAr[r] = p3 - 1;
9141
1.24M
    r -= (sa_sint_t)p3 > 0;
9142
1.24M
  }
9143
9144
22.6k
  for (j -= 3; i >= j; i -= 1) {
9145
13.7k
    sa_uint_t p = (sa_uint_t)SA[i];
9146
13.7k
    SAl[l] = p & SAINT_MAX;
9147
13.7k
    l -= (sa_sint_t)p < 0;
9148
13.7k
    SAr[r] = p - 1;
9149
13.7k
    r -= (sa_sint_t)p > 0;
9150
13.7k
  }
9151
9152
8.97k
  *pl = l + 1;
9153
8.97k
  *pr = r + 1;
9154
8.97k
}
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
8.97k
    sa_sint_t threads, LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9196
8.97k
  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
8.97k
  {
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
8.97k
    UNUSED(threads);
9207
8.97k
    UNUSED(thread_state);
9208
9209
8.97k
    fast_sint_t omp_thread_num = 0;
9210
8.97k
    fast_sint_t omp_num_threads = 1;
9211
8.97k
#endif
9212
8.97k
    fast_sint_t omp_block_stride = (m / omp_num_threads) & (-16);
9213
8.97k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
9214
8.97k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
9215
8.97k
                                     ? omp_block_stride
9216
8.97k
                                     : m - omp_block_start;
9217
9218
8.97k
    if (omp_num_threads == 1) {
9219
8.97k
      f = libsais_renumber_unique_and_nonunique_lms_suffixes_32s(
9220
8.97k
          T, SA, m, 0, omp_block_start, omp_block_size);
9221
8.97k
    }
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
8.97k
  }
9248
9249
8.97k
  return f;
9250
8.97k
}
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
8.97k
    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
8.97k
  {
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
8.97k
    UNUSED(threads);
9265
8.97k
    UNUSED(thread_state);
9266
9267
8.97k
    fast_sint_t omp_thread_num = 0;
9268
8.97k
    fast_sint_t omp_num_threads = 1;
9269
8.97k
#endif
9270
8.97k
    fast_sint_t omp_block_stride =
9271
8.97k
        (((fast_sint_t)n >> 1) / omp_num_threads) & (-16);
9272
8.97k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
9273
8.97k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
9274
8.97k
                                     ? omp_block_stride
9275
8.97k
                                     : ((fast_sint_t)n >> 1) - omp_block_start;
9276
9277
8.97k
    if (omp_num_threads == 1) {
9278
8.97k
      fast_sint_t l = m, r = (fast_sint_t)n + (fast_sint_t)fs;
9279
8.97k
      libsais_compact_unique_and_nonunique_lms_suffixes_32s(
9280
8.97k
          SA, m, &l, &r, omp_block_start, omp_block_size);
9281
8.97k
    }
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
8.97k
  }
9336
9337
8.97k
  memcpy(&SA[(fast_sint_t)n + (fast_sint_t)fs - (fast_sint_t)m],
9338
8.97k
         &SA[(fast_sint_t)m - (fast_sint_t)f], (size_t)f * sizeof(sa_sint_t));
9339
8.97k
}
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
8.97k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9345
8.97k
  sa_sint_t f = libsais_renumber_unique_and_nonunique_lms_suffixes_32s_omp(
9346
8.97k
      T, SA, m, threads, thread_state);
9347
8.97k
  libsais_compact_unique_and_nonunique_lms_suffixes_32s_omp(
9348
8.97k
      SA, n, m, fs, f, threads, thread_state);
9349
9350
8.97k
  return f;
9351
8.97k
}
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
6.81k
    fast_sint_t l, fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
9356
6.81k
  const fast_sint_t prefetch_distance = 64;
9357
9358
6.81k
  const sa_sint_t* RESTRICT SAnm = &SA[(fast_sint_t)n - (fast_sint_t)m - 1 + l];
9359
9360
6.81k
  sa_sint_t i, j;
9361
6.81k
  fast_sint_t tmp = *SAnm++;
9362
6.81k
  for (i = (sa_sint_t)omp_block_start,
9363
6.81k
      j = (sa_sint_t)omp_block_start + (sa_sint_t)omp_block_size - 6;
9364
1.98M
       i < j; i += 4) {
9365
1.97M
    libsais_prefetchr(&T[i + prefetch_distance]);
9366
9367
1.97M
    sa_sint_t c0 = T[i + 0];
9368
1.97M
    if (c0 < 0) {
9369
121k
      T[i + 0] = c0 & SAINT_MAX;
9370
121k
      SA[tmp] = i + 0;
9371
121k
      i++;
9372
121k
      tmp = *SAnm++;
9373
121k
    }
9374
1.97M
    sa_sint_t c1 = T[i + 1];
9375
1.97M
    if (c1 < 0) {
9376
121k
      T[i + 1] = c1 & SAINT_MAX;
9377
121k
      SA[tmp] = i + 1;
9378
121k
      i++;
9379
121k
      tmp = *SAnm++;
9380
121k
    }
9381
1.97M
    sa_sint_t c2 = T[i + 2];
9382
1.97M
    if (c2 < 0) {
9383
121k
      T[i + 2] = c2 & SAINT_MAX;
9384
121k
      SA[tmp] = i + 2;
9385
121k
      i++;
9386
121k
      tmp = *SAnm++;
9387
121k
    }
9388
1.97M
    sa_sint_t c3 = T[i + 3];
9389
1.97M
    if (c3 < 0) {
9390
121k
      T[i + 3] = c3 & SAINT_MAX;
9391
121k
      SA[tmp] = i + 3;
9392
121k
      i++;
9393
121k
      tmp = *SAnm++;
9394
121k
    }
9395
1.97M
  }
9396
9397
31.4k
  for (j += 6; i < j; i += 1) {
9398
24.6k
    sa_sint_t c = T[i];
9399
24.6k
    if (c < 0) {
9400
2.98k
      T[i] = c & SAINT_MAX;
9401
2.98k
      SA[tmp] = i;
9402
2.98k
      i++;
9403
2.98k
      tmp = *SAnm++;
9404
2.98k
    }
9405
24.6k
  }
9406
6.81k
}
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
6.81k
    fast_sint_t omp_block_start, fast_sint_t omp_block_size) {
9411
6.81k
  const fast_sint_t prefetch_distance = 64;
9412
9413
6.81k
  const sa_sint_t* RESTRICT SAnm = &SA[(fast_sint_t)n - (fast_sint_t)m - 1 + l];
9414
9415
6.81k
  fast_sint_t i, j;
9416
6.81k
  sa_sint_t tmp = *SAnm++;
9417
728k
  for (i = omp_block_start, j = omp_block_start + omp_block_size - 3; i < j;
9418
721k
       i += 4) {
9419
721k
    libsais_prefetchr(&SA[i + prefetch_distance]);
9420
9421
721k
    if (SA[i + 0] == 0) {
9422
602k
      SA[i + 0] = tmp;
9423
602k
      tmp = *SAnm++;
9424
602k
    }
9425
721k
    if (SA[i + 1] == 0) {
9426
599k
      SA[i + 1] = tmp;
9427
599k
      tmp = *SAnm++;
9428
599k
    }
9429
721k
    if (SA[i + 2] == 0) {
9430
600k
      SA[i + 2] = tmp;
9431
600k
      tmp = *SAnm++;
9432
600k
    }
9433
721k
    if (SA[i + 3] == 0) {
9434
600k
      SA[i + 3] = tmp;
9435
600k
      tmp = *SAnm++;
9436
600k
    }
9437
721k
  }
9438
9439
16.8k
  for (j += 3; i < j; i += 1) {
9440
10.0k
    if (SA[i] == 0) {
9441
5.42k
      SA[i] = tmp;
9442
5.42k
      tmp = *SAnm++;
9443
5.42k
    }
9444
10.0k
  }
9445
6.81k
}
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
6.81k
    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
6.81k
  {
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
6.81k
    UNUSED(threads);
9459
6.81k
    UNUSED(thread_state);
9460
9461
6.81k
    fast_sint_t omp_thread_num = 0;
9462
6.81k
    fast_sint_t omp_num_threads = 1;
9463
6.81k
#endif
9464
6.81k
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
9465
6.81k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
9466
6.81k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
9467
6.81k
                                     ? omp_block_stride
9468
6.81k
                                     : n - omp_block_start;
9469
9470
6.81k
    if (omp_num_threads == 1) {
9471
6.81k
      libsais_merge_unique_lms_suffixes_32s(T, SA, n, m, 0, omp_block_start,
9472
6.81k
                                            omp_block_size);
9473
6.81k
    }
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
6.81k
  }
9496
6.81k
}
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
6.81k
    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
6.81k
  {
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
6.81k
    UNUSED(threads);
9510
6.81k
    UNUSED(thread_state);
9511
9512
6.81k
    fast_sint_t omp_thread_num = 0;
9513
6.81k
    fast_sint_t omp_num_threads = 1;
9514
6.81k
#endif
9515
6.81k
    fast_sint_t omp_block_stride = (m / omp_num_threads) & (-16);
9516
6.81k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
9517
6.81k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
9518
6.81k
                                     ? omp_block_stride
9519
6.81k
                                     : m - omp_block_start;
9520
9521
6.81k
    if (omp_num_threads == 1) {
9522
6.81k
      libsais_merge_nonunique_lms_suffixes_32s(SA, n, m, f, omp_block_start,
9523
6.81k
                                               omp_block_size);
9524
6.81k
    }
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
6.81k
  }
9547
6.81k
}
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
6.81k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9553
6.81k
  libsais_merge_unique_lms_suffixes_32s_omp(T, SA, n, m, threads, thread_state);
9554
6.81k
  libsais_merge_nonunique_lms_suffixes_32s_omp(SA, n, m, f, threads,
9555
6.81k
                                               thread_state);
9556
6.81k
}
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
8.82k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9563
8.82k
  if (f > 0) {
9564
6.66k
    memmove(&SA[n - m - 1], &SA[n + fs - m], (size_t)f * sizeof(sa_sint_t));
9565
9566
6.66k
    libsais_count_and_gather_compacted_lms_suffixes_32s_2k_omp(
9567
6.66k
        T, SA, n, k, buckets, local_buckets, threads, thread_state);
9568
6.66k
    libsais_reconstruct_lms_suffixes_omp(SA, n, m - f, threads);
9569
9570
6.66k
    memcpy(&SA[n - m - 1 + f], &SA[0],
9571
6.66k
           ((size_t)m - (size_t)f) * sizeof(sa_sint_t));
9572
6.66k
    memset(&SA[0], 0, (size_t)m * sizeof(sa_sint_t));
9573
9574
6.66k
    libsais_merge_compacted_lms_suffixes_32s_omp(T, SA, n, m, f, threads,
9575
6.66k
                                                 thread_state);
9576
6.66k
  } else {
9577
2.16k
    libsais_count_and_gather_lms_suffixes_32s_2k(T, SA, n, k, buckets, 0, n);
9578
2.16k
    libsais_reconstruct_lms_suffixes_omp(SA, n, m, threads);
9579
2.16k
  }
9580
8.82k
}
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
149
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9586
149
  if (f > 0) {
9587
149
    memmove(&SA[n - m - 1], &SA[n + fs - m], (size_t)f * sizeof(sa_sint_t));
9588
9589
149
    libsais_gather_compacted_lms_suffixes_32s(T, SA, n);
9590
149
    libsais_reconstruct_lms_suffixes_omp(SA, n, m - f, threads);
9591
9592
149
    memcpy(&SA[n - m - 1 + f], &SA[0],
9593
149
           ((size_t)m - (size_t)f) * sizeof(sa_sint_t));
9594
149
    memset(&SA[0], 0, (size_t)m * sizeof(sa_sint_t));
9595
9596
149
    libsais_merge_compacted_lms_suffixes_32s_omp(T, SA, n, m, f, threads,
9597
149
                                                 thread_state);
9598
149
  } 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
149
}
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
16.0k
    sa_sint_t* RESTRICT local_buffer) {
9609
16.0k
  fs = fs < (SAINT_MAX - n) ? fs : (SAINT_MAX - n);
9610
9611
16.0k
  if (k > 0 && ((fs / k >= 6) || (LIBSAIS_LOCAL_BUFFER_SIZE / k >= 6))) {
9612
14.9k
    sa_sint_t alignment =
9613
14.9k
        (fs - 1024) / k >= 6 ? (sa_sint_t)1024 : (sa_sint_t)16;
9614
14.9k
    sa_sint_t* RESTRICT buckets =
9615
14.9k
        (fs - alignment) / k >= 6
9616
14.9k
            ? (sa_sint_t*)libsais_align_up(
9617
9.09k
                  &SA[n + fs - 6 * (fast_sint_t)k - alignment],
9618
9.09k
                  (size_t)alignment * sizeof(sa_sint_t))
9619
14.9k
            : &SA[n + fs - 6 * (fast_sint_t)k];
9620
14.9k
    buckets = (LIBSAIS_LOCAL_BUFFER_SIZE > fs) ? local_buffer : buckets;
9621
9622
14.9k
    sa_sint_t m = libsais_count_and_gather_lms_suffixes_32s_4k_omp(
9623
14.9k
        T, SA, n, k, buckets, buckets == local_buffer, threads, thread_state);
9624
14.9k
    if (m > 1) {
9625
13.1k
      memset(SA, 0, ((size_t)n - (size_t)m) * sizeof(sa_sint_t));
9626
9627
13.1k
      sa_sint_t first_lms_suffix = SA[n - m];
9628
13.1k
      sa_sint_t left_suffixes_count =
9629
13.1k
          libsais_initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
9630
13.1k
              T, k, buckets, first_lms_suffix);
9631
9632
13.1k
      libsais_radix_sort_lms_suffixes_32s_6k_omp(
9633
13.1k
          T, SA, n, m, &buckets[4 * (fast_sint_t)k], threads, thread_state);
9634
9635
13.1k
      if ((n / 8192) < k) {
9636
13.1k
        libsais_radix_sort_set_markers_32s_6k_omp(
9637
13.1k
            SA, k, &buckets[4 * (fast_sint_t)k], threads);
9638
13.1k
      }
9639
13.1k
      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.1k
      libsais_initialize_buckets_for_partial_sorting_32s_6k(
9645
13.1k
          T, k, buckets, first_lms_suffix, left_suffixes_count);
9646
13.1k
      libsais_induce_partial_order_32s_6k_omp(
9647
13.1k
          T, SA, n, k, buckets, first_lms_suffix, left_suffixes_count, threads,
9648
13.1k
          thread_state);
9649
9650
13.1k
      sa_sint_t names =
9651
13.1k
          (n / 8192) < k
9652
13.1k
              ? libsais_renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
9653
13.1k
                    SA, n, m, threads, thread_state)
9654
13.1k
              : libsais_renumber_and_gather_lms_suffixes_omp(
9655
0
                    SA, n, m, fs, threads, thread_state);
9656
9657
13.1k
      if (names < m) {
9658
7.98k
        sa_sint_t f = (n / 8192) < k
9659
7.98k
                          ? libsais_compact_lms_suffixes_32s_omp(
9660
7.98k
                                T, SA, n, m, fs, threads, thread_state)
9661
7.98k
                          : 0;
9662
9663
7.98k
        if (libsais_main_32s_recursion(SA + n + fs - m + f, SA, m - f,
9664
7.98k
                                       names - f, fs + n - 2 * m + f, threads,
9665
7.98k
                                       thread_state, local_buffer) != 0) {
9666
0
          return -2;
9667
0
        }
9668
9669
7.98k
        libsais_reconstruct_compacted_lms_suffixes_32s_2k_omp(
9670
7.98k
            T, SA, n, k, m, fs, f, buckets, buckets == local_buffer, threads,
9671
7.98k
            thread_state);
9672
7.98k
      } else {
9673
5.13k
        libsais_count_lms_suffixes_32s_2k(T, n, k, buckets);
9674
5.13k
      }
9675
9676
13.1k
      libsais_initialize_buckets_start_and_end_32s_4k(k, buckets);
9677
13.1k
      libsais_place_lms_suffixes_histogram_32s_4k(SA, n, k, m, buckets);
9678
13.1k
      libsais_induce_final_order_32s_4k(T, SA, n, k, buckets, threads,
9679
13.1k
                                        thread_state);
9680
13.1k
    } else {
9681
1.79k
      SA[0] = SA[n - 1];
9682
9683
1.79k
      libsais_initialize_buckets_start_and_end_32s_6k(k, buckets);
9684
1.79k
      libsais_place_lms_suffixes_histogram_32s_6k(SA, n, k, m, buckets);
9685
1.79k
      libsais_induce_final_order_32s_6k(T, SA, n, k, buckets, threads,
9686
1.79k
                                        thread_state);
9687
1.79k
    }
9688
9689
14.9k
    return 0;
9690
14.9k
  } else if (k > 0 && (n <= SAINT_MAX / 2) &&
9691
1.15k
             ((fs / k >= 4) || (LIBSAIS_LOCAL_BUFFER_SIZE / k >= 4))) {
9692
356
    sa_sint_t alignment =
9693
356
        (fs - 1024) / k >= 4 ? (sa_sint_t)1024 : (sa_sint_t)16;
9694
356
    sa_sint_t* RESTRICT buckets =
9695
356
        (fs - alignment) / k >= 4
9696
356
            ? (sa_sint_t*)libsais_align_up(
9697
73
                  &SA[n + fs - 4 * (fast_sint_t)k - alignment],
9698
73
                  (size_t)alignment * sizeof(sa_sint_t))
9699
356
            : &SA[n + fs - 4 * (fast_sint_t)k];
9700
356
    buckets = (LIBSAIS_LOCAL_BUFFER_SIZE > fs) ? local_buffer : buckets;
9701
9702
356
    sa_sint_t m = libsais_count_and_gather_lms_suffixes_32s_2k_omp(
9703
356
        T, SA, n, k, buckets, buckets == local_buffer, threads, thread_state);
9704
356
    if (m > 1) {
9705
356
      libsais_initialize_buckets_for_radix_and_partial_sorting_32s_4k(
9706
356
          T, k, buckets, SA[n - m]);
9707
9708
356
      libsais_radix_sort_lms_suffixes_32s_2k_omp(T, SA, n, m, &buckets[1],
9709
356
                                                 threads, thread_state);
9710
356
      libsais_radix_sort_set_markers_32s_4k_omp(SA, k, &buckets[1], threads);
9711
9712
356
      libsais_place_lms_suffixes_interval_32s_4k(SA, n, k, m - 1, buckets);
9713
356
      libsais_induce_partial_order_32s_4k_omp(T, SA, n, k, buckets, threads,
9714
356
                                              thread_state);
9715
9716
356
      sa_sint_t names =
9717
356
          libsais_renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
9718
356
              SA, n, m, threads, thread_state);
9719
356
      if (names < m) {
9720
303
        sa_sint_t f = libsais_compact_lms_suffixes_32s_omp(
9721
303
            T, SA, n, m, fs, threads, thread_state);
9722
9723
303
        if (libsais_main_32s_recursion(SA + n + fs - m + f, SA, m - f,
9724
303
                                       names - f, fs + n - 2 * m + f, threads,
9725
303
                                       thread_state, local_buffer) != 0) {
9726
0
          return -2;
9727
0
        }
9728
9729
303
        libsais_reconstruct_compacted_lms_suffixes_32s_2k_omp(
9730
303
            T, SA, n, k, m, fs, f, buckets, buckets == local_buffer, threads,
9731
303
            thread_state);
9732
303
      } else {
9733
53
        libsais_count_lms_suffixes_32s_2k(T, n, k, buckets);
9734
53
      }
9735
356
    } else {
9736
0
      SA[0] = SA[n - 1];
9737
0
    }
9738
9739
356
    libsais_initialize_buckets_start_and_end_32s_4k(k, buckets);
9740
356
    libsais_place_lms_suffixes_histogram_32s_4k(SA, n, k, m, buckets);
9741
356
    libsais_induce_final_order_32s_4k(T, SA, n, k, buckets, threads,
9742
356
                                      thread_state);
9743
9744
356
    return 0;
9745
802
  } else if (k > 0 && ((fs / k >= 2) || (LIBSAIS_LOCAL_BUFFER_SIZE / k >= 2))) {
9746
632
    sa_sint_t alignment =
9747
632
        (fs - 1024) / k >= 2 ? (sa_sint_t)1024 : (sa_sint_t)16;
9748
632
    sa_sint_t* RESTRICT buckets =
9749
632
        (fs - alignment) / k >= 2
9750
632
            ? (sa_sint_t*)libsais_align_up(
9751
445
                  &SA[n + fs - 2 * (fast_sint_t)k - alignment],
9752
445
                  (size_t)alignment * sizeof(sa_sint_t))
9753
632
            : &SA[n + fs - 2 * (fast_sint_t)k];
9754
632
    buckets = (LIBSAIS_LOCAL_BUFFER_SIZE > fs) ? local_buffer : buckets;
9755
9756
632
    sa_sint_t m = libsais_count_and_gather_lms_suffixes_32s_2k_omp(
9757
632
        T, SA, n, k, buckets, buckets == local_buffer, threads, thread_state);
9758
632
    if (m > 1) {
9759
632
      libsais_initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
9760
632
          T, k, buckets, SA[n - m]);
9761
9762
632
      libsais_radix_sort_lms_suffixes_32s_2k_omp(T, SA, n, m, &buckets[1],
9763
632
                                                 threads, thread_state);
9764
632
      libsais_place_lms_suffixes_interval_32s_2k(SA, n, k, m - 1, buckets);
9765
9766
632
      libsais_initialize_buckets_start_and_end_32s_2k(k, buckets);
9767
632
      libsais_induce_partial_order_32s_2k_omp(T, SA, n, k, buckets, threads,
9768
632
                                              thread_state);
9769
9770
632
      sa_sint_t names =
9771
632
          libsais_renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(
9772
632
              T, SA, n, m, threads);
9773
632
      if (names < m) {
9774
533
        sa_sint_t f = libsais_compact_lms_suffixes_32s_omp(
9775
533
            T, SA, n, m, fs, threads, thread_state);
9776
9777
533
        if (libsais_main_32s_recursion(SA + n + fs - m + f, SA, m - f,
9778
533
                                       names - f, fs + n - 2 * m + f, threads,
9779
533
                                       thread_state, local_buffer) != 0) {
9780
0
          return -2;
9781
0
        }
9782
9783
533
        libsais_reconstruct_compacted_lms_suffixes_32s_2k_omp(
9784
533
            T, SA, n, k, m, fs, f, buckets, buckets == local_buffer, threads,
9785
533
            thread_state);
9786
533
      } else {
9787
99
        libsais_count_lms_suffixes_32s_2k(T, n, k, buckets);
9788
99
      }
9789
632
    } else {
9790
0
      SA[0] = SA[n - 1];
9791
0
    }
9792
9793
632
    libsais_initialize_buckets_end_32s_2k(k, buckets);
9794
632
    libsais_place_lms_suffixes_histogram_32s_2k(SA, n, k, m, buckets);
9795
9796
632
    libsais_initialize_buckets_start_and_end_32s_2k(k, buckets);
9797
632
    libsais_induce_final_order_32s_2k(T, SA, n, k, buckets, threads,
9798
632
                                      thread_state);
9799
9800
632
    return 0;
9801
632
  } else {
9802
170
    sa_sint_t* buffer = fs < k ? (sa_sint_t*)libsais_alloc_aligned(
9803
0
                                     (size_t)k * sizeof(sa_sint_t), 4096)
9804
170
                               : (sa_sint_t*)NULL;
9805
9806
170
    sa_sint_t alignment = fs - 1024 >= k ? (sa_sint_t)1024 : (sa_sint_t)16;
9807
170
    sa_sint_t* RESTRICT buckets =
9808
170
        fs - alignment >= k ? (sa_sint_t*)libsais_align_up(
9809
170
                                  &SA[n + fs - k - alignment],
9810
170
                                  (size_t)alignment * sizeof(sa_sint_t))
9811
170
        : fs >= k           ? &SA[n + fs - k]
9812
0
                            : buffer;
9813
9814
170
    if (buckets == NULL) {
9815
0
      return -2;
9816
0
    }
9817
9818
170
    memset(SA, 0, (size_t)n * sizeof(sa_sint_t));
9819
9820
170
    libsais_count_suffixes_32s(T, n, k, buckets);
9821
170
    libsais_initialize_buckets_end_32s_1k(k, buckets);
9822
9823
170
    sa_sint_t m = libsais_radix_sort_lms_suffixes_32s_1k(T, SA, n, buckets);
9824
170
    if (m > 1) {
9825
170
      libsais_induce_partial_order_32s_1k_omp(T, SA, n, k, buckets, threads,
9826
170
                                              thread_state);
9827
9828
170
      sa_sint_t names =
9829
170
          libsais_renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(
9830
170
              T, SA, n, m, threads);
9831
170
      if (names < m) {
9832
149
        if (buffer != NULL) {
9833
0
          libsais_free_aligned(buffer);
9834
0
          buckets = NULL;
9835
0
        }
9836
9837
149
        sa_sint_t f = libsais_compact_lms_suffixes_32s_omp(
9838
149
            T, SA, n, m, fs, threads, thread_state);
9839
9840
149
        if (libsais_main_32s_recursion(SA + n + fs - m + f, SA, m - f,
9841
149
                                       names - f, fs + n - 2 * m + f, threads,
9842
149
                                       thread_state, local_buffer) != 0) {
9843
0
          return -2;
9844
0
        }
9845
9846
149
        libsais_reconstruct_compacted_lms_suffixes_32s_1k_omp(
9847
149
            T, SA, n, m, fs, f, threads, thread_state);
9848
9849
149
        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
149
        if (buckets == NULL) {
9854
0
          return -2;
9855
0
        }
9856
149
      }
9857
9858
170
      libsais_count_suffixes_32s(T, n, k, buckets);
9859
170
      libsais_initialize_buckets_end_32s_1k(k, buckets);
9860
170
      libsais_place_lms_suffixes_interval_32s_1k(T, SA, k, m, buckets);
9861
170
    }
9862
9863
170
    libsais_induce_final_order_32s_1k(T, SA, n, k, buckets, threads,
9864
170
                                      thread_state);
9865
170
    libsais_free_aligned(buffer);
9866
9867
170
    return 0;
9868
170
  }
9869
16.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.10k
    LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9875
7.10k
  sa_sint_t local_buffer[2 * LIBSAIS_LOCAL_BUFFER_SIZE];
9876
9877
7.10k
  return libsais_main_32s_recursion(T, SA, n, k, fs, threads, thread_state,
9878
7.10k
                                    local_buffer + LIBSAIS_LOCAL_BUFFER_SIZE);
9879
7.10k
}
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.12k
                                 LIBSAIS_THREAD_STATE* RESTRICT thread_state) {
9887
8.12k
  fs = fs < (SAINT_MAX - n) ? fs : (SAINT_MAX - n);
9888
9889
8.12k
  sa_sint_t m = libsais_count_and_gather_lms_suffixes_8u_omp(
9890
8.12k
      T, SA, n, buckets, threads, thread_state);
9891
8.12k
  sa_sint_t k = libsais_initialize_buckets_start_and_end_8u(buckets, freq);
9892
9893
8.12k
  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.12k
  if (m > 0) {
9899
8.05k
    sa_sint_t first_lms_suffix = SA[n - m];
9900
8.05k
    sa_sint_t left_suffixes_count =
9901
8.05k
        libsais_initialize_buckets_for_lms_suffixes_radix_sort_8u(
9902
8.05k
            T, buckets, first_lms_suffix);
9903
9904
8.05k
    if (threads > 1 && n >= 65536) {
9905
0
      memset(SA, 0, ((size_t)n - (size_t)m) * sizeof(sa_sint_t));
9906
0
    }
9907
8.05k
    libsais_radix_sort_lms_suffixes_8u_omp(T, SA, n, m, flags, buckets, threads,
9908
8.05k
                                           thread_state);
9909
8.05k
    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.05k
    libsais_initialize_buckets_for_partial_sorting_8u(
9915
8.05k
        T, buckets, first_lms_suffix, left_suffixes_count);
9916
8.05k
    libsais_induce_partial_order_8u_omp(T, SA, n, k, flags, buckets,
9917
8.05k
                                        first_lms_suffix, left_suffixes_count,
9918
8.05k
                                        threads, thread_state);
9919
9920
8.05k
    sa_sint_t names = libsais_renumber_and_gather_lms_suffixes_omp(
9921
8.05k
        SA, n, m, fs, threads, thread_state);
9922
8.05k
    if (names < m) {
9923
7.10k
      if (libsais_main_32s_entry(SA + n + fs - m, SA, m, names, fs + n - 2 * m,
9924
7.10k
                                 threads, thread_state) != 0) {
9925
0
        return -2;
9926
0
      }
9927
9928
7.10k
      libsais_gather_lms_suffixes_8u_omp(T, SA, n, threads, thread_state);
9929
7.10k
      libsais_reconstruct_lms_suffixes_omp(SA, n, m, threads);
9930
7.10k
    }
9931
9932
8.05k
    libsais_place_lms_suffixes_interval_8u(SA, n, m, flags, buckets);
9933
8.05k
  } else {
9934
66
    memset(SA, 0, (size_t)n * sizeof(sa_sint_t));
9935
66
  }
9936
9937
8.12k
  return libsais_induce_final_order_8u_omp(T, SA, n, k, flags, r, I, buckets,
9938
8.12k
                                           threads, thread_state);
9939
8.12k
}
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.12k
                              sa_sint_t threads) {
9945
8.12k
  LIBSAIS_THREAD_STATE* RESTRICT thread_state =
9946
8.12k
      threads > 1 ? libsais_alloc_thread_state(threads) : NULL;
9947
8.12k
  sa_sint_t* RESTRICT buckets = (sa_sint_t*)libsais_alloc_aligned(
9948
8.12k
      (size_t)8 * ALPHABET_SIZE * sizeof(sa_sint_t), 4096);
9949
9950
8.12k
  sa_sint_t index = buckets != NULL && (thread_state != NULL || threads == 1)
9951
8.12k
                        ? libsais_main_8u(T, SA, n, buckets, flags, r, I, fs,
9952
8.12k
                                          freq, threads, thread_state)
9953
8.12k
                        : -2;
9954
9955
8.12k
  libsais_free_aligned(buckets);
9956
8.12k
  libsais_free_thread_state(thread_state);
9957
9958
8.12k
  return index;
9959
8.12k
}
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.12k
                int32_t* freq) {
10045
8.12k
  if ((T == NULL) || (SA == NULL) || (n < 0) || (fs < 0)) {
10046
0
    return -1;
10047
8.12k
  } 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.12k
  return libsais_main(T, SA, n, LIBSAIS_FLAGS_NONE, 0, NULL, fs, freq, 1);
10061
8.12k
}
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.12k
                                fast_sint_t omp_block_size) {
11848
8.12k
  const fast_sint_t prefetch_distance = 64;
11849
11850
8.12k
  fast_sint_t i, j;
11851
8.12k
  sa_sint_t k = omp_block_start > 0 ? SA[omp_block_start - 1] : n;
11852
8.12k
  for (i = omp_block_start,
11853
8.12k
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
11854
4.88M
       i < j; i += 4) {
11855
4.87M
    libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
11856
11857
4.87M
    libsais_prefetchw(&PLCP[SA[i + prefetch_distance + 0]]);
11858
4.87M
    libsais_prefetchw(&PLCP[SA[i + prefetch_distance + 1]]);
11859
11860
4.87M
    PLCP[SA[i + 0]] = k;
11861
4.87M
    k = SA[i + 0];
11862
4.87M
    PLCP[SA[i + 1]] = k;
11863
4.87M
    k = SA[i + 1];
11864
11865
4.87M
    libsais_prefetchw(&PLCP[SA[i + prefetch_distance + 2]]);
11866
4.87M
    libsais_prefetchw(&PLCP[SA[i + prefetch_distance + 3]]);
11867
11868
4.87M
    PLCP[SA[i + 2]] = k;
11869
4.87M
    k = SA[i + 2];
11870
4.87M
    PLCP[SA[i + 3]] = k;
11871
4.87M
    k = SA[i + 3];
11872
4.87M
  }
11873
11874
476k
  for (j += prefetch_distance + 3; i < j; i += 1) {
11875
468k
    PLCP[SA[i]] = k;
11876
468k
    k = SA[i];
11877
468k
  }
11878
8.12k
}
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.12k
                                    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.12k
  {
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.12k
    UNUSED(threads);
11892
11893
8.12k
    fast_sint_t omp_thread_num = 0;
11894
8.12k
    fast_sint_t omp_num_threads = 1;
11895
8.12k
#endif
11896
8.12k
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
11897
8.12k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
11898
8.12k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
11899
8.12k
                                     ? omp_block_stride
11900
8.12k
                                     : n - omp_block_start;
11901
11902
8.12k
    libsais_compute_phi(SA, PLCP, n, omp_block_start, omp_block_size);
11903
8.12k
  }
11904
8.12k
}
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.12k
                                 fast_sint_t omp_block_size) {
11910
8.12k
  const fast_sint_t prefetch_distance = 64;
11911
11912
8.12k
  fast_sint_t i, j, l = 0;
11913
8.12k
  for (i = omp_block_start,
11914
8.12k
      j = omp_block_start + omp_block_size - prefetch_distance;
11915
19.5M
       i < j; i += 1) {
11916
19.5M
    libsais_prefetchw(&PLCP[i + 2 * prefetch_distance]);
11917
19.5M
    libsais_prefetchr(&T[PLCP[i + prefetch_distance] + l]);
11918
11919
19.5M
    fast_sint_t k = PLCP[i], m = n - (i > k ? i : k);
11920
38.9M
    while (l < m && T[i + l] == T[k + l]) {
11921
19.4M
      l++;
11922
19.4M
    }
11923
11924
19.5M
    PLCP[i] = (sa_sint_t)l;
11925
19.5M
    l -= (l != 0);
11926
19.5M
  }
11927
11928
467k
  for (j += prefetch_distance; i < j; i += 1) {
11929
459k
    fast_sint_t k = PLCP[i], m = n - (i > k ? i : k);
11930
742k
    while (l < m && T[i + l] == T[k + l]) {
11931
282k
      l++;
11932
282k
    }
11933
11934
459k
    PLCP[i] = (sa_sint_t)l;
11935
459k
    l -= (l != 0);
11936
459k
  }
11937
8.12k
}
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.12k
                                     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.12k
  {
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.12k
    UNUSED(threads);
11951
11952
8.12k
    fast_sint_t omp_thread_num = 0;
11953
8.12k
    fast_sint_t omp_num_threads = 1;
11954
8.12k
#endif
11955
8.12k
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
11956
8.12k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
11957
8.12k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
11958
8.12k
                                     ? omp_block_stride
11959
8.12k
                                     : n - omp_block_start;
11960
11961
8.12k
    libsais_compute_plcp(T, PLCP, n, omp_block_start, omp_block_size);
11962
8.12k
  }
11963
8.12k
}
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.12k
                                fast_sint_t omp_block_size) {
12088
8.12k
  const fast_sint_t prefetch_distance = 64;
12089
12090
8.12k
  fast_sint_t i, j;
12091
8.12k
  for (i = omp_block_start,
12092
8.12k
      j = omp_block_start + omp_block_size - prefetch_distance - 3;
12093
4.88M
       i < j; i += 4) {
12094
4.87M
    libsais_prefetchr(&SA[i + 2 * prefetch_distance]);
12095
4.87M
    libsais_prefetchw(&LCP[i + prefetch_distance]);
12096
12097
4.87M
    libsais_prefetchr(&PLCP[SA[i + prefetch_distance + 0]]);
12098
4.87M
    libsais_prefetchr(&PLCP[SA[i + prefetch_distance + 1]]);
12099
12100
4.87M
    LCP[i + 0] = PLCP[SA[i + 0]];
12101
4.87M
    LCP[i + 1] = PLCP[SA[i + 1]];
12102
12103
4.87M
    libsais_prefetchr(&PLCP[SA[i + prefetch_distance + 2]]);
12104
4.87M
    libsais_prefetchr(&PLCP[SA[i + prefetch_distance + 3]]);
12105
12106
4.87M
    LCP[i + 2] = PLCP[SA[i + 2]];
12107
4.87M
    LCP[i + 3] = PLCP[SA[i + 3]];
12108
4.87M
  }
12109
12110
476k
  for (j += prefetch_distance + 3; i < j; i += 1) {
12111
468k
    LCP[i] = PLCP[SA[i]];
12112
468k
  }
12113
8.12k
}
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.12k
                                    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.12k
  {
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.12k
    UNUSED(threads);
12128
12129
8.12k
    fast_sint_t omp_thread_num = 0;
12130
8.12k
    fast_sint_t omp_num_threads = 1;
12131
8.12k
#endif
12132
8.12k
    fast_sint_t omp_block_stride = (n / omp_num_threads) & (-16);
12133
8.12k
    fast_sint_t omp_block_start = omp_thread_num * omp_block_stride;
12134
8.12k
    fast_sint_t omp_block_size = omp_thread_num < omp_num_threads - 1
12135
8.12k
                                     ? omp_block_stride
12136
8.12k
                                     : n - omp_block_start;
12137
12138
8.12k
    libsais_compute_lcp(PLCP, SA, LCP, omp_block_start, omp_block_size);
12139
8.12k
  }
12140
8.12k
}
12141
12142
int32_t libsais_plcp(const uint8_t* T, const int32_t* SA, int32_t* PLCP,
12143
8.12k
                     int32_t n) {
12144
8.12k
  if ((T == NULL) || (SA == NULL) || (PLCP == NULL) || (n < 0)) {
12145
0
    return -1;
12146
8.12k
  } 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.12k
  libsais_compute_phi_omp(SA, PLCP, n, 1);
12154
8.12k
  libsais_compute_plcp_omp(T, PLCP, n, 1);
12155
12156
8.12k
  return 0;
12157
8.12k
}
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.12k
                    int32_t n) {
12196
8.12k
  if ((PLCP == NULL) || (SA == NULL) || (LCP == NULL) || (n < 0)) {
12197
0
    return -1;
12198
8.12k
  } 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.12k
  libsais_compute_lcp_omp(PLCP, SA, LCP, n, 1);
12206
12207
8.12k
  return 0;
12208
8.12k
}
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