Skip to main content

libsais_rs/
libsais16.rs

1//! Rust translation of upstream [libsais](https://github.com/IlyaGrebnov/libsais)
2//! 2.10.4 by Ilya Grebnov.
3//!
4//! This module exposes the 16-bit alphabet suffix array, BWT, unBWT, PLCP and
5//! LCP entry points (mirroring `libsais16.h`).
6
7use std::mem;
8
9use rayon::prelude::*;
10
11use crate::{run_rayon_with_threads, SyncMutPtr};
12
13pub type SaSint = i32;
14pub type SaUint = u32;
15
16pub const ALPHABET_SIZE: usize = 1usize << 16;
17const SAINT_MAX: SaSint = SaSint::MAX;
18const SAINT_MIN: SaSint = SaSint::MIN;
19const SAINT_BIT: u32 = 32;
20const SUFFIX_GROUP_BIT: u32 = SAINT_BIT - 1;
21const SUFFIX_GROUP_MARKER: SaSint = 1_i32 << (SUFFIX_GROUP_BIT - 1);
22const LIBSAIS_FLAGS_BWT: SaSint = 1;
23const LIBSAIS_FLAGS_GSA: SaSint = 2;
24const LIBSAIS_LOCAL_BUFFER_SIZE: usize = 2000;
25const UNBWT_FASTBITS: usize = 17;
26const PER_THREAD_CACHE_SIZE: usize = 2_097_184;
27
28#[repr(C)]
29#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
30struct ThreadCache {
31    symbol: SaSint,
32    index: SaSint,
33}
34
35#[derive(Clone, Debug, Default, PartialEq, Eq)]
36pub struct ThreadState {
37    position: SaSint,
38    m: SaSint,
39    last_lms_suffix: SaSint,
40    count: SaSint,
41    buckets: Vec<SaSint>,
42    cache: Vec<ThreadCache>,
43    cache_entries: usize,
44}
45
46#[derive(Clone, Debug, Default, PartialEq, Eq)]
47pub struct Context {
48    buckets: Vec<SaSint>,
49    thread_state: Option<Vec<ThreadState>>,
50    threads: SaSint,
51}
52
53#[derive(Clone, Debug, Default, PartialEq, Eq)]
54pub struct UnbwtContext {
55    bucket2: Vec<usize>,
56    fastbits: Vec<u16>,
57    buckets: Option<Vec<usize>>,
58    threads: SaSint,
59}
60
61/// Creates the libsais16 context that allows reusing allocated memory with each libsais16 operation.
62///
63/// In multi-threaded environments, use one context per thread for parallel executions.
64///
65/// Returns the context, or `None` on allocation failure.
66pub fn create_ctx() -> Option<Context> {
67    create_ctx_main(1)
68}
69
70/// Creates the libsais16 context for parallel operations using OpenMP-style threading.
71///
72/// In multi-threaded environments, use one context per thread for parallel executions.
73///
74/// - `threads`: number of worker threads (can be 0 for the implementation default).
75///
76/// Returns the context, or `None` on allocation failure.
77pub fn create_ctx_omp(threads: SaSint) -> Option<Context> {
78    if threads < 0 {
79        None
80    } else {
81        create_ctx_main(normalize_threads(threads))
82    }
83}
84
85/// Destroys the libsais16 context and frees previously allocated memory.
86pub fn free_ctx(_ctx: Context) {}
87
88/// Creates the libsais16 reverse-BWT context that allows reusing allocated memory with each `libsais16_unbwt_*` operation.
89///
90/// In multi-threaded environments, use one context per thread for parallel executions.
91///
92/// Returns the context, or `None` on allocation failure.
93pub fn unbwt_create_ctx() -> Option<UnbwtContext> {
94    unbwt_create_ctx_main(1)
95}
96
97/// Creates the libsais16 reverse-BWT context for parallel `libsais16_unbwt_*` operations using OpenMP-style threading.
98///
99/// In multi-threaded environments, use one context per thread for parallel executions.
100///
101/// - `threads`: number of worker threads (can be 0 for the implementation default).
102///
103/// Returns the context, or `None` on allocation failure.
104pub fn unbwt_create_ctx_omp(threads: SaSint) -> Option<UnbwtContext> {
105    if threads < 0 {
106        None
107    } else {
108        unbwt_create_ctx_main(normalize_threads(threads))
109    }
110}
111
112/// Destroys the libsais16 reverse-BWT context and frees previously allocated memory.
113pub fn unbwt_free_ctx(_ctx: UnbwtContext) {}
114
115fn normalize_threads(threads: SaSint) -> SaSint {
116    if threads > 0 {
117        threads
118    } else {
119        1
120    }
121}
122
123fn align_up(value: usize, alignment: usize) -> usize {
124    (value + (alignment - 1)) & !(alignment - 1)
125}
126
127fn alloc_thread_state(threads: SaSint) -> Option<Vec<ThreadState>> {
128    let threads = usize::try_from(threads).ok()?;
129    let mut thread_state = Vec::with_capacity(threads);
130    for _ in 0..threads {
131        thread_state.push(ThreadState {
132            position: 0,
133            m: 0,
134            last_lms_suffix: 0,
135            count: 0,
136            buckets: vec![0; 4 * ALPHABET_SIZE],
137            cache: vec![ThreadCache::default(); PER_THREAD_CACHE_SIZE],
138            cache_entries: PER_THREAD_CACHE_SIZE,
139        });
140    }
141    Some(thread_state)
142}
143
144fn create_ctx_main(threads: SaSint) -> Option<Context> {
145    let buckets = vec![0; 8 * ALPHABET_SIZE];
146    let thread_state = if threads > 1 {
147        Some(alloc_thread_state(threads)?)
148    } else {
149        None
150    };
151
152    Some(Context {
153        buckets,
154        thread_state,
155        threads,
156    })
157}
158
159fn unbwt_create_ctx_main(threads: SaSint) -> Option<UnbwtContext> {
160    let bucket2 = vec![0; ALPHABET_SIZE];
161    let fastbits = vec![0; 1 + (1 << UNBWT_FASTBITS)];
162    let buckets = if threads > 1 {
163        Some(vec![0; usize::try_from(threads).ok()? * ALPHABET_SIZE])
164    } else {
165        None
166    };
167
168    Some(UnbwtContext {
169        bucket2,
170        fastbits,
171        buckets,
172        threads,
173    })
174}
175
176fn fill_freq(t: &[u16], freq: Option<&mut [SaSint]>) {
177    if let Some(freq) = freq {
178        freq[..ALPHABET_SIZE].fill(0);
179        for &symbol in t {
180            freq[symbol as usize] += 1;
181        }
182    }
183}
184
185fn buckets_index4(c: usize, s: usize) -> usize {
186    (c << 2) + s
187}
188
189fn buckets_index2(c: usize, s: usize) -> usize {
190    (c << 1) + s
191}
192
193fn place_cached_suffixes(
194    sa: &mut [SaSint],
195    cache: &[ThreadCache],
196    block_start: SaSint,
197    block_size: SaSint,
198) {
199    let start = usize::try_from(block_start).expect("block_start must be non-negative");
200    let len = usize::try_from(block_size).expect("block_size must be non-negative");
201    let entries = if cache.len() >= start + len {
202        &cache[start..start + len]
203    } else {
204        &cache[..len]
205    };
206
207    for entry in entries {
208        sa[entry.symbol as usize] = entry.index;
209    }
210}
211
212fn compact_and_place_cached_suffixes(
213    sa: &mut [SaSint],
214    cache: &mut [ThreadCache],
215    block_start: SaSint,
216    block_size: SaSint,
217) {
218    let start = usize::try_from(block_start).expect("block_start must be non-negative");
219    let len = usize::try_from(block_size).expect("block_size must be non-negative");
220    let read_start = if cache.len() >= start + len { start } else { 0 };
221    let read_end = read_start + len;
222
223    let mut write = read_start;
224    for read in read_start..read_end {
225        let entry = cache[read];
226        if entry.symbol >= 0 {
227            cache[write] = entry;
228            write += 1;
229        }
230    }
231    place_cached_suffixes(sa, cache, block_start, (write - read_start) as SaSint);
232}
233
234fn count_negative_marked_suffixes(
235    sa: &[SaSint],
236    block_start: SaSint,
237    block_size: SaSint,
238) -> SaSint {
239    let start = block_start as usize;
240    let end = start + block_size as usize;
241    sa[start..end].iter().filter(|&&value| value < 0).count() as SaSint
242}
243
244fn count_zero_marked_suffixes(sa: &[SaSint], block_start: SaSint, block_size: SaSint) -> SaSint {
245    let start = block_start as usize;
246    let end = start + block_size as usize;
247    sa[start..end].iter().filter(|&&value| value == 0).count() as SaSint
248}
249
250fn accumulate_counts_s32_n(
251    buckets: &mut [SaSint],
252    bucket00: usize,
253    bucket_size: usize,
254    bucket_stride: usize,
255    num_buckets: usize,
256) {
257    for s in 0..bucket_size {
258        let mut sum = buckets[bucket00 + s];
259        for bucket in 1..num_buckets {
260            sum += buckets[bucket00 - bucket * bucket_stride + s];
261        }
262        buckets[bucket00 + s] = sum;
263    }
264}
265
266fn accumulate_counts_s32_2(
267    buckets: &mut [SaSint],
268    bucket00: usize,
269    bucket_size: usize,
270    bucket_stride: usize,
271) {
272    accumulate_counts_s32_n(buckets, bucket00, bucket_size, bucket_stride, 2);
273}
274
275fn accumulate_counts_s32_3(
276    buckets: &mut [SaSint],
277    bucket00: usize,
278    bucket_size: usize,
279    bucket_stride: usize,
280) {
281    accumulate_counts_s32_n(buckets, bucket00, bucket_size, bucket_stride, 3);
282}
283
284fn accumulate_counts_s32_4(
285    buckets: &mut [SaSint],
286    bucket00: usize,
287    bucket_size: usize,
288    bucket_stride: usize,
289) {
290    accumulate_counts_s32_n(buckets, bucket00, bucket_size, bucket_stride, 4);
291}
292
293fn accumulate_counts_s32_5(
294    buckets: &mut [SaSint],
295    bucket00: usize,
296    bucket_size: usize,
297    bucket_stride: usize,
298) {
299    accumulate_counts_s32_n(buckets, bucket00, bucket_size, bucket_stride, 5);
300}
301
302fn accumulate_counts_s32_6(
303    buckets: &mut [SaSint],
304    bucket00: usize,
305    bucket_size: usize,
306    bucket_stride: usize,
307) {
308    accumulate_counts_s32_n(buckets, bucket00, bucket_size, bucket_stride, 6);
309}
310
311fn accumulate_counts_s32_7(
312    buckets: &mut [SaSint],
313    bucket00: usize,
314    bucket_size: usize,
315    bucket_stride: usize,
316) {
317    accumulate_counts_s32_n(buckets, bucket00, bucket_size, bucket_stride, 7);
318}
319
320fn accumulate_counts_s32_8(
321    buckets: &mut [SaSint],
322    bucket00: usize,
323    bucket_size: usize,
324    bucket_stride: usize,
325) {
326    accumulate_counts_s32_n(buckets, bucket00, bucket_size, bucket_stride, 8);
327}
328
329fn accumulate_counts_s32_9(
330    buckets: &mut [SaSint],
331    bucket00: usize,
332    bucket_size: usize,
333    bucket_stride: usize,
334) {
335    accumulate_counts_s32_n(buckets, bucket00, bucket_size, bucket_stride, 9);
336}
337
338fn accumulate_counts_s32(
339    buckets: &mut [SaSint],
340    bucket00: usize,
341    bucket_size: usize,
342    bucket_stride: usize,
343    mut num_buckets: usize,
344) {
345    while num_buckets >= 9 {
346        accumulate_counts_s32_9(
347            buckets,
348            bucket00 - (num_buckets - 9) * bucket_stride,
349            bucket_size,
350            bucket_stride,
351        );
352        num_buckets -= 8;
353    }
354
355    match num_buckets {
356        2 => accumulate_counts_s32_2(buckets, bucket00, bucket_size, bucket_stride),
357        3 => accumulate_counts_s32_3(buckets, bucket00, bucket_size, bucket_stride),
358        4 => accumulate_counts_s32_4(buckets, bucket00, bucket_size, bucket_stride),
359        5 => accumulate_counts_s32_5(buckets, bucket00, bucket_size, bucket_stride),
360        6 => accumulate_counts_s32_6(buckets, bucket00, bucket_size, bucket_stride),
361        7 => accumulate_counts_s32_7(buckets, bucket00, bucket_size, bucket_stride),
362        8 => accumulate_counts_s32_8(buckets, bucket00, bucket_size, bucket_stride),
363        _ => {}
364    }
365}
366
367fn flip_suffix_markers_omp(sa: &mut [SaSint], l: SaSint, threads: SaSint) {
368    let len = usize::try_from(l).expect("l must be non-negative");
369    let omp_num_threads = if threads > 1 && l >= 65_536 {
370        usize::try_from(threads).expect("threads must be non-negative")
371    } else {
372        1
373    };
374    let omp_block_stride = (len / omp_num_threads) & !15usize;
375    if omp_num_threads > 1 {
376        let chunk_size = omp_block_stride.max(16);
377        run_rayon_with_threads(omp_num_threads, || {
378            sa[..len].par_chunks_mut(chunk_size).for_each(|chunk| {
379                for value in chunk {
380                    *value ^= SAINT_MIN;
381                }
382            });
383        });
384        return;
385    }
386    // single-thread fallback
387    for value in &mut sa[..len] {
388        *value ^= SAINT_MIN;
389    }
390}
391
392fn gather_lms_suffixes_32s(t: &[SaSint], sa: &mut [SaSint], n: SaSint) -> SaSint {
393    let mut i = n - 2;
394    let mut m = n - 1;
395    let mut f0 = 1usize;
396    let mut f1: usize;
397    let mut c0 = t[(n - 1) as usize] as isize;
398    let mut c1: isize;
399
400    while i >= 3 {
401        c1 = t[i as usize] as isize;
402        f1 = usize::from(c1 > c0 - f0 as isize);
403        sa[m as usize] = i + 1;
404        m -= (f1 & !f0) as SaSint;
405
406        c0 = t[(i - 1) as usize] as isize;
407        f0 = usize::from(c0 > c1 - f1 as isize);
408        sa[m as usize] = i;
409        m -= (f0 & !f1) as SaSint;
410
411        c1 = t[(i - 2) as usize] as isize;
412        f1 = usize::from(c1 > c0 - f0 as isize);
413        sa[m as usize] = i - 1;
414        m -= (f1 & !f0) as SaSint;
415
416        c0 = t[(i - 3) as usize] as isize;
417        f0 = usize::from(c0 > c1 - f1 as isize);
418        sa[m as usize] = i - 2;
419        m -= (f0 & !f1) as SaSint;
420
421        i -= 4;
422    }
423
424    while i >= 0 {
425        c1 = c0;
426        c0 = t[i as usize] as isize;
427        f1 = f0;
428        f0 = usize::from(c0 > c1 - f1 as isize);
429        sa[m as usize] = i + 1;
430        m -= (f0 & !f1) as SaSint;
431        i -= 1;
432    }
433
434    n - 1 - m
435}
436
437fn gather_compacted_lms_suffixes_32s(t: &[SaSint], sa: &mut [SaSint], n: SaSint) -> SaSint {
438    let mut i = n - 2;
439    let mut m = n - 1;
440    let mut f0 = 1usize;
441    let mut f1: usize;
442    let mut c0 = t[(n - 1) as usize] as isize;
443    let mut c1: isize;
444
445    while i >= 3 {
446        c1 = t[i as usize] as isize;
447        f1 = usize::from(c1 > c0 - f0 as isize);
448        sa[m as usize] = i + 1;
449        m -= (f1 & !f0 & usize::from(c0 >= 0)) as SaSint;
450
451        c0 = t[(i - 1) as usize] as isize;
452        f0 = usize::from(c0 > c1 - f1 as isize);
453        sa[m as usize] = i;
454        m -= (f0 & !f1 & usize::from(c1 >= 0)) as SaSint;
455
456        c1 = t[(i - 2) as usize] as isize;
457        f1 = usize::from(c1 > c0 - f0 as isize);
458        sa[m as usize] = i - 1;
459        m -= (f1 & !f0 & usize::from(c0 >= 0)) as SaSint;
460
461        c0 = t[(i - 3) as usize] as isize;
462        f0 = usize::from(c0 > c1 - f1 as isize);
463        sa[m as usize] = i - 2;
464        m -= (f0 & !f1 & usize::from(c1 >= 0)) as SaSint;
465
466        i -= 4;
467    }
468
469    while i >= 0 {
470        c1 = c0;
471        c0 = t[i as usize] as isize;
472        f1 = f0;
473        f0 = usize::from(c0 > c1 - f1 as isize);
474        sa[m as usize] = i + 1;
475        m -= (f0 & !f1 & usize::from(c1 >= 0)) as SaSint;
476        i -= 1;
477    }
478
479    n - 1 - m
480}
481
482fn count_lms_suffixes_32s_4k(t: &[SaSint], n: SaSint, k: SaSint, buckets: &mut [SaSint]) {
483    buckets[..4 * k as usize].fill(0);
484    let mut i = n - 2;
485    let mut f0 = 1usize;
486    let mut f1: usize;
487    let mut c0 = t[(n - 1) as usize] as isize;
488    let mut c1: isize;
489
490    while i >= 3 {
491        c1 = t[i as usize] as isize;
492        f1 = usize::from(c1 > c0 - f0 as isize);
493        buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
494
495        c0 = t[(i - 1) as usize] as isize;
496        f0 = usize::from(c0 > c1 - f1 as isize);
497        buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
498
499        c1 = t[(i - 2) as usize] as isize;
500        f1 = usize::from(c1 > c0 - f0 as isize);
501        buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
502
503        c0 = t[(i - 3) as usize] as isize;
504        f0 = usize::from(c0 > c1 - f1 as isize);
505        buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
506
507        i -= 4;
508    }
509
510    while i >= 0 {
511        c1 = c0;
512        c0 = t[i as usize] as isize;
513        f1 = f0;
514        f0 = usize::from(c0 > c1 - f1 as isize);
515        buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
516        i -= 1;
517    }
518
519    buckets[buckets_index4(c0 as usize, f0 + f0)] += 1;
520}
521
522fn count_lms_suffixes_32s_2k(t: &[SaSint], n: SaSint, k: SaSint, buckets: &mut [SaSint]) {
523    buckets[..2 * k as usize].fill(0);
524    let mut i = n - 2;
525    let mut f0 = 1usize;
526    let mut f1: usize;
527    let mut c0 = t[(n - 1) as usize] as isize;
528    let mut c1: isize;
529
530    while i >= 3 {
531        c1 = t[i as usize] as isize;
532        f1 = usize::from(c1 > c0 - f0 as isize);
533        buckets[buckets_index2(c0 as usize, f1 & !f0)] += 1;
534
535        c0 = t[(i - 1) as usize] as isize;
536        f0 = usize::from(c0 > c1 - f1 as isize);
537        buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
538
539        c1 = t[(i - 2) as usize] as isize;
540        f1 = usize::from(c1 > c0 - f0 as isize);
541        buckets[buckets_index2(c0 as usize, f1 & !f0)] += 1;
542
543        c0 = t[(i - 3) as usize] as isize;
544        f0 = usize::from(c0 > c1 - f1 as isize);
545        buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
546
547        i -= 4;
548    }
549
550    while i >= 0 {
551        c1 = c0;
552        c0 = t[i as usize] as isize;
553        f1 = f0;
554        f0 = usize::from(c0 > c1 - f1 as isize);
555        buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
556        i -= 1;
557    }
558
559    buckets[buckets_index2(c0 as usize, 0)] += 1;
560}
561
562fn count_compacted_lms_suffixes_32s_2k(t: &[SaSint], n: SaSint, k: SaSint, buckets: &mut [SaSint]) {
563    buckets[..2 * k as usize].fill(0);
564    let mut i = n - 2;
565    let mut f0 = 1usize;
566    let mut f1: usize;
567    let mut c0 = t[(n - 1) as usize] as isize;
568    let mut c1: isize;
569
570    while i >= 3 {
571        c1 = t[i as usize] as isize;
572        f1 = usize::from(c1 > c0 - f0 as isize);
573        buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
574
575        c0 = t[(i - 1) as usize] as isize;
576        f0 = usize::from(c0 > c1 - f1 as isize);
577        buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
578
579        c1 = t[(i - 2) as usize] as isize;
580        f1 = usize::from(c1 > c0 - f0 as isize);
581        buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
582
583        c0 = t[(i - 3) as usize] as isize;
584        f0 = usize::from(c0 > c1 - f1 as isize);
585        buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
586
587        i -= 4;
588    }
589
590    while i >= 0 {
591        c1 = c0;
592        c0 = t[i as usize] as isize;
593        f1 = f0;
594        f0 = usize::from(c0 > c1 - f1 as isize);
595        buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
596        i -= 1;
597    }
598
599    buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, 0)] += 1;
600}
601
602fn get_bucket_stride(free_space: SaSint, bucket_size: SaSint, num_buckets: SaSint) -> SaSint {
603    let bucket_size_1024 = (bucket_size + 1023) & !1023;
604    if free_space / (num_buckets - 1) >= bucket_size_1024 {
605        return bucket_size_1024;
606    }
607    let bucket_size_16 = (bucket_size + 15) & !15;
608    if free_space / (num_buckets - 1) >= bucket_size_16 {
609        return bucket_size_16;
610    }
611    bucket_size
612}
613
614fn count_and_gather_lms_suffixes_32s_4k(
615    t: &[SaSint],
616    sa: &mut [SaSint],
617    n: SaSint,
618    k: SaSint,
619    buckets: &mut [SaSint],
620    omp_block_start: isize,
621    omp_block_size: isize,
622) -> SaSint {
623    buckets[..4 * k as usize].fill(0);
624    let mut m = omp_block_start + omp_block_size - 1;
625
626    if omp_block_size > 0 {
627        let mut j = m + 1;
628        let mut c0 = t[m as usize] as isize;
629        let mut c1 = -1isize;
630        while j < n as isize {
631            c1 = t[j as usize] as isize;
632            if c1 != c0 {
633                break;
634            }
635            j += 1;
636        }
637
638        let mut f0 = usize::from(c0 >= c1);
639        let mut f1: usize;
640        let mut i = m - 1;
641        j = omp_block_start + 64 + 3;
642        while i >= j {
643            c1 = t[i as usize] as isize;
644            f1 = usize::from(c1 > c0 - f0 as isize);
645            sa[m as usize] = (i + 1) as SaSint;
646            m -= (f1 & !f0) as isize;
647            buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
648
649            c0 = t[(i - 1) as usize] as isize;
650            f0 = usize::from(c0 > c1 - f1 as isize);
651            sa[m as usize] = i as SaSint;
652            m -= (f0 & !f1) as isize;
653            buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
654
655            c1 = t[(i - 2) as usize] as isize;
656            f1 = usize::from(c1 > c0 - f0 as isize);
657            sa[m as usize] = (i - 1) as SaSint;
658            m -= (f1 & !f0) as isize;
659            buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
660
661            c0 = t[(i - 3) as usize] as isize;
662            f0 = usize::from(c0 > c1 - f1 as isize);
663            sa[m as usize] = (i - 2) as SaSint;
664            m -= (f0 & !f1) as isize;
665            buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
666
667            i -= 4;
668        }
669
670        j -= 64 + 3;
671        while i >= j {
672            c1 = c0;
673            c0 = t[i as usize] as isize;
674            f1 = f0;
675            f0 = usize::from(c0 > c1 - f1 as isize);
676            sa[m as usize] = (i + 1) as SaSint;
677            m -= (f0 & !f1) as isize;
678            buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
679            i -= 1;
680        }
681
682        c1 = if i >= 0 { t[i as usize] as isize } else { -1 };
683        f1 = usize::from(c1 > c0 - f0 as isize);
684        sa[m as usize] = (i + 1) as SaSint;
685        m -= (f1 & !f0) as isize;
686        buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
687    }
688
689    (omp_block_start + omp_block_size - 1 - m) as SaSint
690}
691
692fn count_and_gather_lms_suffixes_32s_2k(
693    t: &[SaSint],
694    sa: &mut [SaSint],
695    n: SaSint,
696    k: SaSint,
697    buckets: &mut [SaSint],
698    omp_block_start: isize,
699    omp_block_size: isize,
700) -> SaSint {
701    buckets[..2 * k as usize].fill(0);
702    let mut m = omp_block_start + omp_block_size - 1;
703
704    if omp_block_size > 0 {
705        let mut j = m + 1;
706        let mut c0 = t[m as usize] as isize;
707        let mut c1 = -1isize;
708        while j < n as isize {
709            c1 = t[j as usize] as isize;
710            if c1 != c0 {
711                break;
712            }
713            j += 1;
714        }
715
716        let mut f0 = usize::from(c0 >= c1);
717        let mut f1: usize;
718        let mut i = m - 1;
719        j = omp_block_start + 64 + 3;
720        while i >= j {
721            c1 = t[i as usize] as isize;
722            f1 = usize::from(c1 > c0 - f0 as isize);
723            sa[m as usize] = (i + 1) as SaSint;
724            m -= (f1 & !f0) as isize;
725            buckets[buckets_index2(c0 as usize, f1 & !f0)] += 1;
726
727            c0 = t[(i - 1) as usize] as isize;
728            f0 = usize::from(c0 > c1 - f1 as isize);
729            sa[m as usize] = i as SaSint;
730            m -= (f0 & !f1) as isize;
731            buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
732
733            c1 = t[(i - 2) as usize] as isize;
734            f1 = usize::from(c1 > c0 - f0 as isize);
735            sa[m as usize] = (i - 1) as SaSint;
736            m -= (f1 & !f0) as isize;
737            buckets[buckets_index2(c0 as usize, f1 & !f0)] += 1;
738
739            c0 = t[(i - 3) as usize] as isize;
740            f0 = usize::from(c0 > c1 - f1 as isize);
741            sa[m as usize] = (i - 2) as SaSint;
742            m -= (f0 & !f1) as isize;
743            buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
744
745            i -= 4;
746        }
747
748        j -= 64 + 3;
749        while i >= j {
750            c1 = c0;
751            c0 = t[i as usize] as isize;
752            f1 = f0;
753            f0 = usize::from(c0 > c1 - f1 as isize);
754            sa[m as usize] = (i + 1) as SaSint;
755            m -= (f0 & !f1) as isize;
756            buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
757            i -= 1;
758        }
759
760        c1 = if i >= 0 { t[i as usize] as isize } else { -1 };
761        f1 = usize::from(c1 > c0 - f0 as isize);
762        sa[m as usize] = (i + 1) as SaSint;
763        m -= (f1 & !f0) as isize;
764        buckets[buckets_index2(c0 as usize, f1 & !f0)] += 1;
765    }
766
767    (omp_block_start + omp_block_size - 1 - m) as SaSint
768}
769
770fn count_and_gather_compacted_lms_suffixes_32s_2k(
771    t: &[SaSint],
772    sa: &mut [SaSint],
773    n: SaSint,
774    k: SaSint,
775    buckets: &mut [SaSint],
776    omp_block_start: isize,
777    omp_block_size: isize,
778) -> SaSint {
779    buckets[..2 * k as usize].fill(0);
780    let mut m = omp_block_start + omp_block_size - 1;
781
782    if omp_block_size > 0 {
783        let mut j = m + 1;
784        let mut c0 = t[m as usize] as isize;
785        let mut c1 = -1isize;
786        while j < n as isize {
787            c1 = t[j as usize] as isize;
788            if c1 != c0 {
789                break;
790            }
791            j += 1;
792        }
793
794        let mut f0 = usize::from(c0 >= c1);
795        let mut f1: usize;
796        let mut i = m - 1;
797        j = omp_block_start + 64 + 3;
798        while i >= j {
799            c1 = t[i as usize] as isize;
800            f1 = usize::from(c1 > c0 - f0 as isize);
801            sa[m as usize] = (i + 1) as SaSint;
802            m -= (f1 & !f0 & usize::from(c0 >= 0)) as isize;
803            buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
804
805            c0 = t[(i - 1) as usize] as isize;
806            f0 = usize::from(c0 > c1 - f1 as isize);
807            sa[m as usize] = i as SaSint;
808            m -= (f0 & !f1 & usize::from(c1 >= 0)) as isize;
809            buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
810
811            c1 = t[(i - 2) as usize] as isize;
812            f1 = usize::from(c1 > c0 - f0 as isize);
813            sa[m as usize] = (i - 1) as SaSint;
814            m -= (f1 & !f0 & usize::from(c0 >= 0)) as isize;
815            buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
816
817            c0 = t[(i - 3) as usize] as isize;
818            f0 = usize::from(c0 > c1 - f1 as isize);
819            sa[m as usize] = (i - 2) as SaSint;
820            m -= (f0 & !f1 & usize::from(c1 >= 0)) as isize;
821            buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
822
823            i -= 4;
824        }
825
826        j -= 64 + 3;
827        while i >= j {
828            c1 = c0;
829            c0 = t[i as usize] as isize;
830            f1 = f0;
831            f0 = usize::from(c0 > c1 - f1 as isize);
832            sa[m as usize] = (i + 1) as SaSint;
833            m -= (f0 & !f1 & usize::from(c1 >= 0)) as isize;
834            buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
835            i -= 1;
836        }
837
838        c1 = if i >= 0 { t[i as usize] as isize } else { -1 };
839        f1 = usize::from(c1 > c0 - f0 as isize);
840        sa[m as usize] = (i + 1) as SaSint;
841        m -= (f1 & !f0 & usize::from(c0 >= 0)) as isize;
842        buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
843    }
844
845    (omp_block_start + omp_block_size - 1 - m) as SaSint
846}
847
848fn count_and_gather_lms_suffixes_32s_4k_fs_omp(
849    t: &[SaSint],
850    sa: &mut [SaSint],
851    n: SaSint,
852    k: SaSint,
853    buckets: &mut [SaSint],
854    local_buckets: SaSint,
855    threads: SaSint,
856    thread_state: &mut [ThreadState],
857) -> SaSint {
858    if threads == 1 || n < 65_536 {
859        return count_and_gather_lms_suffixes_32s_4k(t, sa, n, k, buckets, 0, n as isize);
860    }
861
862    let thread_count = threads as usize;
863    let n_usize = n as usize;
864    let bucket_size = 4 * k as usize;
865    let block_stride = (n / threads) & !15;
866    let free_space = if local_buckets != 0 {
867        LIBSAIS_LOCAL_BUFFER_SIZE as SaSint
868    } else {
869        buckets.len() as SaSint
870    };
871    let bucket_stride = get_bucket_stride(free_space, 4 * k, threads) as usize;
872    let workspace_len = bucket_size + bucket_stride * thread_count.saturating_sub(1);
873    let mut workspace = vec![0; workspace_len];
874
875    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
876        let block_start = thread as SaSint * block_stride;
877        let block_size = if thread + 1 < thread_count {
878            block_stride
879        } else {
880            n - block_start
881        };
882        let workspace_end = workspace_len - thread * bucket_stride;
883        let workspace_start = workspace_end - bucket_size;
884        state.count = count_and_gather_lms_suffixes_32s_4k(
885            t,
886            sa,
887            n,
888            k,
889            &mut workspace[workspace_start..workspace_end],
890            block_start as isize,
891            block_size as isize,
892        );
893        state.position = block_start + block_size;
894    }
895
896    let mut m = 0usize;
897    for thread in (0..thread_count).rev() {
898        let count =
899            usize::try_from(thread_state[thread].count).expect("count must be non-negative");
900        m += count;
901        if thread + 1 != thread_count && count > 0 {
902            let src_end = usize::try_from(thread_state[thread].position)
903                .expect("position must be non-negative");
904            let src_start = src_end - count;
905            let dst_start = n_usize - m;
906            sa.copy_within(src_start..src_end, dst_start);
907        }
908    }
909
910    let accumulation_threads = thread_count - 1;
911    let block_stride = (bucket_size / accumulation_threads) & !15usize;
912    for thread in 0..accumulation_threads {
913        let block_start = thread * block_stride;
914        let block_size = if thread + 1 < accumulation_threads {
915            block_stride
916        } else {
917            bucket_size - block_start
918        };
919        accumulate_counts_s32(
920            &mut workspace,
921            block_start,
922            block_size,
923            bucket_stride,
924            accumulation_threads + 1,
925        );
926    }
927
928    buckets[..bucket_size].copy_from_slice(&workspace[..bucket_size]);
929    m as SaSint
930}
931
932fn count_and_gather_lms_suffixes_32s_2k_fs_omp(
933    t: &[SaSint],
934    sa: &mut [SaSint],
935    n: SaSint,
936    k: SaSint,
937    buckets: &mut [SaSint],
938    local_buckets: SaSint,
939    threads: SaSint,
940    thread_state: &mut [ThreadState],
941) -> SaSint {
942    if threads == 1 || n < 65_536 {
943        return count_and_gather_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as isize);
944    }
945
946    let thread_count = threads as usize;
947    let n_usize = n as usize;
948    let bucket_size = 2 * k as usize;
949    let block_stride = (n / threads) & !15;
950    let free_space = if local_buckets != 0 {
951        LIBSAIS_LOCAL_BUFFER_SIZE as SaSint
952    } else {
953        buckets.len() as SaSint
954    };
955    let bucket_stride = get_bucket_stride(free_space, 2 * k, threads) as usize;
956    let workspace_len = bucket_size + bucket_stride * thread_count.saturating_sub(1);
957    let mut workspace = vec![0; workspace_len];
958
959    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
960        let block_start = thread as SaSint * block_stride;
961        let block_size = if thread + 1 < thread_count {
962            block_stride
963        } else {
964            n - block_start
965        };
966        let workspace_end = workspace_len - thread * bucket_stride;
967        let workspace_start = workspace_end - bucket_size;
968        state.count = count_and_gather_lms_suffixes_32s_2k(
969            t,
970            sa,
971            n,
972            k,
973            &mut workspace[workspace_start..workspace_end],
974            block_start as isize,
975            block_size as isize,
976        );
977        state.position = block_start + block_size;
978    }
979
980    let mut m = 0usize;
981    for thread in (0..thread_count).rev() {
982        let count =
983            usize::try_from(thread_state[thread].count).expect("count must be non-negative");
984        m += count;
985        if thread + 1 != thread_count && count > 0 {
986            let src_end = usize::try_from(thread_state[thread].position)
987                .expect("position must be non-negative");
988            let src_start = src_end - count;
989            let dst_start = n_usize - m;
990            sa.copy_within(src_start..src_end, dst_start);
991        }
992    }
993
994    let accumulation_threads = thread_count - 1;
995    let block_stride = (bucket_size / accumulation_threads) & !15usize;
996    for thread in 0..accumulation_threads {
997        let block_start = thread * block_stride;
998        let block_size = if thread + 1 < accumulation_threads {
999            block_stride
1000        } else {
1001            bucket_size - block_start
1002        };
1003        accumulate_counts_s32(
1004            &mut workspace,
1005            block_start,
1006            block_size,
1007            bucket_stride,
1008            accumulation_threads + 1,
1009        );
1010    }
1011
1012    buckets[..bucket_size].copy_from_slice(&workspace[..bucket_size]);
1013    m as SaSint
1014}
1015
1016fn count_and_gather_compacted_lms_suffixes_32s_2k_fs_omp(
1017    t: &[SaSint],
1018    sa: &mut [SaSint],
1019    n: SaSint,
1020    k: SaSint,
1021    buckets: &mut [SaSint],
1022    _local_buckets: SaSint,
1023    threads: SaSint,
1024    thread_state: &mut [ThreadState],
1025) {
1026    if threads == 1 || n < 65_536 {
1027        count_and_gather_compacted_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as isize);
1028        return;
1029    }
1030
1031    let thread_count = threads as usize;
1032    let n_usize = n as usize;
1033    let bucket_size = 2 * k as usize;
1034    let block_stride = (n / threads) & !15;
1035    let mut workspaces = vec![vec![0; bucket_size]; thread_count];
1036    let mut gathered_runs = vec![Vec::<SaSint>::new(); thread_count];
1037
1038    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
1039        let block_start = thread as SaSint * block_stride;
1040        let block_size = if thread + 1 < thread_count {
1041            block_stride
1042        } else {
1043            n - block_start
1044        };
1045        let mut temp_sa = vec![0; n_usize + block_size as usize];
1046        state.count = count_and_gather_compacted_lms_suffixes_32s_2k(
1047            t,
1048            &mut temp_sa,
1049            n,
1050            k,
1051            &mut workspaces[thread],
1052            block_start as isize,
1053            block_size as isize,
1054        );
1055        state.position = block_start + block_size;
1056        let count = usize::try_from(state.count).expect("count must be non-negative");
1057        let src_end =
1058            n_usize + usize::try_from(state.position).expect("position must be non-negative");
1059        let src_start = src_end - count;
1060        gathered_runs[thread].extend_from_slice(&temp_sa[src_start..src_end]);
1061    }
1062
1063    let mut suffixes_before = 0usize;
1064    for thread in (0..thread_count).rev() {
1065        let count =
1066            usize::try_from(thread_state[thread].count).expect("count must be non-negative");
1067        suffixes_before += count;
1068        if count > 0 {
1069            let dst_start = n_usize - suffixes_before;
1070            let dst_end = dst_start + count;
1071            sa[dst_start..dst_end].copy_from_slice(&gathered_runs[thread]);
1072        }
1073    }
1074
1075    buckets.fill(0);
1076    for workspace in &workspaces {
1077        for (dst, src) in buckets.iter_mut().zip(workspace.iter()) {
1078            *dst += *src;
1079        }
1080    }
1081}
1082
1083fn count_and_gather_lms_suffixes_32s_4k_nofs_omp(
1084    t: &[SaSint],
1085    sa: &mut [SaSint],
1086    n: SaSint,
1087    k: SaSint,
1088    buckets: &mut [SaSint],
1089    threads: SaSint,
1090) -> SaSint {
1091    if threads > 1 && n >= 65_536 {
1092        count_lms_suffixes_32s_4k(t, n, k, buckets);
1093        gather_lms_suffixes_32s(t, sa, n)
1094    } else {
1095        count_and_gather_lms_suffixes_32s_4k(t, sa, n, k, buckets, 0, n as isize)
1096    }
1097}
1098
1099fn count_and_gather_lms_suffixes_32s_2k_nofs_omp(
1100    t: &[SaSint],
1101    sa: &mut [SaSint],
1102    n: SaSint,
1103    k: SaSint,
1104    buckets: &mut [SaSint],
1105    threads: SaSint,
1106) -> SaSint {
1107    if threads > 1 && n >= 65_536 {
1108        count_lms_suffixes_32s_2k(t, n, k, buckets);
1109        gather_lms_suffixes_32s(t, sa, n)
1110    } else {
1111        count_and_gather_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as isize)
1112    }
1113}
1114
1115fn count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(
1116    t: &[SaSint],
1117    sa: &mut [SaSint],
1118    n: SaSint,
1119    k: SaSint,
1120    buckets: &mut [SaSint],
1121    threads: SaSint,
1122) -> SaSint {
1123    if threads > 1 && n >= 65_536 {
1124        count_compacted_lms_suffixes_32s_2k(t, n, k, buckets);
1125        gather_compacted_lms_suffixes_32s(t, sa, n)
1126    } else {
1127        count_and_gather_compacted_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as isize)
1128    }
1129}
1130
1131fn count_and_gather_lms_suffixes_32s_4k_omp(
1132    t: &[SaSint],
1133    sa: &mut [SaSint],
1134    n: SaSint,
1135    k: SaSint,
1136    buckets: &mut [SaSint],
1137    local_buckets: SaSint,
1138    threads: SaSint,
1139    thread_state: &mut [ThreadState],
1140) -> SaSint {
1141    let free_space = if local_buckets != 0 {
1142        LIBSAIS_LOCAL_BUFFER_SIZE as SaSint
1143    } else {
1144        buckets.len() as SaSint
1145    };
1146    let mut max_threads = (free_space / (((4 * k) + 15) & !15)).min(threads);
1147
1148    if max_threads > 1 && n >= 65_536 && n / k >= 2 {
1149        let thread_cap = n / (16 * k);
1150        if max_threads > thread_cap {
1151            max_threads = thread_cap;
1152        }
1153        count_and_gather_lms_suffixes_32s_4k_fs_omp(
1154            t,
1155            sa,
1156            n,
1157            k,
1158            buckets,
1159            local_buckets,
1160            max_threads.max(2),
1161            thread_state,
1162        )
1163    } else if threads > 1 && n >= 65_536 {
1164        count_lms_suffixes_32s_4k(t, n, k, buckets);
1165        gather_lms_suffixes_32s(t, sa, n)
1166    } else {
1167        count_and_gather_lms_suffixes_32s_4k_nofs_omp(t, sa, n, k, buckets, threads)
1168    }
1169}
1170
1171fn count_and_gather_lms_suffixes_32s_2k_omp(
1172    t: &[SaSint],
1173    sa: &mut [SaSint],
1174    n: SaSint,
1175    k: SaSint,
1176    buckets: &mut [SaSint],
1177    local_buckets: SaSint,
1178    threads: SaSint,
1179    thread_state: &mut [ThreadState],
1180) -> SaSint {
1181    let free_space = if local_buckets != 0 {
1182        LIBSAIS_LOCAL_BUFFER_SIZE as SaSint
1183    } else {
1184        buckets.len() as SaSint
1185    };
1186    let mut max_threads = (free_space / (((2 * k) + 15) & !15)).min(threads);
1187
1188    if max_threads > 1 && n >= 65_536 && n / k >= 2 {
1189        let thread_cap = n / (8 * k);
1190        if max_threads > thread_cap {
1191            max_threads = thread_cap;
1192        }
1193        count_and_gather_lms_suffixes_32s_2k_fs_omp(
1194            t,
1195            sa,
1196            n,
1197            k,
1198            buckets,
1199            local_buckets,
1200            max_threads.max(2),
1201            thread_state,
1202        )
1203    } else if threads > 1 && n >= 65_536 {
1204        count_lms_suffixes_32s_2k(t, n, k, buckets);
1205        gather_lms_suffixes_32s(t, sa, n)
1206    } else {
1207        count_and_gather_lms_suffixes_32s_2k_nofs_omp(t, sa, n, k, buckets, threads)
1208    }
1209}
1210
1211fn count_suffixes_32s(t: &[SaSint], n: SaSint, k: SaSint, buckets: &mut [SaSint]) {
1212    buckets[..k as usize].fill(0);
1213
1214    let mut i = 0usize;
1215    let mut j = (n as usize).saturating_sub(7);
1216    while i < j {
1217        buckets[t[i] as usize] += 1;
1218        buckets[t[i + 1] as usize] += 1;
1219        buckets[t[i + 2] as usize] += 1;
1220        buckets[t[i + 3] as usize] += 1;
1221        buckets[t[i + 4] as usize] += 1;
1222        buckets[t[i + 5] as usize] += 1;
1223        buckets[t[i + 6] as usize] += 1;
1224        buckets[t[i + 7] as usize] += 1;
1225        i += 8;
1226    }
1227
1228    j += 7;
1229    while i < j {
1230        buckets[t[i] as usize] += 1;
1231        i += 1;
1232    }
1233}
1234
1235fn initialize_buckets_start_and_end_32s_6k(k: SaSint, buckets: &mut [SaSint]) {
1236    let k = k as usize;
1237    let mut sum = 0;
1238    for j in 0..k {
1239        let i = buckets_index4(j, 0);
1240        buckets[4 * k + j] = sum;
1241        sum += buckets[i] + buckets[i + 1] + buckets[i + 2] + buckets[i + 3];
1242        buckets[5 * k + j] = sum;
1243    }
1244}
1245
1246fn initialize_buckets_start_and_end_32s_4k(k: SaSint, buckets: &mut [SaSint]) {
1247    let k = k as usize;
1248    let mut sum = 0;
1249    for j in 0..k {
1250        let i = buckets_index2(j, 0);
1251        buckets[2 * k + j] = sum;
1252        sum += buckets[i] + buckets[i + 1];
1253        buckets[3 * k + j] = sum;
1254    }
1255}
1256
1257fn initialize_buckets_end_32s_2k(k: SaSint, buckets: &mut [SaSint]) {
1258    let mut sum0 = 0;
1259    for j in 0..k as usize {
1260        let i = buckets_index2(j, 0);
1261        sum0 += buckets[i] + buckets[i + 1];
1262        buckets[i] = sum0;
1263    }
1264}
1265
1266fn initialize_buckets_start_and_end_32s_2k(k: SaSint, buckets: &mut [SaSint]) {
1267    let k = k as usize;
1268    for j in 0..k {
1269        let i = buckets_index2(j, 0);
1270        buckets[j] = buckets[i];
1271    }
1272    buckets[k] = 0;
1273    buckets.copy_within(0..k - 1, k + 1);
1274}
1275
1276fn initialize_buckets_start_32s_1k(k: SaSint, buckets: &mut [SaSint]) {
1277    let mut sum = 0;
1278    for bucket in buckets.iter_mut().take(k as usize) {
1279        let tmp = *bucket;
1280        *bucket = sum;
1281        sum += tmp;
1282    }
1283}
1284
1285fn initialize_buckets_end_32s_1k(k: SaSint, buckets: &mut [SaSint]) {
1286    let mut sum = 0;
1287    for bucket in buckets.iter_mut().take(k as usize) {
1288        sum += *bucket;
1289        *bucket = sum;
1290    }
1291}
1292
1293fn initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
1294    t: &[SaSint],
1295    k: SaSint,
1296    buckets: &mut [SaSint],
1297    first_lms_suffix: SaSint,
1298) {
1299    buckets[buckets_index2(t[first_lms_suffix as usize] as usize, 0)] += 1;
1300    buckets[buckets_index2(t[first_lms_suffix as usize] as usize, 1)] -= 1;
1301
1302    let mut sum0 = 0;
1303    let mut sum1 = 0;
1304    for j in 0..k as usize {
1305        let i = buckets_index2(j, 0);
1306        sum0 += buckets[i] + buckets[i + 1];
1307        sum1 += buckets[i + 1];
1308        buckets[i] = sum0;
1309        buckets[i + 1] = sum1;
1310    }
1311}
1312
1313fn initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
1314    t: &[SaSint],
1315    k: SaSint,
1316    buckets: &mut [SaSint],
1317    mut first_lms_suffix: SaSint,
1318) -> SaSint {
1319    let mut f0 = 0usize;
1320    let mut c0 = t[first_lms_suffix as usize] as isize;
1321
1322    loop {
1323        first_lms_suffix -= 1;
1324        if first_lms_suffix < 0 {
1325            break;
1326        }
1327        let c1 = c0;
1328        c0 = t[first_lms_suffix as usize] as isize;
1329        let f1 = f0;
1330        f0 = usize::from(c0 > c1 - f1 as isize);
1331        buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] -= 1;
1332    }
1333    buckets[buckets_index4(c0 as usize, f0 + f0)] -= 1;
1334
1335    let mut sum = 0;
1336    for j in 0..k as usize {
1337        let i = buckets_index4(j, 0);
1338        sum += buckets[i + 1] + buckets[i + 3];
1339        buckets[4 * k as usize + j] = sum;
1340    }
1341    sum
1342}
1343
1344fn initialize_buckets_for_partial_sorting_32s_6k(
1345    t: &[SaSint],
1346    k: SaSint,
1347    buckets: &mut [SaSint],
1348    first_lms_suffix: SaSint,
1349    left_suffixes_count: SaSint,
1350) {
1351    let k = k as usize;
1352    let temp_offset = 4 * k;
1353    let first_symbol = t[first_lms_suffix as usize] as usize;
1354    let mut sum0 = left_suffixes_count + 1;
1355    let mut sum1 = 0;
1356    let mut sum2 = 0;
1357
1358    for j in 0..first_symbol {
1359        let i = buckets_index4(j, 0);
1360        let tj = buckets_index2(j, 0);
1361        let ss = buckets[i];
1362        let ls = buckets[i + 1];
1363        let sl = buckets[i + 2];
1364        let ll = buckets[i + 3];
1365
1366        buckets[i] = sum0;
1367        buckets[i + 1] = sum2;
1368        buckets[i + 2] = 0;
1369        buckets[i + 3] = 0;
1370
1371        sum0 += ss + sl;
1372        sum1 += ls;
1373        sum2 += ls + ll;
1374
1375        buckets[temp_offset + tj] = sum0;
1376        buckets[temp_offset + tj + 1] = sum1;
1377    }
1378
1379    sum1 += 1;
1380    for j in first_symbol..k {
1381        let i = buckets_index4(j, 0);
1382        let tj = buckets_index2(j, 0);
1383        let ss = buckets[i];
1384        let ls = buckets[i + 1];
1385        let sl = buckets[i + 2];
1386        let ll = buckets[i + 3];
1387
1388        buckets[i] = sum0;
1389        buckets[i + 1] = sum2;
1390        buckets[i + 2] = 0;
1391        buckets[i + 3] = 0;
1392
1393        sum0 += ss + sl;
1394        sum1 += ls;
1395        sum2 += ls + ll;
1396
1397        buckets[temp_offset + tj] = sum0;
1398        buckets[temp_offset + tj + 1] = sum1;
1399    }
1400}
1401
1402fn initialize_buckets_for_radix_and_partial_sorting_32s_4k(
1403    t: &[SaSint],
1404    k: SaSint,
1405    buckets: &mut [SaSint],
1406    first_lms_suffix: SaSint,
1407) {
1408    let k = k as usize;
1409    buckets[buckets_index2(t[first_lms_suffix as usize] as usize, 0)] += 1;
1410    buckets[buckets_index2(t[first_lms_suffix as usize] as usize, 1)] -= 1;
1411
1412    let mut sum0 = 0;
1413    let mut sum1 = 0;
1414    for j in 0..k {
1415        let i = buckets_index2(j, 0);
1416        buckets[2 * k + j] = sum1;
1417        sum0 += buckets[i + 1];
1418        sum1 += buckets[i] + buckets[i + 1];
1419        buckets[i + 1] = sum0;
1420        buckets[3 * k + j] = sum1;
1421    }
1422}
1423
1424fn count_and_gather_compacted_lms_suffixes_32s_2k_omp(
1425    t: &[SaSint],
1426    sa: &mut [SaSint],
1427    n: SaSint,
1428    k: SaSint,
1429    buckets: &mut [SaSint],
1430    local_buckets: SaSint,
1431    threads: SaSint,
1432    thread_state: &mut [ThreadState],
1433) {
1434    let free_space = if local_buckets != 0 {
1435        LIBSAIS_LOCAL_BUFFER_SIZE as SaSint
1436    } else {
1437        buckets.len() as SaSint
1438    };
1439    let mut max_threads = (free_space / (((2 * k) + 15) & !15)).min(threads);
1440
1441    if local_buckets == 0 && max_threads > 1 && n >= 65_536 && n / k >= 2 {
1442        let thread_cap = n / (8 * k);
1443        if max_threads > thread_cap {
1444            max_threads = thread_cap;
1445        }
1446        count_and_gather_compacted_lms_suffixes_32s_2k_fs_omp(
1447            t,
1448            sa,
1449            n,
1450            k,
1451            buckets,
1452            local_buckets,
1453            max_threads.max(2),
1454            thread_state,
1455        );
1456    } else {
1457        count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(t, sa, n, k, buckets, threads);
1458    }
1459}
1460
1461fn gather_lms_suffixes_16u(
1462    t: &[u16],
1463    sa: &mut [SaSint],
1464    n: SaSint,
1465    mut m: SaSint,
1466    omp_block_start: SaSint,
1467    omp_block_size: SaSint,
1468) {
1469    if omp_block_size > 0 {
1470        let n = n as isize;
1471        let mut i: isize;
1472        let mut j = (omp_block_start + omp_block_size) as isize;
1473        let mut c0 = t[(omp_block_start + omp_block_size - 1) as usize] as isize;
1474        let mut c1 = -1isize;
1475
1476        while j < n {
1477            c1 = t[j as usize] as isize;
1478            if c1 != c0 {
1479                break;
1480            }
1481            j += 1;
1482        }
1483
1484        let mut f0 = usize::from(c0 >= c1);
1485        let mut f1: usize;
1486
1487        i = (omp_block_start + omp_block_size - 2) as isize;
1488        j = (omp_block_start + 3) as isize;
1489        while i >= j {
1490            c1 = t[i as usize] as isize;
1491            f1 = usize::from(c1 > c0 - f0 as isize);
1492            sa[m as usize] = (i + 1) as SaSint;
1493            m -= (f1 & (1 - f0)) as SaSint;
1494
1495            c0 = t[(i - 1) as usize] as isize;
1496            f0 = usize::from(c0 > c1 - f1 as isize);
1497            sa[m as usize] = i as SaSint;
1498            m -= (f0 & (1 - f1)) as SaSint;
1499
1500            c1 = t[(i - 2) as usize] as isize;
1501            f1 = usize::from(c1 > c0 - f0 as isize);
1502            sa[m as usize] = (i - 1) as SaSint;
1503            m -= (f1 & (1 - f0)) as SaSint;
1504
1505            c0 = t[(i - 3) as usize] as isize;
1506            f0 = usize::from(c0 > c1 - f1 as isize);
1507            sa[m as usize] = (i - 2) as SaSint;
1508            m -= (f0 & (1 - f1)) as SaSint;
1509
1510            i -= 4;
1511        }
1512
1513        j -= 3;
1514        while i >= j {
1515            c1 = c0;
1516            c0 = t[i as usize] as isize;
1517            f1 = f0;
1518            f0 = usize::from(c0 > c1 - f1 as isize);
1519            sa[m as usize] = (i + 1) as SaSint;
1520            m -= (f0 & (1 - f1)) as SaSint;
1521            i -= 1;
1522        }
1523
1524        sa[m as usize] = (i + 1) as SaSint;
1525    }
1526}
1527
1528fn count_and_gather_lms_suffixes_16u(
1529    t: &[u16],
1530    sa: &mut [SaSint],
1531    n: SaSint,
1532    buckets: &mut [SaSint],
1533    omp_block_start: SaSint,
1534    omp_block_size: SaSint,
1535) -> SaSint {
1536    buckets[..4 * ALPHABET_SIZE].fill(0);
1537
1538    let mut m = (omp_block_start + omp_block_size - 1) as isize;
1539
1540    if omp_block_size > 0 {
1541        let n = n as isize;
1542        let mut i: isize;
1543        let mut j = m + 1;
1544        let mut c0 = t[m as usize] as isize;
1545        let mut c1 = -1isize;
1546
1547        while j < n {
1548            c1 = t[j as usize] as isize;
1549            if c1 != c0 {
1550                break;
1551            }
1552            j += 1;
1553        }
1554
1555        let mut f0 = usize::from(c0 >= c1);
1556        let mut f1: usize;
1557
1558        i = m - 1;
1559        j = (omp_block_start + 3) as isize;
1560        while i >= j {
1561            c1 = t[i as usize] as isize;
1562            f1 = usize::from(c1 > c0 - f0 as isize);
1563            sa[m as usize] = (i + 1) as SaSint;
1564            m -= (f1 & (1 - f0)) as isize;
1565            buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
1566
1567            c0 = t[(i - 1) as usize] as isize;
1568            f0 = usize::from(c0 > c1 - f1 as isize);
1569            sa[m as usize] = i as SaSint;
1570            m -= (f0 & (1 - f1)) as isize;
1571            buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
1572
1573            c1 = t[(i - 2) as usize] as isize;
1574            f1 = usize::from(c1 > c0 - f0 as isize);
1575            sa[m as usize] = (i - 1) as SaSint;
1576            m -= (f1 & (1 - f0)) as isize;
1577            buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
1578
1579            c0 = t[(i - 3) as usize] as isize;
1580            f0 = usize::from(c0 > c1 - f1 as isize);
1581            sa[m as usize] = (i - 2) as SaSint;
1582            m -= (f0 & (1 - f1)) as isize;
1583            buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
1584
1585            i -= 4;
1586        }
1587
1588        j -= 3;
1589        while i >= j {
1590            c1 = c0;
1591            c0 = t[i as usize] as isize;
1592            f1 = f0;
1593            f0 = usize::from(c0 > c1 - f1 as isize);
1594            sa[m as usize] = (i + 1) as SaSint;
1595            m -= (f0 & (1 - f1)) as isize;
1596            buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
1597            i -= 1;
1598        }
1599
1600        c1 = if i >= 0 { t[i as usize] as isize } else { -1 };
1601        f1 = usize::from(c1 > c0 - f0 as isize);
1602        sa[m as usize] = (i + 1) as SaSint;
1603        m -= (f1 & (1 - f0)) as isize;
1604        buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
1605    }
1606
1607    omp_block_start + omp_block_size - 1 - m as SaSint
1608}
1609
1610fn gather_lms_suffixes_16u_omp(
1611    t: &[u16],
1612    sa: &mut [SaSint],
1613    n: SaSint,
1614    threads: SaSint,
1615    thread_state: &mut [ThreadState],
1616) {
1617    if threads == 1 || n < 65_536 || thread_state.is_empty() {
1618        gather_lms_suffixes_16u(t, sa, n, n - 1, 0, n);
1619        return;
1620    }
1621
1622    let thread_count = threads as usize;
1623    let block_stride = (n / threads) & !15;
1624    let mut suffix_counts_after = vec![0; thread_count];
1625    let mut m = 0;
1626    for thread in (0..thread_count).rev() {
1627        suffix_counts_after[thread] = m;
1628        m += thread_state[thread].m;
1629    }
1630
1631    for thread in 0..thread_count {
1632        let block_start = thread as SaSint * block_stride;
1633        let block_size = if thread + 1 < thread_count {
1634            block_stride
1635        } else {
1636            n - block_start
1637        };
1638        gather_lms_suffixes_16u(
1639            t,
1640            sa,
1641            n,
1642            n - 1 - suffix_counts_after[thread],
1643            block_start,
1644            block_size,
1645        );
1646    }
1647
1648    for thread in 0..thread_count {
1649        if thread_state[thread].m > 0 {
1650            sa[(n - 1 - suffix_counts_after[thread]) as usize] =
1651                thread_state[thread].last_lms_suffix;
1652        }
1653    }
1654}
1655
1656fn count_and_gather_lms_suffixes_16u_omp(
1657    t: &[u16],
1658    sa: &mut [SaSint],
1659    n: SaSint,
1660    buckets: &mut [SaSint],
1661    threads: SaSint,
1662    thread_state: &mut [ThreadState],
1663) -> SaSint {
1664    if threads == 1 || n < 65_536 || thread_state.is_empty() {
1665        return count_and_gather_lms_suffixes_16u(t, sa, n, buckets, 0, n);
1666    }
1667
1668    let thread_count = threads as usize;
1669    let block_stride = (n / threads) & !15;
1670
1671    for thread in 0..thread_count {
1672        let block_start = thread as SaSint * block_stride;
1673        let block_size = if thread + 1 < thread_count {
1674            block_stride
1675        } else {
1676            n - block_start
1677        };
1678        let count = count_and_gather_lms_suffixes_16u(
1679            t,
1680            sa,
1681            n,
1682            &mut thread_state[thread].buckets,
1683            block_start,
1684            block_size,
1685        );
1686        thread_state[thread].m = count;
1687        thread_state[thread].position = block_start + block_size;
1688        if count > 0 {
1689            thread_state[thread].last_lms_suffix = sa[(block_start + block_size - 1) as usize];
1690        }
1691    }
1692
1693    buckets[..4 * ALPHABET_SIZE].fill(0);
1694    let mut m = 0;
1695    for thread in (0..thread_count).rev() {
1696        let position = thread_state[thread].position;
1697        let count = thread_state[thread].m;
1698        m += count;
1699        if thread + 1 != thread_count && count > 0 {
1700            let src_end = position as usize;
1701            let src_start = src_end - count as usize;
1702            let dst_start = (n - m) as usize;
1703            sa.copy_within(src_start..src_end, dst_start);
1704        }
1705        for s in 0..4 * ALPHABET_SIZE {
1706            let a = buckets[s];
1707            let b = thread_state[thread].buckets[s];
1708            buckets[s] = a + b;
1709            thread_state[thread].buckets[s] = a;
1710        }
1711    }
1712
1713    m
1714}
1715
1716fn initialize_buckets_start_and_end_16u(
1717    buckets: &mut [SaSint],
1718    freq: Option<&mut [SaSint]>,
1719) -> SaSint {
1720    let (count_buckets, start_end) = buckets.split_at_mut(6 * ALPHABET_SIZE);
1721    let (bucket_start, bucket_end) = start_end.split_at_mut(ALPHABET_SIZE);
1722
1723    let mut k = -1;
1724    let mut sum = 0;
1725
1726    if let Some(freq) = freq {
1727        for j in 0..ALPHABET_SIZE {
1728            let i = buckets_index4(j, 0);
1729            let total = count_buckets[i]
1730                + count_buckets[i + buckets_index4(0, 1)]
1731                + count_buckets[i + buckets_index4(0, 2)]
1732                + count_buckets[i + buckets_index4(0, 3)];
1733
1734            bucket_start[j] = sum;
1735            sum += total;
1736            bucket_end[j] = sum;
1737            if total > 0 {
1738                k = j as SaSint;
1739            }
1740            freq[j] = total;
1741        }
1742    } else {
1743        for j in 0..ALPHABET_SIZE {
1744            let i = buckets_index4(j, 0);
1745            let total = count_buckets[i]
1746                + count_buckets[i + buckets_index4(0, 1)]
1747                + count_buckets[i + buckets_index4(0, 2)]
1748                + count_buckets[i + buckets_index4(0, 3)];
1749
1750            bucket_start[j] = sum;
1751            sum += total;
1752            bucket_end[j] = sum;
1753            if total > 0 {
1754                k = j as SaSint;
1755            }
1756        }
1757    }
1758
1759    k + 1
1760}
1761
1762fn initialize_buckets_for_lms_suffixes_radix_sort_16u(
1763    t: &[u16],
1764    buckets: &mut [SaSint],
1765    mut first_lms_suffix: SaSint,
1766) -> SaSint {
1767    let mut f0 = 0usize;
1768    let mut c0 = t[first_lms_suffix as usize] as isize;
1769
1770    loop {
1771        first_lms_suffix -= 1;
1772        if first_lms_suffix < 0 {
1773            break;
1774        }
1775
1776        let c1 = c0;
1777        c0 = t[first_lms_suffix as usize] as isize;
1778        let f1 = f0;
1779        f0 = usize::from(c0 > c1 - f1 as isize);
1780        buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] -= 1;
1781    }
1782
1783    buckets[buckets_index4(c0 as usize, f0 + f0)] -= 1;
1784
1785    let (count_buckets, temp_bucket) = buckets.split_at_mut(4 * ALPHABET_SIZE);
1786    let mut sum = 0;
1787    for c in 0..ALPHABET_SIZE {
1788        let i = buckets_index4(c, 0);
1789        let j = buckets_index2(c, 0);
1790        temp_bucket[j + buckets_index2(0, 1)] = sum;
1791        sum += count_buckets[i + buckets_index4(0, 1)] + count_buckets[i + buckets_index4(0, 3)];
1792        temp_bucket[j] = sum;
1793    }
1794
1795    sum
1796}
1797
1798fn radix_sort_lms_suffixes_16u(
1799    t: &[u16],
1800    sa: &mut [SaSint],
1801    induction_bucket: &mut [SaSint],
1802    omp_block_start: SaSint,
1803    omp_block_size: SaSint,
1804) {
1805    let mut i = omp_block_start + omp_block_size - 1;
1806    let mut j = omp_block_start + 64 + 3;
1807    while i >= j {
1808        let p0 = sa[i as usize];
1809        induction_bucket[buckets_index2(t[p0 as usize] as usize, 0)] -= 1;
1810        sa[induction_bucket[buckets_index2(t[p0 as usize] as usize, 0)] as usize] = p0;
1811
1812        let p1 = sa[(i - 1) as usize];
1813        induction_bucket[buckets_index2(t[p1 as usize] as usize, 0)] -= 1;
1814        sa[induction_bucket[buckets_index2(t[p1 as usize] as usize, 0)] as usize] = p1;
1815
1816        let p2 = sa[(i - 2) as usize];
1817        induction_bucket[buckets_index2(t[p2 as usize] as usize, 0)] -= 1;
1818        sa[induction_bucket[buckets_index2(t[p2 as usize] as usize, 0)] as usize] = p2;
1819
1820        let p3 = sa[(i - 3) as usize];
1821        induction_bucket[buckets_index2(t[p3 as usize] as usize, 0)] -= 1;
1822        sa[induction_bucket[buckets_index2(t[p3 as usize] as usize, 0)] as usize] = p3;
1823
1824        i -= 4;
1825    }
1826
1827    j -= 64 + 3;
1828    while i >= j {
1829        let p = sa[i as usize];
1830        induction_bucket[buckets_index2(t[p as usize] as usize, 0)] -= 1;
1831        sa[induction_bucket[buckets_index2(t[p as usize] as usize, 0)] as usize] = p;
1832        i -= 1;
1833    }
1834}
1835
1836fn radix_sort_lms_suffixes_16u_omp(
1837    t: &[u16],
1838    sa: &mut [SaSint],
1839    n: SaSint,
1840    m: SaSint,
1841    flags: SaSint,
1842    buckets: &mut [SaSint],
1843    threads: SaSint,
1844    thread_state: &mut [ThreadState],
1845) {
1846    if (flags & LIBSAIS_FLAGS_GSA) != 0 {
1847        buckets[4 * ALPHABET_SIZE] -= 1;
1848    }
1849    if threads == 1 || n < 65_536 || m < 65_536 || thread_state.is_empty() {
1850        radix_sort_lms_suffixes_16u(t, sa, &mut buckets[4 * ALPHABET_SIZE..], n - m + 1, m - 1);
1851        return;
1852    }
1853
1854    let thread_count = threads as usize;
1855    for thread in 0..thread_count {
1856        let (src_buckets, state_buckets) = (
1857            &buckets[4 * ALPHABET_SIZE..],
1858            &mut thread_state[thread].buckets,
1859        );
1860        for c in 0..ALPHABET_SIZE {
1861            let i = buckets_index2(c, 0);
1862            let j = buckets_index4(c, 1);
1863            state_buckets[i] = src_buckets[i] - state_buckets[j];
1864        }
1865
1866        let mut block_start = 0;
1867        let mut block_size = thread_state[thread].m;
1868        for idx in (thread..thread_count).rev() {
1869            block_start += thread_state[idx].m;
1870        }
1871
1872        if block_start == m && block_size > 0 {
1873            block_start -= 1;
1874            block_size -= 1;
1875        }
1876
1877        radix_sort_lms_suffixes_16u(
1878            t,
1879            sa,
1880            &mut thread_state[thread].buckets,
1881            n - block_start,
1882            block_size,
1883        );
1884    }
1885}
1886
1887fn radix_sort_lms_suffixes_32s_6k(
1888    t: &[SaSint],
1889    sa: &mut [SaSint],
1890    induction_bucket: &mut [SaSint],
1891    omp_block_start: SaSint,
1892    omp_block_size: SaSint,
1893) {
1894    let mut i = omp_block_start + omp_block_size - 1;
1895    let mut j = omp_block_start + 64 + 3;
1896    while i >= j {
1897        let p0 = sa[i as usize];
1898        induction_bucket[t[p0 as usize] as usize] -= 1;
1899        sa[induction_bucket[t[p0 as usize] as usize] as usize] = p0;
1900        let p1 = sa[(i - 1) as usize];
1901        induction_bucket[t[p1 as usize] as usize] -= 1;
1902        sa[induction_bucket[t[p1 as usize] as usize] as usize] = p1;
1903        let p2 = sa[(i - 2) as usize];
1904        induction_bucket[t[p2 as usize] as usize] -= 1;
1905        sa[induction_bucket[t[p2 as usize] as usize] as usize] = p2;
1906        let p3 = sa[(i - 3) as usize];
1907        induction_bucket[t[p3 as usize] as usize] -= 1;
1908        sa[induction_bucket[t[p3 as usize] as usize] as usize] = p3;
1909        i -= 4;
1910    }
1911
1912    j -= 64 + 3;
1913    while i >= j {
1914        let p = sa[i as usize];
1915        induction_bucket[t[p as usize] as usize] -= 1;
1916        sa[induction_bucket[t[p as usize] as usize] as usize] = p;
1917        i -= 1;
1918    }
1919}
1920
1921fn radix_sort_lms_suffixes_32s_2k(
1922    t: &[SaSint],
1923    sa: &mut [SaSint],
1924    induction_bucket: &mut [SaSint],
1925    omp_block_start: SaSint,
1926    omp_block_size: SaSint,
1927) {
1928    let mut i = omp_block_start + omp_block_size - 1;
1929    let mut j = omp_block_start + 64 + 3;
1930    while i >= j {
1931        let p0 = sa[i as usize];
1932        induction_bucket[buckets_index2(t[p0 as usize] as usize, 0)] -= 1;
1933        sa[induction_bucket[buckets_index2(t[p0 as usize] as usize, 0)] as usize] = p0;
1934        let p1 = sa[(i - 1) as usize];
1935        induction_bucket[buckets_index2(t[p1 as usize] as usize, 0)] -= 1;
1936        sa[induction_bucket[buckets_index2(t[p1 as usize] as usize, 0)] as usize] = p1;
1937        let p2 = sa[(i - 2) as usize];
1938        induction_bucket[buckets_index2(t[p2 as usize] as usize, 0)] -= 1;
1939        sa[induction_bucket[buckets_index2(t[p2 as usize] as usize, 0)] as usize] = p2;
1940        let p3 = sa[(i - 3) as usize];
1941        induction_bucket[buckets_index2(t[p3 as usize] as usize, 0)] -= 1;
1942        sa[induction_bucket[buckets_index2(t[p3 as usize] as usize, 0)] as usize] = p3;
1943        i -= 4;
1944    }
1945
1946    j -= 64 + 3;
1947    while i >= j {
1948        let p = sa[i as usize];
1949        induction_bucket[buckets_index2(t[p as usize] as usize, 0)] -= 1;
1950        sa[induction_bucket[buckets_index2(t[p as usize] as usize, 0)] as usize] = p;
1951        i -= 1;
1952    }
1953}
1954
1955fn radix_sort_lms_suffixes_32s_block_gather(
1956    t: &[SaSint],
1957    sa: &[SaSint],
1958    cache: &mut [ThreadCache],
1959    omp_block_start: SaSint,
1960    omp_block_size: SaSint,
1961) {
1962    if omp_block_size <= 0 {
1963        return;
1964    }
1965
1966    let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
1967    let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
1968    let cache_base = if cache.len() >= start + size {
1969        0
1970    } else {
1971        start
1972    };
1973    let mut i = start;
1974    let mut j = if size > 67 { start + size - 67 } else { start };
1975
1976    while i < j {
1977        for current in [i, i + 1, i + 2, i + 3] {
1978            let ci = current - cache_base;
1979            let index = sa[current];
1980            cache[ci].index = index;
1981            cache[ci].symbol = t[index as usize];
1982        }
1983        i += 4;
1984    }
1985
1986    j = if size > 67 { j + 67 } else { start + size };
1987    while i < j {
1988        let ci = i - cache_base;
1989        let index = sa[i];
1990        cache[ci].index = index;
1991        cache[ci].symbol = t[index as usize];
1992        i += 1;
1993    }
1994}
1995
1996fn radix_sort_lms_suffixes_32s_6k_block_sort(
1997    induction_bucket: &mut [SaSint],
1998    cache: &mut [ThreadCache],
1999    omp_block_start: SaSint,
2000    omp_block_size: SaSint,
2001) {
2002    if omp_block_size <= 0 {
2003        return;
2004    }
2005
2006    let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
2007    let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
2008    let cache_base = if cache.len() >= start + size {
2009        0
2010    } else {
2011        start
2012    };
2013    let mut i = start + size - 1;
2014    let mut j = start + 64 + 3;
2015
2016    while i >= j {
2017        for current in [i, i - 1, i - 2, i - 3] {
2018            let ci = current - cache_base;
2019            let v = cache[ci].symbol as usize;
2020            induction_bucket[v] -= 1;
2021            cache[ci].symbol = induction_bucket[v];
2022        }
2023        i -= 4;
2024    }
2025
2026    j -= 64 + 3;
2027    while i >= j {
2028        let ci = i - cache_base;
2029        let v = cache[ci].symbol as usize;
2030        induction_bucket[v] -= 1;
2031        cache[ci].symbol = induction_bucket[v];
2032        if i == 0 {
2033            break;
2034        }
2035        i -= 1;
2036    }
2037}
2038
2039fn radix_sort_lms_suffixes_32s_2k_block_sort(
2040    induction_bucket: &mut [SaSint],
2041    cache: &mut [ThreadCache],
2042    omp_block_start: SaSint,
2043    omp_block_size: SaSint,
2044) {
2045    if omp_block_size <= 0 {
2046        return;
2047    }
2048
2049    let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
2050    let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
2051    let cache_base = if cache.len() >= start + size {
2052        0
2053    } else {
2054        start
2055    };
2056    let mut i = start + size - 1;
2057    let mut j = start + 64 + 3;
2058
2059    while i >= j {
2060        for current in [i, i - 1, i - 2, i - 3] {
2061            let ci = current - cache_base;
2062            let v = buckets_index2(cache[ci].symbol as usize, 0);
2063            induction_bucket[v] -= 1;
2064            cache[ci].symbol = induction_bucket[v];
2065        }
2066        i -= 4;
2067    }
2068
2069    j -= 64 + 3;
2070    while i >= j {
2071        let ci = i - cache_base;
2072        let v = buckets_index2(cache[ci].symbol as usize, 0);
2073        induction_bucket[v] -= 1;
2074        cache[ci].symbol = induction_bucket[v];
2075        if i == 0 {
2076            break;
2077        }
2078        i -= 1;
2079    }
2080}
2081
2082fn radix_sort_lms_suffixes_32s_6k_block_omp(
2083    t: &[SaSint],
2084    sa: &mut [SaSint],
2085    induction_bucket: &mut [SaSint],
2086    cache: &mut [ThreadCache],
2087    block_start: SaSint,
2088    block_size: SaSint,
2089    threads: SaSint,
2090) {
2091    if threads <= 1 || block_size < 16_384 {
2092        radix_sort_lms_suffixes_32s_6k(t, sa, induction_bucket, block_start, block_size);
2093        return;
2094    }
2095
2096    radix_sort_lms_suffixes_32s_block_gather(t, sa, cache, block_start, block_size);
2097    radix_sort_lms_suffixes_32s_6k_block_sort(induction_bucket, cache, block_start, block_size);
2098    place_cached_suffixes(sa, cache, block_start, block_size);
2099}
2100
2101fn radix_sort_lms_suffixes_32s_2k_block_omp(
2102    t: &[SaSint],
2103    sa: &mut [SaSint],
2104    induction_bucket: &mut [SaSint],
2105    cache: &mut [ThreadCache],
2106    block_start: SaSint,
2107    block_size: SaSint,
2108    threads: SaSint,
2109) {
2110    if threads <= 1 || block_size < 16_384 {
2111        radix_sort_lms_suffixes_32s_2k(t, sa, induction_bucket, block_start, block_size);
2112        return;
2113    }
2114
2115    radix_sort_lms_suffixes_32s_block_gather(t, sa, cache, block_start, block_size);
2116    radix_sort_lms_suffixes_32s_2k_block_sort(induction_bucket, cache, block_start, block_size);
2117    place_cached_suffixes(sa, cache, block_start, block_size);
2118}
2119
2120fn radix_sort_lms_suffixes_32s_6k_omp(
2121    t: &[SaSint],
2122    sa: &mut [SaSint],
2123    n: SaSint,
2124    m: SaSint,
2125    induction_bucket: &mut [SaSint],
2126    threads: SaSint,
2127) {
2128    if threads <= 1 || m < 65_536 {
2129        radix_sort_lms_suffixes_32s_6k(t, sa, induction_bucket, n - m + 1, m - 1);
2130        return;
2131    }
2132
2133    let threads_usize = usize::try_from(threads).expect("threads must be positive");
2134    let mut cache = vec![ThreadCache::default(); threads_usize * PER_THREAD_CACHE_SIZE];
2135    let mut block_start = 0usize;
2136    let m_usize = usize::try_from(m).expect("m must be non-negative");
2137    let n_usize = usize::try_from(n).expect("n must be non-negative");
2138    let last = m_usize - 1;
2139
2140    while block_start < last {
2141        let block_end = (block_start + threads_usize * PER_THREAD_CACHE_SIZE).min(last);
2142        radix_sort_lms_suffixes_32s_6k_block_omp(
2143            t,
2144            sa,
2145            induction_bucket,
2146            &mut cache,
2147            (n_usize - block_end) as SaSint,
2148            (block_end - block_start) as SaSint,
2149            threads,
2150        );
2151        block_start = block_end;
2152    }
2153}
2154
2155fn radix_sort_lms_suffixes_32s_2k_omp(
2156    t: &[SaSint],
2157    sa: &mut [SaSint],
2158    n: SaSint,
2159    m: SaSint,
2160    induction_bucket: &mut [SaSint],
2161    threads: SaSint,
2162) {
2163    if threads <= 1 || m < 65_536 {
2164        radix_sort_lms_suffixes_32s_2k(t, sa, induction_bucket, n - m + 1, m - 1);
2165        return;
2166    }
2167
2168    let threads_usize = usize::try_from(threads).expect("threads must be positive");
2169    let mut cache = vec![ThreadCache::default(); threads_usize * PER_THREAD_CACHE_SIZE];
2170    let mut block_start = 0usize;
2171    let m_usize = usize::try_from(m).expect("m must be non-negative");
2172    let n_usize = usize::try_from(n).expect("n must be non-negative");
2173    let last = m_usize - 1;
2174
2175    while block_start < last {
2176        let block_end = (block_start + threads_usize * PER_THREAD_CACHE_SIZE).min(last);
2177        radix_sort_lms_suffixes_32s_2k_block_omp(
2178            t,
2179            sa,
2180            induction_bucket,
2181            &mut cache,
2182            (n_usize - block_end) as SaSint,
2183            (block_end - block_start) as SaSint,
2184            threads,
2185        );
2186        block_start = block_end;
2187    }
2188}
2189
2190fn radix_sort_lms_suffixes_32s_1k(
2191    t: &[SaSint],
2192    sa: &mut [SaSint],
2193    n: SaSint,
2194    buckets: &mut [SaSint],
2195) -> SaSint {
2196    let mut i = n - 2;
2197    let mut m = 0;
2198    let mut f0 = 1usize;
2199    let mut f1: usize;
2200    let mut c0 = t[(n - 1) as usize] as isize;
2201    let mut c1: isize;
2202    let mut c2 = 0isize;
2203
2204    while i >= 64 + 3 {
2205        c1 = t[i as usize] as isize;
2206        f1 = usize::from(c1 > c0 - f0 as isize);
2207        if (f1 & !f0) != 0 {
2208            c2 = c0;
2209            buckets[c2 as usize] -= 1;
2210            sa[buckets[c2 as usize] as usize] = i + 1;
2211            m += 1;
2212        }
2213        c0 = t[(i - 1) as usize] as isize;
2214        f0 = usize::from(c0 > c1 - f1 as isize);
2215        if (f0 & !f1) != 0 {
2216            c2 = c1;
2217            buckets[c2 as usize] -= 1;
2218            sa[buckets[c2 as usize] as usize] = i;
2219            m += 1;
2220        }
2221        c1 = t[(i - 2) as usize] as isize;
2222        f1 = usize::from(c1 > c0 - f0 as isize);
2223        if (f1 & !f0) != 0 {
2224            c2 = c0;
2225            buckets[c2 as usize] -= 1;
2226            sa[buckets[c2 as usize] as usize] = i - 1;
2227            m += 1;
2228        }
2229        c0 = t[(i - 3) as usize] as isize;
2230        f0 = usize::from(c0 > c1 - f1 as isize);
2231        if (f0 & !f1) != 0 {
2232            c2 = c1;
2233            buckets[c2 as usize] -= 1;
2234            sa[buckets[c2 as usize] as usize] = i - 2;
2235            m += 1;
2236        }
2237        i -= 4;
2238    }
2239
2240    while i >= 0 {
2241        c1 = c0;
2242        c0 = t[i as usize] as isize;
2243        f1 = f0;
2244        f0 = usize::from(c0 > c1 - f1 as isize);
2245        if (f0 & !f1) != 0 {
2246            c2 = c1;
2247            buckets[c2 as usize] -= 1;
2248            sa[buckets[c2 as usize] as usize] = i + 1;
2249            m += 1;
2250        }
2251        i -= 1;
2252    }
2253
2254    if m > 1 {
2255        sa[buckets[c2 as usize] as usize] = 0;
2256    }
2257
2258    m
2259}
2260
2261fn radix_sort_set_markers_32s_6k(
2262    sa: &mut [SaSint],
2263    induction_bucket: &[SaSint],
2264    omp_block_start: SaSint,
2265    omp_block_size: SaSint,
2266) {
2267    let mut i = omp_block_start;
2268    let mut j = omp_block_start + omp_block_size - 64 - 3;
2269
2270    while i < j {
2271        sa[induction_bucket[i as usize] as usize] |= SAINT_MIN;
2272        sa[induction_bucket[(i + 1) as usize] as usize] |= SAINT_MIN;
2273        sa[induction_bucket[(i + 2) as usize] as usize] |= SAINT_MIN;
2274        sa[induction_bucket[(i + 3) as usize] as usize] |= SAINT_MIN;
2275        i += 4;
2276    }
2277
2278    j += 64 + 3;
2279    while i < j {
2280        sa[induction_bucket[i as usize] as usize] |= SAINT_MIN;
2281        i += 1;
2282    }
2283}
2284
2285fn radix_sort_set_markers_32s_4k(
2286    sa: &mut [SaSint],
2287    induction_bucket: &[SaSint],
2288    omp_block_start: SaSint,
2289    omp_block_size: SaSint,
2290) {
2291    let mut i = omp_block_start;
2292    let mut j = omp_block_start + omp_block_size - 64 - 3;
2293
2294    while i < j {
2295        sa[induction_bucket[buckets_index2(i as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2296        sa[induction_bucket[buckets_index2((i + 1) as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2297        sa[induction_bucket[buckets_index2((i + 2) as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2298        sa[induction_bucket[buckets_index2((i + 3) as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2299        i += 4;
2300    }
2301
2302    j += 64 + 3;
2303    while i < j {
2304        sa[induction_bucket[buckets_index2(i as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2305        i += 1;
2306    }
2307}
2308
2309fn radix_sort_set_markers_32s_6k_omp(
2310    sa: &mut [SaSint],
2311    k: SaSint,
2312    induction_bucket: &[SaSint],
2313    threads: SaSint,
2314) {
2315    if k <= 1 {
2316        return;
2317    }
2318
2319    if threads <= 1 || k < 65_536 {
2320        radix_sort_set_markers_32s_6k(sa, induction_bucket, 0, k - 1);
2321        return;
2322    }
2323
2324    let threads_usize = usize::try_from(threads).expect("threads must be positive");
2325    let last = usize::try_from(k - 1).expect("k must be positive");
2326    let stride = (last / threads_usize) & !15usize;
2327
2328    {
2329        let sa_ptr = SyncMutPtr::new(sa);
2330        run_rayon_with_threads(threads_usize, || {
2331            (0..threads_usize).into_par_iter().for_each(|thread| {
2332                let start = thread * stride;
2333                let end = if thread + 1 == threads_usize {
2334                    last
2335                } else {
2336                    start + stride
2337                };
2338                if end > start {
2339                    let sa = unsafe { sa_ptr.as_slice() };
2340                    radix_sort_set_markers_32s_6k(
2341                        sa,
2342                        induction_bucket,
2343                        start as SaSint,
2344                        (end - start) as SaSint,
2345                    );
2346                }
2347            });
2348        });
2349    }
2350}
2351
2352fn radix_sort_set_markers_32s_4k_omp(
2353    sa: &mut [SaSint],
2354    k: SaSint,
2355    induction_bucket: &[SaSint],
2356    threads: SaSint,
2357) {
2358    if k <= 1 {
2359        return;
2360    }
2361
2362    if threads <= 1 || k < 65_536 {
2363        radix_sort_set_markers_32s_4k(sa, induction_bucket, 0, k - 1);
2364        return;
2365    }
2366
2367    let threads_usize = usize::try_from(threads).expect("threads must be positive");
2368    let last = usize::try_from(k - 1).expect("k must be positive");
2369    let stride = (last / threads_usize) & !15usize;
2370
2371    {
2372        let sa_ptr = SyncMutPtr::new(sa);
2373        run_rayon_with_threads(threads_usize, || {
2374            (0..threads_usize).into_par_iter().for_each(|thread| {
2375                let start = thread * stride;
2376                let end = if thread + 1 == threads_usize {
2377                    last
2378                } else {
2379                    start + stride
2380                };
2381                if end > start {
2382                    let sa = unsafe { sa_ptr.as_slice() };
2383                    radix_sort_set_markers_32s_4k(
2384                        sa,
2385                        induction_bucket,
2386                        start as SaSint,
2387                        (end - start) as SaSint,
2388                    );
2389                }
2390            });
2391        });
2392    }
2393}
2394
2395fn initialize_buckets_for_partial_sorting_16u(
2396    t: &[u16],
2397    buckets: &mut [SaSint],
2398    first_lms_suffix: SaSint,
2399    left_suffixes_count: SaSint,
2400) {
2401    buckets[buckets_index4(t[first_lms_suffix as usize] as usize, 1)] += 1;
2402
2403    let (front, temp_bucket) = buckets.split_at_mut(4 * ALPHABET_SIZE);
2404    let mut sum0 = left_suffixes_count + 1;
2405    let mut sum1 = 0;
2406
2407    for c in 0..ALPHABET_SIZE {
2408        let i = buckets_index4(c, 0);
2409        let j = buckets_index2(c, 0);
2410
2411        temp_bucket[j + buckets_index2(0, 0)] = sum0;
2412
2413        sum0 += front[i + buckets_index4(0, 0)] + front[i + buckets_index4(0, 2)];
2414        sum1 += front[i + buckets_index4(0, 1)];
2415
2416        front[j + buckets_index2(0, 0)] = sum0;
2417        front[j + buckets_index2(0, 1)] = sum1;
2418    }
2419}
2420
2421fn partial_sorting_shift_markers_32s_6k_omp(
2422    sa: &mut [SaSint],
2423    k: SaSint,
2424    buckets: &[SaSint],
2425    threads: SaSint,
2426) {
2427    let k_usize = usize::try_from(k).expect("k must be non-negative");
2428    let temp_bucket = &buckets[4 * k_usize..];
2429    let thread_count = if threads > 1 && k >= 65536 {
2430        usize::try_from(threads).expect("threads must be positive")
2431    } else {
2432        1
2433    };
2434    {
2435        let sa_ptr = SyncMutPtr::new(sa);
2436        let buckets_ref: &[SaSint] = buckets;
2437        let temp_bucket_ref: &[SaSint] = temp_bucket;
2438        run_rayon_with_threads(thread_count, || {
2439            (0..thread_count).into_par_iter().for_each(|t| {
2440                let mut c = k_usize as isize - 1 - t as isize;
2441                let sa = unsafe { sa_ptr.as_slice() };
2442                while c >= 1 {
2443                    let c_usize = c as usize;
2444                    let mut i = buckets_ref[buckets_index4(c_usize, 0)] - 1;
2445                    let mut j = temp_bucket_ref[buckets_index2(c_usize - 1, 0)] + 3;
2446                    let mut s = SAINT_MIN;
2447
2448                    while i >= j {
2449                        let p0 = sa[i as usize];
2450                        let q0 = (p0 & SAINT_MIN) ^ s;
2451                        s ^= q0;
2452                        sa[i as usize] = p0 ^ q0;
2453
2454                        let p1 = sa[(i - 1) as usize];
2455                        let q1 = (p1 & SAINT_MIN) ^ s;
2456                        s ^= q1;
2457                        sa[(i - 1) as usize] = p1 ^ q1;
2458
2459                        let p2 = sa[(i - 2) as usize];
2460                        let q2 = (p2 & SAINT_MIN) ^ s;
2461                        s ^= q2;
2462                        sa[(i - 2) as usize] = p2 ^ q2;
2463
2464                        let p3 = sa[(i - 3) as usize];
2465                        let q3 = (p3 & SAINT_MIN) ^ s;
2466                        s ^= q3;
2467                        sa[(i - 3) as usize] = p3 ^ q3;
2468
2469                        i -= 4;
2470                    }
2471
2472                    j -= 3;
2473                    while i >= j {
2474                        let p = sa[i as usize];
2475                        let q = (p & SAINT_MIN) ^ s;
2476                        s ^= q;
2477                        sa[i as usize] = p ^ q;
2478                        i -= 1;
2479                    }
2480
2481                    c -= thread_count as isize;
2482                }
2483            });
2484        });
2485    }
2486}
2487
2488fn partial_sorting_shift_markers_32s_4k(sa: &mut [SaSint], n: SaSint) {
2489    let mut i = n - 1;
2490    let mut s = SUFFIX_GROUP_MARKER;
2491
2492    while i >= 3 {
2493        let p0 = sa[i as usize];
2494        let q0 =
2495            ((p0 & SUFFIX_GROUP_MARKER) ^ s) & (((p0 > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
2496        s ^= q0;
2497        sa[i as usize] = p0 ^ q0;
2498
2499        let p1 = sa[(i - 1) as usize];
2500        let q1 =
2501            ((p1 & SUFFIX_GROUP_MARKER) ^ s) & (((p1 > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
2502        s ^= q1;
2503        sa[(i - 1) as usize] = p1 ^ q1;
2504
2505        let p2 = sa[(i - 2) as usize];
2506        let q2 =
2507            ((p2 & SUFFIX_GROUP_MARKER) ^ s) & (((p2 > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
2508        s ^= q2;
2509        sa[(i - 2) as usize] = p2 ^ q2;
2510
2511        let p3 = sa[(i - 3) as usize];
2512        let q3 =
2513            ((p3 & SUFFIX_GROUP_MARKER) ^ s) & (((p3 > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
2514        s ^= q3;
2515        sa[(i - 3) as usize] = p3 ^ q3;
2516
2517        i -= 4;
2518    }
2519
2520    while i >= 0 {
2521        let p = sa[i as usize];
2522        let q = ((p & SUFFIX_GROUP_MARKER) ^ s) & (((p > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
2523        s ^= q;
2524        sa[i as usize] = p ^ q;
2525        i -= 1;
2526    }
2527}
2528
2529fn partial_sorting_shift_buckets_32s_6k(k: SaSint, buckets: &mut [SaSint]) {
2530    let temp_offset = 4 * k as usize;
2531    let mut i = buckets_index2(0, 0);
2532
2533    while i <= buckets_index2(k as usize - 1, 0) {
2534        buckets[2 * i + buckets_index4(0, 0)] = buckets[temp_offset + i + buckets_index2(0, 0)];
2535        buckets[2 * i + buckets_index4(0, 1)] = buckets[temp_offset + i + buckets_index2(0, 1)];
2536        i += buckets_index2(1, 0);
2537    }
2538}
2539
2540fn partial_sorting_scan_left_to_right_16u(
2541    t: &[u16],
2542    sa: &mut [SaSint],
2543    buckets: &mut [SaSint],
2544    mut d: SaSint,
2545    omp_block_start: SaSint,
2546    omp_block_size: SaSint,
2547) -> SaSint {
2548    let mut i = omp_block_start as isize;
2549    let mut j = (omp_block_start + omp_block_size - 64 - 1) as isize;
2550    while i < j {
2551        let mut p0 = sa[i as usize];
2552        d += SaSint::from(p0 < 0);
2553        p0 &= SAINT_MAX;
2554        let v0 = buckets_index2(
2555            t[(p0 - 1) as usize] as usize,
2556            usize::from(t[(p0 - 2) as usize] >= t[(p0 - 1) as usize]),
2557        );
2558        let mark0 = if buckets[2 * ALPHABET_SIZE + v0] != d {
2559            SAINT_MIN
2560        } else {
2561            0
2562        };
2563        let dst0 = buckets[4 * ALPHABET_SIZE + v0] as usize;
2564        sa[dst0] = (p0 - 1) | mark0;
2565        buckets[4 * ALPHABET_SIZE + v0] += 1;
2566        buckets[2 * ALPHABET_SIZE + v0] = d;
2567
2568        let mut p1 = sa[(i + 1) as usize];
2569        d += SaSint::from(p1 < 0);
2570        p1 &= SAINT_MAX;
2571        let v1 = buckets_index2(
2572            t[(p1 - 1) as usize] as usize,
2573            usize::from(t[(p1 - 2) as usize] >= t[(p1 - 1) as usize]),
2574        );
2575        let mark1 = if buckets[2 * ALPHABET_SIZE + v1] != d {
2576            SAINT_MIN
2577        } else {
2578            0
2579        };
2580        let dst1 = buckets[4 * ALPHABET_SIZE + v1] as usize;
2581        sa[dst1] = (p1 - 1) | mark1;
2582        buckets[4 * ALPHABET_SIZE + v1] += 1;
2583        buckets[2 * ALPHABET_SIZE + v1] = d;
2584
2585        i += 2;
2586    }
2587
2588    j += 64 + 1;
2589    while i < j {
2590        let mut p = sa[i as usize];
2591        d += SaSint::from(p < 0);
2592        p &= SAINT_MAX;
2593        let v = buckets_index2(
2594            t[(p - 1) as usize] as usize,
2595            usize::from(t[(p - 2) as usize] >= t[(p - 1) as usize]),
2596        );
2597        let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
2598            SAINT_MIN
2599        } else {
2600            0
2601        };
2602        let dst = buckets[4 * ALPHABET_SIZE + v] as usize;
2603        sa[dst] = (p - 1) | mark;
2604        buckets[4 * ALPHABET_SIZE + v] += 1;
2605        buckets[2 * ALPHABET_SIZE + v] = d;
2606        i += 1;
2607    }
2608
2609    d
2610}
2611
2612fn partial_sorting_scan_left_to_right_16u_block_prepare(
2613    t: &[u16],
2614    sa: &mut [SaSint],
2615    k: SaSint,
2616    buckets: &mut [SaSint],
2617    cache: &mut [ThreadCache],
2618    omp_block_start: SaSint,
2619    omp_block_size: SaSint,
2620    state: &mut ThreadState,
2621) -> SaSint {
2622    let width = 2 * k as usize;
2623    buckets[..width].fill(0);
2624    buckets[2 * ALPHABET_SIZE..2 * ALPHABET_SIZE + width].fill(0);
2625
2626    let mut count = 0usize;
2627    let mut d = 1;
2628    for i in omp_block_start as usize..(omp_block_start + omp_block_size) as usize {
2629        let mut p = sa[i];
2630        cache[count].index = p;
2631        d += SaSint::from(p < 0);
2632        p &= SAINT_MAX;
2633        let v = buckets_index2(
2634            t[(p - 1) as usize] as usize,
2635            usize::from(t[(p - 2) as usize] >= t[(p - 1) as usize]),
2636        );
2637        cache[count].symbol = v as SaSint;
2638        buckets[v] += 1;
2639        buckets[2 * ALPHABET_SIZE + v] = d;
2640        count += 1;
2641    }
2642    state.cache_entries = count;
2643    d - 1
2644}
2645
2646fn partial_sorting_scan_left_to_right_16u_block_place(
2647    sa: &mut [SaSint],
2648    buckets: &mut [SaSint],
2649    cache: &[ThreadCache],
2650    count: SaSint,
2651    mut d: SaSint,
2652) {
2653    for entry in cache.iter().take(count as usize) {
2654        let mut p = entry.index;
2655        d += SaSint::from(p < 0);
2656        p &= SAINT_MAX;
2657        let v = entry.symbol as usize;
2658        let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
2659            SAINT_MIN
2660        } else {
2661            0
2662        };
2663        let dst = buckets[v] as usize;
2664        sa[dst] = (p - 1) | mark;
2665        buckets[v] += 1;
2666        buckets[2 * ALPHABET_SIZE + v] = d;
2667    }
2668}
2669
2670fn partial_sorting_scan_left_to_right_16u_block_omp(
2671    t: &[u16],
2672    sa: &mut [SaSint],
2673    k: SaSint,
2674    buckets: &mut [SaSint],
2675    d: SaSint,
2676    block_start: SaSint,
2677    block_size: SaSint,
2678    threads: SaSint,
2679    thread_state: &mut [ThreadState],
2680) -> SaSint {
2681    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
2682        usize::try_from(threads)
2683            .expect("threads must be non-negative")
2684            .min(thread_state.len())
2685    } else {
2686        1
2687    };
2688    if thread_count <= 1 {
2689        return partial_sorting_scan_left_to_right_16u(t, sa, buckets, d, block_start, block_size);
2690    }
2691
2692    let bucket_width = 2 * k as usize;
2693    let block_stride = (block_size / thread_count as SaSint) & !15;
2694
2695    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
2696        let local_start = thread as SaSint * block_stride;
2697        let local_size = if thread + 1 < thread_count {
2698            block_stride
2699        } else {
2700            block_size - local_start
2701        };
2702        let mut local_state = ThreadState::default();
2703        state.position = partial_sorting_scan_left_to_right_16u_block_prepare(
2704            t,
2705            sa,
2706            k,
2707            &mut state.buckets,
2708            &mut state.cache,
2709            block_start + local_start,
2710            local_size,
2711            &mut local_state,
2712        );
2713        state.count = local_state.cache_entries as SaSint;
2714    }
2715
2716    let mut next_d = d;
2717    for state in thread_state.iter_mut().take(thread_count) {
2718        for c in 0..bucket_width {
2719            let a = buckets[4 * ALPHABET_SIZE + c];
2720            let b = state.buckets[c];
2721            buckets[4 * ALPHABET_SIZE + c] = a + b;
2722            state.buckets[c] = a;
2723        }
2724
2725        next_d -= 1;
2726        for c in 0..bucket_width {
2727            let a = buckets[2 * ALPHABET_SIZE + c];
2728            let b = state.buckets[2 * ALPHABET_SIZE + c];
2729            let shifted = b + next_d;
2730            buckets[2 * ALPHABET_SIZE + c] = if b > 0 { shifted } else { a };
2731            state.buckets[2 * ALPHABET_SIZE + c] = a;
2732        }
2733        next_d += 1 + state.position;
2734        state.position = next_d - state.position;
2735    }
2736
2737    for state in thread_state.iter_mut().take(thread_count) {
2738        partial_sorting_scan_left_to_right_16u_block_place(
2739            sa,
2740            &mut state.buckets,
2741            &state.cache,
2742            state.count,
2743            state.position,
2744        );
2745    }
2746
2747    next_d
2748}
2749
2750fn partial_sorting_scan_left_to_right_16u_omp(
2751    t: &[u16],
2752    sa: &mut [SaSint],
2753    n: SaSint,
2754    k: SaSint,
2755    buckets: &mut [SaSint],
2756    left_suffixes_count: SaSint,
2757    mut d: SaSint,
2758    threads: SaSint,
2759) -> SaSint {
2760    let v = buckets_index2(
2761        t[(n - 1) as usize] as usize,
2762        usize::from(t[(n - 2) as usize] >= t[(n - 1) as usize]),
2763    );
2764    let dst = buckets[4 * ALPHABET_SIZE + v] as usize;
2765    buckets[4 * ALPHABET_SIZE + v] += 1;
2766    sa[dst] = (n - 1) | SAINT_MIN;
2767    d += 1;
2768    buckets[2 * ALPHABET_SIZE + v] = d;
2769
2770    if threads == 1 || left_suffixes_count < 65536 {
2771        d = partial_sorting_scan_left_to_right_16u(t, sa, buckets, d, 0, left_suffixes_count);
2772    } else {
2773        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
2774        let mut block_start = 0;
2775        while block_start < left_suffixes_count {
2776            if sa[block_start as usize] == 0 {
2777                block_start += 1;
2778            } else {
2779                let mut block_end =
2780                    block_start + threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
2781                if block_end > left_suffixes_count {
2782                    block_end = left_suffixes_count;
2783                }
2784                let mut block_scan_end = block_start + 1;
2785                while block_scan_end < block_end && sa[block_scan_end as usize] != 0 {
2786                    block_scan_end += 1;
2787                }
2788                let block_size = block_scan_end - block_start;
2789
2790                if block_size < 32 {
2791                    while block_start < block_scan_end {
2792                        let mut p = sa[block_start as usize];
2793                        d += SaSint::from(p < 0);
2794                        p &= SAINT_MAX;
2795                        let v = buckets_index2(
2796                            t[(p - 1) as usize] as usize,
2797                            usize::from(t[(p - 2) as usize] >= t[(p - 1) as usize]),
2798                        );
2799                        let dst = buckets[4 * ALPHABET_SIZE + v] as usize;
2800                        buckets[4 * ALPHABET_SIZE + v] += 1;
2801                        let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
2802                            SAINT_MIN
2803                        } else {
2804                            0
2805                        };
2806                        sa[dst] = (p - 1) | mark;
2807                        buckets[2 * ALPHABET_SIZE + v] = d;
2808                        block_start += 1;
2809                    }
2810                } else {
2811                    d = partial_sorting_scan_left_to_right_16u_block_omp(
2812                        t,
2813                        sa,
2814                        k,
2815                        buckets,
2816                        d,
2817                        block_start,
2818                        block_size,
2819                        threads,
2820                        &mut thread_state,
2821                    );
2822                    block_start = block_scan_end;
2823                }
2824            }
2825        }
2826    }
2827    d
2828}
2829
2830fn partial_sorting_scan_right_to_left_16u(
2831    t: &[u16],
2832    sa: &mut [SaSint],
2833    buckets: &mut [SaSint],
2834    mut d: SaSint,
2835    omp_block_start: SaSint,
2836    omp_block_size: SaSint,
2837) -> SaSint {
2838    let mut i = (omp_block_start + omp_block_size - 1) as isize;
2839    let mut j = (omp_block_start + 64 + 1) as isize;
2840    while i >= j {
2841        let mut p0 = sa[i as usize];
2842        d += SaSint::from(p0 < 0);
2843        p0 &= SAINT_MAX;
2844        let v0 = buckets_index2(
2845            t[(p0 - 1) as usize] as usize,
2846            usize::from(t[(p0 - 2) as usize] > t[(p0 - 1) as usize]),
2847        );
2848        let mark0 = if buckets[2 * ALPHABET_SIZE + v0] != d {
2849            SAINT_MIN
2850        } else {
2851            0
2852        };
2853        buckets[v0] -= 1;
2854        sa[buckets[v0] as usize] = (p0 - 1) | mark0;
2855        buckets[2 * ALPHABET_SIZE + v0] = d;
2856
2857        let mut p1 = sa[(i - 1) as usize];
2858        d += SaSint::from(p1 < 0);
2859        p1 &= SAINT_MAX;
2860        let v1 = buckets_index2(
2861            t[(p1 - 1) as usize] as usize,
2862            usize::from(t[(p1 - 2) as usize] > t[(p1 - 1) as usize]),
2863        );
2864        let mark1 = if buckets[2 * ALPHABET_SIZE + v1] != d {
2865            SAINT_MIN
2866        } else {
2867            0
2868        };
2869        buckets[v1] -= 1;
2870        sa[buckets[v1] as usize] = (p1 - 1) | mark1;
2871        buckets[2 * ALPHABET_SIZE + v1] = d;
2872
2873        i -= 2;
2874    }
2875
2876    j -= 64 + 1;
2877    while i >= j {
2878        let mut p = sa[i as usize];
2879        d += SaSint::from(p < 0);
2880        p &= SAINT_MAX;
2881        let v = buckets_index2(
2882            t[(p - 1) as usize] as usize,
2883            usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
2884        );
2885        let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
2886            SAINT_MIN
2887        } else {
2888            0
2889        };
2890        buckets[v] -= 1;
2891        sa[buckets[v] as usize] = (p - 1) | mark;
2892        buckets[2 * ALPHABET_SIZE + v] = d;
2893        i -= 1;
2894    }
2895
2896    d
2897}
2898
2899fn partial_sorting_scan_right_to_left_16u_block_prepare(
2900    t: &[u16],
2901    sa: &mut [SaSint],
2902    k: SaSint,
2903    buckets: &mut [SaSint],
2904    cache: &mut [ThreadCache],
2905    omp_block_start: SaSint,
2906    omp_block_size: SaSint,
2907    state: &mut ThreadState,
2908) -> SaSint {
2909    let width = 2 * k as usize;
2910    buckets[..width].fill(0);
2911    buckets[2 * ALPHABET_SIZE..2 * ALPHABET_SIZE + width].fill(0);
2912
2913    let mut count = 0usize;
2914    let mut d = 1;
2915    for i in (omp_block_start as usize..(omp_block_start + omp_block_size) as usize).rev() {
2916        let mut p = sa[i];
2917        cache[count].index = p;
2918        d += SaSint::from(p < 0);
2919        p &= SAINT_MAX;
2920        let v = buckets_index2(
2921            t[(p - 1) as usize] as usize,
2922            usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
2923        );
2924        cache[count].symbol = v as SaSint;
2925        buckets[v] += 1;
2926        buckets[2 * ALPHABET_SIZE + v] = d;
2927        count += 1;
2928    }
2929    state.cache_entries = count;
2930    d - 1
2931}
2932
2933fn partial_sorting_scan_right_to_left_16u_block_place(
2934    sa: &mut [SaSint],
2935    buckets: &mut [SaSint],
2936    cache: &[ThreadCache],
2937    count: SaSint,
2938    mut d: SaSint,
2939) {
2940    for entry in cache.iter().take(count as usize) {
2941        let mut p = entry.index;
2942        d += SaSint::from(p < 0);
2943        p &= SAINT_MAX;
2944        let v = entry.symbol as usize;
2945        let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
2946            SAINT_MIN
2947        } else {
2948            0
2949        };
2950        buckets[v] -= 1;
2951        sa[buckets[v] as usize] = (p - 1) | mark;
2952        buckets[2 * ALPHABET_SIZE + v] = d;
2953    }
2954}
2955
2956fn partial_gsa_scan_right_to_left_16u_block_place(
2957    sa: &mut [SaSint],
2958    buckets: &mut [SaSint],
2959    cache: &[ThreadCache],
2960    count: SaSint,
2961    mut d: SaSint,
2962) {
2963    for entry in cache.iter().take(count as usize) {
2964        let mut p = entry.index;
2965        d += SaSint::from(p < 0);
2966        p &= SAINT_MAX;
2967        let v = entry.symbol as usize;
2968        if v != 1 {
2969            let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
2970                SAINT_MIN
2971            } else {
2972                0
2973            };
2974            buckets[v] -= 1;
2975            sa[buckets[v] as usize] = (p - 1) | mark;
2976            buckets[2 * ALPHABET_SIZE + v] = d;
2977        }
2978    }
2979}
2980
2981fn partial_sorting_scan_right_to_left_16u_block_omp(
2982    t: &[u16],
2983    sa: &mut [SaSint],
2984    k: SaSint,
2985    buckets: &mut [SaSint],
2986    d: SaSint,
2987    block_start: SaSint,
2988    block_size: SaSint,
2989    threads: SaSint,
2990    thread_state: &mut [ThreadState],
2991) -> SaSint {
2992    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
2993        usize::try_from(threads)
2994            .expect("threads must be non-negative")
2995            .min(thread_state.len())
2996    } else {
2997        1
2998    };
2999    if thread_count <= 1 {
3000        return partial_sorting_scan_right_to_left_16u(t, sa, buckets, d, block_start, block_size);
3001    }
3002
3003    let width = 2 * k as usize;
3004    let distinct_offset = 2 * ALPHABET_SIZE;
3005    let block_stride = (block_size / thread_count as SaSint) & !15;
3006
3007    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
3008        let local_start = thread as SaSint * block_stride;
3009        let local_size = if thread + 1 < thread_count {
3010            block_stride
3011        } else {
3012            block_size - local_start
3013        };
3014        let mut local_state = ThreadState::default();
3015        state.position = partial_sorting_scan_right_to_left_16u_block_prepare(
3016            t,
3017            sa,
3018            k,
3019            &mut state.buckets,
3020            &mut state.cache,
3021            block_start + local_start,
3022            local_size,
3023            &mut local_state,
3024        );
3025        state.count = local_state.cache_entries as SaSint;
3026    }
3027
3028    let mut next_d = d;
3029    for state in thread_state.iter_mut().take(thread_count).rev() {
3030        for c in 0..width {
3031            let a = buckets[c];
3032            let b = state.buckets[c];
3033            buckets[c] = a - b;
3034            state.buckets[c] = a;
3035        }
3036
3037        next_d -= 1;
3038        for c in 0..width {
3039            let offset = distinct_offset + c;
3040            let a = buckets[offset];
3041            let b = state.buckets[offset];
3042            let shifted = b + next_d;
3043            buckets[offset] = if b > 0 { shifted } else { a };
3044            state.buckets[offset] = a;
3045        }
3046        next_d += 1 + state.position;
3047        state.position = next_d - state.position;
3048    }
3049
3050    for state in thread_state.iter_mut().take(thread_count) {
3051        partial_sorting_scan_right_to_left_16u_block_place(
3052            sa,
3053            &mut state.buckets,
3054            &state.cache,
3055            state.count,
3056            state.position,
3057        );
3058    }
3059
3060    next_d
3061}
3062
3063fn partial_sorting_scan_right_to_left_16u_omp(
3064    t: &[u16],
3065    sa: &mut [SaSint],
3066    n: SaSint,
3067    k: SaSint,
3068    buckets: &mut [SaSint],
3069    first_lms_suffix: SaSint,
3070    left_suffixes_count: SaSint,
3071    d: SaSint,
3072    threads: SaSint,
3073) {
3074    let scan_start = left_suffixes_count + 1;
3075    let scan_end = n - first_lms_suffix;
3076
3077    if threads == 1 || scan_end - scan_start < 65536 {
3078        partial_sorting_scan_right_to_left_16u(
3079            t,
3080            sa,
3081            buckets,
3082            d,
3083            scan_start,
3084            scan_end - scan_start,
3085        );
3086    } else {
3087        let mut d = d;
3088        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
3089        let mut block_start = scan_end - 1;
3090        while block_start >= scan_start {
3091            if sa[block_start as usize] == 0 {
3092                block_start -= 1;
3093            } else {
3094                let block_limit = threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
3095                let mut block_max_end = block_start - block_limit;
3096                if block_max_end < scan_start {
3097                    block_max_end = scan_start - 1;
3098                }
3099                let mut block_end = block_start - 1;
3100                while block_end > block_max_end && sa[block_end as usize] != 0 {
3101                    block_end -= 1;
3102                }
3103                let block_size = block_start - block_end;
3104
3105                if block_size < 32 {
3106                    while block_start > block_end {
3107                        let mut p = sa[block_start as usize];
3108                        d += SaSint::from(p < 0);
3109                        p &= SAINT_MAX;
3110                        let v = buckets_index2(
3111                            t[(p - 1) as usize] as usize,
3112                            usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
3113                        );
3114                        let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
3115                            SAINT_MIN
3116                        } else {
3117                            0
3118                        };
3119                        buckets[v] -= 1;
3120                        sa[buckets[v] as usize] = (p - 1) | mark;
3121                        buckets[2 * ALPHABET_SIZE + v] = d;
3122                        block_start -= 1;
3123                    }
3124                } else {
3125                    d = partial_sorting_scan_right_to_left_16u_block_omp(
3126                        t,
3127                        sa,
3128                        k,
3129                        buckets,
3130                        d,
3131                        block_end + 1,
3132                        block_size,
3133                        threads,
3134                        &mut thread_state,
3135                    );
3136                    block_start = block_end;
3137                }
3138            }
3139        }
3140    }
3141}
3142
3143fn partial_sorting_scan_left_to_right_32s_6k(
3144    t: &[SaSint],
3145    sa: &mut [SaSint],
3146    buckets: &mut [SaSint],
3147    mut d: SaSint,
3148    omp_block_start: SaSint,
3149    omp_block_size: SaSint,
3150) -> SaSint {
3151    let mut i = omp_block_start;
3152    let mut j = omp_block_start + omp_block_size - 2 * 64 - 1;
3153
3154    while i < j {
3155        let mut p2 = sa[i as usize];
3156        d += SaSint::from(p2 < 0);
3157        p2 &= SAINT_MAX;
3158        let v2 = buckets_index4(
3159            t[(p2 - 1) as usize] as usize,
3160            usize::from(t[(p2 - 2) as usize] >= t[(p2 - 1) as usize]),
3161        );
3162        let pos2 = buckets[v2] as usize;
3163        buckets[v2] += 1;
3164        sa[pos2] = (p2 - 1) | (((buckets[2 + v2] != d) as SaSint) << (SAINT_BIT - 1));
3165        buckets[2 + v2] = d;
3166
3167        let mut p3 = sa[(i + 1) as usize];
3168        d += SaSint::from(p3 < 0);
3169        p3 &= SAINT_MAX;
3170        let v3 = buckets_index4(
3171            t[(p3 - 1) as usize] as usize,
3172            usize::from(t[(p3 - 2) as usize] >= t[(p3 - 1) as usize]),
3173        );
3174        let pos3 = buckets[v3] as usize;
3175        buckets[v3] += 1;
3176        sa[pos3] = (p3 - 1) | (((buckets[2 + v3] != d) as SaSint) << (SAINT_BIT - 1));
3177        buckets[2 + v3] = d;
3178
3179        i += 2;
3180    }
3181
3182    j += 2 * 64 + 1;
3183    while i < j {
3184        let mut p = sa[i as usize];
3185        d += SaSint::from(p < 0);
3186        p &= SAINT_MAX;
3187        let v = buckets_index4(
3188            t[(p - 1) as usize] as usize,
3189            usize::from(t[(p - 2) as usize] >= t[(p - 1) as usize]),
3190        );
3191        let pos = buckets[v] as usize;
3192        buckets[v] += 1;
3193        sa[pos] = (p - 1) | (((buckets[2 + v] != d) as SaSint) << (SAINT_BIT - 1));
3194        buckets[2 + v] = d;
3195        i += 1;
3196    }
3197
3198    d
3199}
3200
3201fn partial_sorting_scan_left_to_right_32s_4k(
3202    t: &[SaSint],
3203    sa: &mut [SaSint],
3204    k: SaSint,
3205    buckets: &mut [SaSint],
3206    mut d: SaSint,
3207    omp_block_start: SaSint,
3208    omp_block_size: SaSint,
3209) -> SaSint {
3210    let k = k as usize;
3211    let mut i = omp_block_start;
3212    let mut j = omp_block_start + omp_block_size - 2 * 64 - 1;
3213
3214    while i < j {
3215        let mut p0 = sa[i as usize];
3216        sa[i as usize] = p0 & SAINT_MAX;
3217        if p0 > 0 {
3218            sa[i as usize] = 0;
3219            d += p0 >> (SUFFIX_GROUP_BIT - 1);
3220            p0 &= !SUFFIX_GROUP_MARKER;
3221            let v0 = buckets_index2(
3222                t[(p0 - 1) as usize] as usize,
3223                usize::from(t[(p0 - 2) as usize] < t[(p0 - 1) as usize]),
3224            );
3225            let c0 = t[(p0 - 1) as usize] as usize;
3226            let pos0 = buckets[2 * k + c0] as usize;
3227            buckets[2 * k + c0] += 1;
3228            sa[pos0] = (p0 - 1)
3229                | ((usize::from(t[(p0 - 2) as usize] < t[(p0 - 1) as usize]) as SaSint)
3230                    << (SAINT_BIT - 1))
3231                | (((buckets[v0] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3232            buckets[v0] = d;
3233        }
3234
3235        let mut p1 = sa[(i + 1) as usize];
3236        sa[(i + 1) as usize] = p1 & SAINT_MAX;
3237        if p1 > 0 {
3238            sa[(i + 1) as usize] = 0;
3239            d += p1 >> (SUFFIX_GROUP_BIT - 1);
3240            p1 &= !SUFFIX_GROUP_MARKER;
3241            let v1 = buckets_index2(
3242                t[(p1 - 1) as usize] as usize,
3243                usize::from(t[(p1 - 2) as usize] < t[(p1 - 1) as usize]),
3244            );
3245            let c1 = t[(p1 - 1) as usize] as usize;
3246            let pos1 = buckets[2 * k + c1] as usize;
3247            buckets[2 * k + c1] += 1;
3248            sa[pos1] = (p1 - 1)
3249                | ((usize::from(t[(p1 - 2) as usize] < t[(p1 - 1) as usize]) as SaSint)
3250                    << (SAINT_BIT - 1))
3251                | (((buckets[v1] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3252            buckets[v1] = d;
3253        }
3254
3255        i += 2;
3256    }
3257
3258    j += 2 * 64 + 1;
3259    while i < j {
3260        let mut p = sa[i as usize];
3261        sa[i as usize] = p & SAINT_MAX;
3262        if p > 0 {
3263            sa[i as usize] = 0;
3264            d += p >> (SUFFIX_GROUP_BIT - 1);
3265            p &= !SUFFIX_GROUP_MARKER;
3266            let v = buckets_index2(
3267                t[(p - 1) as usize] as usize,
3268                usize::from(t[(p - 2) as usize] < t[(p - 1) as usize]),
3269            );
3270            let c = t[(p - 1) as usize] as usize;
3271            let pos = buckets[2 * k + c] as usize;
3272            buckets[2 * k + c] += 1;
3273            sa[pos] = (p - 1)
3274                | ((usize::from(t[(p - 2) as usize] < t[(p - 1) as usize]) as SaSint)
3275                    << (SAINT_BIT - 1))
3276                | (((buckets[v] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3277            buckets[v] = d;
3278        }
3279        i += 1;
3280    }
3281
3282    d
3283}
3284
3285fn partial_sorting_scan_left_to_right_32s_1k(
3286    t: &[SaSint],
3287    sa: &mut [SaSint],
3288    induction_bucket: &mut [SaSint],
3289    omp_block_start: SaSint,
3290    omp_block_size: SaSint,
3291) {
3292    let mut i = omp_block_start;
3293    let mut j = omp_block_start + omp_block_size - 2 * 64 - 1;
3294
3295    while i < j {
3296        let p0 = sa[i as usize];
3297        sa[i as usize] = p0 & SAINT_MAX;
3298        if p0 > 0 {
3299            sa[i as usize] = 0;
3300            let c0 = t[(p0 - 1) as usize] as usize;
3301            let pos0 = induction_bucket[c0] as usize;
3302            induction_bucket[c0] += 1;
3303            sa[pos0] = (p0 - 1)
3304                | ((usize::from(t[(p0 - 2) as usize] < t[(p0 - 1) as usize]) as SaSint)
3305                    << (SAINT_BIT - 1));
3306        }
3307
3308        let p1 = sa[(i + 1) as usize];
3309        sa[(i + 1) as usize] = p1 & SAINT_MAX;
3310        if p1 > 0 {
3311            sa[(i + 1) as usize] = 0;
3312            let c1 = t[(p1 - 1) as usize] as usize;
3313            let pos1 = induction_bucket[c1] as usize;
3314            induction_bucket[c1] += 1;
3315            sa[pos1] = (p1 - 1)
3316                | ((usize::from(t[(p1 - 2) as usize] < t[(p1 - 1) as usize]) as SaSint)
3317                    << (SAINT_BIT - 1));
3318        }
3319
3320        i += 2;
3321    }
3322
3323    j += 2 * 64 + 1;
3324    while i < j {
3325        let p = sa[i as usize];
3326        sa[i as usize] = p & SAINT_MAX;
3327        if p > 0 {
3328            sa[i as usize] = 0;
3329            let c = t[(p - 1) as usize] as usize;
3330            let pos = induction_bucket[c] as usize;
3331            induction_bucket[c] += 1;
3332            sa[pos] = (p - 1)
3333                | ((usize::from(t[(p - 2) as usize] < t[(p - 1) as usize]) as SaSint)
3334                    << (SAINT_BIT - 1));
3335        }
3336        i += 1;
3337    }
3338}
3339
3340fn partial_sorting_scan_left_to_right_32s_6k_omp(
3341    t: &[SaSint],
3342    sa: &mut [SaSint],
3343    n: SaSint,
3344    buckets: &mut [SaSint],
3345    left_suffixes_count: SaSint,
3346    mut d: SaSint,
3347    threads: SaSint,
3348    _thread_state: &mut [ThreadState],
3349) -> SaSint {
3350    let v = buckets_index4(
3351        t[(n - 1) as usize] as usize,
3352        usize::from(t[(n - 2) as usize] >= t[(n - 1) as usize]),
3353    );
3354    let pos = buckets[v] as usize;
3355    buckets[v] += 1;
3356    sa[pos] = (n - 1) | SAINT_MIN;
3357    d += 1;
3358    buckets[2 + v] = d;
3359
3360    if threads == 1 || left_suffixes_count < 65536 {
3361        d = partial_sorting_scan_left_to_right_32s_6k(t, sa, buckets, d, 0, left_suffixes_count);
3362    } else {
3363        let mut cache = vec![ThreadCache::default(); left_suffixes_count as usize];
3364        let mut block_start = 0;
3365        while block_start < left_suffixes_count {
3366            let mut block_end = block_start + threads * PER_THREAD_CACHE_SIZE as SaSint;
3367            if block_end > left_suffixes_count {
3368                block_end = left_suffixes_count;
3369            }
3370            d = partial_sorting_scan_left_to_right_32s_6k_block_omp(
3371                t,
3372                sa,
3373                buckets,
3374                d,
3375                &mut cache,
3376                block_start,
3377                block_end - block_start,
3378                threads,
3379            );
3380            block_start = block_end;
3381        }
3382    }
3383
3384    d
3385}
3386
3387fn partial_sorting_scan_left_to_right_32s_4k_omp(
3388    t: &[SaSint],
3389    sa: &mut [SaSint],
3390    n: SaSint,
3391    k: SaSint,
3392    buckets: &mut [SaSint],
3393    mut d: SaSint,
3394    threads: SaSint,
3395    _thread_state: &mut [ThreadState],
3396) -> SaSint {
3397    let k_usize = k as usize;
3398    let pos = buckets[2 * k_usize + t[(n - 1) as usize] as usize] as usize;
3399    buckets[2 * k_usize + t[(n - 1) as usize] as usize] += 1;
3400    sa[pos] = (n - 1)
3401        | ((usize::from(t[(n - 2) as usize] < t[(n - 1) as usize]) as SaSint) << (SAINT_BIT - 1))
3402        | SUFFIX_GROUP_MARKER;
3403    d += 1;
3404    buckets[buckets_index2(
3405        t[(n - 1) as usize] as usize,
3406        usize::from(t[(n - 2) as usize] < t[(n - 1) as usize]),
3407    )] = d;
3408
3409    if threads == 1 || n < 65536 {
3410        d = partial_sorting_scan_left_to_right_32s_4k(t, sa, k, buckets, d, 0, n);
3411    } else {
3412        let mut cache = vec![ThreadCache::default(); n as usize];
3413        let mut block_start = 0;
3414        while block_start < n {
3415            let mut block_end = block_start + threads * PER_THREAD_CACHE_SIZE as SaSint;
3416            if block_end > n {
3417                block_end = n;
3418            }
3419            d = partial_sorting_scan_left_to_right_32s_4k_block_omp(
3420                t,
3421                sa,
3422                k,
3423                buckets,
3424                d,
3425                &mut cache,
3426                block_start,
3427                block_end - block_start,
3428                threads,
3429            );
3430            block_start = block_end;
3431        }
3432    }
3433
3434    d
3435}
3436
3437fn partial_sorting_scan_left_to_right_32s_1k_omp(
3438    t: &[SaSint],
3439    sa: &mut [SaSint],
3440    n: SaSint,
3441    buckets: &mut [SaSint],
3442    threads: SaSint,
3443    _thread_state: &mut [ThreadState],
3444) {
3445    let pos = buckets[t[(n - 1) as usize] as usize] as usize;
3446    buckets[t[(n - 1) as usize] as usize] += 1;
3447    sa[pos] = (n - 1)
3448        | ((usize::from(t[(n - 2) as usize] < t[(n - 1) as usize]) as SaSint) << (SAINT_BIT - 1));
3449
3450    if threads == 1 || n < 65536 {
3451        partial_sorting_scan_left_to_right_32s_1k(t, sa, buckets, 0, n);
3452    } else {
3453        let mut cache = vec![ThreadCache::default(); n as usize];
3454        let mut block_start = 0;
3455        while block_start < n {
3456            let mut block_end = block_start + threads * PER_THREAD_CACHE_SIZE as SaSint;
3457            if block_end > n {
3458                block_end = n;
3459            }
3460            partial_sorting_scan_left_to_right_32s_1k_block_omp(
3461                t,
3462                sa,
3463                buckets,
3464                &mut cache,
3465                block_start,
3466                block_end - block_start,
3467                threads,
3468            );
3469            block_start = block_end;
3470        }
3471    }
3472}
3473
3474fn partial_sorting_scan_right_to_left_32s_6k(
3475    t: &[SaSint],
3476    sa: &mut [SaSint],
3477    buckets: &mut [SaSint],
3478    mut d: SaSint,
3479    omp_block_start: SaSint,
3480    omp_block_size: SaSint,
3481) -> SaSint {
3482    if omp_block_size <= 0 {
3483        return d;
3484    }
3485
3486    let mut i = omp_block_start + omp_block_size - 1;
3487    let mut j = omp_block_start + 2 * 64 + 1;
3488
3489    while i >= j {
3490        let mut p2 = sa[i as usize];
3491        d += SaSint::from(p2 < 0);
3492        p2 &= SAINT_MAX;
3493        let v2 = buckets_index4(
3494            t[(p2 - 1) as usize] as usize,
3495            usize::from(t[(p2 - 2) as usize] > t[(p2 - 1) as usize]),
3496        );
3497        buckets[v2] -= 1;
3498        sa[buckets[v2] as usize] =
3499            (p2 - 1) | (((buckets[2 + v2] != d) as SaSint) << (SAINT_BIT - 1));
3500        buckets[2 + v2] = d;
3501
3502        let mut p3 = sa[(i - 1) as usize];
3503        d += SaSint::from(p3 < 0);
3504        p3 &= SAINT_MAX;
3505        let v3 = buckets_index4(
3506            t[(p3 - 1) as usize] as usize,
3507            usize::from(t[(p3 - 2) as usize] > t[(p3 - 1) as usize]),
3508        );
3509        buckets[v3] -= 1;
3510        sa[buckets[v3] as usize] =
3511            (p3 - 1) | (((buckets[2 + v3] != d) as SaSint) << (SAINT_BIT - 1));
3512        buckets[2 + v3] = d;
3513
3514        i -= 2;
3515    }
3516
3517    j -= 2 * 64 + 1;
3518    while i >= j {
3519        let mut p = sa[i as usize];
3520        d += SaSint::from(p < 0);
3521        p &= SAINT_MAX;
3522        let v = buckets_index4(
3523            t[(p - 1) as usize] as usize,
3524            usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
3525        );
3526        buckets[v] -= 1;
3527        sa[buckets[v] as usize] = (p - 1) | (((buckets[2 + v] != d) as SaSint) << (SAINT_BIT - 1));
3528        buckets[2 + v] = d;
3529        i -= 1;
3530    }
3531
3532    d
3533}
3534
3535fn partial_sorting_scan_right_to_left_32s_4k(
3536    t: &[SaSint],
3537    sa: &mut [SaSint],
3538    k: SaSint,
3539    buckets: &mut [SaSint],
3540    mut d: SaSint,
3541    omp_block_start: SaSint,
3542    omp_block_size: SaSint,
3543) -> SaSint {
3544    if omp_block_size <= 0 {
3545        return d;
3546    }
3547
3548    let k = k as usize;
3549    let mut i = omp_block_start + omp_block_size - 1;
3550    let mut j = omp_block_start + 2 * 64 + 1;
3551
3552    while i >= j {
3553        let mut p0 = sa[i as usize];
3554        if p0 > 0 {
3555            sa[i as usize] = 0;
3556            d += p0 >> (SUFFIX_GROUP_BIT - 1);
3557            p0 &= !SUFFIX_GROUP_MARKER;
3558            let v0 = buckets_index2(
3559                t[(p0 - 1) as usize] as usize,
3560                usize::from(t[(p0 - 2) as usize] > t[(p0 - 1) as usize]),
3561            );
3562            let c0 = t[(p0 - 1) as usize] as usize;
3563            buckets[3 * k + c0] -= 1;
3564            sa[buckets[3 * k + c0] as usize] = (p0 - 1)
3565                | ((usize::from(t[(p0 - 2) as usize] > t[(p0 - 1) as usize]) as SaSint)
3566                    << (SAINT_BIT - 1))
3567                | (((buckets[v0] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3568            buckets[v0] = d;
3569        }
3570
3571        let mut p1 = sa[(i - 1) as usize];
3572        if p1 > 0 {
3573            sa[(i - 1) as usize] = 0;
3574            d += p1 >> (SUFFIX_GROUP_BIT - 1);
3575            p1 &= !SUFFIX_GROUP_MARKER;
3576            let v1 = buckets_index2(
3577                t[(p1 - 1) as usize] as usize,
3578                usize::from(t[(p1 - 2) as usize] > t[(p1 - 1) as usize]),
3579            );
3580            let c1 = t[(p1 - 1) as usize] as usize;
3581            buckets[3 * k + c1] -= 1;
3582            sa[buckets[3 * k + c1] as usize] = (p1 - 1)
3583                | ((usize::from(t[(p1 - 2) as usize] > t[(p1 - 1) as usize]) as SaSint)
3584                    << (SAINT_BIT - 1))
3585                | (((buckets[v1] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3586            buckets[v1] = d;
3587        }
3588
3589        i -= 2;
3590    }
3591
3592    j -= 2 * 64 + 1;
3593    while i >= j {
3594        let mut p = sa[i as usize];
3595        if p > 0 {
3596            sa[i as usize] = 0;
3597            d += p >> (SUFFIX_GROUP_BIT - 1);
3598            p &= !SUFFIX_GROUP_MARKER;
3599            let v = buckets_index2(
3600                t[(p - 1) as usize] as usize,
3601                usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
3602            );
3603            let c = t[(p - 1) as usize] as usize;
3604            buckets[3 * k + c] -= 1;
3605            sa[buckets[3 * k + c] as usize] = (p - 1)
3606                | ((usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]) as SaSint)
3607                    << (SAINT_BIT - 1))
3608                | (((buckets[v] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3609            buckets[v] = d;
3610        }
3611        i -= 1;
3612    }
3613
3614    d
3615}
3616
3617fn partial_sorting_scan_right_to_left_32s_1k(
3618    t: &[SaSint],
3619    sa: &mut [SaSint],
3620    induction_bucket: &mut [SaSint],
3621    omp_block_start: SaSint,
3622    omp_block_size: SaSint,
3623) {
3624    if omp_block_size <= 0 {
3625        return;
3626    }
3627
3628    let mut i = omp_block_start + omp_block_size - 1;
3629    let mut j = omp_block_start + 2 * 64 + 1;
3630
3631    while i >= j {
3632        let p0 = sa[i as usize];
3633        if p0 > 0 {
3634            sa[i as usize] = 0;
3635            let c0 = t[(p0 - 1) as usize] as usize;
3636            induction_bucket[c0] -= 1;
3637            sa[induction_bucket[c0] as usize] = (p0 - 1)
3638                | ((usize::from(t[(p0 - 2) as usize] > t[(p0 - 1) as usize]) as SaSint)
3639                    << (SAINT_BIT - 1));
3640        }
3641
3642        let p1 = sa[(i - 1) as usize];
3643        if p1 > 0 {
3644            sa[(i - 1) as usize] = 0;
3645            let c1 = t[(p1 - 1) as usize] as usize;
3646            induction_bucket[c1] -= 1;
3647            sa[induction_bucket[c1] as usize] = (p1 - 1)
3648                | ((usize::from(t[(p1 - 2) as usize] > t[(p1 - 1) as usize]) as SaSint)
3649                    << (SAINT_BIT - 1));
3650        }
3651
3652        i -= 2;
3653    }
3654
3655    j -= 2 * 64 + 1;
3656    while i >= j {
3657        let p = sa[i as usize];
3658        if p > 0 {
3659            sa[i as usize] = 0;
3660            let c = t[(p - 1) as usize] as usize;
3661            induction_bucket[c] -= 1;
3662            sa[induction_bucket[c] as usize] = (p - 1)
3663                | ((usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]) as SaSint)
3664                    << (SAINT_BIT - 1));
3665        }
3666        i -= 1;
3667    }
3668}
3669
3670fn partial_sorting_scan_right_to_left_32s_6k_omp(
3671    t: &[SaSint],
3672    sa: &mut [SaSint],
3673    n: SaSint,
3674    buckets: &mut [SaSint],
3675    first_lms_suffix: SaSint,
3676    left_suffixes_count: SaSint,
3677    mut d: SaSint,
3678    threads: SaSint,
3679    _thread_state: &mut [ThreadState],
3680) -> SaSint {
3681    let scan_start = left_suffixes_count + 1;
3682    let scan_end = n - first_lms_suffix;
3683
3684    if threads == 1 || scan_end - scan_start < 65536 {
3685        d = partial_sorting_scan_right_to_left_32s_6k(
3686            t,
3687            sa,
3688            buckets,
3689            d,
3690            scan_start,
3691            scan_end - scan_start,
3692        );
3693    } else {
3694        let mut cache = vec![ThreadCache::default(); (scan_end - scan_start) as usize];
3695        let mut block_start = scan_end;
3696        while block_start > scan_start {
3697            let block_size =
3698                (block_start - scan_start).min(threads * PER_THREAD_CACHE_SIZE as SaSint);
3699            block_start -= block_size;
3700            d = partial_sorting_scan_right_to_left_32s_6k_block_omp(
3701                t,
3702                sa,
3703                buckets,
3704                d,
3705                &mut cache,
3706                block_start,
3707                block_size,
3708                threads,
3709            );
3710        }
3711    }
3712
3713    d
3714}
3715
3716fn partial_sorting_scan_right_to_left_32s_4k_omp(
3717    t: &[SaSint],
3718    sa: &mut [SaSint],
3719    n: SaSint,
3720    k: SaSint,
3721    buckets: &mut [SaSint],
3722    mut d: SaSint,
3723    threads: SaSint,
3724    _thread_state: &mut [ThreadState],
3725) -> SaSint {
3726    if threads == 1 || n < 65536 {
3727        d = partial_sorting_scan_right_to_left_32s_4k(t, sa, k, buckets, d, 0, n);
3728    } else {
3729        let mut cache = vec![ThreadCache::default(); n as usize];
3730        let mut block_start = n;
3731        while block_start > 0 {
3732            let block_size = block_start.min(threads * PER_THREAD_CACHE_SIZE as SaSint);
3733            block_start -= block_size;
3734            d = partial_sorting_scan_right_to_left_32s_4k_block_omp(
3735                t,
3736                sa,
3737                k,
3738                buckets,
3739                d,
3740                &mut cache,
3741                block_start,
3742                block_size,
3743                threads,
3744            );
3745        }
3746    }
3747
3748    d
3749}
3750
3751fn partial_sorting_scan_right_to_left_32s_1k_omp(
3752    t: &[SaSint],
3753    sa: &mut [SaSint],
3754    n: SaSint,
3755    buckets: &mut [SaSint],
3756    threads: SaSint,
3757    _thread_state: &mut [ThreadState],
3758) {
3759    if threads == 1 || n < 65536 {
3760        partial_sorting_scan_right_to_left_32s_1k(t, sa, buckets, 0, n);
3761    } else {
3762        let mut cache = vec![ThreadCache::default(); n as usize];
3763        let mut block_start = n;
3764        while block_start > 0 {
3765            let block_size = block_start.min(threads * PER_THREAD_CACHE_SIZE as SaSint);
3766            block_start -= block_size;
3767            partial_sorting_scan_right_to_left_32s_1k_block_omp(
3768                t,
3769                sa,
3770                buckets,
3771                &mut cache,
3772                block_start,
3773                block_size,
3774                threads,
3775            );
3776        }
3777    }
3778}
3779
3780fn partial_sorting_scan_left_to_right_32s_6k_block_gather(
3781    t: &[SaSint],
3782    sa: &mut [SaSint],
3783    cache: &mut [ThreadCache],
3784    omp_block_start: SaSint,
3785    omp_block_size: SaSint,
3786) {
3787    let mut i = omp_block_start;
3788    let mut j = omp_block_start + omp_block_size - 64 - 1;
3789
3790    while i < j {
3791        let p0 = sa[i as usize];
3792        cache[i as usize].index = p0;
3793        let p0 = p0 & SAINT_MAX;
3794        cache[i as usize].symbol = if p0 != 0 {
3795            buckets_index4(
3796                t[(p0 - 1) as usize] as usize,
3797                usize::from(t[(p0 - 2) as usize] >= t[(p0 - 1) as usize]),
3798            ) as SaSint
3799        } else {
3800            0
3801        };
3802
3803        let p1 = sa[(i + 1) as usize];
3804        cache[(i + 1) as usize].index = p1;
3805        let p1 = p1 & SAINT_MAX;
3806        cache[(i + 1) as usize].symbol = if p1 != 0 {
3807            buckets_index4(
3808                t[(p1 - 1) as usize] as usize,
3809                usize::from(t[(p1 - 2) as usize] >= t[(p1 - 1) as usize]),
3810            ) as SaSint
3811        } else {
3812            0
3813        };
3814
3815        i += 2;
3816    }
3817
3818    j += 64 + 1;
3819    while i < j {
3820        let p = sa[i as usize];
3821        cache[i as usize].index = p;
3822        let p = p & SAINT_MAX;
3823        cache[i as usize].symbol = if p != 0 {
3824            buckets_index4(
3825                t[(p - 1) as usize] as usize,
3826                usize::from(t[(p - 2) as usize] >= t[(p - 1) as usize]),
3827            ) as SaSint
3828        } else {
3829            0
3830        };
3831        i += 1;
3832    }
3833}
3834
3835fn partial_sorting_scan_left_to_right_32s_4k_block_gather(
3836    t: &[SaSint],
3837    sa: &mut [SaSint],
3838    cache: &mut [ThreadCache],
3839    omp_block_start: SaSint,
3840    omp_block_size: SaSint,
3841) {
3842    let mut i = omp_block_start;
3843    let mut j = omp_block_start + omp_block_size - 64 - 1;
3844
3845    while i < j {
3846        let mut symbol0 = SAINT_MIN;
3847        let mut p0 = sa[i as usize];
3848        if p0 > 0 {
3849            cache[i as usize].index = p0;
3850            p0 &= !SUFFIX_GROUP_MARKER;
3851            symbol0 = buckets_index2(
3852                t[(p0 - 1) as usize] as usize,
3853                usize::from(t[(p0 - 2) as usize] < t[(p0 - 1) as usize]),
3854            ) as SaSint;
3855            p0 = 0;
3856        }
3857        cache[i as usize].symbol = symbol0;
3858        sa[i as usize] = p0 & SAINT_MAX;
3859
3860        let mut symbol1 = SAINT_MIN;
3861        let mut p1 = sa[(i + 1) as usize];
3862        if p1 > 0 {
3863            cache[(i + 1) as usize].index = p1;
3864            p1 &= !SUFFIX_GROUP_MARKER;
3865            symbol1 = buckets_index2(
3866                t[(p1 - 1) as usize] as usize,
3867                usize::from(t[(p1 - 2) as usize] < t[(p1 - 1) as usize]),
3868            ) as SaSint;
3869            p1 = 0;
3870        }
3871        cache[(i + 1) as usize].symbol = symbol1;
3872        sa[(i + 1) as usize] = p1 & SAINT_MAX;
3873
3874        i += 2;
3875    }
3876
3877    j += 64 + 1;
3878    while i < j {
3879        let mut symbol = SAINT_MIN;
3880        let mut p = sa[i as usize];
3881        if p > 0 {
3882            cache[i as usize].index = p;
3883            p &= !SUFFIX_GROUP_MARKER;
3884            symbol = buckets_index2(
3885                t[(p - 1) as usize] as usize,
3886                usize::from(t[(p - 2) as usize] < t[(p - 1) as usize]),
3887            ) as SaSint;
3888            p = 0;
3889        }
3890        cache[i as usize].symbol = symbol;
3891        sa[i as usize] = p & SAINT_MAX;
3892        i += 1;
3893    }
3894}
3895
3896fn partial_sorting_scan_left_to_right_32s_1k_block_gather(
3897    t: &[SaSint],
3898    sa: &mut [SaSint],
3899    cache: &mut [ThreadCache],
3900    omp_block_start: SaSint,
3901    omp_block_size: SaSint,
3902) {
3903    let mut i = omp_block_start;
3904    let mut j = omp_block_start + omp_block_size - 64 - 1;
3905
3906    while i < j {
3907        let mut symbol0 = SAINT_MIN;
3908        let mut p0 = sa[i as usize];
3909        if p0 > 0 {
3910            cache[i as usize].index = (p0 - 1)
3911                | ((usize::from(t[(p0 - 2) as usize] < t[(p0 - 1) as usize]) as SaSint)
3912                    << (SAINT_BIT - 1));
3913            symbol0 = t[(p0 - 1) as usize];
3914            p0 = 0;
3915        }
3916        cache[i as usize].symbol = symbol0;
3917        sa[i as usize] = p0 & SAINT_MAX;
3918
3919        let mut symbol1 = SAINT_MIN;
3920        let mut p1 = sa[(i + 1) as usize];
3921        if p1 > 0 {
3922            cache[(i + 1) as usize].index = (p1 - 1)
3923                | ((usize::from(t[(p1 - 2) as usize] < t[(p1 - 1) as usize]) as SaSint)
3924                    << (SAINT_BIT - 1));
3925            symbol1 = t[(p1 - 1) as usize];
3926            p1 = 0;
3927        }
3928        cache[(i + 1) as usize].symbol = symbol1;
3929        sa[(i + 1) as usize] = p1 & SAINT_MAX;
3930
3931        i += 2;
3932    }
3933
3934    j += 64 + 1;
3935    while i < j {
3936        let mut symbol = SAINT_MIN;
3937        let mut p = sa[i as usize];
3938        if p > 0 {
3939            cache[i as usize].index = (p - 1)
3940                | ((usize::from(t[(p - 2) as usize] < t[(p - 1) as usize]) as SaSint)
3941                    << (SAINT_BIT - 1));
3942            symbol = t[(p - 1) as usize];
3943            p = 0;
3944        }
3945        cache[i as usize].symbol = symbol;
3946        sa[i as usize] = p & SAINT_MAX;
3947        i += 1;
3948    }
3949}
3950
3951fn partial_sorting_scan_right_to_left_32s_6k_block_gather(
3952    t: &[SaSint],
3953    sa: &mut [SaSint],
3954    cache: &mut [ThreadCache],
3955    omp_block_start: SaSint,
3956    omp_block_size: SaSint,
3957) {
3958    let mut i = omp_block_start;
3959    let mut j = omp_block_start + omp_block_size - 64 - 1;
3960
3961    while i < j {
3962        let p0 = sa[i as usize];
3963        cache[i as usize].index = p0;
3964        let p0 = p0 & SAINT_MAX;
3965        cache[i as usize].symbol = if p0 != 0 {
3966            buckets_index4(
3967                t[(p0 - 1) as usize] as usize,
3968                usize::from(t[(p0 - 2) as usize] > t[(p0 - 1) as usize]),
3969            ) as SaSint
3970        } else {
3971            0
3972        };
3973
3974        let p1 = sa[(i + 1) as usize];
3975        cache[(i + 1) as usize].index = p1;
3976        let p1 = p1 & SAINT_MAX;
3977        cache[(i + 1) as usize].symbol = if p1 != 0 {
3978            buckets_index4(
3979                t[(p1 - 1) as usize] as usize,
3980                usize::from(t[(p1 - 2) as usize] > t[(p1 - 1) as usize]),
3981            ) as SaSint
3982        } else {
3983            0
3984        };
3985
3986        i += 2;
3987    }
3988
3989    j += 64 + 1;
3990    while i < j {
3991        let p = sa[i as usize];
3992        cache[i as usize].index = p;
3993        let p = p & SAINT_MAX;
3994        cache[i as usize].symbol = if p != 0 {
3995            buckets_index4(
3996                t[(p - 1) as usize] as usize,
3997                usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
3998            ) as SaSint
3999        } else {
4000            0
4001        };
4002        i += 1;
4003    }
4004}
4005
4006fn partial_sorting_scan_right_to_left_32s_4k_block_gather(
4007    t: &[SaSint],
4008    sa: &mut [SaSint],
4009    cache: &mut [ThreadCache],
4010    omp_block_start: SaSint,
4011    omp_block_size: SaSint,
4012) {
4013    let mut i = omp_block_start;
4014    let mut j = omp_block_start + omp_block_size - 64 - 1;
4015
4016    while i < j {
4017        let mut symbol0 = SAINT_MIN;
4018        let mut p0 = sa[i as usize];
4019        if p0 > 0 {
4020            sa[i as usize] = 0;
4021            cache[i as usize].index = p0;
4022            p0 &= !SUFFIX_GROUP_MARKER;
4023            symbol0 = buckets_index2(
4024                t[(p0 - 1) as usize] as usize,
4025                usize::from(t[(p0 - 2) as usize] > t[(p0 - 1) as usize]),
4026            ) as SaSint;
4027        }
4028        cache[i as usize].symbol = symbol0;
4029
4030        let mut symbol1 = SAINT_MIN;
4031        let mut p1 = sa[(i + 1) as usize];
4032        if p1 > 0 {
4033            sa[(i + 1) as usize] = 0;
4034            cache[(i + 1) as usize].index = p1;
4035            p1 &= !SUFFIX_GROUP_MARKER;
4036            symbol1 = buckets_index2(
4037                t[(p1 - 1) as usize] as usize,
4038                usize::from(t[(p1 - 2) as usize] > t[(p1 - 1) as usize]),
4039            ) as SaSint;
4040        }
4041        cache[(i + 1) as usize].symbol = symbol1;
4042
4043        i += 2;
4044    }
4045
4046    j += 64 + 1;
4047    while i < j {
4048        let mut symbol = SAINT_MIN;
4049        let mut p = sa[i as usize];
4050        if p > 0 {
4051            sa[i as usize] = 0;
4052            cache[i as usize].index = p;
4053            p &= !SUFFIX_GROUP_MARKER;
4054            symbol = buckets_index2(
4055                t[(p - 1) as usize] as usize,
4056                usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
4057            ) as SaSint;
4058        }
4059        cache[i as usize].symbol = symbol;
4060        i += 1;
4061    }
4062}
4063
4064fn partial_sorting_scan_right_to_left_32s_1k_block_gather(
4065    t: &[SaSint],
4066    sa: &mut [SaSint],
4067    cache: &mut [ThreadCache],
4068    omp_block_start: SaSint,
4069    omp_block_size: SaSint,
4070) {
4071    let mut i = omp_block_start;
4072    let mut j = omp_block_start + omp_block_size - 64 - 1;
4073
4074    while i < j {
4075        let mut symbol0 = SAINT_MIN;
4076        let p0 = sa[i as usize];
4077        if p0 > 0 {
4078            sa[i as usize] = 0;
4079            cache[i as usize].index = (p0 - 1)
4080                | ((usize::from(t[(p0 - 2) as usize] > t[(p0 - 1) as usize]) as SaSint)
4081                    << (SAINT_BIT - 1));
4082            symbol0 = t[(p0 - 1) as usize];
4083        }
4084        cache[i as usize].symbol = symbol0;
4085
4086        let mut symbol1 = SAINT_MIN;
4087        let p1 = sa[(i + 1) as usize];
4088        if p1 > 0 {
4089            sa[(i + 1) as usize] = 0;
4090            cache[(i + 1) as usize].index = (p1 - 1)
4091                | ((usize::from(t[(p1 - 2) as usize] > t[(p1 - 1) as usize]) as SaSint)
4092                    << (SAINT_BIT - 1));
4093            symbol1 = t[(p1 - 1) as usize];
4094        }
4095        cache[(i + 1) as usize].symbol = symbol1;
4096
4097        i += 2;
4098    }
4099
4100    j += 64 + 1;
4101    while i < j {
4102        let mut symbol = SAINT_MIN;
4103        let p = sa[i as usize];
4104        if p > 0 {
4105            sa[i as usize] = 0;
4106            cache[i as usize].index = (p - 1)
4107                | ((usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]) as SaSint)
4108                    << (SAINT_BIT - 1));
4109            symbol = t[(p - 1) as usize];
4110        }
4111        cache[i as usize].symbol = symbol;
4112        i += 1;
4113    }
4114}
4115
4116fn partial_sorting_scan_left_to_right_32s_6k_block_sort(
4117    t: &[SaSint],
4118    buckets: &mut [SaSint],
4119    mut d: SaSint,
4120    cache: &mut [ThreadCache],
4121    omp_block_start: SaSint,
4122    omp_block_size: SaSint,
4123) -> SaSint {
4124    let mut i = omp_block_start;
4125    let omp_block_end = omp_block_start + omp_block_size;
4126    let mut j = omp_block_end - 64 - 1;
4127
4128    while i < j {
4129        let v0 = cache[i as usize].symbol as usize;
4130        let p0 = cache[i as usize].index;
4131        d += SaSint::from(p0 < 0);
4132        cache[i as usize].symbol = buckets[v0];
4133        buckets[v0] += 1;
4134        cache[i as usize].index =
4135            (p0 - 1) | (((buckets[2 + v0] != d) as SaSint) << (SAINT_BIT - 1));
4136        buckets[2 + v0] = d;
4137        if cache[i as usize].symbol < omp_block_end {
4138            let s = cache[i as usize].symbol as usize;
4139            let q = cache[i as usize].index & SAINT_MAX;
4140            cache[s].index = cache[i as usize].index;
4141            cache[s].symbol = buckets_index4(
4142                t[(q - 1) as usize] as usize,
4143                usize::from(t[(q - 2) as usize] >= t[(q - 1) as usize]),
4144            ) as SaSint;
4145        }
4146
4147        let v1 = cache[(i + 1) as usize].symbol as usize;
4148        let p1 = cache[(i + 1) as usize].index;
4149        d += SaSint::from(p1 < 0);
4150        cache[(i + 1) as usize].symbol = buckets[v1];
4151        buckets[v1] += 1;
4152        cache[(i + 1) as usize].index =
4153            (p1 - 1) | (((buckets[2 + v1] != d) as SaSint) << (SAINT_BIT - 1));
4154        buckets[2 + v1] = d;
4155        if cache[(i + 1) as usize].symbol < omp_block_end {
4156            let s = cache[(i + 1) as usize].symbol as usize;
4157            let q = cache[(i + 1) as usize].index & SAINT_MAX;
4158            cache[s].index = cache[(i + 1) as usize].index;
4159            cache[s].symbol = buckets_index4(
4160                t[(q - 1) as usize] as usize,
4161                usize::from(t[(q - 2) as usize] >= t[(q - 1) as usize]),
4162            ) as SaSint;
4163        }
4164
4165        i += 2;
4166    }
4167
4168    j += 64 + 1;
4169    while i < j {
4170        let v = cache[i as usize].symbol as usize;
4171        let p = cache[i as usize].index;
4172        d += SaSint::from(p < 0);
4173        cache[i as usize].symbol = buckets[v];
4174        buckets[v] += 1;
4175        cache[i as usize].index = (p - 1) | (((buckets[2 + v] != d) as SaSint) << (SAINT_BIT - 1));
4176        buckets[2 + v] = d;
4177        if cache[i as usize].symbol < omp_block_end {
4178            let s = cache[i as usize].symbol as usize;
4179            let q = cache[i as usize].index & SAINT_MAX;
4180            cache[s].index = cache[i as usize].index;
4181            cache[s].symbol = buckets_index4(
4182                t[(q - 1) as usize] as usize,
4183                usize::from(t[(q - 2) as usize] >= t[(q - 1) as usize]),
4184            ) as SaSint;
4185        }
4186        i += 1;
4187    }
4188
4189    d
4190}
4191
4192fn partial_sorting_scan_left_to_right_32s_4k_block_sort(
4193    t: &[SaSint],
4194    k: SaSint,
4195    buckets: &mut [SaSint],
4196    mut d: SaSint,
4197    cache: &mut [ThreadCache],
4198    omp_block_start: SaSint,
4199    omp_block_size: SaSint,
4200) -> SaSint {
4201    let k = k as usize;
4202    let mut i = omp_block_start;
4203    let omp_block_end = omp_block_start + omp_block_size;
4204    let mut j = omp_block_end - 64 - 1;
4205
4206    while i < j {
4207        for current in [i, i + 1] {
4208            let v = cache[current as usize].symbol;
4209            if v >= 0 {
4210                let p = cache[current as usize].index;
4211                d += p >> (SUFFIX_GROUP_BIT - 1);
4212                let bucket_index = (v >> 1) as usize;
4213                let v_usize = v as usize;
4214                cache[current as usize].symbol = buckets[2 * k + bucket_index];
4215                buckets[2 * k + bucket_index] += 1;
4216                cache[current as usize].index = (p - 1)
4217                    | ((v & 1) << (SAINT_BIT - 1))
4218                    | (((buckets[v_usize] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
4219                buckets[v_usize] = d;
4220                if cache[current as usize].symbol < omp_block_end {
4221                    let ni = cache[current as usize].symbol as usize;
4222                    let mut np = cache[current as usize].index;
4223                    if np > 0 {
4224                        cache[ni].index = np;
4225                        np &= !SUFFIX_GROUP_MARKER;
4226                        cache[ni].symbol = buckets_index2(
4227                            t[(np - 1) as usize] as usize,
4228                            usize::from(t[(np - 2) as usize] < t[(np - 1) as usize]),
4229                        ) as SaSint;
4230                        np = 0;
4231                    }
4232                    cache[current as usize].index = np & SAINT_MAX;
4233                }
4234            }
4235        }
4236        i += 2;
4237    }
4238
4239    j += 64 + 1;
4240    while i < j {
4241        let v = cache[i as usize].symbol;
4242        if v >= 0 {
4243            let p = cache[i as usize].index;
4244            d += p >> (SUFFIX_GROUP_BIT - 1);
4245            let bucket_index = (v >> 1) as usize;
4246            let v_usize = v as usize;
4247            cache[i as usize].symbol = buckets[2 * k + bucket_index];
4248            buckets[2 * k + bucket_index] += 1;
4249            cache[i as usize].index = (p - 1)
4250                | ((v & 1) << (SAINT_BIT - 1))
4251                | (((buckets[v_usize] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
4252            buckets[v_usize] = d;
4253            if cache[i as usize].symbol < omp_block_end {
4254                let ni = cache[i as usize].symbol as usize;
4255                let mut np = cache[i as usize].index;
4256                if np > 0 {
4257                    cache[ni].index = np;
4258                    np &= !SUFFIX_GROUP_MARKER;
4259                    cache[ni].symbol = buckets_index2(
4260                        t[(np - 1) as usize] as usize,
4261                        usize::from(t[(np - 2) as usize] < t[(np - 1) as usize]),
4262                    ) as SaSint;
4263                    np = 0;
4264                }
4265                cache[i as usize].index = np & SAINT_MAX;
4266            }
4267        }
4268        i += 1;
4269    }
4270
4271    d
4272}
4273
4274fn partial_sorting_scan_left_to_right_32s_1k_block_sort(
4275    t: &[SaSint],
4276    induction_bucket: &mut [SaSint],
4277    cache: &mut [ThreadCache],
4278    omp_block_start: SaSint,
4279    omp_block_size: SaSint,
4280) {
4281    let mut i = omp_block_start;
4282    let omp_block_end = omp_block_start + omp_block_size;
4283    let mut j = omp_block_end - 64 - 1;
4284
4285    while i < j {
4286        for current in [i, i + 1] {
4287            let v = cache[current as usize].symbol;
4288            if v >= 0 {
4289                cache[current as usize].symbol = induction_bucket[v as usize];
4290                induction_bucket[v as usize] += 1;
4291                if cache[current as usize].symbol < omp_block_end {
4292                    let ni = cache[current as usize].symbol as usize;
4293                    let mut np = cache[current as usize].index;
4294                    if np > 0 {
4295                        cache[ni].index = (np - 1)
4296                            | ((usize::from(t[(np - 2) as usize] < t[(np - 1) as usize])
4297                                as SaSint)
4298                                << (SAINT_BIT - 1));
4299                        cache[ni].symbol = t[(np - 1) as usize];
4300                        np = 0;
4301                    }
4302                    cache[current as usize].index = np & SAINT_MAX;
4303                }
4304            }
4305        }
4306        i += 2;
4307    }
4308
4309    j = omp_block_end;
4310    while i < j {
4311        let v = cache[i as usize].symbol;
4312        if v >= 0 {
4313            cache[i as usize].symbol = induction_bucket[v as usize];
4314            induction_bucket[v as usize] += 1;
4315            if cache[i as usize].symbol < omp_block_end {
4316                let ni = cache[i as usize].symbol as usize;
4317                let mut np = cache[i as usize].index;
4318                if np > 0 {
4319                    cache[ni].index = (np - 1)
4320                        | ((usize::from(t[(np - 2) as usize] < t[(np - 1) as usize]) as SaSint)
4321                            << (SAINT_BIT - 1));
4322                    cache[ni].symbol = t[(np - 1) as usize];
4323                    np = 0;
4324                }
4325                cache[i as usize].index = np & SAINT_MAX;
4326            }
4327        }
4328        i += 1;
4329    }
4330}
4331
4332fn partial_sorting_scan_right_to_left_32s_6k_block_sort(
4333    t: &[SaSint],
4334    buckets: &mut [SaSint],
4335    mut d: SaSint,
4336    cache: &mut [ThreadCache],
4337    omp_block_start: SaSint,
4338    omp_block_size: SaSint,
4339) -> SaSint {
4340    let mut i = omp_block_start + omp_block_size - 1;
4341    let mut j = omp_block_start + 64 + 1;
4342
4343    while i >= j {
4344        let v0 = cache[i as usize].symbol as usize;
4345        let p0 = cache[i as usize].index;
4346        d += SaSint::from(p0 < 0);
4347        buckets[v0] -= 1;
4348        cache[i as usize].symbol = buckets[v0];
4349        cache[i as usize].index =
4350            (p0 - 1) | (((buckets[2 + v0] != d) as SaSint) << (SAINT_BIT - 1));
4351        buckets[2 + v0] = d;
4352        if cache[i as usize].symbol >= omp_block_start {
4353            let s = cache[i as usize].symbol as usize;
4354            let q = cache[i as usize].index & SAINT_MAX;
4355            cache[s].index = cache[i as usize].index;
4356            cache[s].symbol = buckets_index4(
4357                t[(q - 1) as usize] as usize,
4358                usize::from(t[(q - 2) as usize] > t[(q - 1) as usize]),
4359            ) as SaSint;
4360        }
4361
4362        let v1 = cache[(i - 1) as usize].symbol as usize;
4363        let p1 = cache[(i - 1) as usize].index;
4364        d += SaSint::from(p1 < 0);
4365        buckets[v1] -= 1;
4366        cache[(i - 1) as usize].symbol = buckets[v1];
4367        cache[(i - 1) as usize].index =
4368            (p1 - 1) | (((buckets[2 + v1] != d) as SaSint) << (SAINT_BIT - 1));
4369        buckets[2 + v1] = d;
4370        if cache[(i - 1) as usize].symbol >= omp_block_start {
4371            let s = cache[(i - 1) as usize].symbol as usize;
4372            let q = cache[(i - 1) as usize].index & SAINT_MAX;
4373            cache[s].index = cache[(i - 1) as usize].index;
4374            cache[s].symbol = buckets_index4(
4375                t[(q - 1) as usize] as usize,
4376                usize::from(t[(q - 2) as usize] > t[(q - 1) as usize]),
4377            ) as SaSint;
4378        }
4379
4380        i -= 2;
4381    }
4382
4383    j -= 64 + 1;
4384    while i >= j {
4385        let v = cache[i as usize].symbol as usize;
4386        let p = cache[i as usize].index;
4387        d += SaSint::from(p < 0);
4388        buckets[v] -= 1;
4389        cache[i as usize].symbol = buckets[v];
4390        cache[i as usize].index = (p - 1) | (((buckets[2 + v] != d) as SaSint) << (SAINT_BIT - 1));
4391        buckets[2 + v] = d;
4392        if cache[i as usize].symbol >= omp_block_start {
4393            let s = cache[i as usize].symbol as usize;
4394            let q = cache[i as usize].index & SAINT_MAX;
4395            cache[s].index = cache[i as usize].index;
4396            cache[s].symbol = buckets_index4(
4397                t[(q - 1) as usize] as usize,
4398                usize::from(t[(q - 2) as usize] > t[(q - 1) as usize]),
4399            ) as SaSint;
4400        }
4401        i -= 1;
4402    }
4403
4404    d
4405}
4406
4407fn partial_sorting_scan_right_to_left_32s_4k_block_sort(
4408    t: &[SaSint],
4409    k: SaSint,
4410    buckets: &mut [SaSint],
4411    mut d: SaSint,
4412    cache: &mut [ThreadCache],
4413    omp_block_start: SaSint,
4414    omp_block_size: SaSint,
4415) -> SaSint {
4416    let k = k as usize;
4417    let mut i = omp_block_start + omp_block_size - 1;
4418    let mut j = omp_block_start + 64 + 1;
4419
4420    while i >= j {
4421        for current in [i, i - 1] {
4422            let v = cache[current as usize].symbol;
4423            if v >= 0 {
4424                let p = cache[current as usize].index;
4425                d += p >> (SUFFIX_GROUP_BIT - 1);
4426                let bucket_index = (v >> 1) as usize;
4427                let v_usize = v as usize;
4428                buckets[3 * k + bucket_index] -= 1;
4429                cache[current as usize].symbol = buckets[3 * k + bucket_index];
4430                cache[current as usize].index = (p - 1)
4431                    | ((v & 1) << (SAINT_BIT - 1))
4432                    | (((buckets[v_usize] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
4433                buckets[v_usize] = d;
4434                if cache[current as usize].symbol >= omp_block_start {
4435                    let ni = cache[current as usize].symbol as usize;
4436                    let mut np = cache[current as usize].index;
4437                    if np > 0 {
4438                        cache[current as usize].index = 0;
4439                        cache[ni].index = np;
4440                        np &= !SUFFIX_GROUP_MARKER;
4441                        cache[ni].symbol = buckets_index2(
4442                            t[(np - 1) as usize] as usize,
4443                            usize::from(t[(np - 2) as usize] > t[(np - 1) as usize]),
4444                        ) as SaSint;
4445                    }
4446                }
4447            }
4448        }
4449        i -= 2;
4450    }
4451
4452    j -= 64 + 1;
4453    while i >= j {
4454        let v = cache[i as usize].symbol;
4455        if v >= 0 {
4456            let p = cache[i as usize].index;
4457            d += p >> (SUFFIX_GROUP_BIT - 1);
4458            let bucket_index = (v >> 1) as usize;
4459            let v_usize = v as usize;
4460            buckets[3 * k + bucket_index] -= 1;
4461            cache[i as usize].symbol = buckets[3 * k + bucket_index];
4462            cache[i as usize].index = (p - 1)
4463                | ((v & 1) << (SAINT_BIT - 1))
4464                | (((buckets[v_usize] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
4465            buckets[v_usize] = d;
4466            if cache[i as usize].symbol >= omp_block_start {
4467                let ni = cache[i as usize].symbol as usize;
4468                let mut np = cache[i as usize].index;
4469                if np > 0 {
4470                    cache[i as usize].index = 0;
4471                    cache[ni].index = np;
4472                    np &= !SUFFIX_GROUP_MARKER;
4473                    cache[ni].symbol = buckets_index2(
4474                        t[(np - 1) as usize] as usize,
4475                        usize::from(t[(np - 2) as usize] > t[(np - 1) as usize]),
4476                    ) as SaSint;
4477                }
4478            }
4479        }
4480        i -= 1;
4481    }
4482
4483    d
4484}
4485
4486fn partial_sorting_scan_right_to_left_32s_1k_block_sort(
4487    t: &[SaSint],
4488    induction_bucket: &mut [SaSint],
4489    cache: &mut [ThreadCache],
4490    omp_block_start: SaSint,
4491    omp_block_size: SaSint,
4492) {
4493    let mut i = omp_block_start + omp_block_size - 1;
4494    let mut j = omp_block_start + 64 + 1;
4495
4496    while i >= j {
4497        for current in [i, i - 1] {
4498            let v = cache[current as usize].symbol;
4499            if v >= 0 {
4500                induction_bucket[v as usize] -= 1;
4501                cache[current as usize].symbol = induction_bucket[v as usize];
4502                if cache[current as usize].symbol >= omp_block_start {
4503                    let ni = cache[current as usize].symbol as usize;
4504                    let np = cache[current as usize].index;
4505                    if np > 0 {
4506                        cache[current as usize].index = 0;
4507                        cache[ni].index = (np - 1)
4508                            | ((usize::from(t[(np - 2) as usize] > t[(np - 1) as usize])
4509                                as SaSint)
4510                                << (SAINT_BIT - 1));
4511                        cache[ni].symbol = t[(np - 1) as usize];
4512                    }
4513                }
4514            }
4515        }
4516        i -= 2;
4517    }
4518
4519    j -= 64 + 1;
4520    while i >= j {
4521        let v = cache[i as usize].symbol;
4522        if v >= 0 {
4523            induction_bucket[v as usize] -= 1;
4524            cache[i as usize].symbol = induction_bucket[v as usize];
4525            if cache[i as usize].symbol >= omp_block_start {
4526                let ni = cache[i as usize].symbol as usize;
4527                let np = cache[i as usize].index;
4528                if np > 0 {
4529                    cache[i as usize].index = 0;
4530                    cache[ni].index = (np - 1)
4531                        | ((usize::from(t[(np - 2) as usize] > t[(np - 1) as usize]) as SaSint)
4532                            << (SAINT_BIT - 1));
4533                    cache[ni].symbol = t[(np - 1) as usize];
4534                }
4535            }
4536        }
4537        i -= 1;
4538    }
4539}
4540
4541fn partial_sorting_scan_left_to_right_32s_6k_block_omp(
4542    t: &[SaSint],
4543    sa: &mut [SaSint],
4544    buckets: &mut [SaSint],
4545    d: SaSint,
4546    cache: &mut [ThreadCache],
4547    block_start: SaSint,
4548    block_size: SaSint,
4549    threads: SaSint,
4550) -> SaSint {
4551    if block_size <= 0 {
4552        return d;
4553    }
4554    if threads == 1 || block_size < 16_384 {
4555        return partial_sorting_scan_left_to_right_32s_6k(
4556            t,
4557            sa,
4558            buckets,
4559            d,
4560            block_start,
4561            block_size,
4562        );
4563    }
4564
4565    let threads_usize = usize::try_from(threads)
4566        .expect("threads must be non-negative")
4567        .max(1);
4568    let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
4569    let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
4570    let omp_num_threads = threads_usize.min(block_size_usize.max(1));
4571    let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
4572
4573    {
4574        let sa_ptr = SyncMutPtr::new(sa);
4575        let t_ro: &[SaSint] = t;
4576        let cache_ptr = SyncMutPtr::new(cache);
4577        run_rayon_with_threads(omp_num_threads, || {
4578            (0..omp_num_threads)
4579                .into_par_iter()
4580                .for_each(|omp_thread_num| {
4581                    let mut omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4582                        omp_block_stride
4583                    } else {
4584                        block_size_usize - omp_thread_num * omp_block_stride
4585                    };
4586                    let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
4587                    if omp_block_size == 0 {
4588                        omp_block_size = block_size_usize - (omp_block_start - block_start_usize);
4589                    }
4590                    let cache = unsafe { cache_ptr.as_slice() };
4591                    let sa = unsafe { sa_ptr.as_slice() };
4592                    partial_sorting_scan_left_to_right_32s_6k_block_gather(
4593                        t_ro,
4594                        sa,
4595                        &mut cache[omp_thread_num * omp_block_stride
4596                            ..omp_thread_num * omp_block_stride + omp_block_size],
4597                        omp_block_start as SaSint,
4598                        omp_block_size as SaSint,
4599                    );
4600                });
4601        });
4602    }
4603
4604    let d = partial_sorting_scan_left_to_right_32s_6k_block_sort(
4605        t,
4606        buckets,
4607        d,
4608        &mut cache[..block_size_usize],
4609        block_start,
4610        block_size,
4611    );
4612    place_cached_suffixes(sa, &cache[..block_size_usize], 0, block_size);
4613    d
4614}
4615
4616fn partial_sorting_scan_left_to_right_32s_4k_block_omp(
4617    t: &[SaSint],
4618    sa: &mut [SaSint],
4619    k: SaSint,
4620    buckets: &mut [SaSint],
4621    d: SaSint,
4622    cache: &mut [ThreadCache],
4623    block_start: SaSint,
4624    block_size: SaSint,
4625    threads: SaSint,
4626) -> SaSint {
4627    if block_size <= 0 {
4628        return d;
4629    }
4630    if threads == 1 || block_size < 16_384 {
4631        return partial_sorting_scan_left_to_right_32s_4k(
4632            t,
4633            sa,
4634            k,
4635            buckets,
4636            d,
4637            block_start,
4638            block_size,
4639        );
4640    }
4641
4642    let threads_usize = usize::try_from(threads)
4643        .expect("threads must be non-negative")
4644        .max(1);
4645    let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
4646    let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
4647    let omp_num_threads = threads_usize.min(block_size_usize.max(1));
4648    let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
4649
4650    {
4651        let sa_ptr = SyncMutPtr::new(sa);
4652        let t_ro: &[SaSint] = t;
4653        let cache_ptr = SyncMutPtr::new(cache);
4654        run_rayon_with_threads(omp_num_threads, || {
4655            (0..omp_num_threads)
4656                .into_par_iter()
4657                .for_each(|omp_thread_num| {
4658                    let mut omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4659                        omp_block_stride
4660                    } else {
4661                        block_size_usize - omp_thread_num * omp_block_stride
4662                    };
4663                    let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
4664                    if omp_block_size == 0 {
4665                        omp_block_size = block_size_usize - (omp_block_start - block_start_usize);
4666                    }
4667                    let cache = unsafe { cache_ptr.as_slice() };
4668                    let sa = unsafe { sa_ptr.as_slice() };
4669                    partial_sorting_scan_left_to_right_32s_4k_block_gather(
4670                        t_ro,
4671                        sa,
4672                        &mut cache[omp_thread_num * omp_block_stride
4673                            ..omp_thread_num * omp_block_stride + omp_block_size],
4674                        omp_block_start as SaSint,
4675                        omp_block_size as SaSint,
4676                    );
4677                });
4678        });
4679    }
4680
4681    let cache = &mut cache[..block_size_usize];
4682    let d = partial_sorting_scan_left_to_right_32s_4k_block_sort(
4683        t,
4684        k,
4685        buckets,
4686        d,
4687        cache,
4688        block_start,
4689        block_size,
4690    );
4691    for entry in cache.iter() {
4692        if entry.symbol >= 0 {
4693            sa[entry.symbol as usize] = entry.index;
4694        }
4695    }
4696    d
4697}
4698
4699fn partial_sorting_scan_left_to_right_32s_1k_block_omp(
4700    t: &[SaSint],
4701    sa: &mut [SaSint],
4702    buckets: &mut [SaSint],
4703    cache: &mut [ThreadCache],
4704    block_start: SaSint,
4705    block_size: SaSint,
4706    threads: SaSint,
4707) {
4708    if block_size <= 0 {
4709        return;
4710    }
4711    if threads == 1 || block_size < 16_384 {
4712        partial_sorting_scan_left_to_right_32s_1k(t, sa, buckets, block_start, block_size);
4713        return;
4714    }
4715
4716    let threads_usize = usize::try_from(threads)
4717        .expect("threads must be non-negative")
4718        .max(1);
4719    let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
4720    let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
4721    let omp_num_threads = threads_usize.min(block_size_usize.max(1));
4722    let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
4723
4724    {
4725        let sa_ptr = SyncMutPtr::new(sa);
4726        let t_ro: &[SaSint] = t;
4727        let cache_ptr = SyncMutPtr::new(cache);
4728        run_rayon_with_threads(omp_num_threads, || {
4729            (0..omp_num_threads)
4730                .into_par_iter()
4731                .for_each(|omp_thread_num| {
4732                    let mut omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4733                        omp_block_stride
4734                    } else {
4735                        block_size_usize - omp_thread_num * omp_block_stride
4736                    };
4737                    let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
4738                    if omp_block_size == 0 {
4739                        omp_block_size = block_size_usize - (omp_block_start - block_start_usize);
4740                    }
4741                    let cache = unsafe { cache_ptr.as_slice() };
4742                    let sa = unsafe { sa_ptr.as_slice() };
4743                    partial_sorting_scan_left_to_right_32s_1k_block_gather(
4744                        t_ro,
4745                        sa,
4746                        &mut cache[omp_thread_num * omp_block_stride
4747                            ..omp_thread_num * omp_block_stride + omp_block_size],
4748                        omp_block_start as SaSint,
4749                        omp_block_size as SaSint,
4750                    );
4751                });
4752        });
4753    }
4754
4755    let cache = &mut cache[..block_size_usize];
4756    partial_sorting_scan_left_to_right_32s_1k_block_sort(
4757        t,
4758        buckets,
4759        cache,
4760        block_start,
4761        block_size,
4762    );
4763    compact_and_place_cached_suffixes(sa, cache, block_start, block_size);
4764}
4765
4766fn partial_sorting_scan_right_to_left_32s_6k_block_omp(
4767    t: &[SaSint],
4768    sa: &mut [SaSint],
4769    buckets: &mut [SaSint],
4770    mut d: SaSint,
4771    cache: &mut [ThreadCache],
4772    block_start: SaSint,
4773    block_size: SaSint,
4774    threads: SaSint,
4775) -> SaSint {
4776    if block_size <= 0 {
4777        return d;
4778    }
4779    if threads == 1 || block_size < 16_384 {
4780        return partial_sorting_scan_right_to_left_32s_6k(
4781            t,
4782            sa,
4783            buckets,
4784            d,
4785            block_start,
4786            block_size,
4787        );
4788    }
4789
4790    let threads_usize = usize::try_from(threads)
4791        .expect("threads must be non-negative")
4792        .max(1);
4793    let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
4794    let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
4795    let omp_num_threads = threads_usize.min(block_size_usize.max(1));
4796    let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
4797
4798    {
4799        let sa_ptr = SyncMutPtr::new(sa);
4800        let t_ro: &[SaSint] = t;
4801        let cache_ptr = SyncMutPtr::new(cache);
4802        run_rayon_with_threads(omp_num_threads, || {
4803            (0..omp_num_threads)
4804                .into_par_iter()
4805                .for_each(|omp_thread_num| {
4806                    let mut omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4807                        omp_block_stride
4808                    } else {
4809                        block_size_usize - omp_thread_num * omp_block_stride
4810                    };
4811                    let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
4812                    if omp_block_size == 0 {
4813                        omp_block_size = block_size_usize - (omp_block_start - block_start_usize);
4814                    }
4815                    let cache = unsafe { cache_ptr.as_slice() };
4816                    let sa = unsafe { sa_ptr.as_slice() };
4817                    partial_sorting_scan_right_to_left_32s_6k_block_gather(
4818                        t_ro,
4819                        sa,
4820                        &mut cache[omp_thread_num * omp_block_stride
4821                            ..omp_thread_num * omp_block_stride + omp_block_size],
4822                        omp_block_start as SaSint,
4823                        omp_block_size as SaSint,
4824                    );
4825                });
4826        });
4827    }
4828
4829    d = partial_sorting_scan_right_to_left_32s_6k_block_sort(
4830        t,
4831        buckets,
4832        d,
4833        &mut cache[..block_size_usize],
4834        block_start,
4835        block_size,
4836    );
4837    {
4838        let sa_ptr = SyncMutPtr::new(sa);
4839        let cache_ro: &[ThreadCache] = cache;
4840        run_rayon_with_threads(omp_num_threads, || {
4841            (0..omp_num_threads)
4842                .into_par_iter()
4843                .for_each(|omp_thread_num| {
4844                    let mut omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4845                        omp_block_stride
4846                    } else {
4847                        block_size_usize - omp_thread_num * omp_block_stride
4848                    };
4849                    let cache_start = omp_thread_num * omp_block_stride;
4850                    if omp_block_size == 0 {
4851                        omp_block_size = block_size_usize - cache_start;
4852                    }
4853                    let sa = unsafe { sa_ptr.as_slice() };
4854                    for entry in &cache_ro[cache_start..cache_start + omp_block_size] {
4855                        sa[entry.symbol as usize] = entry.index;
4856                    }
4857                });
4858        });
4859    }
4860    d
4861}
4862
4863fn partial_sorting_scan_right_to_left_32s_4k_block_omp(
4864    t: &[SaSint],
4865    sa: &mut [SaSint],
4866    k: SaSint,
4867    buckets: &mut [SaSint],
4868    mut d: SaSint,
4869    cache: &mut [ThreadCache],
4870    block_start: SaSint,
4871    block_size: SaSint,
4872    threads: SaSint,
4873) -> SaSint {
4874    if block_size <= 0 {
4875        return d;
4876    }
4877    if threads == 1 || block_size < 16_384 {
4878        return partial_sorting_scan_right_to_left_32s_4k(
4879            t,
4880            sa,
4881            k,
4882            buckets,
4883            d,
4884            block_start,
4885            block_size,
4886        );
4887    }
4888
4889    let threads_usize = usize::try_from(threads)
4890        .expect("threads must be non-negative")
4891        .max(1);
4892    let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
4893    let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
4894    let omp_num_threads = threads_usize.min(block_size_usize.max(1));
4895    let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
4896
4897    {
4898        let sa_ptr = SyncMutPtr::new(sa);
4899        let t_ro: &[SaSint] = t;
4900        let cache_ptr = SyncMutPtr::new(cache);
4901        run_rayon_with_threads(omp_num_threads, || {
4902            (0..omp_num_threads)
4903                .into_par_iter()
4904                .for_each(|omp_thread_num| {
4905                    let mut omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4906                        omp_block_stride
4907                    } else {
4908                        block_size_usize - omp_thread_num * omp_block_stride
4909                    };
4910                    let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
4911                    if omp_block_size == 0 {
4912                        omp_block_size = block_size_usize - (omp_block_start - block_start_usize);
4913                    }
4914                    let cache = unsafe { cache_ptr.as_slice() };
4915                    let sa = unsafe { sa_ptr.as_slice() };
4916                    partial_sorting_scan_right_to_left_32s_4k_block_gather(
4917                        t_ro,
4918                        sa,
4919                        &mut cache[omp_thread_num * omp_block_stride
4920                            ..omp_thread_num * omp_block_stride + omp_block_size],
4921                        omp_block_start as SaSint,
4922                        omp_block_size as SaSint,
4923                    );
4924                });
4925        });
4926    }
4927
4928    d = partial_sorting_scan_right_to_left_32s_4k_block_sort(
4929        t,
4930        k,
4931        buckets,
4932        d,
4933        &mut cache[..block_size_usize],
4934        block_start,
4935        block_size,
4936    );
4937    let mut write = 0usize;
4938    for read in 0..block_size_usize {
4939        let entry = cache[read];
4940        if entry.symbol >= 0 {
4941            cache[write] = entry;
4942            write += 1;
4943        }
4944    }
4945    for entry in &cache[..write] {
4946        sa[entry.symbol as usize] = entry.index;
4947    }
4948    d
4949}
4950
4951fn partial_sorting_scan_right_to_left_32s_1k_block_omp(
4952    t: &[SaSint],
4953    sa: &mut [SaSint],
4954    buckets: &mut [SaSint],
4955    cache: &mut [ThreadCache],
4956    block_start: SaSint,
4957    block_size: SaSint,
4958    threads: SaSint,
4959) {
4960    if block_size <= 0 {
4961        return;
4962    }
4963    if threads == 1 || block_size < 16_384 {
4964        partial_sorting_scan_right_to_left_32s_1k(t, sa, buckets, block_start, block_size);
4965        return;
4966    }
4967
4968    let threads_usize = usize::try_from(threads)
4969        .expect("threads must be non-negative")
4970        .max(1);
4971    let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
4972    let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
4973    let omp_num_threads = threads_usize.min(block_size_usize.max(1));
4974    let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
4975
4976    {
4977        let sa_ptr = SyncMutPtr::new(sa);
4978        let t_ro: &[SaSint] = t;
4979        let cache_ptr = SyncMutPtr::new(cache);
4980        run_rayon_with_threads(omp_num_threads, || {
4981            (0..omp_num_threads)
4982                .into_par_iter()
4983                .for_each(|omp_thread_num| {
4984                    let mut omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4985                        omp_block_stride
4986                    } else {
4987                        block_size_usize - omp_thread_num * omp_block_stride
4988                    };
4989                    let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
4990                    if omp_block_size == 0 {
4991                        omp_block_size = block_size_usize - (omp_block_start - block_start_usize);
4992                    }
4993                    let cache = unsafe { cache_ptr.as_slice() };
4994                    let sa = unsafe { sa_ptr.as_slice() };
4995                    partial_sorting_scan_right_to_left_32s_1k_block_gather(
4996                        t_ro,
4997                        sa,
4998                        &mut cache[omp_thread_num * omp_block_stride
4999                            ..omp_thread_num * omp_block_stride + omp_block_size],
5000                        omp_block_start as SaSint,
5001                        omp_block_size as SaSint,
5002                    );
5003                });
5004        });
5005    }
5006
5007    let cache = &mut cache[..block_size_usize];
5008    partial_sorting_scan_right_to_left_32s_1k_block_sort(
5009        t,
5010        buckets,
5011        cache,
5012        block_start,
5013        block_size,
5014    );
5015    compact_and_place_cached_suffixes(sa, cache, block_start, block_size);
5016}
5017
5018fn partial_sorting_gather_lms_suffixes_32s_4k(
5019    sa: &mut [SaSint],
5020    omp_block_start: SaSint,
5021    omp_block_size: SaSint,
5022) -> SaSint {
5023    let mut i = omp_block_start;
5024    let mut j = omp_block_start + omp_block_size - 3;
5025    let mut l = omp_block_start;
5026
5027    while i < j {
5028        let s0 = sa[i as usize] as SaUint;
5029        sa[l as usize] = (s0.wrapping_sub(SUFFIX_GROUP_MARKER as SaUint)
5030            & !(SUFFIX_GROUP_MARKER as SaUint)) as SaSint;
5031        l += SaSint::from((s0 as SaSint) < 0);
5032
5033        let s1 = sa[(i + 1) as usize] as SaUint;
5034        sa[l as usize] = (s1.wrapping_sub(SUFFIX_GROUP_MARKER as SaUint)
5035            & !(SUFFIX_GROUP_MARKER as SaUint)) as SaSint;
5036        l += SaSint::from((s1 as SaSint) < 0);
5037
5038        let s2 = sa[(i + 2) as usize] as SaUint;
5039        sa[l as usize] = (s2.wrapping_sub(SUFFIX_GROUP_MARKER as SaUint)
5040            & !(SUFFIX_GROUP_MARKER as SaUint)) as SaSint;
5041        l += SaSint::from((s2 as SaSint) < 0);
5042
5043        let s3 = sa[(i + 3) as usize] as SaUint;
5044        sa[l as usize] = (s3.wrapping_sub(SUFFIX_GROUP_MARKER as SaUint)
5045            & !(SUFFIX_GROUP_MARKER as SaUint)) as SaSint;
5046        l += SaSint::from((s3 as SaSint) < 0);
5047
5048        i += 4;
5049    }
5050
5051    j += 3;
5052    while i < j {
5053        let s = sa[i as usize] as SaUint;
5054        sa[l as usize] = (s.wrapping_sub(SUFFIX_GROUP_MARKER as SaUint)
5055            & !(SUFFIX_GROUP_MARKER as SaUint)) as SaSint;
5056        l += SaSint::from((s as SaSint) < 0);
5057        i += 1;
5058    }
5059
5060    l
5061}
5062
5063fn partial_sorting_gather_lms_suffixes_32s_1k(
5064    sa: &mut [SaSint],
5065    omp_block_start: SaSint,
5066    omp_block_size: SaSint,
5067) -> SaSint {
5068    let mut i = omp_block_start;
5069    let mut j = omp_block_start + omp_block_size - 3;
5070    let mut l = omp_block_start;
5071
5072    while i < j {
5073        let s0 = sa[i as usize];
5074        sa[l as usize] = s0 & SAINT_MAX;
5075        l += SaSint::from(s0 < 0);
5076
5077        let s1 = sa[(i + 1) as usize];
5078        sa[l as usize] = s1 & SAINT_MAX;
5079        l += SaSint::from(s1 < 0);
5080
5081        let s2 = sa[(i + 2) as usize];
5082        sa[l as usize] = s2 & SAINT_MAX;
5083        l += SaSint::from(s2 < 0);
5084
5085        let s3 = sa[(i + 3) as usize];
5086        sa[l as usize] = s3 & SAINT_MAX;
5087        l += SaSint::from(s3 < 0);
5088
5089        i += 4;
5090    }
5091
5092    j += 3;
5093    while i < j {
5094        let s = sa[i as usize];
5095        sa[l as usize] = s & SAINT_MAX;
5096        l += SaSint::from(s < 0);
5097        i += 1;
5098    }
5099
5100    l
5101}
5102
5103fn partial_sorting_gather_lms_suffixes_32s_4k_omp(
5104    sa: &mut [SaSint],
5105    n: SaSint,
5106    threads: SaSint,
5107    thread_state: &mut [ThreadState],
5108) {
5109    let n_usize = usize::try_from(n).expect("n must be non-negative");
5110    let thread_count = if threads > 1 && n >= 65_536 {
5111        usize::try_from(threads)
5112            .expect("threads must be non-negative")
5113            .min(thread_state.len())
5114            .max(1)
5115    } else {
5116        1
5117    };
5118
5119    if thread_count == 1 {
5120        let _ = partial_sorting_gather_lms_suffixes_32s_4k(sa, 0, n);
5121        return;
5122    }
5123
5124    let block_stride = (n_usize / thread_count) & !15usize;
5125    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
5126        let block_start = thread * block_stride;
5127        let block_size = if thread + 1 < thread_count {
5128            block_stride
5129        } else {
5130            n_usize - block_start
5131        };
5132        state.position = block_start as SaSint;
5133        state.count = partial_sorting_gather_lms_suffixes_32s_4k(
5134            sa,
5135            block_start as SaSint,
5136            block_size as SaSint,
5137        ) - block_start as SaSint;
5138    }
5139
5140    let mut position = 0usize;
5141    for (thread, state) in thread_state.iter().take(thread_count).enumerate() {
5142        let count = usize::try_from(state.count).expect("count must be non-negative");
5143        let src = usize::try_from(state.position).expect("position must be non-negative");
5144        if thread > 0 && count > 0 {
5145            sa.copy_within(src..src + count, position);
5146        }
5147        position += count;
5148    }
5149}
5150
5151fn partial_sorting_gather_lms_suffixes_32s_1k_omp(
5152    sa: &mut [SaSint],
5153    n: SaSint,
5154    threads: SaSint,
5155    thread_state: &mut [ThreadState],
5156) {
5157    let n_usize = usize::try_from(n).expect("n must be non-negative");
5158    let thread_count = if threads > 1 && n >= 65_536 {
5159        usize::try_from(threads)
5160            .expect("threads must be non-negative")
5161            .min(thread_state.len())
5162            .max(1)
5163    } else {
5164        1
5165    };
5166
5167    if thread_count == 1 {
5168        let _ = partial_sorting_gather_lms_suffixes_32s_1k(sa, 0, n);
5169        return;
5170    }
5171
5172    let block_stride = (n_usize / thread_count) & !15usize;
5173    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
5174        let block_start = thread * block_stride;
5175        let block_size = if thread + 1 < thread_count {
5176            block_stride
5177        } else {
5178            n_usize - block_start
5179        };
5180        state.position = block_start as SaSint;
5181        state.count = partial_sorting_gather_lms_suffixes_32s_1k(
5182            sa,
5183            block_start as SaSint,
5184            block_size as SaSint,
5185        ) - block_start as SaSint;
5186    }
5187
5188    let mut position = 0usize;
5189    for (thread, state) in thread_state.iter().take(thread_count).enumerate() {
5190        let count = usize::try_from(state.count).expect("count must be non-negative");
5191        let src = usize::try_from(state.position).expect("position must be non-negative");
5192        if thread > 0 && count > 0 {
5193            sa.copy_within(src..src + count, position);
5194        }
5195        position += count;
5196    }
5197}
5198
5199fn partial_gsa_scan_right_to_left_16u(
5200    t: &[u16],
5201    sa: &mut [SaSint],
5202    buckets: &mut [SaSint],
5203    mut d: SaSint,
5204    omp_block_start: SaSint,
5205    omp_block_size: SaSint,
5206) -> SaSint {
5207    let mut i = (omp_block_start + omp_block_size - 1) as isize;
5208    let mut j = (omp_block_start + 64 + 1) as isize;
5209    while i >= j {
5210        let mut p0 = sa[i as usize];
5211        d += SaSint::from(p0 < 0);
5212        p0 &= SAINT_MAX;
5213        let v0 = buckets_index2(
5214            t[(p0 - 1) as usize] as usize,
5215            usize::from(t[(p0 - 2) as usize] > t[(p0 - 1) as usize]),
5216        );
5217        if v0 != 1 {
5218            let mark0 = if buckets[2 * ALPHABET_SIZE + v0] != d {
5219                SAINT_MIN
5220            } else {
5221                0
5222            };
5223            buckets[v0] -= 1;
5224            sa[buckets[v0] as usize] = (p0 - 1) | mark0;
5225            buckets[2 * ALPHABET_SIZE + v0] = d;
5226        }
5227
5228        let mut p1 = sa[(i - 1) as usize];
5229        d += SaSint::from(p1 < 0);
5230        p1 &= SAINT_MAX;
5231        let v1 = buckets_index2(
5232            t[(p1 - 1) as usize] as usize,
5233            usize::from(t[(p1 - 2) as usize] > t[(p1 - 1) as usize]),
5234        );
5235        if v1 != 1 {
5236            let mark1 = if buckets[2 * ALPHABET_SIZE + v1] != d {
5237                SAINT_MIN
5238            } else {
5239                0
5240            };
5241            buckets[v1] -= 1;
5242            sa[buckets[v1] as usize] = (p1 - 1) | mark1;
5243            buckets[2 * ALPHABET_SIZE + v1] = d;
5244        }
5245
5246        i -= 2;
5247    }
5248
5249    j -= 64 + 1;
5250    while i >= j {
5251        let mut p = sa[i as usize];
5252        d += SaSint::from(p < 0);
5253        p &= SAINT_MAX;
5254        let v = buckets_index2(
5255            t[(p - 1) as usize] as usize,
5256            usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
5257        );
5258        if v != 1 {
5259            let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
5260                SAINT_MIN
5261            } else {
5262                0
5263            };
5264            buckets[v] -= 1;
5265            sa[buckets[v] as usize] = (p - 1) | mark;
5266            buckets[2 * ALPHABET_SIZE + v] = d;
5267        }
5268        i -= 1;
5269    }
5270
5271    d
5272}
5273
5274fn partial_gsa_scan_right_to_left_16u_block_omp(
5275    t: &[u16],
5276    sa: &mut [SaSint],
5277    k: SaSint,
5278    buckets: &mut [SaSint],
5279    d: SaSint,
5280    block_start: SaSint,
5281    block_size: SaSint,
5282    threads: SaSint,
5283    thread_state: &mut [ThreadState],
5284) -> SaSint {
5285    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
5286        usize::try_from(threads)
5287            .expect("threads must be non-negative")
5288            .min(thread_state.len())
5289    } else {
5290        1
5291    };
5292    if thread_count <= 1 {
5293        return partial_gsa_scan_right_to_left_16u(t, sa, buckets, d, block_start, block_size);
5294    }
5295
5296    let width = 2 * k as usize;
5297    let distinct_offset = 2 * ALPHABET_SIZE;
5298    let block_stride = (block_size / thread_count as SaSint) & !15;
5299
5300    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
5301        let local_start = thread as SaSint * block_stride;
5302        let local_size = if thread + 1 < thread_count {
5303            block_stride
5304        } else {
5305            block_size - local_start
5306        };
5307        let mut local_state = ThreadState::default();
5308        state.position = partial_sorting_scan_right_to_left_16u_block_prepare(
5309            t,
5310            sa,
5311            k,
5312            &mut state.buckets,
5313            &mut state.cache,
5314            block_start + local_start,
5315            local_size,
5316            &mut local_state,
5317        );
5318        state.count = local_state.cache_entries as SaSint;
5319    }
5320
5321    let mut next_d = d;
5322    for state in thread_state.iter_mut().take(thread_count).rev() {
5323        for c in 0..width {
5324            let a = buckets[c];
5325            let b = state.buckets[c];
5326            buckets[c] = a - b;
5327            state.buckets[c] = a;
5328        }
5329
5330        next_d -= 1;
5331        for c in 0..width {
5332            let offset = distinct_offset + c;
5333            let a = buckets[offset];
5334            let b = state.buckets[offset];
5335            let shifted = b + next_d;
5336            buckets[offset] = if b > 0 { shifted } else { a };
5337            state.buckets[offset] = a;
5338        }
5339        next_d += 1 + state.position;
5340        state.position = next_d - state.position;
5341    }
5342
5343    for state in thread_state.iter_mut().take(thread_count) {
5344        partial_gsa_scan_right_to_left_16u_block_place(
5345            sa,
5346            &mut state.buckets,
5347            &state.cache,
5348            state.count,
5349            state.position,
5350        );
5351    }
5352
5353    next_d
5354}
5355
5356fn partial_gsa_scan_right_to_left_16u_omp(
5357    t: &[u16],
5358    sa: &mut [SaSint],
5359    n: SaSint,
5360    k: SaSint,
5361    buckets: &mut [SaSint],
5362    first_lms_suffix: SaSint,
5363    left_suffixes_count: SaSint,
5364    d: SaSint,
5365    threads: SaSint,
5366) {
5367    let scan_start = left_suffixes_count + 1;
5368    let scan_end = n - first_lms_suffix;
5369
5370    if threads == 1 || scan_end - scan_start < 65536 {
5371        partial_gsa_scan_right_to_left_16u(t, sa, buckets, d, scan_start, scan_end - scan_start);
5372    } else {
5373        let mut d = d;
5374        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
5375        let mut block_start = scan_end - 1;
5376        while block_start >= scan_start {
5377            if sa[block_start as usize] == 0 {
5378                block_start -= 1;
5379            } else {
5380                let block_limit = threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
5381                let mut block_max_end = block_start - block_limit;
5382                if block_max_end < scan_start {
5383                    block_max_end = scan_start - 1;
5384                }
5385                let mut block_end = block_start - 1;
5386                while block_end > block_max_end && sa[block_end as usize] != 0 {
5387                    block_end -= 1;
5388                }
5389                let block_size = block_start - block_end;
5390
5391                if block_size < 32 {
5392                    while block_start > block_end {
5393                        let mut p = sa[block_start as usize];
5394                        d += SaSint::from(p < 0);
5395                        p &= SAINT_MAX;
5396                        let v = buckets_index2(
5397                            t[(p - 1) as usize] as usize,
5398                            usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
5399                        );
5400                        if v != 1 {
5401                            let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
5402                                SAINT_MIN
5403                            } else {
5404                                0
5405                            };
5406                            buckets[v] -= 1;
5407                            sa[buckets[v] as usize] = (p - 1) | mark;
5408                            buckets[2 * ALPHABET_SIZE + v] = d;
5409                        }
5410                        block_start -= 1;
5411                    }
5412                } else {
5413                    d = partial_gsa_scan_right_to_left_16u_block_omp(
5414                        t,
5415                        sa,
5416                        k,
5417                        buckets,
5418                        d,
5419                        block_end + 1,
5420                        block_size,
5421                        threads,
5422                        &mut thread_state,
5423                    );
5424                    block_start = block_end;
5425                }
5426            }
5427        }
5428    }
5429}
5430
5431fn partial_sorting_shift_markers_16u_omp(
5432    sa: &mut [SaSint],
5433    n: SaSint,
5434    buckets: &[SaSint],
5435    threads: SaSint,
5436) {
5437    let thread_count = if threads > 1 && n >= 65536 {
5438        usize::try_from(threads).expect("threads must be positive")
5439    } else {
5440        1
5441    };
5442    let c_step = buckets_index2(1, 0) as isize;
5443    let c_min = buckets_index2(1, 0) as isize;
5444    let c_max = buckets_index2(ALPHABET_SIZE - 1, 0) as isize;
5445    let sa_ptr = SyncMutPtr::new(sa);
5446    let buckets_ref: &[SaSint] = buckets;
5447    run_rayon_with_threads(thread_count, || {
5448        (0..thread_count).into_par_iter().for_each(|t| {
5449            let mut c = c_max - (t as isize * c_step);
5450            let sa = unsafe { sa_ptr.as_slice() };
5451            while c >= c_min {
5452                let c_usize = c as usize;
5453                let mut s = SAINT_MIN;
5454                let mut i = buckets_ref[4 * ALPHABET_SIZE + c_usize] as isize - 1;
5455                let mut j = buckets_ref[c_usize - buckets_index2(1, 0)] as isize + 3;
5456                while i >= j {
5457                    let p0 = sa[i as usize];
5458                    let q0 = (p0 & SAINT_MIN) ^ s;
5459                    s ^= q0;
5460                    sa[i as usize] = p0 ^ q0;
5461
5462                    let p1 = sa[(i - 1) as usize];
5463                    let q1 = (p1 & SAINT_MIN) ^ s;
5464                    s ^= q1;
5465                    sa[(i - 1) as usize] = p1 ^ q1;
5466
5467                    let p2 = sa[(i - 2) as usize];
5468                    let q2 = (p2 & SAINT_MIN) ^ s;
5469                    s ^= q2;
5470                    sa[(i - 2) as usize] = p2 ^ q2;
5471
5472                    let p3 = sa[(i - 3) as usize];
5473                    let q3 = (p3 & SAINT_MIN) ^ s;
5474                    s ^= q3;
5475                    sa[(i - 3) as usize] = p3 ^ q3;
5476
5477                    i -= 4;
5478                }
5479
5480                j -= 3;
5481                while i >= j {
5482                    let p = sa[i as usize];
5483                    let q = (p & SAINT_MIN) ^ s;
5484                    s ^= q;
5485                    sa[i as usize] = p ^ q;
5486                    i -= 1;
5487                }
5488
5489                c -= c_step * thread_count as isize;
5490            }
5491        });
5492    });
5493}
5494
5495fn induce_partial_order_16u_omp(
5496    t: &[u16],
5497    sa: &mut [SaSint],
5498    n: SaSint,
5499    k: SaSint,
5500    flags: SaSint,
5501    buckets: &mut [SaSint],
5502    first_lms_suffix: SaSint,
5503    left_suffixes_count: SaSint,
5504    threads: SaSint,
5505) {
5506    buckets[2 * ALPHABET_SIZE..4 * ALPHABET_SIZE].fill(0);
5507
5508    if (flags & LIBSAIS_FLAGS_GSA) != 0 {
5509        let marker = 4 * ALPHABET_SIZE + buckets_index2(0, 1);
5510        buckets[marker] = buckets[4 * ALPHABET_SIZE + buckets_index2(1, 1)] - 1;
5511        flip_suffix_markers_omp(sa, buckets[marker], threads);
5512    }
5513
5514    let d = partial_sorting_scan_left_to_right_16u_omp(
5515        t,
5516        sa,
5517        n,
5518        k,
5519        buckets,
5520        left_suffixes_count,
5521        0,
5522        threads,
5523    );
5524    partial_sorting_shift_markers_16u_omp(sa, n, buckets, threads);
5525
5526    if (flags & LIBSAIS_FLAGS_GSA) != 0 {
5527        partial_gsa_scan_right_to_left_16u_omp(
5528            t,
5529            sa,
5530            n,
5531            k,
5532            buckets,
5533            first_lms_suffix,
5534            left_suffixes_count,
5535            d,
5536            threads,
5537        );
5538
5539        if t[first_lms_suffix as usize] == 0 {
5540            let count = (buckets[buckets_index2(1, 1)] - 1) as usize;
5541            sa.copy_within(0..count, 1);
5542            sa[0] = first_lms_suffix | SAINT_MIN;
5543        }
5544
5545        buckets[buckets_index2(0, 1)] = 0;
5546    } else {
5547        partial_sorting_scan_right_to_left_16u_omp(
5548            t,
5549            sa,
5550            n,
5551            k,
5552            buckets,
5553            first_lms_suffix,
5554            left_suffixes_count,
5555            d,
5556            threads,
5557        );
5558    }
5559}
5560
5561fn induce_partial_order_32s_6k_omp(
5562    t: &[SaSint],
5563    sa: &mut [SaSint],
5564    n: SaSint,
5565    k: SaSint,
5566    buckets: &mut [SaSint],
5567    first_lms_suffix: SaSint,
5568    left_suffixes_count: SaSint,
5569    threads: SaSint,
5570    thread_state: &mut [ThreadState],
5571) {
5572    let d = partial_sorting_scan_left_to_right_32s_6k_omp(
5573        t,
5574        sa,
5575        n,
5576        buckets,
5577        left_suffixes_count,
5578        0,
5579        threads,
5580        thread_state,
5581    );
5582    partial_sorting_shift_markers_32s_6k_omp(sa, k, buckets, threads);
5583    partial_sorting_shift_buckets_32s_6k(k, buckets);
5584    partial_sorting_scan_right_to_left_32s_6k_omp(
5585        t,
5586        sa,
5587        n,
5588        buckets,
5589        first_lms_suffix,
5590        left_suffixes_count,
5591        d,
5592        threads,
5593        thread_state,
5594    );
5595}
5596
5597fn induce_partial_order_32s_4k_omp(
5598    t: &[SaSint],
5599    sa: &mut [SaSint],
5600    n: SaSint,
5601    k: SaSint,
5602    buckets: &mut [SaSint],
5603    threads: SaSint,
5604    thread_state: &mut [ThreadState],
5605) {
5606    buckets[..2 * k as usize].fill(0);
5607    let d = partial_sorting_scan_left_to_right_32s_4k_omp(
5608        t,
5609        sa,
5610        n,
5611        k,
5612        buckets,
5613        0,
5614        threads,
5615        thread_state,
5616    );
5617    partial_sorting_shift_markers_32s_4k(sa, n);
5618    partial_sorting_scan_right_to_left_32s_4k_omp(t, sa, n, k, buckets, d, threads, thread_state);
5619    partial_sorting_gather_lms_suffixes_32s_4k_omp(sa, n, threads, thread_state);
5620}
5621
5622fn induce_partial_order_32s_2k_omp(
5623    t: &[SaSint],
5624    sa: &mut [SaSint],
5625    n: SaSint,
5626    k: SaSint,
5627    buckets: &mut [SaSint],
5628    threads: SaSint,
5629    thread_state: &mut [ThreadState],
5630) {
5631    let k = k as usize;
5632    let (left, right) = buckets.split_at_mut(k);
5633    partial_sorting_scan_left_to_right_32s_1k_omp(t, sa, n, right, threads, thread_state);
5634    partial_sorting_scan_right_to_left_32s_1k_omp(t, sa, n, left, threads, thread_state);
5635    partial_sorting_gather_lms_suffixes_32s_1k_omp(sa, n, threads, thread_state);
5636}
5637
5638fn induce_partial_order_32s_1k_omp(
5639    t: &[SaSint],
5640    sa: &mut [SaSint],
5641    n: SaSint,
5642    k: SaSint,
5643    buckets: &mut [SaSint],
5644    threads: SaSint,
5645    thread_state: &mut [ThreadState],
5646) {
5647    count_suffixes_32s(t, n, k, buckets);
5648    initialize_buckets_start_32s_1k(k, buckets);
5649    partial_sorting_scan_left_to_right_32s_1k_omp(t, sa, n, buckets, threads, thread_state);
5650
5651    count_suffixes_32s(t, n, k, buckets);
5652    initialize_buckets_end_32s_1k(k, buckets);
5653    partial_sorting_scan_right_to_left_32s_1k_omp(t, sa, n, buckets, threads, thread_state);
5654
5655    partial_sorting_gather_lms_suffixes_32s_1k_omp(sa, n, threads, thread_state);
5656}
5657
5658fn final_sorting_scan_left_to_right_16u(
5659    t: &[u16],
5660    sa: &mut [SaSint],
5661    induction_bucket: &mut [SaSint],
5662    omp_block_start: SaSint,
5663    omp_block_size: SaSint,
5664) {
5665    let mut i = omp_block_start as isize;
5666    let mut j = (omp_block_start + omp_block_size - 64 - 1) as isize;
5667    while i < j {
5668        final_sorting_ltr_step(t, sa, induction_bucket, i as usize);
5669        final_sorting_ltr_step(t, sa, induction_bucket, (i + 1) as usize);
5670        i += 2;
5671    }
5672    j += 64 + 1;
5673    while i < j {
5674        final_sorting_ltr_step(t, sa, induction_bucket, i as usize);
5675        i += 1;
5676    }
5677}
5678
5679fn final_sorting_scan_right_to_left_16u(
5680    t: &[u16],
5681    sa: &mut [SaSint],
5682    induction_bucket: &mut [SaSint],
5683    omp_block_start: SaSint,
5684    omp_block_size: SaSint,
5685) {
5686    let mut i = (omp_block_start + omp_block_size - 1) as isize;
5687    let mut j = (omp_block_start + 64 + 1) as isize;
5688    while i >= j {
5689        final_sorting_rtl_step(t, sa, induction_bucket, i as usize, false);
5690        final_sorting_rtl_step(t, sa, induction_bucket, (i - 1) as usize, false);
5691        i -= 2;
5692    }
5693    j -= 64 + 1;
5694    while i >= j {
5695        final_sorting_rtl_step(t, sa, induction_bucket, i as usize, false);
5696        i -= 1;
5697    }
5698}
5699
5700fn final_sorting_scan_left_to_right_32s(
5701    t: &[SaSint],
5702    sa: &mut [SaSint],
5703    induction_bucket: &mut [SaSint],
5704    omp_block_start: SaSint,
5705    omp_block_size: SaSint,
5706) {
5707    let mut i = omp_block_start as isize;
5708    let mut j = (omp_block_start + omp_block_size - 2 * 64 - 1) as isize;
5709    while i < j {
5710        for current in [i, i + 1] {
5711            let current = current as usize;
5712            let mut p = sa[current];
5713            sa[current] = p ^ SAINT_MIN;
5714            if p > 0 {
5715                p -= 1;
5716                let p_usize = p as usize;
5717                let bucket = t[p_usize] as usize;
5718                let slot = induction_bucket[bucket] as usize;
5719                sa[slot] = p
5720                    | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
5721                        << (SAINT_BIT - 1));
5722                induction_bucket[bucket] += 1;
5723            }
5724        }
5725        i += 2;
5726    }
5727
5728    j += 2 * 64 + 1;
5729    while i < j {
5730        let current = i as usize;
5731        let mut p = sa[current];
5732        sa[current] = p ^ SAINT_MIN;
5733        if p > 0 {
5734            p -= 1;
5735            let p_usize = p as usize;
5736            let bucket = t[p_usize] as usize;
5737            let slot = induction_bucket[bucket] as usize;
5738            sa[slot] = p
5739                | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
5740                    << (SAINT_BIT - 1));
5741            induction_bucket[bucket] += 1;
5742        }
5743        i += 1;
5744    }
5745}
5746
5747fn final_sorting_scan_left_to_right_32s_block_gather(
5748    t: &[SaSint],
5749    sa: &mut [SaSint],
5750    cache: &mut [ThreadCache],
5751    omp_block_start: SaSint,
5752    omp_block_size: SaSint,
5753) {
5754    if omp_block_size <= 0 {
5755        return;
5756    }
5757
5758    let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5759    let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5760    for offset in 0..size {
5761        let current = start + offset;
5762        let mut symbol = SAINT_MIN;
5763        let mut p = sa[current];
5764        sa[current] = p ^ SAINT_MIN;
5765        if p > 0 {
5766            p -= 1;
5767            let p_usize = p as usize;
5768            cache[offset].index = p
5769                | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
5770                    << (SAINT_BIT - 1));
5771            symbol = t[p_usize];
5772        }
5773        cache[offset].symbol = symbol;
5774    }
5775}
5776
5777fn final_sorting_scan_left_to_right_32s_block_sort(
5778    t: &[SaSint],
5779    induction_bucket: &mut [SaSint],
5780    cache: &mut [ThreadCache],
5781    omp_block_start: SaSint,
5782    omp_block_size: SaSint,
5783) {
5784    if omp_block_size <= 0 {
5785        return;
5786    }
5787
5788    let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5789    let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5790    let block_end = start + size;
5791
5792    for offset in 0..size {
5793        let v = cache[offset].symbol;
5794        if v >= 0 {
5795            let bucket_index = v as usize;
5796            let target = induction_bucket[bucket_index];
5797            cache[offset].symbol = target;
5798            induction_bucket[bucket_index] += 1;
5799            if target >= omp_block_start && target < block_end as SaSint {
5800                let ni = usize::try_from(target - omp_block_start)
5801                    .expect("cache slot must be non-negative");
5802                let mut np = cache[offset].index;
5803                cache[offset].index = np ^ SAINT_MIN;
5804                if np > 0 {
5805                    np -= 1;
5806                    let np_usize = np as usize;
5807                    cache[ni].index = np
5808                        | ((usize::from(t[np_usize - usize::from(np > 0)] < t[np_usize])
5809                            as SaSint)
5810                            << (SAINT_BIT - 1));
5811                    cache[ni].symbol = t[np_usize];
5812                }
5813            }
5814        }
5815    }
5816}
5817
5818fn final_sorting_scan_left_to_right_32s_block_omp(
5819    t: &[SaSint],
5820    sa: &mut [SaSint],
5821    buckets: &mut [SaSint],
5822    cache: &mut [ThreadCache],
5823    block_start: SaSint,
5824    block_size: SaSint,
5825    threads: SaSint,
5826) {
5827    if threads <= 1 || block_size < 16_384 {
5828        final_sorting_scan_left_to_right_32s(t, sa, buckets, block_start, block_size);
5829        return;
5830    }
5831
5832    final_sorting_scan_left_to_right_32s_block_gather(t, sa, cache, block_start, block_size);
5833    final_sorting_scan_left_to_right_32s_block_sort(t, buckets, cache, block_start, block_size);
5834
5835    let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
5836    let threads_usize = usize::try_from(threads.max(1)).expect("threads must be positive");
5837    let omp_num_threads = threads_usize.min(block_size_usize.max(1));
5838    let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
5839    {
5840        let sa_ptr = SyncMutPtr::new(sa);
5841        let cache_ptr = SyncMutPtr::new(cache);
5842        run_rayon_with_threads(omp_num_threads, || {
5843            (0..omp_num_threads)
5844                .into_par_iter()
5845                .for_each(|omp_thread_num| {
5846                    let omp_block_start = omp_thread_num * omp_block_stride;
5847                    let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5848                        omp_block_stride
5849                    } else {
5850                        block_size_usize - omp_block_start
5851                    };
5852                    let sa = unsafe { sa_ptr.as_slice() };
5853                    let cache = unsafe { cache_ptr.as_slice() };
5854                    compact_and_place_cached_suffixes(
5855                        sa,
5856                        cache,
5857                        omp_block_start as SaSint,
5858                        omp_block_size as SaSint,
5859                    );
5860                });
5861        });
5862    }
5863}
5864
5865fn final_sorting_scan_left_to_right_32s_omp(
5866    t: &[SaSint],
5867    sa: &mut [SaSint],
5868    n: SaSint,
5869    induction_bucket: &mut [SaSint],
5870    threads: SaSint,
5871    thread_state: &mut [ThreadState],
5872) {
5873    let last = (n - 1) as usize;
5874    let bucket = t[last] as usize;
5875    let slot = induction_bucket[bucket] as usize;
5876    sa[slot] = (n - 1) | ((usize::from(t[last - 1] < t[last]) as SaSint) << (SAINT_BIT - 1));
5877    induction_bucket[bucket] += 1;
5878
5879    if threads == 1 || n < 65536 || thread_state.is_empty() {
5880        final_sorting_scan_left_to_right_32s(t, sa, induction_bucket, 0, n);
5881        return;
5882    }
5883
5884    let threads_usize = usize::try_from(threads)
5885        .expect("threads must be non-negative")
5886        .max(1);
5887    let block_span = threads_usize * PER_THREAD_CACHE_SIZE;
5888    let mut cache = vec![ThreadCache::default(); block_span];
5889    let mut block_start = 0;
5890    while block_start < n {
5891        let block_end = (block_start + block_span as SaSint).min(n);
5892        final_sorting_scan_left_to_right_32s_block_omp(
5893            t,
5894            sa,
5895            induction_bucket,
5896            &mut cache,
5897            block_start,
5898            block_end - block_start,
5899            threads,
5900        );
5901        block_start = block_end;
5902    }
5903}
5904
5905fn final_sorting_scan_right_to_left_32s(
5906    t: &[SaSint],
5907    sa: &mut [SaSint],
5908    induction_bucket: &mut [SaSint],
5909    omp_block_start: SaSint,
5910    omp_block_size: SaSint,
5911) {
5912    let mut i = (omp_block_start + omp_block_size - 1) as isize;
5913    let mut j = (omp_block_start + 2 * 64 + 1) as isize;
5914    while i >= j {
5915        for current in [i, i - 1] {
5916            let current = current as usize;
5917            let mut p = sa[current];
5918            sa[current] = p & SAINT_MAX;
5919            if p > 0 {
5920                p -= 1;
5921                let p_usize = p as usize;
5922                let bucket = t[p_usize] as usize;
5923                induction_bucket[bucket] -= 1;
5924                let slot = induction_bucket[bucket] as usize;
5925                sa[slot] = p
5926                    | ((usize::from(t[p_usize - usize::from(p > 0)] > t[p_usize]) as SaSint)
5927                        << (SAINT_BIT - 1));
5928            }
5929        }
5930        i -= 2;
5931    }
5932
5933    j -= 2 * 64 + 1;
5934    while i >= j {
5935        let current = i as usize;
5936        let mut p = sa[current];
5937        sa[current] = p & SAINT_MAX;
5938        if p > 0 {
5939            p -= 1;
5940            let p_usize = p as usize;
5941            let bucket = t[p_usize] as usize;
5942            induction_bucket[bucket] -= 1;
5943            let slot = induction_bucket[bucket] as usize;
5944            sa[slot] = p
5945                | ((usize::from(t[p_usize - usize::from(p > 0)] > t[p_usize]) as SaSint)
5946                    << (SAINT_BIT - 1));
5947        }
5948        i -= 1;
5949    }
5950}
5951
5952fn final_sorting_scan_right_to_left_32s_block_gather(
5953    t: &[SaSint],
5954    sa: &mut [SaSint],
5955    cache: &mut [ThreadCache],
5956    omp_block_start: SaSint,
5957    omp_block_size: SaSint,
5958) {
5959    if omp_block_size <= 0 {
5960        return;
5961    }
5962
5963    let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5964    let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5965    for offset in 0..size {
5966        let current = start + offset;
5967        let mut symbol = SAINT_MIN;
5968        let mut p = sa[current];
5969        sa[current] = p & SAINT_MAX;
5970        if p > 0 {
5971            p -= 1;
5972            let p_usize = p as usize;
5973            cache[offset].index = p
5974                | ((usize::from(t[p_usize - usize::from(p > 0)] > t[p_usize]) as SaSint)
5975                    << (SAINT_BIT - 1));
5976            symbol = t[p_usize];
5977        }
5978        cache[offset].symbol = symbol;
5979    }
5980}
5981
5982fn final_sorting_scan_right_to_left_32s_block_sort(
5983    t: &[SaSint],
5984    induction_bucket: &mut [SaSint],
5985    cache: &mut [ThreadCache],
5986    omp_block_start: SaSint,
5987    omp_block_size: SaSint,
5988) {
5989    if omp_block_size <= 0 {
5990        return;
5991    }
5992
5993    let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5994    let block_end = omp_block_start + omp_block_size;
5995    let mut offset = size;
5996
5997    while offset > 0 {
5998        offset -= 1;
5999        let v = cache[offset].symbol;
6000        if v >= 0 {
6001            let bucket_index = v as usize;
6002            induction_bucket[bucket_index] -= 1;
6003            let target = induction_bucket[bucket_index];
6004            cache[offset].symbol = target;
6005            if target >= omp_block_start && target < block_end {
6006                let ni = usize::try_from(target - omp_block_start)
6007                    .expect("cache slot must be non-negative");
6008                let mut np = cache[offset].index;
6009                cache[offset].index = np & SAINT_MAX;
6010                if np > 0 {
6011                    np -= 1;
6012                    let np_usize = np as usize;
6013                    cache[ni].index = np
6014                        | ((usize::from(t[np_usize - usize::from(np > 0)] > t[np_usize])
6015                            as SaSint)
6016                            << (SAINT_BIT - 1));
6017                    cache[ni].symbol = t[np_usize];
6018                }
6019            }
6020        }
6021    }
6022}
6023
6024fn final_sorting_scan_right_to_left_32s_block_omp(
6025    t: &[SaSint],
6026    sa: &mut [SaSint],
6027    buckets: &mut [SaSint],
6028    cache: &mut [ThreadCache],
6029    block_start: SaSint,
6030    block_size: SaSint,
6031    threads: SaSint,
6032) {
6033    if threads <= 1 || block_size < 16_384 {
6034        final_sorting_scan_right_to_left_32s(t, sa, buckets, block_start, block_size);
6035        return;
6036    }
6037
6038    final_sorting_scan_right_to_left_32s_block_gather(t, sa, cache, block_start, block_size);
6039    final_sorting_scan_right_to_left_32s_block_sort(t, buckets, cache, block_start, block_size);
6040
6041    let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
6042    let threads_usize = usize::try_from(threads.max(1)).expect("threads must be positive");
6043    let omp_num_threads = threads_usize.min(block_size_usize.max(1));
6044    let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
6045    {
6046        let sa_ptr = SyncMutPtr::new(sa);
6047        let cache_ptr = SyncMutPtr::new(cache);
6048        run_rayon_with_threads(omp_num_threads, || {
6049            (0..omp_num_threads)
6050                .into_par_iter()
6051                .for_each(|omp_thread_num| {
6052                    let omp_block_start = omp_thread_num * omp_block_stride;
6053                    let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
6054                        omp_block_stride
6055                    } else {
6056                        block_size_usize - omp_block_start
6057                    };
6058                    let sa = unsafe { sa_ptr.as_slice() };
6059                    let cache = unsafe { cache_ptr.as_slice() };
6060                    compact_and_place_cached_suffixes(
6061                        sa,
6062                        cache,
6063                        omp_block_start as SaSint,
6064                        omp_block_size as SaSint,
6065                    );
6066                });
6067        });
6068    }
6069}
6070
6071fn final_sorting_scan_right_to_left_32s_omp(
6072    t: &[SaSint],
6073    sa: &mut [SaSint],
6074    n: SaSint,
6075    induction_bucket: &mut [SaSint],
6076    threads: SaSint,
6077    thread_state: &mut [ThreadState],
6078) {
6079    if threads == 1 || n < 65536 || thread_state.is_empty() {
6080        final_sorting_scan_right_to_left_32s(t, sa, induction_bucket, 0, n);
6081        return;
6082    }
6083
6084    let threads_usize = usize::try_from(threads)
6085        .expect("threads must be non-negative")
6086        .max(1);
6087    let block_span = threads_usize * PER_THREAD_CACHE_SIZE;
6088    let mut cache = vec![ThreadCache::default(); block_span];
6089    let mut block_start = n - 1;
6090    while block_start >= 0 {
6091        let block_end = (block_start - block_span as SaSint).max(-1);
6092        final_sorting_scan_right_to_left_32s_block_omp(
6093            t,
6094            sa,
6095            induction_bucket,
6096            &mut cache,
6097            block_end + 1,
6098            block_start - block_end,
6099            threads,
6100        );
6101        block_start = block_end;
6102    }
6103}
6104
6105fn induce_final_order_32s_6k(
6106    t: &[SaSint],
6107    sa: &mut [SaSint],
6108    n: SaSint,
6109    k: SaSint,
6110    buckets: &mut [SaSint],
6111    threads: SaSint,
6112    thread_state: &mut [ThreadState],
6113) {
6114    let k = k as usize;
6115    final_sorting_scan_left_to_right_32s_omp(
6116        t,
6117        sa,
6118        n,
6119        &mut buckets[4 * k..5 * k],
6120        threads,
6121        thread_state,
6122    );
6123    final_sorting_scan_right_to_left_32s_omp(
6124        t,
6125        sa,
6126        n,
6127        &mut buckets[5 * k..6 * k],
6128        threads,
6129        thread_state,
6130    );
6131}
6132
6133fn induce_final_order_32s_4k(
6134    t: &[SaSint],
6135    sa: &mut [SaSint],
6136    n: SaSint,
6137    k: SaSint,
6138    buckets: &mut [SaSint],
6139    threads: SaSint,
6140    thread_state: &mut [ThreadState],
6141) {
6142    let k = k as usize;
6143    final_sorting_scan_left_to_right_32s_omp(
6144        t,
6145        sa,
6146        n,
6147        &mut buckets[2 * k..3 * k],
6148        threads,
6149        thread_state,
6150    );
6151    final_sorting_scan_right_to_left_32s_omp(
6152        t,
6153        sa,
6154        n,
6155        &mut buckets[3 * k..4 * k],
6156        threads,
6157        thread_state,
6158    );
6159}
6160
6161fn induce_final_order_32s_2k(
6162    t: &[SaSint],
6163    sa: &mut [SaSint],
6164    n: SaSint,
6165    k: SaSint,
6166    buckets: &mut [SaSint],
6167    threads: SaSint,
6168    thread_state: &mut [ThreadState],
6169) {
6170    let k = k as usize;
6171    final_sorting_scan_left_to_right_32s_omp(
6172        t,
6173        sa,
6174        n,
6175        &mut buckets[k..2 * k],
6176        threads,
6177        thread_state,
6178    );
6179    final_sorting_scan_right_to_left_32s_omp(t, sa, n, &mut buckets[..k], threads, thread_state);
6180}
6181
6182fn induce_final_order_32s_1k(
6183    t: &[SaSint],
6184    sa: &mut [SaSint],
6185    n: SaSint,
6186    k: SaSint,
6187    buckets: &mut [SaSint],
6188    threads: SaSint,
6189    thread_state: &mut [ThreadState],
6190) {
6191    count_suffixes_32s(t, n, k, buckets);
6192    initialize_buckets_start_32s_1k(k, buckets);
6193    final_sorting_scan_left_to_right_32s_omp(t, sa, n, buckets, threads, thread_state);
6194
6195    count_suffixes_32s(t, n, k, buckets);
6196    initialize_buckets_end_32s_1k(k, buckets);
6197    final_sorting_scan_right_to_left_32s_omp(t, sa, n, buckets, threads, thread_state);
6198}
6199
6200fn clear_lms_suffixes_omp(
6201    sa: &mut [SaSint],
6202    n: SaSint,
6203    k: SaSint,
6204    bucket_start: &[SaSint],
6205    bucket_end: &[SaSint],
6206    threads: SaSint,
6207) {
6208    let k_usize = usize::try_from(k).expect("k must be non-negative");
6209    let thread_count = if threads > 1 && n >= 65536 {
6210        usize::try_from(threads).expect("threads must be positive")
6211    } else {
6212        1
6213    };
6214    {
6215        let sa_ptr = SyncMutPtr::new(sa);
6216        let bucket_start_ref: &[SaSint] = bucket_start;
6217        let bucket_end_ref: &[SaSint] = bucket_end;
6218        run_rayon_with_threads(thread_count, || {
6219            (0..thread_count).into_par_iter().for_each(|t| {
6220                let mut c = t;
6221                let sa = unsafe { sa_ptr.as_slice() };
6222                while c < k_usize {
6223                    if bucket_end_ref[c] > bucket_start_ref[c] {
6224                        let start = bucket_start_ref[c] as usize;
6225                        let end = bucket_end_ref[c] as usize;
6226                        sa[start..end].fill(0);
6227                    }
6228                    c += thread_count;
6229                }
6230            });
6231        });
6232    }
6233}
6234
6235fn final_gsa_scan_right_to_left_16u(
6236    t: &[u16],
6237    sa: &mut [SaSint],
6238    induction_bucket: &mut [SaSint],
6239    omp_block_start: SaSint,
6240    omp_block_size: SaSint,
6241) {
6242    let mut i = (omp_block_start + omp_block_size - 1) as isize;
6243    let mut j = (omp_block_start + 64 + 1) as isize;
6244    while i >= j {
6245        final_sorting_rtl_step(t, sa, induction_bucket, i as usize, true);
6246        final_sorting_rtl_step(t, sa, induction_bucket, (i - 1) as usize, true);
6247        i -= 2;
6248    }
6249    j -= 64 + 1;
6250    while i >= j {
6251        final_sorting_rtl_step(t, sa, induction_bucket, i as usize, true);
6252        i -= 1;
6253    }
6254}
6255
6256fn final_sorting_ltr_step(
6257    t: &[u16],
6258    sa: &mut [SaSint],
6259    induction_bucket: &mut [SaSint],
6260    index: usize,
6261) {
6262    let mut p = sa[index];
6263    sa[index] = p ^ SAINT_MIN;
6264    if p > 0 {
6265        p -= 1;
6266        let c = t[p as usize] as usize;
6267        let mark = if t[(p - SaSint::from(p > 0)) as usize] < t[p as usize] {
6268            SAINT_MIN
6269        } else {
6270            0
6271        };
6272        let dst = induction_bucket[c] as usize;
6273        sa[dst] = p | mark;
6274        induction_bucket[c] += 1;
6275    }
6276}
6277
6278fn final_sorting_rtl_step(
6279    t: &[u16],
6280    sa: &mut [SaSint],
6281    induction_bucket: &mut [SaSint],
6282    index: usize,
6283    gsa: bool,
6284) {
6285    let mut p = sa[index];
6286    sa[index] = p & SAINT_MAX;
6287    if p > 0 && (!gsa || t[(p - 1) as usize] > 0) {
6288        p -= 1;
6289        let c = t[p as usize] as usize;
6290        let mark = if t[(p - SaSint::from(p > 0)) as usize] > t[p as usize] {
6291            SAINT_MIN
6292        } else {
6293            0
6294        };
6295        induction_bucket[c] -= 1;
6296        sa[induction_bucket[c] as usize] = p | mark;
6297    }
6298}
6299
6300fn final_bwt_scan_left_to_right_16u(
6301    t: &[u16],
6302    sa: &mut [SaSint],
6303    induction_bucket: &mut [SaSint],
6304    omp_block_start: SaSint,
6305    omp_block_size: SaSint,
6306) {
6307    let mut i = omp_block_start as isize;
6308    let mut j = (omp_block_start + omp_block_size - 64 - 1) as isize;
6309    while i < j {
6310        final_bwt_ltr_step(t, sa, induction_bucket, i as usize);
6311        final_bwt_ltr_step(t, sa, induction_bucket, (i + 1) as usize);
6312        i += 2;
6313    }
6314    j += 64 + 1;
6315    while i < j {
6316        final_bwt_ltr_step(t, sa, induction_bucket, i as usize);
6317        i += 1;
6318    }
6319}
6320
6321fn final_bwt_scan_right_to_left_16u(
6322    t: &[u16],
6323    sa: &mut [SaSint],
6324    induction_bucket: &mut [SaSint],
6325    omp_block_start: SaSint,
6326    omp_block_size: SaSint,
6327) -> SaSint {
6328    let mut index = -1;
6329    let mut i = (omp_block_start + omp_block_size - 1) as isize;
6330    let mut j = (omp_block_start + 64 + 1) as isize;
6331    while i >= j {
6332        final_bwt_rtl_step(t, sa, induction_bucket, i as usize, &mut index);
6333        final_bwt_rtl_step(t, sa, induction_bucket, (i - 1) as usize, &mut index);
6334        i -= 2;
6335    }
6336    j -= 64 + 1;
6337    while i >= j {
6338        final_bwt_rtl_step(t, sa, induction_bucket, i as usize, &mut index);
6339        i -= 1;
6340    }
6341    index
6342}
6343
6344fn final_bwt_aux_scan_left_to_right_16u(
6345    t: &[u16],
6346    sa: &mut [SaSint],
6347    rm: SaSint,
6348    i_sample: &mut [SaSint],
6349    induction_bucket: &mut [SaSint],
6350    omp_block_start: SaSint,
6351    omp_block_size: SaSint,
6352) {
6353    let mut i = omp_block_start as isize;
6354    let mut j = (omp_block_start + omp_block_size - 64 - 1) as isize;
6355    while i < j {
6356        final_bwt_aux_ltr_step(t, sa, rm, i_sample, induction_bucket, i as usize);
6357        final_bwt_aux_ltr_step(t, sa, rm, i_sample, induction_bucket, (i + 1) as usize);
6358        i += 2;
6359    }
6360    j += 64 + 1;
6361    while i < j {
6362        final_bwt_aux_ltr_step(t, sa, rm, i_sample, induction_bucket, i as usize);
6363        i += 1;
6364    }
6365}
6366
6367fn final_bwt_aux_scan_right_to_left_16u(
6368    t: &[u16],
6369    sa: &mut [SaSint],
6370    rm: SaSint,
6371    i_sample: &mut [SaSint],
6372    induction_bucket: &mut [SaSint],
6373    omp_block_start: SaSint,
6374    omp_block_size: SaSint,
6375) {
6376    let mut i = (omp_block_start + omp_block_size - 1) as isize;
6377    let mut j = (omp_block_start + 64 + 1) as isize;
6378    while i >= j {
6379        final_bwt_aux_rtl_step(t, sa, rm, i_sample, induction_bucket, i as usize);
6380        final_bwt_aux_rtl_step(t, sa, rm, i_sample, induction_bucket, (i - 1) as usize);
6381        i -= 2;
6382    }
6383    j -= 64 + 1;
6384    while i >= j {
6385        final_bwt_aux_rtl_step(t, sa, rm, i_sample, induction_bucket, i as usize);
6386        i -= 1;
6387    }
6388}
6389
6390fn renumber_lms_suffixes_16u(
6391    sa: &mut [SaSint],
6392    m: SaSint,
6393    mut name: SaSint,
6394    omp_block_start: SaSint,
6395    omp_block_size: SaSint,
6396) -> SaSint {
6397    let mut i = omp_block_start as isize;
6398    let mut j = (omp_block_start + omp_block_size - 64 - 3) as isize;
6399    while i < j {
6400        let p0 = sa[i as usize];
6401        sa[m as usize + ((p0 & SAINT_MAX) >> 1) as usize] = name | SAINT_MIN;
6402        name += SaSint::from(p0 < 0);
6403
6404        let p1 = sa[(i + 1) as usize];
6405        sa[m as usize + ((p1 & SAINT_MAX) >> 1) as usize] = name | SAINT_MIN;
6406        name += SaSint::from(p1 < 0);
6407
6408        let p2 = sa[(i + 2) as usize];
6409        sa[m as usize + ((p2 & SAINT_MAX) >> 1) as usize] = name | SAINT_MIN;
6410        name += SaSint::from(p2 < 0);
6411
6412        let p3 = sa[(i + 3) as usize];
6413        sa[m as usize + ((p3 & SAINT_MAX) >> 1) as usize] = name | SAINT_MIN;
6414        name += SaSint::from(p3 < 0);
6415
6416        i += 4;
6417    }
6418
6419    j += 64 + 3;
6420    while i < j {
6421        let p = sa[i as usize];
6422        sa[m as usize + ((p & SAINT_MAX) >> 1) as usize] = name | SAINT_MIN;
6423        name += SaSint::from(p < 0);
6424        i += 1;
6425    }
6426
6427    name
6428}
6429
6430fn renumber_lms_suffixes_16u_omp(
6431    sa: &mut [SaSint],
6432    m: SaSint,
6433    threads: SaSint,
6434    thread_state: &mut [ThreadState],
6435) -> SaSint {
6436    if threads == 1 || m < 65_536 || thread_state.is_empty() {
6437        return renumber_lms_suffixes_16u(sa, m, 0, 0, m);
6438    }
6439
6440    let thread_count = usize::try_from(threads)
6441        .expect("threads must be non-negative")
6442        .min(thread_state.len());
6443    let block_stride = (m / thread_count as SaSint) & !15;
6444
6445    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
6446        let block_start = thread as SaSint * block_stride;
6447        let block_size = if thread + 1 < thread_count {
6448            block_stride
6449        } else {
6450            m - block_start
6451        };
6452        state.count = count_negative_marked_suffixes(sa, block_start, block_size);
6453    }
6454
6455    let mut name = 0;
6456    for thread in 0..thread_count {
6457        let block_start = thread as SaSint * block_stride;
6458        let block_size = if thread + 1 < thread_count {
6459            block_stride
6460        } else {
6461            m - block_start
6462        };
6463        renumber_lms_suffixes_16u(sa, m, name, block_start, block_size);
6464        name += thread_state[thread].count;
6465    }
6466
6467    name
6468}
6469
6470fn gather_marked_lms_suffixes(
6471    sa: &mut [SaSint],
6472    m: SaSint,
6473    mut l: isize,
6474    omp_block_start: isize,
6475    omp_block_size: isize,
6476) -> isize {
6477    if omp_block_size <= 0 {
6478        return l;
6479    }
6480
6481    l -= 1;
6482    let mut i = m as isize + omp_block_start + omp_block_size - 1;
6483    let mut j = m as isize + omp_block_start + 3;
6484    while i >= j {
6485        let s0 = sa[i as usize];
6486        sa[l as usize] = s0 & SAINT_MAX;
6487        l -= isize::from(s0 < 0);
6488
6489        let s1 = sa[(i - 1) as usize];
6490        sa[l as usize] = s1 & SAINT_MAX;
6491        l -= isize::from(s1 < 0);
6492
6493        let s2 = sa[(i - 2) as usize];
6494        sa[l as usize] = s2 & SAINT_MAX;
6495        l -= isize::from(s2 < 0);
6496
6497        let s3 = sa[(i - 3) as usize];
6498        sa[l as usize] = s3 & SAINT_MAX;
6499        l -= isize::from(s3 < 0);
6500
6501        i -= 4;
6502    }
6503
6504    j -= 3;
6505    while i >= j {
6506        let s = sa[i as usize];
6507        sa[l as usize] = s & SAINT_MAX;
6508        l -= isize::from(s < 0);
6509        i -= 1;
6510    }
6511
6512    l + 1
6513}
6514
6515fn gather_marked_lms_suffixes_omp(
6516    sa: &mut [SaSint],
6517    n: SaSint,
6518    m: SaSint,
6519    fs: SaSint,
6520    threads: SaSint,
6521    thread_state: &mut [ThreadState],
6522) {
6523    let half_n = n >> 1;
6524    if threads == 1 || n < 131_072 || thread_state.is_empty() {
6525        let _ = gather_marked_lms_suffixes(sa, m, (n + fs) as isize, 0, half_n as isize);
6526        return;
6527    }
6528
6529    let thread_count = usize::try_from(threads)
6530        .expect("threads must be non-negative")
6531        .min(thread_state.len());
6532    let block_stride = (half_n / thread_count as SaSint) & !15;
6533
6534    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
6535        let block_start = thread as SaSint * block_stride;
6536        let block_size = if thread + 1 < thread_count {
6537            block_stride
6538        } else {
6539            half_n - block_start
6540        };
6541        let local_end = if thread + 1 < thread_count {
6542            m + block_start + block_size
6543        } else {
6544            n + fs
6545        } as isize;
6546        let gathered_position =
6547            gather_marked_lms_suffixes(sa, m, local_end, block_start as isize, block_size as isize);
6548        state.position = gathered_position as SaSint;
6549        state.count = (local_end - gathered_position) as SaSint;
6550    }
6551
6552    let mut position = (n + fs) as isize;
6553    for thread in (0..thread_count).rev() {
6554        let count =
6555            usize::try_from(thread_state[thread].count).expect("count must be non-negative");
6556        position -= thread_state[thread].count as isize;
6557        if thread + 1 != thread_count && count > 0 {
6558            let src = usize::try_from(thread_state[thread].position)
6559                .expect("position must be non-negative");
6560            let dst = position as usize;
6561            sa.copy_within(src..src + count, dst);
6562        }
6563    }
6564}
6565
6566fn renumber_and_gather_lms_suffixes_omp(
6567    sa: &mut [SaSint],
6568    n: SaSint,
6569    m: SaSint,
6570    fs: SaSint,
6571    threads: SaSint,
6572    thread_state: &mut [ThreadState],
6573) -> SaSint {
6574    let m_usize = m as usize;
6575    let half_n = (n >> 1) as usize;
6576    sa[m_usize..m_usize + half_n].fill(0);
6577
6578    let name = renumber_lms_suffixes_16u_omp(sa, m, threads, thread_state);
6579    if name < m {
6580        gather_marked_lms_suffixes_omp(sa, n, m, fs, threads, thread_state);
6581    } else {
6582        for item in &mut sa[..m_usize] {
6583            *item &= SAINT_MAX;
6584        }
6585    }
6586
6587    name
6588}
6589
6590fn reconstruct_lms_suffixes(
6591    sa: &mut [SaSint],
6592    n: SaSint,
6593    m: SaSint,
6594    omp_block_start: isize,
6595    omp_block_size: isize,
6596) {
6597    if omp_block_size <= 0 {
6598        return;
6599    }
6600
6601    let base = (n - m) as usize;
6602    let mut i = omp_block_start;
6603    let mut j = omp_block_start + omp_block_size - 64 - 3;
6604    while i < j {
6605        let iu = i as usize;
6606        let s0 = sa[iu] as usize;
6607        let s1 = sa[iu + 1] as usize;
6608        let s2 = sa[iu + 2] as usize;
6609        let s3 = sa[iu + 3] as usize;
6610        sa[iu] = sa[base + s0];
6611        sa[iu + 1] = sa[base + s1];
6612        sa[iu + 2] = sa[base + s2];
6613        sa[iu + 3] = sa[base + s3];
6614        i += 4;
6615    }
6616
6617    j += 64 + 3;
6618    while i < j {
6619        let iu = i as usize;
6620        let s = sa[iu] as usize;
6621        sa[iu] = sa[base + s];
6622        i += 1;
6623    }
6624}
6625
6626fn reconstruct_lms_suffixes_omp(sa: &mut [SaSint], n: SaSint, m: SaSint, threads: SaSint) {
6627    if threads == 1 || m < 65_536 {
6628        reconstruct_lms_suffixes(sa, n, m, 0, m as isize);
6629        return;
6630    }
6631
6632    let thread_count = threads as usize;
6633    let block_stride = (m / threads) & !15;
6634    for thread in 0..thread_count {
6635        let block_start = thread as SaSint * block_stride;
6636        let block_size = if thread + 1 < thread_count {
6637            block_stride
6638        } else {
6639            m - block_start
6640        };
6641        reconstruct_lms_suffixes(sa, n, m, block_start as isize, block_size as isize);
6642    }
6643}
6644
6645fn renumber_distinct_lms_suffixes_32s_4k(
6646    sa: &mut [SaSint],
6647    m: SaSint,
6648    mut name: SaSint,
6649    omp_block_start: isize,
6650    omp_block_size: isize,
6651) -> SaSint {
6652    if omp_block_size <= 0 {
6653        return name;
6654    }
6655
6656    let m_usize = m as usize;
6657    let start = omp_block_start as usize;
6658    let size = omp_block_size as usize;
6659    let (sa_head, sam) = sa.split_at_mut(m_usize);
6660    let mut i = start;
6661    let mut j = start + size.saturating_sub(64 + 3);
6662    let mut p3 = 0;
6663
6664    while i < j {
6665        let p0 = sa_head[i];
6666        sa_head[i] = p0 & SAINT_MAX;
6667        sam[(sa_head[i] >> 1) as usize] = name | (p0 & p3 & SAINT_MIN);
6668        name += SaSint::from(p0 < 0);
6669
6670        let p1 = sa_head[i + 1];
6671        sa_head[i + 1] = p1 & SAINT_MAX;
6672        sam[(sa_head[i + 1] >> 1) as usize] = name | (p1 & p0 & SAINT_MIN);
6673        name += SaSint::from(p1 < 0);
6674
6675        let p2 = sa_head[i + 2];
6676        sa_head[i + 2] = p2 & SAINT_MAX;
6677        sam[(sa_head[i + 2] >> 1) as usize] = name | (p2 & p1 & SAINT_MIN);
6678        name += SaSint::from(p2 < 0);
6679
6680        p3 = sa_head[i + 3];
6681        sa_head[i + 3] = p3 & SAINT_MAX;
6682        sam[(sa_head[i + 3] >> 1) as usize] = name | (p3 & p2 & SAINT_MIN);
6683        name += SaSint::from(p3 < 0);
6684
6685        i += 4;
6686    }
6687
6688    j = start + size;
6689    while i < j {
6690        let p2 = p3;
6691        p3 = sa_head[i];
6692        sa_head[i] = p3 & SAINT_MAX;
6693        sam[(sa_head[i] >> 1) as usize] = name | (p3 & p2 & SAINT_MIN);
6694        name += SaSint::from(p3 < 0);
6695        i += 1;
6696    }
6697
6698    name
6699}
6700
6701fn mark_distinct_lms_suffixes_32s(
6702    sa: &mut [SaSint],
6703    m: SaSint,
6704    omp_block_start: isize,
6705    omp_block_size: isize,
6706) {
6707    if omp_block_size <= 0 {
6708        return;
6709    }
6710
6711    let mut i = m as usize + omp_block_start as usize;
6712    let mut j = i + (omp_block_size as usize).saturating_sub(3);
6713    let mut p3 = 0;
6714    while i < j {
6715        let mut p0 = sa[i];
6716        sa[i] = p0 & (p3 | SAINT_MAX);
6717        p0 = if p0 == 0 { p3 } else { p0 };
6718
6719        let mut p1 = sa[i + 1];
6720        sa[i + 1] = p1 & (p0 | SAINT_MAX);
6721        p1 = if p1 == 0 { p0 } else { p1 };
6722
6723        let mut p2 = sa[i + 2];
6724        sa[i + 2] = p2 & (p1 | SAINT_MAX);
6725        p2 = if p2 == 0 { p1 } else { p2 };
6726
6727        p3 = sa[i + 3];
6728        sa[i + 3] = p3 & (p2 | SAINT_MAX);
6729        p3 = if p3 == 0 { p2 } else { p3 };
6730        i += 4;
6731    }
6732
6733    j = m as usize + omp_block_start as usize + omp_block_size as usize;
6734    while i < j {
6735        let p2 = p3;
6736        p3 = sa[i];
6737        sa[i] = p3 & (p2 | SAINT_MAX);
6738        p3 = if p3 == 0 { p2 } else { p3 };
6739        i += 1;
6740    }
6741}
6742
6743fn clamp_lms_suffixes_length_32s(
6744    sa: &mut [SaSint],
6745    m: SaSint,
6746    omp_block_start: isize,
6747    omp_block_size: isize,
6748) {
6749    if omp_block_size <= 0 {
6750        return;
6751    }
6752
6753    let mut i = m as usize + omp_block_start as usize;
6754    let mut j = i + (omp_block_size as usize).saturating_sub(3);
6755    while i < j {
6756        let s0 = sa[i];
6757        sa[i] = if s0 < 0 { s0 } else { 0 } & SAINT_MAX;
6758
6759        let s1 = sa[i + 1];
6760        sa[i + 1] = if s1 < 0 { s1 } else { 0 } & SAINT_MAX;
6761
6762        let s2 = sa[i + 2];
6763        sa[i + 2] = if s2 < 0 { s2 } else { 0 } & SAINT_MAX;
6764
6765        let s3 = sa[i + 3];
6766        sa[i + 3] = if s3 < 0 { s3 } else { 0 } & SAINT_MAX;
6767
6768        i += 4;
6769    }
6770
6771    j = m as usize + omp_block_start as usize + omp_block_size as usize;
6772    while i < j {
6773        let s = sa[i];
6774        sa[i] = if s < 0 { s } else { 0 } & SAINT_MAX;
6775        i += 1;
6776    }
6777}
6778
6779fn renumber_distinct_lms_suffixes_32s_4k_omp(
6780    sa: &mut [SaSint],
6781    m: SaSint,
6782    threads: SaSint,
6783    thread_state: &mut [ThreadState],
6784) -> SaSint {
6785    if threads == 1 || m < 65_536 || thread_state.is_empty() {
6786        return renumber_distinct_lms_suffixes_32s_4k(sa, m, 1, 0, m as isize) - 1;
6787    }
6788
6789    let thread_count = usize::try_from(threads)
6790        .expect("threads must be non-negative")
6791        .min(thread_state.len());
6792    let block_stride = (m / thread_count as SaSint) & !15;
6793
6794    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
6795        let block_start = thread as SaSint * block_stride;
6796        let block_size = if thread + 1 < thread_count {
6797            block_stride
6798        } else {
6799            m - block_start
6800        };
6801        state.count = count_negative_marked_suffixes(sa, block_start, block_size);
6802    }
6803
6804    let mut count = 1;
6805    for thread in 0..thread_count {
6806        let block_start = thread as SaSint * block_stride;
6807        let block_size = if thread + 1 < thread_count {
6808            block_stride
6809        } else {
6810            m - block_start
6811        };
6812        renumber_distinct_lms_suffixes_32s_4k(
6813            sa,
6814            m,
6815            count,
6816            block_start as isize,
6817            block_size as isize,
6818        );
6819        count += thread_state[thread].count;
6820    }
6821
6822    count - 1
6823}
6824
6825fn mark_distinct_lms_suffixes_32s_omp(sa: &mut [SaSint], n: SaSint, m: SaSint, threads: SaSint) {
6826    let half_n = n >> 1;
6827    if threads == 1 || n < 131_072 {
6828        mark_distinct_lms_suffixes_32s(sa, m, 0, half_n as isize);
6829        return;
6830    }
6831
6832    let thread_count = threads as usize;
6833    let block_stride = (half_n / threads) & !15;
6834    for thread in 0..thread_count {
6835        let block_start = thread as SaSint * block_stride;
6836        let block_size = if thread + 1 < thread_count {
6837            block_stride
6838        } else {
6839            half_n - block_start
6840        };
6841        mark_distinct_lms_suffixes_32s(sa, m, block_start as isize, block_size as isize);
6842    }
6843}
6844
6845fn clamp_lms_suffixes_length_32s_omp(sa: &mut [SaSint], n: SaSint, m: SaSint, threads: SaSint) {
6846    let half_n = n >> 1;
6847    if threads == 1 || n < 131_072 {
6848        clamp_lms_suffixes_length_32s(sa, m, 0, half_n as isize);
6849        return;
6850    }
6851
6852    let thread_count = threads as usize;
6853    let block_stride = (half_n / threads) & !15;
6854    for thread in 0..thread_count {
6855        let block_start = thread as SaSint * block_stride;
6856        let block_size = if thread + 1 < thread_count {
6857            block_stride
6858        } else {
6859            half_n - block_start
6860        };
6861        clamp_lms_suffixes_length_32s(sa, m, block_start as isize, block_size as isize);
6862    }
6863}
6864
6865fn renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
6866    sa: &mut [SaSint],
6867    n: SaSint,
6868    m: SaSint,
6869    threads: SaSint,
6870    thread_state: &mut [ThreadState],
6871) -> SaSint {
6872    let m_usize = m as usize;
6873    let half_n = (n >> 1) as usize;
6874    sa[m_usize..m_usize + half_n].fill(0);
6875
6876    let name = renumber_distinct_lms_suffixes_32s_4k_omp(sa, m, threads, thread_state);
6877    if name < m {
6878        mark_distinct_lms_suffixes_32s_omp(sa, n, m, threads);
6879    }
6880
6881    name
6882}
6883
6884fn renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(
6885    t: &[SaSint],
6886    sa: &mut [SaSint],
6887    n: SaSint,
6888    m: SaSint,
6889    threads: SaSint,
6890) -> SaSint {
6891    let m_usize = m as usize;
6892    let n_usize = n as usize;
6893
6894    gather_lms_suffixes_32s(t, sa, n);
6895    sa[m_usize..n_usize - m_usize].fill(0);
6896
6897    let mut i = n - m;
6898    let mut j = n - 1 - 64 - 3;
6899    while i < j {
6900        let s0 = (sa[i as usize] as SaUint >> 1) as usize;
6901        let s1 = (sa[(i + 1) as usize] as SaUint >> 1) as usize;
6902        let s2 = (sa[(i + 2) as usize] as SaUint >> 1) as usize;
6903        let s3 = (sa[(i + 3) as usize] as SaUint >> 1) as usize;
6904        sa[m_usize + s0] = sa[(i + 1) as usize] - sa[i as usize] + 1 + SAINT_MIN;
6905        sa[m_usize + s1] = sa[(i + 2) as usize] - sa[(i + 1) as usize] + 1 + SAINT_MIN;
6906        sa[m_usize + s2] = sa[(i + 3) as usize] - sa[(i + 2) as usize] + 1 + SAINT_MIN;
6907        sa[m_usize + s3] = sa[(i + 4) as usize] - sa[(i + 3) as usize] + 1 + SAINT_MIN;
6908        i += 4;
6909    }
6910
6911    j += 64 + 3;
6912    while i < j {
6913        let s = (sa[i as usize] as SaUint >> 1) as usize;
6914        sa[m_usize + s] = sa[(i + 1) as usize] - sa[i as usize] + 1 + SAINT_MIN;
6915        i += 1;
6916    }
6917
6918    let tail = (sa[n_usize - 1] as SaUint >> 1) as usize;
6919    sa[m_usize + tail] = 1 + SAINT_MIN;
6920
6921    clamp_lms_suffixes_length_32s_omp(sa, n, m, threads);
6922
6923    let mut name = 1;
6924    if m_usize > 0 {
6925        let mut i = 1usize;
6926        let mut j = m_usize.saturating_sub(64 + 1);
6927        let mut p = sa[0] as usize;
6928        let mut plen = sa[m_usize + (p >> 1)];
6929        let mut pdiff = SAINT_MIN;
6930
6931        while i < j {
6932            let q = sa[i] as usize;
6933            let qlen = sa[m_usize + (q >> 1)];
6934            let mut qdiff = SAINT_MIN;
6935            if plen == qlen {
6936                let mut l = 0;
6937                while l < qlen as usize {
6938                    if t[p + l] != t[q + l] {
6939                        break;
6940                    }
6941                    l += 1;
6942                }
6943                qdiff = ((l as SaSint) - qlen) & SAINT_MIN;
6944            }
6945            sa[m_usize + (p >> 1)] = name | (pdiff & qdiff);
6946            name += SaSint::from(qdiff < 0);
6947
6948            p = sa[i + 1] as usize;
6949            plen = sa[m_usize + (p >> 1)];
6950            pdiff = SAINT_MIN;
6951            if qlen == plen {
6952                let mut l = 0;
6953                while l < plen as usize {
6954                    if t[q + l] != t[p + l] {
6955                        break;
6956                    }
6957                    l += 1;
6958                }
6959                pdiff = ((l as SaSint) - plen) & SAINT_MIN;
6960            }
6961            sa[m_usize + (q >> 1)] = name | (qdiff & pdiff);
6962            name += SaSint::from(pdiff < 0);
6963            i += 2;
6964        }
6965
6966        j = m_usize;
6967        while i < j {
6968            let q = sa[i] as usize;
6969            let qlen = sa[m_usize + (q >> 1)];
6970            let mut qdiff = SAINT_MIN;
6971            if plen == qlen {
6972                let mut l = 0;
6973                while l < plen as usize {
6974                    if t[p + l] != t[q + l] {
6975                        break;
6976                    }
6977                    l += 1;
6978                }
6979                qdiff = ((l as SaSint) - plen) & SAINT_MIN;
6980            }
6981            sa[m_usize + (p >> 1)] = name | (pdiff & qdiff);
6982            name += SaSint::from(qdiff < 0);
6983            p = q;
6984            plen = qlen;
6985            pdiff = qdiff;
6986            i += 1;
6987        }
6988
6989        sa[m_usize + (p >> 1)] = name | pdiff;
6990        name += 1;
6991    }
6992
6993    if name <= m {
6994        mark_distinct_lms_suffixes_32s_omp(sa, n, m, threads);
6995    }
6996
6997    name - 1
6998}
6999
7000fn renumber_unique_and_nonunique_lms_suffixes_32s(
7001    t: &mut [SaSint],
7002    sa: &mut [SaSint],
7003    m: SaSint,
7004    mut f: SaSint,
7005    omp_block_start: isize,
7006    omp_block_size: isize,
7007) -> SaSint {
7008    if omp_block_size <= 0 {
7009        return f;
7010    }
7011
7012    let m_usize = m as usize;
7013    let (sa_head, sam) = sa.split_at_mut(m_usize);
7014    let mut i = omp_block_start;
7015    let mut j = omp_block_start + omp_block_size - 128 - 3;
7016    while i < j {
7017        for offset in 0..4 {
7018            let idx = (i + offset) as usize;
7019            let p = sa_head[idx] as SaUint;
7020            let mut s = sam[(p >> 1) as usize];
7021            if s < 0 {
7022                t[p as usize] |= SAINT_MIN;
7023                f += 1;
7024                s = i as SaSint + offset as SaSint + SAINT_MIN + f;
7025            }
7026            sam[(p >> 1) as usize] = s - f;
7027        }
7028        i += 4;
7029    }
7030
7031    j += 128 + 3;
7032    while i < j {
7033        let p = sa_head[i as usize] as SaUint;
7034        let mut s = sam[(p >> 1) as usize];
7035        if s < 0 {
7036            t[p as usize] |= SAINT_MIN;
7037            f += 1;
7038            s = i as SaSint + SAINT_MIN + f;
7039        }
7040        sam[(p >> 1) as usize] = s - f;
7041        i += 1;
7042    }
7043
7044    f
7045}
7046
7047fn compact_unique_and_nonunique_lms_suffixes_32s(
7048    sa: &mut [SaSint],
7049    m: SaSint,
7050    pl: &mut isize,
7051    pr: &mut isize,
7052    omp_block_start: isize,
7053    omp_block_size: isize,
7054) {
7055    if omp_block_size <= 0 {
7056        return;
7057    }
7058
7059    let m_usize = m as usize;
7060    let source: Vec<SaSint> = sa
7061        [m_usize + omp_block_start as usize..m_usize + (omp_block_start + omp_block_size) as usize]
7062        .to_vec();
7063    let mut l = *pl - 1;
7064    let mut r = *pr - 1;
7065
7066    for &p in source.iter().rev() {
7067        sa[l as usize] = p & SAINT_MAX;
7068        l -= isize::from(p < 0);
7069
7070        sa[r as usize] = p.wrapping_sub(1);
7071        r -= isize::from(p > 0);
7072    }
7073
7074    *pl = l + 1;
7075    *pr = r + 1;
7076}
7077
7078fn count_unique_suffixes(
7079    sa: &[SaSint],
7080    m: SaSint,
7081    omp_block_start: isize,
7082    omp_block_size: isize,
7083) -> SaSint {
7084    let base = m as usize;
7085    let start = omp_block_start as usize;
7086    let end = start + omp_block_size as usize;
7087    let mut count = 0;
7088    for i in start..end {
7089        count += SaSint::from(sa[base + ((sa[i] as SaUint) >> 1) as usize] < 0);
7090    }
7091    count
7092}
7093
7094fn renumber_unique_and_nonunique_lms_suffixes_32s_omp(
7095    t: &mut [SaSint],
7096    sa: &mut [SaSint],
7097    m: SaSint,
7098    threads: SaSint,
7099) -> SaSint {
7100    if threads == 1 || m < 65_536 {
7101        return renumber_unique_and_nonunique_lms_suffixes_32s(t, sa, m, 0, 0, m as isize);
7102    }
7103
7104    let thread_count = threads as usize;
7105    let block_stride = (m / threads) & !15;
7106    let mut counts = vec![0; thread_count];
7107
7108    for thread in 0..thread_count {
7109        let block_start = thread as SaSint * block_stride;
7110        let block_size = if thread + 1 < thread_count {
7111            block_stride
7112        } else {
7113            m - block_start
7114        };
7115        counts[thread] = count_unique_suffixes(sa, m, block_start as isize, block_size as isize);
7116    }
7117
7118    let mut f = 0;
7119    for thread in 0..thread_count {
7120        let block_start = thread as SaSint * block_stride;
7121        let block_size = if thread + 1 < thread_count {
7122            block_stride
7123        } else {
7124            m - block_start
7125        };
7126        renumber_unique_and_nonunique_lms_suffixes_32s(
7127            t,
7128            sa,
7129            m,
7130            f,
7131            block_start as isize,
7132            block_size as isize,
7133        );
7134        f += counts[thread];
7135    }
7136
7137    f
7138}
7139
7140fn compact_unique_and_nonunique_lms_suffixes_32s_omp(
7141    sa: &mut [SaSint],
7142    n: SaSint,
7143    m: SaSint,
7144    fs: SaSint,
7145    f: SaSint,
7146    threads: SaSint,
7147) {
7148    let half_n = n >> 1;
7149    if threads == 1 || n < 131_072 || m >= fs {
7150        let mut l = m as isize;
7151        let mut r = (n + fs) as isize;
7152        compact_unique_and_nonunique_lms_suffixes_32s(sa, m, &mut l, &mut r, 0, half_n as isize);
7153    } else {
7154        let thread_count = threads as usize;
7155        let block_stride = (half_n / threads) & !15;
7156        let mut positions = vec![0isize; thread_count];
7157        let mut counts = vec![0isize; thread_count];
7158
7159        for thread in 0..thread_count {
7160            let block_start = thread as SaSint * block_stride;
7161            let block_size = if thread + 1 < thread_count {
7162                block_stride
7163            } else {
7164                half_n - block_start
7165            };
7166            let mut position = (m + half_n + block_start + block_size) as isize;
7167            let mut count = (m + block_start + block_size) as isize;
7168            compact_unique_and_nonunique_lms_suffixes_32s(
7169                sa,
7170                m,
7171                &mut position,
7172                &mut count,
7173                block_start as isize,
7174                block_size as isize,
7175            );
7176            positions[thread] = position;
7177            counts[thread] = count;
7178        }
7179
7180        let mut position = m as isize;
7181        for thread in (0..thread_count).rev() {
7182            let block_end = if thread + 1 < thread_count {
7183                block_stride * (thread as SaSint + 1)
7184            } else {
7185                half_n
7186            };
7187            let count = (m + half_n + block_end) as isize - positions[thread];
7188            if count > 0 {
7189                position -= count;
7190                let src = positions[thread] as usize;
7191                let dst = position as usize;
7192                sa.copy_within(src..src + count as usize, dst);
7193            }
7194        }
7195
7196        let mut position = (n + fs) as isize;
7197        for thread in (0..thread_count).rev() {
7198            let block_end = if thread + 1 < thread_count {
7199                block_stride * (thread as SaSint + 1)
7200            } else {
7201                half_n
7202            };
7203            let count = (m + block_end) as isize - counts[thread];
7204            if count > 0 {
7205                position -= count;
7206                let src = counts[thread] as usize;
7207                let dst = position as usize;
7208                sa.copy_within(src..src + count as usize, dst);
7209            }
7210        }
7211    }
7212
7213    let dst = (n + fs - m) as usize;
7214    let src = (m - f) as usize;
7215    sa.copy_within(src..src + f as usize, dst);
7216}
7217
7218fn compact_lms_suffixes_32s_omp(
7219    t: &mut [SaSint],
7220    sa: &mut [SaSint],
7221    n: SaSint,
7222    m: SaSint,
7223    fs: SaSint,
7224    threads: SaSint,
7225) -> SaSint {
7226    let f = renumber_unique_and_nonunique_lms_suffixes_32s_omp(t, sa, m, threads);
7227    compact_unique_and_nonunique_lms_suffixes_32s_omp(sa, n, m, fs, f, threads);
7228    f
7229}
7230
7231fn merge_unique_lms_suffixes_32s(
7232    t: &mut [SaSint],
7233    sa: &mut [SaSint],
7234    n: SaSint,
7235    m: SaSint,
7236    l: isize,
7237    omp_block_start: isize,
7238    omp_block_size: isize,
7239) {
7240    let mut src_index = (n as isize - m as isize - 1 + l) as usize;
7241    let mut tmp = sa[src_index] as isize;
7242    src_index += 1;
7243
7244    let mut i = omp_block_start;
7245    let mut j = omp_block_start + omp_block_size - 6;
7246    while i < j {
7247        let iu = i as usize;
7248
7249        let c0 = t[iu];
7250        if c0 < 0 {
7251            t[iu] = c0 & SAINT_MAX;
7252            sa[tmp as usize] = i as SaSint;
7253            i += 1;
7254            tmp = sa[src_index] as isize;
7255            src_index += 1;
7256        }
7257
7258        let c1 = t[(i + 1) as usize];
7259        if c1 < 0 {
7260            t[(i + 1) as usize] = c1 & SAINT_MAX;
7261            sa[tmp as usize] = i as SaSint + 1;
7262            i += 1;
7263            tmp = sa[src_index] as isize;
7264            src_index += 1;
7265        }
7266
7267        let c2 = t[(i + 2) as usize];
7268        if c2 < 0 {
7269            t[(i + 2) as usize] = c2 & SAINT_MAX;
7270            sa[tmp as usize] = i as SaSint + 2;
7271            i += 1;
7272            tmp = sa[src_index] as isize;
7273            src_index += 1;
7274        }
7275
7276        let c3 = t[(i + 3) as usize];
7277        if c3 < 0 {
7278            t[(i + 3) as usize] = c3 & SAINT_MAX;
7279            sa[tmp as usize] = i as SaSint + 3;
7280            i += 1;
7281            tmp = sa[src_index] as isize;
7282            src_index += 1;
7283        }
7284
7285        i += 4;
7286    }
7287
7288    j += 6;
7289    while i < j {
7290        let c = t[i as usize];
7291        if c < 0 {
7292            t[i as usize] = c & SAINT_MAX;
7293            sa[tmp as usize] = i as SaSint;
7294            i += 1;
7295            tmp = sa[src_index] as isize;
7296            src_index += 1;
7297        }
7298        i += 1;
7299    }
7300}
7301
7302fn merge_nonunique_lms_suffixes_32s(
7303    sa: &mut [SaSint],
7304    n: SaSint,
7305    m: SaSint,
7306    l: isize,
7307    omp_block_start: isize,
7308    omp_block_size: isize,
7309) {
7310    let mut src_index = (n as isize - m as isize - 1 + l) as usize;
7311    let mut tmp = sa[src_index];
7312    src_index += 1;
7313
7314    let mut i = omp_block_start;
7315    let mut j = omp_block_start + omp_block_size - 3;
7316    while i < j {
7317        if sa[i as usize] == 0 {
7318            sa[i as usize] = tmp;
7319            tmp = sa[src_index];
7320            src_index += 1;
7321        }
7322        if sa[(i + 1) as usize] == 0 {
7323            sa[(i + 1) as usize] = tmp;
7324            tmp = sa[src_index];
7325            src_index += 1;
7326        }
7327        if sa[(i + 2) as usize] == 0 {
7328            sa[(i + 2) as usize] = tmp;
7329            tmp = sa[src_index];
7330            src_index += 1;
7331        }
7332        if sa[(i + 3) as usize] == 0 {
7333            sa[(i + 3) as usize] = tmp;
7334            tmp = sa[src_index];
7335            src_index += 1;
7336        }
7337        i += 4;
7338    }
7339
7340    j += 3;
7341    while i < j {
7342        if sa[i as usize] == 0 {
7343            sa[i as usize] = tmp;
7344            tmp = sa[src_index];
7345            src_index += 1;
7346        }
7347        i += 1;
7348    }
7349}
7350
7351fn merge_unique_lms_suffixes_32s_omp(
7352    t: &mut [SaSint],
7353    sa: &mut [SaSint],
7354    n: SaSint,
7355    m: SaSint,
7356    threads: SaSint,
7357) {
7358    if threads == 1 || n < 65_536 {
7359        merge_unique_lms_suffixes_32s(t, sa, n, m, 0, 0, n as isize);
7360        return;
7361    }
7362
7363    let thread_count = threads as usize;
7364    let block_stride = (n / threads) & !15;
7365    let mut counts = vec![0; thread_count];
7366
7367    for thread in 0..thread_count {
7368        let block_start = thread as SaSint * block_stride;
7369        let block_size = if thread + 1 < thread_count {
7370            block_stride
7371        } else {
7372            n - block_start
7373        };
7374        counts[thread] = count_negative_marked_suffixes(t, block_start, block_size);
7375    }
7376
7377    let mut count = 0;
7378    for thread in 0..thread_count {
7379        let block_start = thread as SaSint * block_stride;
7380        let block_size = if thread + 1 < thread_count {
7381            block_stride
7382        } else {
7383            n - block_start
7384        };
7385        merge_unique_lms_suffixes_32s(
7386            t,
7387            sa,
7388            n,
7389            m,
7390            count as isize,
7391            block_start as isize,
7392            block_size as isize,
7393        );
7394        count += counts[thread];
7395    }
7396}
7397
7398fn merge_nonunique_lms_suffixes_32s_omp(
7399    sa: &mut [SaSint],
7400    n: SaSint,
7401    m: SaSint,
7402    f: SaSint,
7403    threads: SaSint,
7404) {
7405    if threads == 1 || m < 65_536 {
7406        merge_nonunique_lms_suffixes_32s(sa, n, m, f as isize, 0, m as isize);
7407        return;
7408    }
7409
7410    let thread_count = threads as usize;
7411    let block_stride = (m / threads) & !15;
7412    let mut counts = vec![0; thread_count];
7413
7414    for thread in 0..thread_count {
7415        let block_start = thread as SaSint * block_stride;
7416        let block_size = if thread + 1 < thread_count {
7417            block_stride
7418        } else {
7419            m - block_start
7420        };
7421        counts[thread] = count_zero_marked_suffixes(sa, block_start, block_size);
7422    }
7423
7424    let mut count = f;
7425    for thread in 0..thread_count {
7426        let block_start = thread as SaSint * block_stride;
7427        let block_size = if thread + 1 < thread_count {
7428            block_stride
7429        } else {
7430            m - block_start
7431        };
7432        merge_nonunique_lms_suffixes_32s(
7433            sa,
7434            n,
7435            m,
7436            count as isize,
7437            block_start as isize,
7438            block_size as isize,
7439        );
7440        count += counts[thread];
7441    }
7442}
7443
7444fn merge_compacted_lms_suffixes_32s_omp(
7445    t: &mut [SaSint],
7446    sa: &mut [SaSint],
7447    n: SaSint,
7448    m: SaSint,
7449    f: SaSint,
7450    threads: SaSint,
7451) {
7452    merge_unique_lms_suffixes_32s_omp(t, sa, n, m, threads);
7453    merge_nonunique_lms_suffixes_32s_omp(sa, n, m, f, threads);
7454}
7455
7456fn reconstruct_compacted_lms_suffixes_32s_2k_omp(
7457    t: &mut [SaSint],
7458    sa: &mut [SaSint],
7459    n: SaSint,
7460    k: SaSint,
7461    m: SaSint,
7462    fs: SaSint,
7463    f: SaSint,
7464    buckets: &mut [SaSint],
7465    local_buckets: SaSint,
7466    threads: SaSint,
7467    thread_state: &mut [ThreadState],
7468) {
7469    if f > 0 {
7470        let dst = (n - m - 1) as usize;
7471        let src = (n + fs - m) as usize;
7472        sa.copy_within(src..src + f as usize, dst);
7473
7474        count_and_gather_compacted_lms_suffixes_32s_2k_omp(
7475            t,
7476            sa,
7477            n,
7478            k,
7479            buckets,
7480            local_buckets,
7481            threads,
7482            thread_state,
7483        );
7484        reconstruct_lms_suffixes_omp(sa, n, m - f, threads);
7485
7486        let dst = (n - m - 1 + f) as usize;
7487        sa.copy_within(0..(m - f) as usize, dst);
7488        sa[..m as usize].fill(0);
7489
7490        merge_compacted_lms_suffixes_32s_omp(t, sa, n, m, f, threads);
7491    } else {
7492        count_and_gather_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as isize);
7493        reconstruct_lms_suffixes_omp(sa, n, m, threads);
7494    }
7495}
7496
7497fn reconstruct_compacted_lms_suffixes_32s_1k_omp(
7498    t: &mut [SaSint],
7499    sa: &mut [SaSint],
7500    n: SaSint,
7501    m: SaSint,
7502    fs: SaSint,
7503    f: SaSint,
7504    threads: SaSint,
7505) {
7506    if f > 0 {
7507        let dst = (n - m - 1) as usize;
7508        let src = (n + fs - m) as usize;
7509        sa.copy_within(src..src + f as usize, dst);
7510
7511        gather_compacted_lms_suffixes_32s(t, sa, n);
7512        reconstruct_lms_suffixes_omp(sa, n, m - f, threads);
7513
7514        let dst = (n - m - 1 + f) as usize;
7515        sa.copy_within(0..(m - f) as usize, dst);
7516        sa[..m as usize].fill(0);
7517
7518        merge_compacted_lms_suffixes_32s_omp(t, sa, n, m, f, threads);
7519    } else {
7520        gather_lms_suffixes_32s(t, sa, n);
7521        reconstruct_lms_suffixes_omp(sa, n, m, threads);
7522    }
7523}
7524
7525fn place_lms_suffixes_interval_16u(
7526    sa: &mut [SaSint],
7527    n: SaSint,
7528    mut m: SaSint,
7529    flags: SaSint,
7530    buckets: &mut [SaSint],
7531) {
7532    if (flags & LIBSAIS_FLAGS_GSA) != 0 {
7533        buckets[7 * ALPHABET_SIZE] -= 1;
7534    }
7535
7536    let mut j = n as isize;
7537    let mut c = ALPHABET_SIZE as isize - 2;
7538    while c >= 0 {
7539        let ci = c as usize;
7540        let l =
7541            buckets[buckets_index2(ci, 1) + buckets_index2(1, 0)] - buckets[buckets_index2(ci, 1)];
7542        if l > 0 {
7543            let i = buckets[7 * ALPHABET_SIZE + ci] as isize;
7544            if j - i > 0 {
7545                sa[i as usize..j as usize].fill(0);
7546            }
7547
7548            m -= l;
7549            j = i - l as isize;
7550            let src = m as usize;
7551            let dst = j as usize;
7552            sa.copy_within(src..src + l as usize, dst);
7553        }
7554        c -= 1;
7555    }
7556
7557    sa[..j as usize].fill(0);
7558
7559    if (flags & LIBSAIS_FLAGS_GSA) != 0 {
7560        buckets[7 * ALPHABET_SIZE] += 1;
7561    }
7562}
7563
7564fn place_lms_suffixes_interval_32s_4k(
7565    sa: &mut [SaSint],
7566    n: SaSint,
7567    k: SaSint,
7568    mut m: SaSint,
7569    buckets: &[SaSint],
7570) {
7571    let bucket_end = &buckets[3 * k as usize..4 * k as usize];
7572    let mut j = n as usize;
7573    let mut c = k - 2;
7574    while c >= 0 {
7575        let cu = c as usize;
7576        let l =
7577            buckets[buckets_index2(cu, 1) + buckets_index2(1, 0)] - buckets[buckets_index2(cu, 1)];
7578        if l > 0 {
7579            let i = bucket_end[cu] as usize;
7580            if j > i {
7581                sa[i..j].fill(0);
7582            }
7583
7584            m -= l;
7585            let dst = i - l as usize;
7586            sa.copy_within(m as usize..m as usize + l as usize, dst);
7587            j = dst;
7588        }
7589        c -= 1;
7590    }
7591
7592    sa[..j].fill(0);
7593}
7594
7595fn place_lms_suffixes_interval_32s_2k(
7596    sa: &mut [SaSint],
7597    n: SaSint,
7598    k: SaSint,
7599    mut m: SaSint,
7600    buckets: &[SaSint],
7601) {
7602    let mut j = n as usize;
7603    if k > 1 {
7604        let mut c = buckets_index2(k as usize - 2, 0) as isize;
7605        while c >= buckets_index2(0, 0) as isize {
7606            let cu = c as usize;
7607            let l = buckets[cu + buckets_index2(1, 1)] - buckets[cu + buckets_index2(0, 1)];
7608            if l > 0 {
7609                let i = buckets[cu] as usize;
7610                if j > i {
7611                    sa[i..j].fill(0);
7612                }
7613
7614                m -= l;
7615                let dst = i - l as usize;
7616                sa.copy_within(m as usize..m as usize + l as usize, dst);
7617                j = dst;
7618            }
7619            c -= buckets_index2(1, 0) as isize;
7620        }
7621    }
7622
7623    sa[..j].fill(0);
7624}
7625
7626fn place_lms_suffixes_interval_32s_1k(
7627    t: &[SaSint],
7628    sa: &mut [SaSint],
7629    k: SaSint,
7630    m: SaSint,
7631    buckets: &[SaSint],
7632) {
7633    let mut c = k - 1;
7634    let mut l = buckets[c as usize] as usize;
7635
7636    let mut i = m - 1;
7637    while i >= 0 {
7638        let p = sa[i as usize] as usize;
7639        if t[p] != c {
7640            c = t[p];
7641            let bucket_pos = buckets[c as usize] as usize;
7642            if l > bucket_pos {
7643                sa[bucket_pos..l].fill(0);
7644            }
7645            l = bucket_pos;
7646        }
7647        l -= 1;
7648        sa[l] = p as SaSint;
7649        i -= 1;
7650    }
7651
7652    sa[..l].fill(0);
7653}
7654
7655fn place_lms_suffixes_histogram_32s_6k(
7656    sa: &mut [SaSint],
7657    n: SaSint,
7658    k: SaSint,
7659    mut m: SaSint,
7660    buckets: &[SaSint],
7661) {
7662    let bucket_end = &buckets[5 * k as usize..6 * k as usize];
7663    let mut j = n as usize;
7664    let mut c = k - 2;
7665    while c >= 0 {
7666        let l = buckets[buckets_index4(c as usize, 1)] as usize;
7667        if l > 0 {
7668            let i = bucket_end[c as usize] as usize;
7669            if j > i {
7670                sa[i..j].fill(0);
7671            }
7672            let dst = i - l;
7673            m -= l as SaSint;
7674            sa.copy_within(m as usize..m as usize + l, dst);
7675            j = dst;
7676        }
7677        c -= 1;
7678    }
7679    sa[..j].fill(0);
7680}
7681
7682fn place_lms_suffixes_histogram_32s_4k(
7683    sa: &mut [SaSint],
7684    n: SaSint,
7685    k: SaSint,
7686    mut m: SaSint,
7687    buckets: &[SaSint],
7688) {
7689    let bucket_end = &buckets[3 * k as usize..4 * k as usize];
7690    let mut j = n as usize;
7691    let mut c = k - 2;
7692    while c >= 0 {
7693        let l = buckets[buckets_index2(c as usize, 1)] as usize;
7694        if l > 0 {
7695            let i = bucket_end[c as usize] as usize;
7696            if j > i {
7697                sa[i..j].fill(0);
7698            }
7699            let dst = i - l;
7700            m -= l as SaSint;
7701            sa.copy_within(m as usize..m as usize + l, dst);
7702            j = dst;
7703        }
7704        c -= 1;
7705    }
7706    sa[..j].fill(0);
7707}
7708
7709fn place_lms_suffixes_histogram_32s_2k(
7710    sa: &mut [SaSint],
7711    n: SaSint,
7712    k: SaSint,
7713    mut m: SaSint,
7714    buckets: &[SaSint],
7715) {
7716    let mut j = n as usize;
7717    if k > 1 {
7718        let mut c = buckets_index2(k as usize - 2, 0) as isize;
7719        while c >= buckets_index2(0, 0) as isize {
7720            let cu = c as usize;
7721            let l = buckets[cu + buckets_index2(0, 1)] as usize;
7722            if l > 0 {
7723                let i = buckets[cu] as usize;
7724                if j > i {
7725                    sa[i..j].fill(0);
7726                }
7727                let dst = i - l;
7728                m -= l as SaSint;
7729                sa.copy_within(m as usize..m as usize + l, dst);
7730                j = dst;
7731            }
7732            c -= buckets_index2(1, 0) as isize;
7733        }
7734    }
7735    sa[..j].fill(0);
7736}
7737
7738fn final_bwt_scan_left_to_right_16u_block_prepare(
7739    t: &[u16],
7740    sa: &mut [SaSint],
7741    k: SaSint,
7742    buckets: &mut [SaSint],
7743    cache: &mut [ThreadCache],
7744    omp_block_start: SaSint,
7745    omp_block_size: SaSint,
7746) -> SaSint {
7747    buckets[..k as usize].fill(0);
7748    let mut count = 0usize;
7749    for i in omp_block_start as usize..(omp_block_start + omp_block_size) as usize {
7750        let mut p = sa[i];
7751        sa[i] = p & SAINT_MAX;
7752        if p > 0 {
7753            p -= 1;
7754            let c = t[p as usize] as usize;
7755            sa[i] = c as SaSint | SAINT_MIN;
7756            buckets[c] += 1;
7757            cache[count].symbol = c as SaSint;
7758            cache[count].index = p
7759                | ((usize::from(t[(p - SaSint::from(p > 0)) as usize] < t[p as usize]) as SaSint)
7760                    << (SAINT_BIT - 1));
7761            count += 1;
7762        }
7763    }
7764    count as SaSint
7765}
7766
7767fn final_sorting_scan_left_to_right_16u_block_prepare(
7768    t: &[u16],
7769    sa: &mut [SaSint],
7770    k: SaSint,
7771    buckets: &mut [SaSint],
7772    cache: &mut [ThreadCache],
7773    omp_block_start: SaSint,
7774    omp_block_size: SaSint,
7775) -> SaSint {
7776    buckets[..k as usize].fill(0);
7777    let mut count = 0usize;
7778    for i in omp_block_start as usize..(omp_block_start + omp_block_size) as usize {
7779        let mut p = sa[i];
7780        sa[i] = p ^ SAINT_MIN;
7781        if p > 0 {
7782            p -= 1;
7783            let c = t[p as usize] as usize;
7784            buckets[c] += 1;
7785            cache[count].symbol = c as SaSint;
7786            cache[count].index = p
7787                | ((usize::from(t[(p - SaSint::from(p > 0)) as usize] < t[p as usize]) as SaSint)
7788                    << (SAINT_BIT - 1));
7789            count += 1;
7790        }
7791    }
7792    count as SaSint
7793}
7794
7795fn final_order_scan_left_to_right_16u_block_place(
7796    sa: &mut [SaSint],
7797    buckets: &mut [SaSint],
7798    cache: &[ThreadCache],
7799    count: SaSint,
7800) {
7801    for entry in cache.iter().take(count as usize) {
7802        let c = entry.symbol as usize;
7803        let dst = buckets[c] as usize;
7804        sa[dst] = entry.index;
7805        buckets[c] += 1;
7806    }
7807}
7808
7809fn final_bwt_aux_scan_left_to_right_16u_block_place(
7810    sa: &mut [SaSint],
7811    rm: SaSint,
7812    i_sample: &mut [SaSint],
7813    buckets: &mut [SaSint],
7814    cache: &[ThreadCache],
7815    count: SaSint,
7816) {
7817    for entry in cache.iter().take(count as usize) {
7818        let c = entry.symbol as usize;
7819        let dst = buckets[c] as usize;
7820        sa[dst] = entry.index;
7821        buckets[c] += 1;
7822        let p = entry.index & SAINT_MAX;
7823        if (p & rm) == 0 {
7824            i_sample[(p / (rm + 1)) as usize] = buckets[c];
7825        }
7826    }
7827}
7828
7829fn final_bwt_scan_left_to_right_16u_block_omp(
7830    t: &[u16],
7831    sa: &mut [SaSint],
7832    k: SaSint,
7833    induction_bucket: &mut [SaSint],
7834    block_start: SaSint,
7835    block_size: SaSint,
7836    threads: SaSint,
7837    thread_state: &mut [ThreadState],
7838) {
7839    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
7840        usize::try_from(threads)
7841            .expect("threads must be non-negative")
7842            .min(thread_state.len())
7843    } else {
7844        1
7845    };
7846    if thread_count <= 1 {
7847        final_bwt_scan_left_to_right_16u(t, sa, induction_bucket, block_start, block_size);
7848        return;
7849    }
7850
7851    let k_usize = usize::try_from(k).expect("k must be non-negative");
7852    let block_stride = (block_size / thread_count as SaSint) & !15;
7853
7854    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
7855        let local_start = thread as SaSint * block_stride;
7856        let local_size = if thread + 1 < thread_count {
7857            block_stride
7858        } else {
7859            block_size - local_start
7860        };
7861        state.count = final_bwt_scan_left_to_right_16u_block_prepare(
7862            t,
7863            sa,
7864            k,
7865            &mut state.buckets[..k_usize],
7866            &mut state.cache,
7867            block_start + local_start,
7868            local_size,
7869        );
7870    }
7871
7872    for state in thread_state.iter_mut().take(thread_count) {
7873        for c in 0..k_usize {
7874            let a = induction_bucket[c];
7875            let b = state.buckets[c];
7876            induction_bucket[c] = a + b;
7877            state.buckets[c] = a;
7878        }
7879    }
7880
7881    for state in thread_state.iter_mut().take(thread_count) {
7882        final_order_scan_left_to_right_16u_block_place(
7883            sa,
7884            &mut state.buckets[..k_usize],
7885            &state.cache,
7886            state.count,
7887        );
7888    }
7889}
7890
7891fn final_bwt_aux_scan_left_to_right_16u_block_omp(
7892    t: &[u16],
7893    sa: &mut [SaSint],
7894    k: SaSint,
7895    rm: SaSint,
7896    i_sample: &mut [SaSint],
7897    induction_bucket: &mut [SaSint],
7898    block_start: SaSint,
7899    block_size: SaSint,
7900    threads: SaSint,
7901    thread_state: &mut [ThreadState],
7902) {
7903    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
7904        usize::try_from(threads)
7905            .expect("threads must be non-negative")
7906            .min(thread_state.len())
7907    } else {
7908        1
7909    };
7910    if thread_count <= 1 {
7911        final_bwt_aux_scan_left_to_right_16u(
7912            t,
7913            sa,
7914            rm,
7915            i_sample,
7916            induction_bucket,
7917            block_start,
7918            block_size,
7919        );
7920        return;
7921    }
7922
7923    let k_usize = usize::try_from(k).expect("k must be non-negative");
7924    let block_stride = (block_size / thread_count as SaSint) & !15;
7925
7926    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
7927        let local_start = thread as SaSint * block_stride;
7928        let local_size = if thread + 1 < thread_count {
7929            block_stride
7930        } else {
7931            block_size - local_start
7932        };
7933        state.count = final_bwt_scan_left_to_right_16u_block_prepare(
7934            t,
7935            sa,
7936            k,
7937            &mut state.buckets[..k_usize],
7938            &mut state.cache,
7939            block_start + local_start,
7940            local_size,
7941        );
7942    }
7943
7944    for state in thread_state.iter_mut().take(thread_count) {
7945        for c in 0..k_usize {
7946            let a = induction_bucket[c];
7947            let b = state.buckets[c];
7948            induction_bucket[c] = a + b;
7949            state.buckets[c] = a;
7950        }
7951    }
7952
7953    for state in thread_state.iter_mut().take(thread_count) {
7954        final_bwt_aux_scan_left_to_right_16u_block_place(
7955            sa,
7956            rm,
7957            i_sample,
7958            &mut state.buckets[..k_usize],
7959            &state.cache,
7960            state.count,
7961        );
7962    }
7963}
7964
7965fn final_sorting_scan_left_to_right_16u_block_omp(
7966    t: &[u16],
7967    sa: &mut [SaSint],
7968    k: SaSint,
7969    induction_bucket: &mut [SaSint],
7970    block_start: SaSint,
7971    block_size: SaSint,
7972    threads: SaSint,
7973    thread_state: &mut [ThreadState],
7974) {
7975    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
7976        usize::try_from(threads)
7977            .expect("threads must be non-negative")
7978            .min(thread_state.len())
7979    } else {
7980        1
7981    };
7982    if thread_count <= 1 {
7983        final_sorting_scan_left_to_right_16u(t, sa, induction_bucket, block_start, block_size);
7984        return;
7985    }
7986
7987    let k_usize = usize::try_from(k).expect("k must be non-negative");
7988    let block_stride = (block_size / thread_count as SaSint) & !15;
7989
7990    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
7991        let local_start = thread as SaSint * block_stride;
7992        let local_size = if thread + 1 < thread_count {
7993            block_stride
7994        } else {
7995            block_size - local_start
7996        };
7997        state.count = final_sorting_scan_left_to_right_16u_block_prepare(
7998            t,
7999            sa,
8000            k,
8001            &mut state.buckets[..k_usize],
8002            &mut state.cache,
8003            block_start + local_start,
8004            local_size,
8005        );
8006    }
8007
8008    for state in thread_state.iter_mut().take(thread_count) {
8009        for c in 0..k_usize {
8010            let a = induction_bucket[c];
8011            let b = state.buckets[c];
8012            induction_bucket[c] = a + b;
8013            state.buckets[c] = a;
8014        }
8015    }
8016
8017    for state in thread_state.iter_mut().take(thread_count) {
8018        final_order_scan_left_to_right_16u_block_place(
8019            sa,
8020            &mut state.buckets[..k_usize],
8021            &state.cache,
8022            state.count,
8023        );
8024    }
8025}
8026
8027fn final_bwt_scan_left_to_right_16u_omp(
8028    t: &[u16],
8029    sa: &mut [SaSint],
8030    n: SaSint,
8031    k: SaSint,
8032    induction_bucket: &mut [SaSint],
8033    threads: SaSint,
8034) {
8035    let c = t[(n - 1) as usize] as usize;
8036    let dst = induction_bucket[c] as usize;
8037    induction_bucket[c] += 1;
8038    let mark = if t[(n - 2) as usize] < t[(n - 1) as usize] {
8039        SAINT_MIN
8040    } else {
8041        0
8042    };
8043    sa[dst] = (n - 1) | mark;
8044
8045    if threads == 1 || n < 65536 {
8046        final_bwt_scan_left_to_right_16u(t, sa, induction_bucket, 0, n);
8047    } else {
8048        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
8049        let mut block_start = 0;
8050        while block_start < n {
8051            if sa[block_start as usize] == 0 {
8052                block_start += 1;
8053            } else {
8054                let mut block_end =
8055                    block_start + threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
8056                if block_end > n {
8057                    block_end = n;
8058                }
8059                let mut block_scan_end = block_start + 1;
8060                while block_scan_end < block_end && sa[block_scan_end as usize] != 0 {
8061                    block_scan_end += 1;
8062                }
8063                let block_size = block_scan_end - block_start;
8064                if block_size < 32 {
8065                    while block_start < block_scan_end {
8066                        let mut p = sa[block_start as usize];
8067                        sa[block_start as usize] = p & SAINT_MAX;
8068                        if p > 0 {
8069                            p -= 1;
8070                            let c = t[p as usize] as usize;
8071                            sa[block_start as usize] = c as SaSint | SAINT_MIN;
8072                            let dst = induction_bucket[c] as usize;
8073                            induction_bucket[c] += 1;
8074                            let mark = if t[(p - SaSint::from(p > 0)) as usize] < t[p as usize] {
8075                                SAINT_MIN
8076                            } else {
8077                                0
8078                            };
8079                            sa[dst] = p | mark;
8080                        }
8081                        block_start += 1;
8082                    }
8083                } else {
8084                    final_bwt_scan_left_to_right_16u_block_omp(
8085                        t,
8086                        sa,
8087                        k,
8088                        induction_bucket,
8089                        block_start,
8090                        block_size,
8091                        threads,
8092                        &mut thread_state,
8093                    );
8094                    block_start = block_scan_end;
8095                }
8096            }
8097        }
8098    }
8099}
8100
8101fn final_bwt_aux_scan_left_to_right_16u_omp(
8102    t: &[u16],
8103    sa: &mut [SaSint],
8104    n: SaSint,
8105    k: SaSint,
8106    rm: SaSint,
8107    i_sample: &mut [SaSint],
8108    induction_bucket: &mut [SaSint],
8109    threads: SaSint,
8110) {
8111    let c = t[(n - 1) as usize] as usize;
8112    let dst = induction_bucket[c] as usize;
8113    induction_bucket[c] += 1;
8114    let mark = if t[(n - 2) as usize] < t[(n - 1) as usize] {
8115        SAINT_MIN
8116    } else {
8117        0
8118    };
8119    sa[dst] = (n - 1) | mark;
8120
8121    if ((n - 1) & rm) == 0 {
8122        i_sample[((n - 1) / (rm + 1)) as usize] = induction_bucket[c];
8123    }
8124
8125    if threads == 1 || n < 65536 {
8126        final_bwt_aux_scan_left_to_right_16u(t, sa, rm, i_sample, induction_bucket, 0, n);
8127    } else {
8128        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
8129        let mut block_start = 0;
8130        while block_start < n {
8131            if sa[block_start as usize] == 0 {
8132                block_start += 1;
8133            } else {
8134                let mut block_end =
8135                    block_start + threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
8136                if block_end > n {
8137                    block_end = n;
8138                }
8139                let mut block_scan_end = block_start + 1;
8140                while block_scan_end < block_end && sa[block_scan_end as usize] != 0 {
8141                    block_scan_end += 1;
8142                }
8143                let block_size = block_scan_end - block_start;
8144                if block_size < 32 {
8145                    while block_start < block_scan_end {
8146                        let mut p = sa[block_start as usize];
8147                        sa[block_start as usize] = p & SAINT_MAX;
8148                        if p > 0 {
8149                            p -= 1;
8150                            let c = t[p as usize] as usize;
8151                            sa[block_start as usize] = c as SaSint | SAINT_MIN;
8152                            let dst = induction_bucket[c] as usize;
8153                            induction_bucket[c] += 1;
8154                            let mark = if t[(p - SaSint::from(p > 0)) as usize] < t[p as usize] {
8155                                SAINT_MIN
8156                            } else {
8157                                0
8158                            };
8159                            sa[dst] = p | mark;
8160                            if (p & rm) == 0 {
8161                                i_sample[(p / (rm + 1)) as usize] = induction_bucket[c];
8162                            }
8163                        }
8164                        block_start += 1;
8165                    }
8166                } else {
8167                    final_bwt_aux_scan_left_to_right_16u_block_omp(
8168                        t,
8169                        sa,
8170                        k,
8171                        rm,
8172                        i_sample,
8173                        induction_bucket,
8174                        block_start,
8175                        block_size,
8176                        threads,
8177                        &mut thread_state,
8178                    );
8179                    block_start = block_scan_end;
8180                }
8181            }
8182        }
8183    }
8184}
8185
8186fn final_sorting_scan_left_to_right_16u_omp(
8187    t: &[u16],
8188    sa: &mut [SaSint],
8189    n: SaSint,
8190    k: SaSint,
8191    induction_bucket: &mut [SaSint],
8192    threads: SaSint,
8193) {
8194    let c = t[(n - 1) as usize] as usize;
8195    let dst = induction_bucket[c] as usize;
8196    induction_bucket[c] += 1;
8197    let mark = if t[(n - 2) as usize] < t[(n - 1) as usize] {
8198        SAINT_MIN
8199    } else {
8200        0
8201    };
8202    sa[dst] = (n - 1) | mark;
8203
8204    if threads == 1 || n < 65536 {
8205        final_sorting_scan_left_to_right_16u(t, sa, induction_bucket, 0, n);
8206    } else {
8207        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
8208        let mut block_start = 0;
8209        while block_start < n {
8210            if sa[block_start as usize] == 0 {
8211                block_start += 1;
8212            } else {
8213                let mut block_end =
8214                    block_start + threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
8215                if block_end > n {
8216                    block_end = n;
8217                }
8218                let mut block_scan_end = block_start + 1;
8219                while block_scan_end < block_end && sa[block_scan_end as usize] != 0 {
8220                    block_scan_end += 1;
8221                }
8222                let block_size = block_scan_end - block_start;
8223                if block_size < 32 {
8224                    while block_start < block_scan_end {
8225                        let mut p = sa[block_start as usize];
8226                        sa[block_start as usize] = p ^ SAINT_MIN;
8227                        if p > 0 {
8228                            p -= 1;
8229                            let c = t[p as usize] as usize;
8230                            let dst = induction_bucket[c] as usize;
8231                            induction_bucket[c] += 1;
8232                            let mark = if t[(p - SaSint::from(p > 0)) as usize] < t[p as usize] {
8233                                SAINT_MIN
8234                            } else {
8235                                0
8236                            };
8237                            sa[dst] = p | mark;
8238                        }
8239                        block_start += 1;
8240                    }
8241                } else {
8242                    final_sorting_scan_left_to_right_16u_block_omp(
8243                        t,
8244                        sa,
8245                        k,
8246                        induction_bucket,
8247                        block_start,
8248                        block_size,
8249                        threads,
8250                        &mut thread_state,
8251                    );
8252                    block_start = block_scan_end;
8253                }
8254            }
8255        }
8256    }
8257}
8258
8259fn final_bwt_scan_right_to_left_16u_block_prepare(
8260    t: &[u16],
8261    sa: &mut [SaSint],
8262    k: SaSint,
8263    buckets: &mut [SaSint],
8264    cache: &mut [ThreadCache],
8265    omp_block_start: SaSint,
8266    omp_block_size: SaSint,
8267) -> SaSint {
8268    buckets[..k as usize].fill(0);
8269    let mut count = 0usize;
8270    for i in (omp_block_start as usize..(omp_block_start + omp_block_size) as usize).rev() {
8271        let mut p = sa[i];
8272        sa[i] = p & SAINT_MAX;
8273        if p > 0 {
8274            p -= 1;
8275            let c0 = t[(p - SaSint::from(p > 0)) as usize];
8276            let c1 = t[p as usize];
8277            sa[i] = c1 as SaSint;
8278            buckets[c1 as usize] += 1;
8279            cache[count].symbol = c1 as SaSint;
8280            cache[count].index = if c0 <= c1 {
8281                p
8282            } else {
8283                c0 as SaSint | SAINT_MIN
8284            };
8285            count += 1;
8286        }
8287    }
8288    count as SaSint
8289}
8290
8291fn final_bwt_aux_scan_right_to_left_16u_block_prepare(
8292    t: &[u16],
8293    sa: &mut [SaSint],
8294    k: SaSint,
8295    buckets: &mut [SaSint],
8296    cache: &mut [ThreadCache],
8297    omp_block_start: SaSint,
8298    omp_block_size: SaSint,
8299) -> SaSint {
8300    buckets[..k as usize].fill(0);
8301    let mut count = 0usize;
8302    for i in (omp_block_start as usize..(omp_block_start + omp_block_size) as usize).rev() {
8303        let mut p = sa[i];
8304        sa[i] = p & SAINT_MAX;
8305        if p > 0 {
8306            p -= 1;
8307            let c0 = t[(p - SaSint::from(p > 0)) as usize];
8308            let c1 = t[p as usize];
8309            sa[i] = c1 as SaSint;
8310            buckets[c1 as usize] += 1;
8311            cache[count].symbol = c1 as SaSint;
8312            cache[count].index = if c0 <= c1 {
8313                p
8314            } else {
8315                c0 as SaSint | SAINT_MIN
8316            };
8317            cache[count + 1].index = p;
8318            count += 2;
8319        }
8320    }
8321    count as SaSint
8322}
8323
8324fn final_sorting_scan_right_to_left_16u_block_prepare(
8325    t: &[u16],
8326    sa: &mut [SaSint],
8327    k: SaSint,
8328    buckets: &mut [SaSint],
8329    cache: &mut [ThreadCache],
8330    omp_block_start: SaSint,
8331    omp_block_size: SaSint,
8332) -> SaSint {
8333    buckets[..k as usize].fill(0);
8334    let mut count = 0usize;
8335    for i in (omp_block_start as usize..(omp_block_start + omp_block_size) as usize).rev() {
8336        let mut p = sa[i];
8337        sa[i] = p & SAINT_MAX;
8338        if p > 0 {
8339            p -= 1;
8340            let c = t[p as usize] as usize;
8341            buckets[c] += 1;
8342            cache[count].symbol = c as SaSint;
8343            cache[count].index = p
8344                | ((usize::from(t[(p - SaSint::from(p > 0)) as usize] > t[p as usize]) as SaSint)
8345                    << (SAINT_BIT - 1));
8346            count += 1;
8347        }
8348    }
8349    count as SaSint
8350}
8351
8352fn final_order_scan_right_to_left_16u_block_place(
8353    sa: &mut [SaSint],
8354    buckets: &mut [SaSint],
8355    cache: &[ThreadCache],
8356    count: SaSint,
8357) {
8358    for entry in cache.iter().take(count as usize) {
8359        let c = entry.symbol as usize;
8360        buckets[c] -= 1;
8361        sa[buckets[c] as usize] = entry.index;
8362    }
8363}
8364
8365fn final_gsa_scan_right_to_left_16u_block_place(
8366    sa: &mut [SaSint],
8367    buckets: &mut [SaSint],
8368    cache: &[ThreadCache],
8369    count: SaSint,
8370) {
8371    for entry in cache.iter().take(count as usize) {
8372        let c = entry.symbol as usize;
8373        if c > 0 {
8374            buckets[c] -= 1;
8375            sa[buckets[c] as usize] = entry.index;
8376        }
8377    }
8378}
8379
8380fn final_bwt_aux_scan_right_to_left_16u_block_place(
8381    sa: &mut [SaSint],
8382    rm: SaSint,
8383    i_sample: &mut [SaSint],
8384    buckets: &mut [SaSint],
8385    cache: &[ThreadCache],
8386    count: SaSint,
8387) {
8388    let mut i = 0usize;
8389    while i < count as usize {
8390        let c = cache[i].symbol as usize;
8391        buckets[c] -= 1;
8392        sa[buckets[c] as usize] = cache[i].index;
8393        let p = cache[i + 1].index;
8394        if (p & rm) == 0 {
8395            i_sample[(p / (rm + 1)) as usize] = buckets[c] + 1;
8396        }
8397        i += 2;
8398    }
8399}
8400
8401fn final_bwt_scan_right_to_left_16u_block_omp(
8402    t: &[u16],
8403    sa: &mut [SaSint],
8404    k: SaSint,
8405    induction_bucket: &mut [SaSint],
8406    block_start: SaSint,
8407    block_size: SaSint,
8408    threads: SaSint,
8409    thread_state: &mut [ThreadState],
8410) -> SaSint {
8411    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
8412        usize::try_from(threads)
8413            .expect("threads must be non-negative")
8414            .min(thread_state.len())
8415    } else {
8416        1
8417    };
8418    if thread_count <= 1 {
8419        return final_bwt_scan_right_to_left_16u(t, sa, induction_bucket, block_start, block_size);
8420    }
8421
8422    let k_usize = usize::try_from(k).expect("k must be non-negative");
8423    let block_stride = (block_size / thread_count as SaSint) & !15;
8424
8425    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
8426        let local_start = thread as SaSint * block_stride;
8427        let local_size = if thread + 1 < thread_count {
8428            block_stride
8429        } else {
8430            block_size - local_start
8431        };
8432        state.count = final_bwt_scan_right_to_left_16u_block_prepare(
8433            t,
8434            sa,
8435            k,
8436            &mut state.buckets[..k_usize],
8437            &mut state.cache,
8438            block_start + local_start,
8439            local_size,
8440        );
8441    }
8442
8443    for state in thread_state.iter_mut().take(thread_count).rev() {
8444        for c in 0..k_usize {
8445            let a = induction_bucket[c];
8446            let b = state.buckets[c];
8447            induction_bucket[c] = a - b;
8448            state.buckets[c] = a;
8449        }
8450    }
8451
8452    for state in thread_state.iter_mut().take(thread_count) {
8453        final_order_scan_right_to_left_16u_block_place(
8454            sa,
8455            &mut state.buckets[..k_usize],
8456            &state.cache,
8457            state.count,
8458        );
8459    }
8460
8461    -1
8462}
8463
8464fn final_bwt_aux_scan_right_to_left_16u_block_omp(
8465    t: &[u16],
8466    sa: &mut [SaSint],
8467    k: SaSint,
8468    rm: SaSint,
8469    i_sample: &mut [SaSint],
8470    induction_bucket: &mut [SaSint],
8471    block_start: SaSint,
8472    block_size: SaSint,
8473    threads: SaSint,
8474    thread_state: &mut [ThreadState],
8475) {
8476    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
8477        usize::try_from(threads)
8478            .expect("threads must be non-negative")
8479            .min(thread_state.len())
8480    } else {
8481        1
8482    };
8483    if thread_count <= 1 {
8484        final_bwt_aux_scan_right_to_left_16u(
8485            t,
8486            sa,
8487            rm,
8488            i_sample,
8489            induction_bucket,
8490            block_start,
8491            block_size,
8492        );
8493        return;
8494    }
8495
8496    let k_usize = usize::try_from(k).expect("k must be non-negative");
8497    let block_stride = (block_size / thread_count as SaSint) & !15;
8498
8499    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
8500        let local_start = thread as SaSint * block_stride;
8501        let local_size = if thread + 1 < thread_count {
8502            block_stride
8503        } else {
8504            block_size - local_start
8505        };
8506        state.count = final_bwt_aux_scan_right_to_left_16u_block_prepare(
8507            t,
8508            sa,
8509            k,
8510            &mut state.buckets[..k_usize],
8511            &mut state.cache,
8512            block_start + local_start,
8513            local_size,
8514        );
8515    }
8516
8517    for state in thread_state.iter_mut().take(thread_count).rev() {
8518        for c in 0..k_usize {
8519            let a = induction_bucket[c];
8520            let b = state.buckets[c];
8521            induction_bucket[c] = a - b;
8522            state.buckets[c] = a;
8523        }
8524    }
8525
8526    for state in thread_state.iter_mut().take(thread_count) {
8527        final_bwt_aux_scan_right_to_left_16u_block_place(
8528            sa,
8529            rm,
8530            i_sample,
8531            &mut state.buckets[..k_usize],
8532            &state.cache,
8533            state.count,
8534        );
8535    }
8536}
8537
8538fn final_sorting_scan_right_to_left_16u_block_omp(
8539    t: &[u16],
8540    sa: &mut [SaSint],
8541    k: SaSint,
8542    induction_bucket: &mut [SaSint],
8543    block_start: SaSint,
8544    block_size: SaSint,
8545    threads: SaSint,
8546    thread_state: &mut [ThreadState],
8547) {
8548    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
8549        usize::try_from(threads)
8550            .expect("threads must be non-negative")
8551            .min(thread_state.len())
8552    } else {
8553        1
8554    };
8555    if thread_count <= 1 {
8556        final_sorting_scan_right_to_left_16u(t, sa, induction_bucket, block_start, block_size);
8557        return;
8558    }
8559
8560    let k_usize = usize::try_from(k).expect("k must be non-negative");
8561    let block_stride = (block_size / thread_count as SaSint) & !15;
8562
8563    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
8564        let local_start = thread as SaSint * block_stride;
8565        let local_size = if thread + 1 < thread_count {
8566            block_stride
8567        } else {
8568            block_size - local_start
8569        };
8570        state.count = final_sorting_scan_right_to_left_16u_block_prepare(
8571            t,
8572            sa,
8573            k,
8574            &mut state.buckets[..k_usize],
8575            &mut state.cache,
8576            block_start + local_start,
8577            local_size,
8578        );
8579    }
8580
8581    for state in thread_state.iter_mut().take(thread_count).rev() {
8582        for c in 0..k_usize {
8583            let a = induction_bucket[c];
8584            let b = state.buckets[c];
8585            induction_bucket[c] = a - b;
8586            state.buckets[c] = a;
8587        }
8588    }
8589
8590    for state in thread_state.iter_mut().take(thread_count) {
8591        final_order_scan_right_to_left_16u_block_place(
8592            sa,
8593            &mut state.buckets[..k_usize],
8594            &state.cache,
8595            state.count,
8596        );
8597    }
8598}
8599
8600fn final_gsa_scan_right_to_left_16u_block_omp(
8601    t: &[u16],
8602    sa: &mut [SaSint],
8603    k: SaSint,
8604    induction_bucket: &mut [SaSint],
8605    block_start: SaSint,
8606    block_size: SaSint,
8607    threads: SaSint,
8608    thread_state: &mut [ThreadState],
8609) {
8610    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
8611        usize::try_from(threads)
8612            .expect("threads must be non-negative")
8613            .min(thread_state.len())
8614    } else {
8615        1
8616    };
8617    if thread_count <= 1 {
8618        final_gsa_scan_right_to_left_16u(t, sa, induction_bucket, block_start, block_size);
8619        return;
8620    }
8621
8622    let k_usize = usize::try_from(k).expect("k must be non-negative");
8623    let block_stride = (block_size / thread_count as SaSint) & !15;
8624
8625    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
8626        let local_start = thread as SaSint * block_stride;
8627        let local_size = if thread + 1 < thread_count {
8628            block_stride
8629        } else {
8630            block_size - local_start
8631        };
8632        state.count = final_sorting_scan_right_to_left_16u_block_prepare(
8633            t,
8634            sa,
8635            k,
8636            &mut state.buckets[..k_usize],
8637            &mut state.cache,
8638            block_start + local_start,
8639            local_size,
8640        );
8641    }
8642
8643    for state in thread_state.iter_mut().take(thread_count).rev() {
8644        for c in 0..k_usize {
8645            let a = induction_bucket[c];
8646            let b = state.buckets[c];
8647            induction_bucket[c] = a - b;
8648            state.buckets[c] = a;
8649        }
8650    }
8651
8652    for state in thread_state.iter_mut().take(thread_count) {
8653        final_gsa_scan_right_to_left_16u_block_place(
8654            sa,
8655            &mut state.buckets[..k_usize],
8656            &state.cache,
8657            state.count,
8658        );
8659    }
8660}
8661
8662fn final_bwt_scan_right_to_left_16u_omp(
8663    t: &[u16],
8664    sa: &mut [SaSint],
8665    n: SaSint,
8666    k: SaSint,
8667    induction_bucket: &mut [SaSint],
8668    threads: SaSint,
8669) -> SaSint {
8670    let mut index = -1;
8671
8672    if threads == 1 || n < 65536 {
8673        index = final_bwt_scan_right_to_left_16u(t, sa, induction_bucket, 0, n);
8674    } else {
8675        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
8676        let mut block_start = n - 1;
8677        while block_start >= 0 {
8678            if sa[block_start as usize] == 0 {
8679                index = block_start;
8680                block_start -= 1;
8681            } else {
8682                let mut block_max_end =
8683                    block_start - threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
8684                if block_max_end < 0 {
8685                    block_max_end = -1;
8686                }
8687                let mut block_end = block_start - 1;
8688                while block_end > block_max_end && sa[block_end as usize] != 0 {
8689                    block_end -= 1;
8690                }
8691                let block_size = block_start - block_end;
8692                if block_size < 32 {
8693                    while block_start > block_end {
8694                        let mut p = sa[block_start as usize];
8695                        sa[block_start as usize] = p & SAINT_MAX;
8696                        if p > 0 {
8697                            p -= 1;
8698                            let c0 = t[(p - SaSint::from(p > 0)) as usize];
8699                            let c1 = t[p as usize] as usize;
8700                            sa[block_start as usize] = c1 as SaSint;
8701                            induction_bucket[c1] -= 1;
8702                            sa[induction_bucket[c1] as usize] = if c0 <= c1 as u16 {
8703                                p
8704                            } else {
8705                                c0 as SaSint | SAINT_MIN
8706                            };
8707                        }
8708                        block_start -= 1;
8709                    }
8710                } else {
8711                    final_bwt_scan_right_to_left_16u_block_omp(
8712                        t,
8713                        sa,
8714                        k,
8715                        induction_bucket,
8716                        block_end + 1,
8717                        block_size,
8718                        threads,
8719                        &mut thread_state,
8720                    );
8721                    block_start = block_end;
8722                }
8723            }
8724        }
8725    }
8726    index
8727}
8728
8729fn final_bwt_aux_scan_right_to_left_16u_omp(
8730    t: &[u16],
8731    sa: &mut [SaSint],
8732    n: SaSint,
8733    k: SaSint,
8734    rm: SaSint,
8735    i_sample: &mut [SaSint],
8736    induction_bucket: &mut [SaSint],
8737    threads: SaSint,
8738) {
8739    if threads == 1 || n < 65536 {
8740        final_bwt_aux_scan_right_to_left_16u(t, sa, rm, i_sample, induction_bucket, 0, n);
8741    } else {
8742        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
8743        let mut block_start = n - 1;
8744        while block_start >= 0 {
8745            if sa[block_start as usize] == 0 {
8746                block_start -= 1;
8747            } else {
8748                let mut block_max_end =
8749                    block_start - threads * ((PER_THREAD_CACHE_SIZE as SaSint - 16 * threads) / 2);
8750                if block_max_end < 0 {
8751                    block_max_end = -1;
8752                }
8753                let mut block_end = block_start - 1;
8754                while block_end > block_max_end && sa[block_end as usize] != 0 {
8755                    block_end -= 1;
8756                }
8757                let block_size = block_start - block_end;
8758                if block_size < 32 {
8759                    while block_start > block_end {
8760                        let mut p = sa[block_start as usize];
8761                        sa[block_start as usize] = p & SAINT_MAX;
8762                        if p > 0 {
8763                            p -= 1;
8764                            let c0 = t[(p - SaSint::from(p > 0)) as usize];
8765                            let c1 = t[p as usize] as usize;
8766                            sa[block_start as usize] = c1 as SaSint;
8767                            induction_bucket[c1] -= 1;
8768                            sa[induction_bucket[c1] as usize] = if c0 <= c1 as u16 {
8769                                p
8770                            } else {
8771                                c0 as SaSint | SAINT_MIN
8772                            };
8773                            if (p & rm) == 0 {
8774                                i_sample[(p / (rm + 1)) as usize] = induction_bucket[c1] + 1;
8775                            }
8776                        }
8777                        block_start -= 1;
8778                    }
8779                } else {
8780                    final_bwt_aux_scan_right_to_left_16u_block_omp(
8781                        t,
8782                        sa,
8783                        k,
8784                        rm,
8785                        i_sample,
8786                        induction_bucket,
8787                        block_end + 1,
8788                        block_size,
8789                        threads,
8790                        &mut thread_state,
8791                    );
8792                    block_start = block_end;
8793                }
8794            }
8795        }
8796    }
8797}
8798
8799fn final_sorting_scan_right_to_left_16u_omp(
8800    t: &[u16],
8801    sa: &mut [SaSint],
8802    omp_block_start: SaSint,
8803    omp_block_size: SaSint,
8804    k: SaSint,
8805    induction_bucket: &mut [SaSint],
8806    threads: SaSint,
8807) {
8808    if threads == 1 || omp_block_size < 65536 {
8809        final_sorting_scan_right_to_left_16u(
8810            t,
8811            sa,
8812            induction_bucket,
8813            omp_block_start,
8814            omp_block_size,
8815        );
8816    } else {
8817        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
8818        let mut block_start = omp_block_start + omp_block_size - 1;
8819        while block_start >= omp_block_start {
8820            if sa[block_start as usize] == 0 {
8821                block_start -= 1;
8822            } else {
8823                let mut block_max_end =
8824                    block_start - threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
8825                if block_max_end < omp_block_start {
8826                    block_max_end = omp_block_start - 1;
8827                }
8828                let mut block_end = block_start - 1;
8829                while block_end > block_max_end && sa[block_end as usize] != 0 {
8830                    block_end -= 1;
8831                }
8832                let block_size = block_start - block_end;
8833                if block_size < 32 {
8834                    while block_start > block_end {
8835                        let mut p = sa[block_start as usize];
8836                        sa[block_start as usize] = p & SAINT_MAX;
8837                        if p > 0 {
8838                            p -= 1;
8839                            let c = t[p as usize] as usize;
8840                            induction_bucket[c] -= 1;
8841                            let mark = if t[(p - SaSint::from(p > 0)) as usize] > t[p as usize] {
8842                                SAINT_MIN
8843                            } else {
8844                                0
8845                            };
8846                            sa[induction_bucket[c] as usize] = p | mark;
8847                        }
8848                        block_start -= 1;
8849                    }
8850                } else {
8851                    final_sorting_scan_right_to_left_16u_block_omp(
8852                        t,
8853                        sa,
8854                        k,
8855                        induction_bucket,
8856                        block_end + 1,
8857                        block_size,
8858                        threads,
8859                        &mut thread_state,
8860                    );
8861                    block_start = block_end;
8862                }
8863            }
8864        }
8865    }
8866}
8867
8868fn final_gsa_scan_right_to_left_16u_omp(
8869    t: &[u16],
8870    sa: &mut [SaSint],
8871    omp_block_start: SaSint,
8872    omp_block_size: SaSint,
8873    k: SaSint,
8874    induction_bucket: &mut [SaSint],
8875    threads: SaSint,
8876) {
8877    if threads == 1 || omp_block_size < 65536 {
8878        final_gsa_scan_right_to_left_16u(t, sa, induction_bucket, omp_block_start, omp_block_size);
8879    } else {
8880        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
8881        let mut block_start = omp_block_start + omp_block_size - 1;
8882        while block_start >= omp_block_start {
8883            if sa[block_start as usize] == 0 {
8884                block_start -= 1;
8885            } else {
8886                let mut block_max_end =
8887                    block_start - threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
8888                if block_max_end < omp_block_start {
8889                    block_max_end = omp_block_start - 1;
8890                }
8891                let mut block_end = block_start - 1;
8892                while block_end > block_max_end && sa[block_end as usize] != 0 {
8893                    block_end -= 1;
8894                }
8895                let block_size = block_start - block_end;
8896                if block_size < 32 {
8897                    while block_start > block_end {
8898                        let mut p = sa[block_start as usize];
8899                        sa[block_start as usize] = p & SAINT_MAX;
8900                        if p > 0 && t[(p - 1) as usize] > 0 {
8901                            p -= 1;
8902                            let c = t[p as usize] as usize;
8903                            induction_bucket[c] -= 1;
8904                            let mark = if t[(p - SaSint::from(p > 0)) as usize] > t[p as usize] {
8905                                SAINT_MIN
8906                            } else {
8907                                0
8908                            };
8909                            sa[induction_bucket[c] as usize] = p | mark;
8910                        }
8911                        block_start -= 1;
8912                    }
8913                } else {
8914                    final_gsa_scan_right_to_left_16u_block_omp(
8915                        t,
8916                        sa,
8917                        k,
8918                        induction_bucket,
8919                        block_end + 1,
8920                        block_size,
8921                        threads,
8922                        &mut thread_state,
8923                    );
8924                    block_start = block_end;
8925                }
8926            }
8927        }
8928    }
8929}
8930
8931fn induce_final_order_16u_omp(
8932    t: &[u16],
8933    sa: &mut [SaSint],
8934    n: SaSint,
8935    k: SaSint,
8936    flags: SaSint,
8937    r: SaSint,
8938    i_out: Option<&mut [SaSint]>,
8939    buckets: &mut [SaSint],
8940    threads: SaSint,
8941    _thread_state: &mut [ThreadState],
8942) -> SaSint {
8943    if (flags & LIBSAIS_FLAGS_BWT) == 0 {
8944        if (flags & LIBSAIS_FLAGS_GSA) != 0 {
8945            buckets[6 * ALPHABET_SIZE] = buckets[7 * ALPHABET_SIZE] - 1;
8946        }
8947
8948        let (left_buckets, right_tail) = buckets.split_at_mut(7 * ALPHABET_SIZE);
8949        let bucket_start = &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE];
8950        let bucket_end = &mut right_tail[..ALPHABET_SIZE];
8951
8952        final_sorting_scan_left_to_right_16u_omp(t, sa, n, k, bucket_start, threads);
8953        if threads > 1 && n >= 65_536 {
8954            clear_lms_suffixes_omp(
8955                sa,
8956                n,
8957                ALPHABET_SIZE as SaSint,
8958                bucket_start,
8959                bucket_end,
8960                threads,
8961            );
8962        }
8963
8964        if (flags & LIBSAIS_FLAGS_GSA) != 0 {
8965            flip_suffix_markers_omp(sa, bucket_end[0], threads);
8966            final_gsa_scan_right_to_left_16u_omp(
8967                t,
8968                sa,
8969                bucket_end[0],
8970                n - bucket_end[0],
8971                k,
8972                bucket_end,
8973                threads,
8974            );
8975        } else {
8976            final_sorting_scan_right_to_left_16u_omp(t, sa, 0, n, k, bucket_end, threads);
8977        }
8978
8979        0
8980    } else if let Some(i_out) = i_out {
8981        let (left_buckets, right_tail) = buckets.split_at_mut(7 * ALPHABET_SIZE);
8982        let bucket_start = &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE];
8983        let bucket_end = &mut right_tail[..ALPHABET_SIZE];
8984
8985        final_bwt_aux_scan_left_to_right_16u_omp(t, sa, n, k, r - 1, i_out, bucket_start, threads);
8986        if threads > 1 && n >= 65_536 {
8987            clear_lms_suffixes_omp(
8988                sa,
8989                n,
8990                ALPHABET_SIZE as SaSint,
8991                bucket_start,
8992                bucket_end,
8993                threads,
8994            );
8995        }
8996        final_bwt_aux_scan_right_to_left_16u_omp(t, sa, n, k, r - 1, i_out, bucket_end, threads);
8997        0
8998    } else {
8999        let (left_buckets, right_tail) = buckets.split_at_mut(7 * ALPHABET_SIZE);
9000        let bucket_start = &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE];
9001        let bucket_end = &mut right_tail[..ALPHABET_SIZE];
9002
9003        final_bwt_scan_left_to_right_16u_omp(t, sa, n, k, bucket_start, threads);
9004        if threads > 1 && n >= 65_536 {
9005            clear_lms_suffixes_omp(
9006                sa,
9007                n,
9008                ALPHABET_SIZE as SaSint,
9009                bucket_start,
9010                bucket_end,
9011                threads,
9012            );
9013        }
9014        final_bwt_scan_right_to_left_16u_omp(t, sa, n, k, bucket_end, threads)
9015    }
9016}
9017
9018fn bwt_copy_16u(u: &mut [u16], a: &[SaSint], n: SaSint) {
9019    let mut i = 0isize;
9020    let mut j = n as isize - 7;
9021    while i < j {
9022        u[i as usize] = a[i as usize] as u16;
9023        u[(i + 1) as usize] = a[(i + 1) as usize] as u16;
9024        u[(i + 2) as usize] = a[(i + 2) as usize] as u16;
9025        u[(i + 3) as usize] = a[(i + 3) as usize] as u16;
9026        u[(i + 4) as usize] = a[(i + 4) as usize] as u16;
9027        u[(i + 5) as usize] = a[(i + 5) as usize] as u16;
9028        u[(i + 6) as usize] = a[(i + 6) as usize] as u16;
9029        u[(i + 7) as usize] = a[(i + 7) as usize] as u16;
9030        i += 8;
9031    }
9032
9033    j += 7;
9034    while i < j {
9035        u[i as usize] = a[i as usize] as u16;
9036        i += 1;
9037    }
9038}
9039
9040#[allow(dead_code)]
9041fn bwt_copy_16u_omp(u: &mut [u16], a: &[SaSint], n: SaSint, threads: SaSint) {
9042    if threads == 1 || n < 65_536 {
9043        bwt_copy_16u(u, a, n);
9044        return;
9045    }
9046
9047    let block_stride = (n / threads) & !15;
9048    for thread in 0..threads {
9049        let block_start = thread * block_stride;
9050        let block_size = if thread < threads - 1 {
9051            block_stride
9052        } else {
9053            n - block_start
9054        };
9055        let start = block_start as usize;
9056        bwt_copy_16u(&mut u[start..], &a[start..], block_size);
9057    }
9058}
9059
9060fn final_bwt_ltr_step(t: &[u16], sa: &mut [SaSint], induction_bucket: &mut [SaSint], index: usize) {
9061    let mut p = sa[index];
9062    sa[index] = p & SAINT_MAX;
9063    if p > 0 {
9064        p -= 1;
9065        let c = t[p as usize] as usize;
9066        sa[index] = t[p as usize] as SaSint | SAINT_MIN;
9067        let mark = if t[(p - SaSint::from(p > 0)) as usize] < t[p as usize] {
9068            SAINT_MIN
9069        } else {
9070            0
9071        };
9072        let dst = induction_bucket[c] as usize;
9073        sa[dst] = p | mark;
9074        induction_bucket[c] += 1;
9075    }
9076}
9077
9078fn final_bwt_rtl_step(
9079    t: &[u16],
9080    sa: &mut [SaSint],
9081    induction_bucket: &mut [SaSint],
9082    index: usize,
9083    primary_index: &mut SaSint,
9084) {
9085    let mut p = sa[index];
9086    if p == 0 {
9087        *primary_index = index as SaSint;
9088    }
9089    sa[index] = p & SAINT_MAX;
9090    if p > 0 {
9091        p -= 1;
9092        let c0 = t[(p - SaSint::from(p > 0)) as usize];
9093        let c1 = t[p as usize];
9094        sa[index] = c1 as SaSint;
9095        let induced = if c0 <= c1 {
9096            p
9097        } else {
9098            c0 as SaSint | SAINT_MIN
9099        };
9100        induction_bucket[c1 as usize] -= 1;
9101        sa[induction_bucket[c1 as usize] as usize] = induced;
9102    }
9103}
9104
9105fn final_bwt_aux_ltr_step(
9106    t: &[u16],
9107    sa: &mut [SaSint],
9108    rm: SaSint,
9109    i_sample: &mut [SaSint],
9110    induction_bucket: &mut [SaSint],
9111    index: usize,
9112) {
9113    let mut p = sa[index];
9114    sa[index] = p & SAINT_MAX;
9115    if p > 0 {
9116        p -= 1;
9117        let c = t[p as usize] as usize;
9118        sa[index] = t[p as usize] as SaSint | SAINT_MIN;
9119        let mark = if t[(p - SaSint::from(p > 0)) as usize] < t[p as usize] {
9120            SAINT_MIN
9121        } else {
9122            0
9123        };
9124        let dst = induction_bucket[c] as usize;
9125        sa[dst] = p | mark;
9126        induction_bucket[c] += 1;
9127        if (p & rm) == 0 {
9128            i_sample[(p / (rm + 1)) as usize] = induction_bucket[c];
9129        }
9130    }
9131}
9132
9133fn final_bwt_aux_rtl_step(
9134    t: &[u16],
9135    sa: &mut [SaSint],
9136    rm: SaSint,
9137    i_sample: &mut [SaSint],
9138    induction_bucket: &mut [SaSint],
9139    index: usize,
9140) {
9141    let mut p = sa[index];
9142    sa[index] = p & SAINT_MAX;
9143    if p > 0 {
9144        p -= 1;
9145        let c0 = t[(p - SaSint::from(p > 0)) as usize];
9146        let c1 = t[p as usize];
9147        sa[index] = c1 as SaSint;
9148        let induced = if c0 <= c1 {
9149            p
9150        } else {
9151            c0 as SaSint | SAINT_MIN
9152        };
9153        induction_bucket[c1 as usize] -= 1;
9154        sa[induction_bucket[c1 as usize] as usize] = induced;
9155        if (p & rm) == 0 {
9156            i_sample[(p / (rm + 1)) as usize] = induction_bucket[c1 as usize] + 1;
9157        }
9158    }
9159}
9160
9161fn main_32s_recursion(
9162    t_ptr: *mut SaSint,
9163    sa_ptr: *mut SaSint,
9164    sa_capacity: usize,
9165    n: SaSint,
9166    k: SaSint,
9167    fs: SaSint,
9168    threads: SaSint,
9169    thread_state: &mut [ThreadState],
9170    local_buffer: &mut [SaSint],
9171) -> SaSint {
9172    let fs = fs.min(SAINT_MAX - n);
9173    let local_buffer_size = SaSint::try_from(LIBSAIS_LOCAL_BUFFER_SIZE).expect("fits");
9174    let n_usize = usize::try_from(n).expect("n must be non-negative");
9175    let fs_usize = usize::try_from(fs).expect("fs must be non-negative");
9176    let total_len = n_usize + fs_usize;
9177    assert!(total_len <= sa_capacity);
9178
9179    if k > 0 && ((fs / k) >= 6 || (local_buffer_size / k) >= 6) {
9180        let k_usize = usize::try_from(k).expect("k must be non-negative");
9181        let alignment = if fs >= 1024 && ((fs - 1024) / k) >= 6 {
9182            1024usize
9183        } else {
9184            16usize
9185        };
9186        let need = 6 * k_usize;
9187        let use_local_buffer = local_buffer_size > fs;
9188        let buckets_ptr = if use_local_buffer {
9189            local_buffer.as_mut_ptr()
9190        } else {
9191            unsafe {
9192                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9193                let start =
9194                    if fs_usize >= need + alignment && ((fs_usize - alignment) / k_usize) >= 6 {
9195                        let byte_ptr = sa[total_len - need - alignment..].as_mut_ptr() as usize;
9196                        let aligned = align_up(byte_ptr, alignment * mem::size_of::<SaSint>());
9197                        (aligned - sa_ptr as usize) / mem::size_of::<SaSint>()
9198                    } else {
9199                        total_len - need
9200                    };
9201                sa[start..].as_mut_ptr()
9202            }
9203        };
9204
9205        let m = unsafe {
9206            let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9207            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9208            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9209            count_and_gather_lms_suffixes_32s_4k_omp(
9210                t,
9211                sa,
9212                n,
9213                k,
9214                buckets,
9215                SaSint::from(use_local_buffer),
9216                threads,
9217                thread_state,
9218            )
9219        };
9220        if m > 1 {
9221            let m_usize = usize::try_from(m).expect("m must be non-negative");
9222            unsafe {
9223                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9224                sa[..n_usize - m_usize].fill(0);
9225            }
9226
9227            let first_lms_suffix = unsafe {
9228                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9229                sa[n_usize - m_usize]
9230            };
9231            let left_suffixes_count = unsafe {
9232                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9233                initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
9234                    std::slice::from_raw_parts(t_ptr, n_usize),
9235                    k,
9236                    buckets,
9237                    first_lms_suffix,
9238                )
9239            };
9240
9241            unsafe {
9242                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9243                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9244                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9245                let (_, induction_bucket) = buckets.split_at_mut(4 * k_usize);
9246                radix_sort_lms_suffixes_32s_6k_omp(t, sa, n, m, induction_bucket, threads);
9247                if (n / 8192) < k {
9248                    radix_sort_set_markers_32s_6k_omp(sa, k, induction_bucket, threads);
9249                }
9250                if threads > 1 && n >= 65_536 {
9251                    sa[n_usize - m_usize..n_usize].fill(0);
9252                }
9253                initialize_buckets_for_partial_sorting_32s_6k(
9254                    t,
9255                    k,
9256                    buckets,
9257                    first_lms_suffix,
9258                    left_suffixes_count,
9259                );
9260                induce_partial_order_32s_6k_omp(
9261                    t,
9262                    sa,
9263                    n,
9264                    k,
9265                    buckets,
9266                    first_lms_suffix,
9267                    left_suffixes_count,
9268                    threads,
9269                    thread_state,
9270                );
9271            }
9272
9273            let names = unsafe {
9274                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9275                if (n / 8192) < k {
9276                    renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
9277                        sa,
9278                        n,
9279                        m,
9280                        threads,
9281                        thread_state,
9282                    )
9283                } else {
9284                    renumber_and_gather_lms_suffixes_omp(sa, n, m, fs, threads, thread_state)
9285                }
9286            };
9287
9288            if names < m {
9289                let f = if (n / 8192) < k {
9290                    unsafe {
9291                        let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9292                        let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9293                        compact_lms_suffixes_32s_omp(t, sa, n, m, fs, threads)
9294                    }
9295                } else {
9296                    0
9297                };
9298
9299                let new_t_start =
9300                    total_len - usize::try_from(m - f).expect("m - f must be non-negative");
9301                if main_32s_recursion(
9302                    unsafe {
9303                        std::slice::from_raw_parts_mut(sa_ptr, total_len)[new_t_start..]
9304                            .as_mut_ptr()
9305                    },
9306                    sa_ptr,
9307                    sa_capacity,
9308                    m - f,
9309                    names - f,
9310                    fs + n - 2 * m + f,
9311                    threads,
9312                    thread_state,
9313                    local_buffer,
9314                ) != 0
9315                {
9316                    return -2;
9317                }
9318
9319                unsafe {
9320                    let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9321                    let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9322                    let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9323                    reconstruct_compacted_lms_suffixes_32s_2k_omp(
9324                        t,
9325                        sa,
9326                        n,
9327                        k,
9328                        m,
9329                        fs,
9330                        f,
9331                        buckets,
9332                        SaSint::from(use_local_buffer),
9333                        threads,
9334                        thread_state,
9335                    );
9336                }
9337            } else {
9338                unsafe {
9339                    let t = std::slice::from_raw_parts(t_ptr, n_usize);
9340                    let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9341                    count_lms_suffixes_32s_2k(t, n, k, buckets);
9342                }
9343            }
9344
9345            unsafe {
9346                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9347                initialize_buckets_start_and_end_32s_4k(k, buckets);
9348                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9349                place_lms_suffixes_histogram_32s_4k(sa, n, k, m, buckets);
9350                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9351                induce_final_order_32s_4k(t, sa, n, k, buckets, threads, thread_state);
9352            }
9353        } else {
9354            unsafe {
9355                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9356                sa[0] = sa[n_usize - 1];
9357            }
9358
9359            unsafe {
9360                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9361                initialize_buckets_start_and_end_32s_6k(k, buckets);
9362                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9363                place_lms_suffixes_histogram_32s_6k(sa, n, k, m, buckets);
9364                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9365                induce_final_order_32s_6k(t, sa, n, k, buckets, threads, thread_state);
9366            }
9367        }
9368
9369        return 0;
9370    } else if k > 0 && n <= SAINT_MAX / 2 && ((fs / k) >= 4 || (local_buffer_size / k) >= 4) {
9371        let k_usize = usize::try_from(k).expect("k must be non-negative");
9372        let alignment = if fs >= 1024 && ((fs - 1024) / k) >= 4 {
9373            1024usize
9374        } else {
9375            16usize
9376        };
9377        let need = 4 * k_usize;
9378        let use_local_buffer = local_buffer_size > fs;
9379        let buckets_ptr = if use_local_buffer {
9380            local_buffer.as_mut_ptr()
9381        } else {
9382            unsafe {
9383                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9384                let start =
9385                    if fs_usize >= need + alignment && ((fs_usize - alignment) / k_usize) >= 4 {
9386                        let byte_ptr = sa[total_len - need - alignment..].as_mut_ptr() as usize;
9387                        let aligned = align_up(byte_ptr, alignment * mem::size_of::<SaSint>());
9388                        (aligned - sa_ptr as usize) / mem::size_of::<SaSint>()
9389                    } else {
9390                        total_len - need
9391                    };
9392                sa[start..].as_mut_ptr()
9393            }
9394        };
9395
9396        let m = unsafe {
9397            let t = std::slice::from_raw_parts(t_ptr, n_usize);
9398            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9399            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9400            count_and_gather_lms_suffixes_32s_2k_omp(
9401                t,
9402                sa,
9403                n,
9404                k,
9405                buckets,
9406                SaSint::from(use_local_buffer),
9407                threads,
9408                thread_state,
9409            )
9410        };
9411        if m > 1 {
9412            let m_usize = usize::try_from(m).expect("m must be non-negative");
9413            unsafe {
9414                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9415                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9416                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9417                initialize_buckets_for_radix_and_partial_sorting_32s_4k(
9418                    t,
9419                    k,
9420                    buckets,
9421                    sa[n_usize - m_usize],
9422                );
9423                let (_, induction_bucket) = buckets.split_at_mut(1);
9424                radix_sort_lms_suffixes_32s_2k_omp(t, sa, n, m, induction_bucket, threads);
9425                radix_sort_set_markers_32s_4k_omp(sa, k, induction_bucket, threads);
9426                place_lms_suffixes_interval_32s_4k(sa, n, k, m - 1, buckets);
9427                induce_partial_order_32s_4k_omp(t, sa, n, k, buckets, threads, thread_state);
9428            }
9429
9430            let names = unsafe {
9431                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9432                renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(sa, n, m, threads, thread_state)
9433            };
9434            if names < m {
9435                let f = unsafe {
9436                    let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9437                    let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9438                    compact_lms_suffixes_32s_omp(t, sa, n, m, fs, threads)
9439                };
9440
9441                let new_t_start =
9442                    total_len - usize::try_from(m - f).expect("m - f must be non-negative");
9443                if main_32s_recursion(
9444                    unsafe {
9445                        std::slice::from_raw_parts_mut(sa_ptr, total_len)[new_t_start..]
9446                            .as_mut_ptr()
9447                    },
9448                    sa_ptr,
9449                    sa_capacity,
9450                    m - f,
9451                    names - f,
9452                    fs + n - 2 * m + f,
9453                    threads,
9454                    thread_state,
9455                    local_buffer,
9456                ) != 0
9457                {
9458                    return -2;
9459                }
9460
9461                unsafe {
9462                    let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9463                    let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9464                    let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9465                    reconstruct_compacted_lms_suffixes_32s_2k_omp(
9466                        t,
9467                        sa,
9468                        n,
9469                        k,
9470                        m,
9471                        fs,
9472                        f,
9473                        buckets,
9474                        SaSint::from(use_local_buffer),
9475                        threads,
9476                        thread_state,
9477                    );
9478                }
9479            } else {
9480                unsafe {
9481                    let t = std::slice::from_raw_parts(t_ptr, n_usize);
9482                    let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9483                    count_lms_suffixes_32s_2k(t, n, k, buckets);
9484                }
9485            }
9486        } else {
9487            unsafe {
9488                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9489                sa[0] = sa[n_usize - 1];
9490            }
9491        }
9492
9493        unsafe {
9494            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9495            initialize_buckets_start_and_end_32s_4k(k, buckets);
9496            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9497            place_lms_suffixes_histogram_32s_4k(sa, n, k, m, buckets);
9498            let t = std::slice::from_raw_parts(t_ptr, n_usize);
9499            induce_final_order_32s_4k(t, sa, n, k, buckets, threads, thread_state);
9500        }
9501
9502        return 0;
9503    } else if k > 0 && ((fs / k) >= 2 || (local_buffer_size / k) >= 2) {
9504        let k_usize = usize::try_from(k).expect("k must be non-negative");
9505        let alignment = if fs >= 1024 && ((fs - 1024) / k) >= 2 {
9506            1024usize
9507        } else {
9508            16usize
9509        };
9510        let need = 2 * k_usize;
9511        let use_local_buffer = local_buffer_size > fs;
9512        let buckets_ptr = if use_local_buffer {
9513            local_buffer.as_mut_ptr()
9514        } else {
9515            unsafe {
9516                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9517                let start =
9518                    if fs_usize >= need + alignment && ((fs_usize - alignment) / k_usize) >= 2 {
9519                        let byte_ptr = sa[total_len - need - alignment..].as_mut_ptr() as usize;
9520                        let aligned = align_up(byte_ptr, alignment * mem::size_of::<SaSint>());
9521                        (aligned - sa_ptr as usize) / mem::size_of::<SaSint>()
9522                    } else {
9523                        total_len - need
9524                    };
9525                sa[start..].as_mut_ptr()
9526            }
9527        };
9528
9529        let m = unsafe {
9530            let t = std::slice::from_raw_parts(t_ptr, n_usize);
9531            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9532            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9533            count_and_gather_lms_suffixes_32s_2k_omp(
9534                t,
9535                sa,
9536                n,
9537                k,
9538                buckets,
9539                SaSint::from(use_local_buffer),
9540                threads,
9541                thread_state,
9542            )
9543        };
9544        if m > 1 {
9545            let m_usize = usize::try_from(m).expect("m must be non-negative");
9546            unsafe {
9547                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9548                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9549                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9550                initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
9551                    t,
9552                    k,
9553                    buckets,
9554                    sa[n_usize - m_usize],
9555                );
9556                let (_, induction_bucket) = buckets.split_at_mut(1);
9557                radix_sort_lms_suffixes_32s_2k_omp(t, sa, n, m, induction_bucket, threads);
9558                place_lms_suffixes_interval_32s_2k(sa, n, k, m - 1, buckets);
9559            }
9560
9561            unsafe {
9562                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9563                initialize_buckets_start_and_end_32s_2k(k, buckets);
9564                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9565                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9566                induce_partial_order_32s_2k_omp(t, sa, n, k, buckets, threads, thread_state);
9567            }
9568
9569            let names = unsafe {
9570                let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9571                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9572                renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(t, sa, n, m, threads)
9573            };
9574            if names < m {
9575                let f = unsafe {
9576                    let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9577                    let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9578                    compact_lms_suffixes_32s_omp(t, sa, n, m, fs, threads)
9579                };
9580
9581                let new_t_start =
9582                    total_len - usize::try_from(m - f).expect("m - f must be non-negative");
9583                if main_32s_recursion(
9584                    unsafe {
9585                        std::slice::from_raw_parts_mut(sa_ptr, total_len)[new_t_start..]
9586                            .as_mut_ptr()
9587                    },
9588                    sa_ptr,
9589                    sa_capacity,
9590                    m - f,
9591                    names - f,
9592                    fs + n - 2 * m + f,
9593                    threads,
9594                    thread_state,
9595                    local_buffer,
9596                ) != 0
9597                {
9598                    return -2;
9599                }
9600
9601                unsafe {
9602                    let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9603                    let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9604                    let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9605                    reconstruct_compacted_lms_suffixes_32s_2k_omp(
9606                        t,
9607                        sa,
9608                        n,
9609                        k,
9610                        m,
9611                        fs,
9612                        f,
9613                        buckets,
9614                        SaSint::from(use_local_buffer),
9615                        threads,
9616                        thread_state,
9617                    );
9618                }
9619            } else {
9620                unsafe {
9621                    let t = std::slice::from_raw_parts(t_ptr, n_usize);
9622                    let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9623                    count_lms_suffixes_32s_2k(t, n, k, buckets);
9624                }
9625            }
9626        } else {
9627            unsafe {
9628                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9629                sa[0] = sa[n_usize - 1];
9630            }
9631        }
9632
9633        unsafe {
9634            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9635            initialize_buckets_end_32s_2k(k, buckets);
9636            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9637            place_lms_suffixes_histogram_32s_2k(sa, n, k, m, buckets);
9638        }
9639
9640        unsafe {
9641            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9642            initialize_buckets_start_and_end_32s_2k(k, buckets);
9643            let t = std::slice::from_raw_parts(t_ptr, n_usize);
9644            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9645            induce_final_order_32s_2k(t, sa, n, k, buckets, threads, thread_state);
9646        }
9647
9648        0
9649    } else {
9650        let k_usize = usize::try_from(k).expect("k must be non-negative");
9651        let mut heap_buckets = if fs < k { Some(vec![0; k_usize]) } else { None };
9652        let alignment = if fs >= 1024 && (fs - 1024) >= k {
9653            1024usize
9654        } else {
9655            16usize
9656        };
9657        let mut buckets_ptr = if let Some(ref mut heap) = heap_buckets {
9658            heap.as_mut_ptr()
9659        } else {
9660            unsafe {
9661                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9662                let start = if fs_usize >= k_usize + alignment {
9663                    let byte_ptr = sa[total_len - k_usize - alignment..].as_mut_ptr() as usize;
9664                    let aligned = align_up(byte_ptr, alignment * mem::size_of::<SaSint>());
9665                    (aligned - sa_ptr as usize) / mem::size_of::<SaSint>()
9666                } else {
9667                    total_len - k_usize
9668                };
9669                sa[start..].as_mut_ptr()
9670            }
9671        };
9672
9673        if buckets_ptr.is_null() {
9674            return -2;
9675        }
9676
9677        unsafe {
9678            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9679            sa[..n_usize].fill(0);
9680        }
9681
9682        unsafe {
9683            let t = std::slice::from_raw_parts(t_ptr, n_usize);
9684            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
9685            count_suffixes_32s(t, n, k, buckets);
9686            initialize_buckets_end_32s_1k(k, buckets);
9687        }
9688
9689        let m = unsafe {
9690            let t = std::slice::from_raw_parts(t_ptr, n_usize);
9691            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9692            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
9693            radix_sort_lms_suffixes_32s_1k(t, sa, n, buckets)
9694        };
9695        if m > 1 {
9696            unsafe {
9697                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9698                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9699                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
9700                induce_partial_order_32s_1k_omp(t, sa, n, k, buckets, threads, thread_state);
9701            }
9702
9703            let names = unsafe {
9704                let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9705                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9706                renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(t, sa, n, m, threads)
9707            };
9708            if names < m {
9709                if heap_buckets.is_some() {
9710                    let _ = heap_buckets.take();
9711                    buckets_ptr = std::ptr::null_mut();
9712                }
9713
9714                let f = unsafe {
9715                    let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9716                    let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9717                    compact_lms_suffixes_32s_omp(t, sa, n, m, fs, threads)
9718                };
9719
9720                let new_t_start =
9721                    total_len - usize::try_from(m - f).expect("m - f must be non-negative");
9722                if main_32s_recursion(
9723                    unsafe {
9724                        std::slice::from_raw_parts_mut(sa_ptr, total_len)[new_t_start..]
9725                            .as_mut_ptr()
9726                    },
9727                    sa_ptr,
9728                    sa_capacity,
9729                    m - f,
9730                    names - f,
9731                    fs + n - 2 * m + f,
9732                    threads,
9733                    thread_state,
9734                    local_buffer,
9735                ) != 0
9736                {
9737                    return -2;
9738                }
9739
9740                unsafe {
9741                    let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9742                    let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9743                    reconstruct_compacted_lms_suffixes_32s_1k_omp(t, sa, n, m, fs, f, threads);
9744                }
9745
9746                if buckets_ptr.is_null() {
9747                    heap_buckets = Some(vec![0; k_usize]);
9748                    buckets_ptr = heap_buckets.as_mut().unwrap().as_mut_ptr();
9749                    if buckets_ptr.is_null() {
9750                        return -2;
9751                    }
9752                }
9753            }
9754
9755            unsafe {
9756                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9757                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
9758                count_suffixes_32s(t, n, k, buckets);
9759                initialize_buckets_end_32s_1k(k, buckets);
9760            }
9761            unsafe {
9762                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9763                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9764                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
9765                place_lms_suffixes_interval_32s_1k(t, sa, k, m, buckets);
9766            }
9767        }
9768
9769        unsafe {
9770            let t = std::slice::from_raw_parts(t_ptr, n_usize);
9771            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9772            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
9773            induce_final_order_32s_1k(t, sa, n, k, buckets, threads, thread_state);
9774        }
9775
9776        0
9777    }
9778}
9779
9780fn main_32s_entry(
9781    t_ptr: *mut SaSint,
9782    sa: &mut [SaSint],
9783    n: SaSint,
9784    k: SaSint,
9785    fs: SaSint,
9786    threads: SaSint,
9787    thread_state: &mut [ThreadState],
9788) -> SaSint {
9789    let mut local_buffer = [0; 2 * LIBSAIS_LOCAL_BUFFER_SIZE];
9790    main_32s_recursion(
9791        t_ptr,
9792        sa.as_mut_ptr(),
9793        sa.len(),
9794        n,
9795        k,
9796        fs,
9797        threads,
9798        thread_state,
9799        &mut local_buffer[LIBSAIS_LOCAL_BUFFER_SIZE..],
9800    )
9801}
9802
9803fn main_16u(
9804    t: &[u16],
9805    sa: &mut [SaSint],
9806    n: SaSint,
9807    buckets: &mut [SaSint],
9808    flags: SaSint,
9809    r: SaSint,
9810    i_out: Option<&mut [SaSint]>,
9811    fs: SaSint,
9812    freq: Option<&mut [SaSint]>,
9813    threads: SaSint,
9814    thread_state: &mut [ThreadState],
9815) -> SaSint {
9816    let fs = fs.min(SAINT_MAX - n);
9817
9818    let m = count_and_gather_lms_suffixes_16u_omp(t, sa, n, buckets, threads, thread_state);
9819    let k = initialize_buckets_start_and_end_16u(buckets, freq);
9820
9821    if (flags & LIBSAIS_FLAGS_GSA) != 0 && (buckets[0] != 0 || buckets[2] != 0 || buckets[3] != 1) {
9822        return -1;
9823    }
9824
9825    if m > 0 {
9826        let first_lms_suffix = sa[(n - m) as usize];
9827        let left_suffixes_count =
9828            initialize_buckets_for_lms_suffixes_radix_sort_16u(t, buckets, first_lms_suffix);
9829
9830        if threads > 1 && n >= 65_536 {
9831            sa[..(n - m) as usize].fill(0);
9832        }
9833        radix_sort_lms_suffixes_16u_omp(t, sa, n, m, flags, buckets, threads, thread_state);
9834        if threads > 1 && n >= 65_536 {
9835            sa[(n - m) as usize..n as usize].fill(0);
9836        }
9837
9838        initialize_buckets_for_partial_sorting_16u(
9839            t,
9840            buckets,
9841            first_lms_suffix,
9842            left_suffixes_count,
9843        );
9844        induce_partial_order_16u_omp(
9845            t,
9846            sa,
9847            n,
9848            k,
9849            flags,
9850            buckets,
9851            first_lms_suffix,
9852            left_suffixes_count,
9853            threads,
9854        );
9855
9856        let names = renumber_and_gather_lms_suffixes_omp(sa, n, m, fs, threads, thread_state);
9857        if names < m {
9858            let recursive_t_start = (n + fs - m) as usize;
9859            let recursive_t_ptr = sa[recursive_t_start..].as_mut_ptr();
9860            if main_32s_entry(
9861                recursive_t_ptr,
9862                sa,
9863                m,
9864                names,
9865                fs + n - 2 * m,
9866                threads,
9867                thread_state,
9868            ) != 0
9869            {
9870                return -2;
9871            }
9872
9873            gather_lms_suffixes_16u_omp(t, sa, n, threads, thread_state);
9874            reconstruct_lms_suffixes_omp(sa, n, m, threads);
9875        }
9876
9877        place_lms_suffixes_interval_16u(sa, n, m, flags, buckets);
9878    } else {
9879        sa[..n as usize].fill(0);
9880    }
9881
9882    induce_final_order_16u_omp(t, sa, n, k, flags, r, i_out, buckets, threads, thread_state)
9883}
9884
9885fn main_16u_alloc(
9886    t: &[u16],
9887    sa: &mut [SaSint],
9888    flags: SaSint,
9889    r: SaSint,
9890    i_out: Option<&mut [SaSint]>,
9891    fs: SaSint,
9892    freq: Option<&mut [SaSint]>,
9893    threads: SaSint,
9894) -> SaSint {
9895    if fs < 0
9896        || threads < 0
9897        || sa.len()
9898            < t.len()
9899                .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
9900        || freq.as_ref().is_some_and(|freq| freq.len() < ALPHABET_SIZE)
9901    {
9902        return -1;
9903    }
9904
9905    fill_freq(t, freq);
9906    if t.len() <= 1 {
9907        if t.len() == 1 {
9908            sa[0] = 0;
9909        }
9910        return if (flags & LIBSAIS_FLAGS_BWT) != 0 {
9911            t.len() as SaSint
9912        } else {
9913            0
9914        };
9915    }
9916
9917    let mut buckets = vec![0; 8 * ALPHABET_SIZE];
9918    let threads = normalize_threads(threads);
9919    let mut thread_state = if threads > 1 {
9920        match alloc_thread_state(threads) {
9921            Some(thread_state) => thread_state,
9922            None => return -2,
9923        }
9924    } else {
9925        Vec::new()
9926    };
9927
9928    main_16u(
9929        t,
9930        sa,
9931        t.len() as SaSint,
9932        &mut buckets,
9933        flags,
9934        r,
9935        i_out,
9936        fs,
9937        None,
9938        threads,
9939        &mut thread_state,
9940    )
9941}
9942
9943fn main_16u_ctx(
9944    ctx: &mut Context,
9945    t: &[u16],
9946    sa: &mut [SaSint],
9947    flags: SaSint,
9948    r: SaSint,
9949    i_out: Option<&mut [SaSint]>,
9950    fs: SaSint,
9951    freq: Option<&mut [SaSint]>,
9952) -> SaSint {
9953    if fs < 0
9954        || sa.len()
9955            < t.len()
9956                .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
9957        || freq.as_ref().is_some_and(|freq| freq.len() < ALPHABET_SIZE)
9958    {
9959        return -1;
9960    }
9961
9962    if ctx.threads <= 0 || ctx.buckets.len() < 8 * ALPHABET_SIZE {
9963        return -2;
9964    }
9965
9966    fill_freq(t, freq);
9967    if t.len() <= 1 {
9968        if t.len() == 1 {
9969            sa[0] = 0;
9970        }
9971        return if (flags & LIBSAIS_FLAGS_BWT) != 0 {
9972            t.len() as SaSint
9973        } else {
9974            0
9975        };
9976    }
9977
9978    let mut empty_thread_state = [];
9979    let thread_state = if ctx.threads > 1 {
9980        match ctx.thread_state.as_deref_mut() {
9981            Some(thread_state) if thread_state.len() >= ctx.threads as usize => thread_state,
9982            None => return -2,
9983            Some(_) => return -2,
9984        }
9985    } else {
9986        &mut empty_thread_state
9987    };
9988
9989    main_16u(
9990        t,
9991        sa,
9992        t.len() as SaSint,
9993        &mut ctx.buckets,
9994        flags,
9995        r,
9996        i_out,
9997        fs,
9998        None,
9999        ctx.threads,
10000        thread_state,
10001    )
10002}
10003
10004fn main_int(t: &mut [SaSint], sa: &mut [SaSint], k: SaSint, fs: SaSint, threads: SaSint) -> SaSint {
10005    let threads = normalize_threads(threads);
10006    let mut thread_state = if threads > 1 {
10007        match alloc_thread_state(threads) {
10008            Some(thread_state) => thread_state,
10009            None => return -2,
10010        }
10011    } else {
10012        Vec::new()
10013    };
10014
10015    main_32s_entry(
10016        t.as_mut_ptr(),
10017        sa,
10018        t.len() as SaSint,
10019        k,
10020        fs,
10021        threads,
10022        &mut thread_state,
10023    )
10024}
10025
10026/// Constructs the suffix array of a given 16-bit string.
10027///
10028/// - `t` (`[0..n-1]`): the input 16-bit string.
10029/// - `sa` (`[0..n-1+fs]`): the output array of suffixes.
10030/// - `fs`: extra space available at the end of `sa` (0 should be enough for most cases).
10031/// - `freq` (`[0..65535]`): optional output symbol frequency table.
10032///
10033/// Returns 0 on success, -1 or -2 on error.
10034pub fn libsais16(t: &[u16], sa: &mut [SaSint], fs: SaSint, freq: Option<&mut [SaSint]>) -> SaSint {
10035    main_16u_alloc(t, sa, 0, 0, None, fs, freq, 1)
10036}
10037
10038/// Constructs the generalized suffix array (GSA) of a given 16-bit string set.
10039///
10040/// - `t` (`[0..n-1]`): the input 16-bit string set using 0 as separators (`t[n-1]` must be 0).
10041/// - `sa` (`[0..n-1+fs]`): the output array of suffixes.
10042/// - `fs`: extra space available at the end of `sa` (0 should be enough for most cases).
10043/// - `freq` (`[0..65535]`): optional output symbol frequency table.
10044///
10045/// Returns 0 on success, -1 or -2 on error.
10046pub fn libsais16_gsa(
10047    t: &[u16],
10048    sa: &mut [SaSint],
10049    fs: SaSint,
10050    freq: Option<&mut [SaSint]>,
10051) -> SaSint {
10052    main_16u_alloc(t, sa, LIBSAIS_FLAGS_GSA, 0, None, fs, freq, 1)
10053}
10054
10055/// Constructs the suffix array of a given integer array.
10056///
10057/// During construction the input array is modified, but restored at the end if no error occurred.
10058///
10059/// - `t` (`[0..n-1]`): the input integer array.
10060/// - `sa` (`[0..n-1+fs]`): the output array of suffixes.
10061/// - `k`: the alphabet size of the input integer array.
10062/// - `fs`: extra space available at the end of `sa` (can be 0, but 4k or better 6k is recommended for optimal performance).
10063///
10064/// Returns 0 on success, -1 or -2 on error.
10065pub fn libsais16_int(t: &mut [SaSint], sa: &mut [SaSint], k: SaSint, fs: SaSint) -> SaSint {
10066    if fs < 0
10067        || sa.len()
10068            < t.len()
10069                .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
10070    {
10071        return -1;
10072    }
10073
10074    if t.len() <= 1 {
10075        if t.len() == 1 {
10076            sa[0] = 0;
10077        }
10078        return 0;
10079    }
10080
10081    main_int(t, sa, k, fs, 1)
10082}
10083
10084/// Constructs the suffix array of a given 16-bit string using a libsais16 context.
10085///
10086/// - `ctx`: the libsais16 context.
10087/// - `t` (`[0..n-1]`): the input 16-bit string.
10088/// - `sa` (`[0..n-1+fs]`): the output array of suffixes.
10089/// - `fs`: extra space available at the end of `sa` (0 should be enough for most cases).
10090/// - `freq` (`[0..65535]`): optional output symbol frequency table.
10091///
10092/// Returns 0 on success, -1 or -2 on error.
10093pub fn libsais16_ctx(
10094    ctx: &mut Context,
10095    t: &[u16],
10096    sa: &mut [SaSint],
10097    fs: SaSint,
10098    freq: Option<&mut [SaSint]>,
10099) -> SaSint {
10100    main_16u_ctx(ctx, t, sa, 0, 0, None, fs, freq)
10101}
10102
10103/// Constructs the generalized suffix array (GSA) of a given 16-bit string set using a libsais16 context.
10104///
10105/// - `ctx`: the libsais16 context.
10106/// - `t` (`[0..n-1]`): the input 16-bit string set using 0 as separators (`t[n-1]` must be 0).
10107/// - `sa` (`[0..n-1+fs]`): the output array of suffixes.
10108/// - `fs`: extra space available at the end of `sa` (0 should be enough for most cases).
10109/// - `freq` (`[0..65535]`): optional output symbol frequency table.
10110///
10111/// Returns 0 on success, -1 or -2 on error.
10112pub fn libsais16_gsa_ctx(
10113    ctx: &mut Context,
10114    t: &[u16],
10115    sa: &mut [SaSint],
10116    fs: SaSint,
10117    freq: Option<&mut [SaSint]>,
10118) -> SaSint {
10119    main_16u_ctx(ctx, t, sa, LIBSAIS_FLAGS_GSA, 0, None, fs, freq)
10120}
10121
10122/// Constructs the suffix array of a given 16-bit string in parallel using OpenMP-style threading.
10123///
10124/// - `t` (`[0..n-1]`): the input 16-bit string.
10125/// - `sa` (`[0..n-1+fs]`): the output array of suffixes.
10126/// - `fs`: extra space available at the end of `sa` (0 should be enough for most cases).
10127/// - `freq` (`[0..65535]`): optional output symbol frequency table.
10128/// - `threads`: number of worker threads (can be 0 for the implementation default).
10129///
10130/// Returns 0 on success, -1 or -2 on error.
10131pub fn libsais16_omp(
10132    t: &[u16],
10133    sa: &mut [SaSint],
10134    fs: SaSint,
10135    freq: Option<&mut [SaSint]>,
10136    threads: SaSint,
10137) -> SaSint {
10138    if threads < 0 {
10139        -1
10140    } else {
10141        main_16u_alloc(t, sa, 0, 0, None, fs, freq, threads)
10142    }
10143}
10144
10145/// Constructs the generalized suffix array (GSA) of a given 16-bit string set in parallel using OpenMP-style threading.
10146///
10147/// - `t` (`[0..n-1]`): the input 16-bit string set using 0 as separators (`t[n-1]` must be 0).
10148/// - `sa` (`[0..n-1+fs]`): the output array of suffixes.
10149/// - `fs`: extra space available at the end of `sa` (0 should be enough for most cases).
10150/// - `freq` (`[0..65535]`): optional output symbol frequency table.
10151/// - `threads`: number of worker threads (can be 0 for the implementation default).
10152///
10153/// Returns 0 on success, -1 or -2 on error.
10154pub fn libsais16_gsa_omp(
10155    t: &[u16],
10156    sa: &mut [SaSint],
10157    fs: SaSint,
10158    freq: Option<&mut [SaSint]>,
10159    threads: SaSint,
10160) -> SaSint {
10161    if threads < 0 {
10162        -1
10163    } else {
10164        main_16u_alloc(t, sa, LIBSAIS_FLAGS_GSA, 0, None, fs, freq, threads)
10165    }
10166}
10167
10168/// Constructs the suffix array of a given integer array in parallel using OpenMP-style threading.
10169///
10170/// During construction the input array is modified, but restored at the end if no error occurred.
10171///
10172/// - `t` (`[0..n-1]`): the input integer array.
10173/// - `sa` (`[0..n-1+fs]`): the output array of suffixes.
10174/// - `k`: the alphabet size of the input integer array.
10175/// - `fs`: extra space available at the end of `sa` (can be 0, but 4k or better 6k is recommended for optimal performance).
10176/// - `threads`: number of worker threads (can be 0 for the implementation default).
10177///
10178/// Returns 0 on success, -1 or -2 on error.
10179pub fn libsais16_int_omp(
10180    t: &mut [SaSint],
10181    sa: &mut [SaSint],
10182    k: SaSint,
10183    fs: SaSint,
10184    threads: SaSint,
10185) -> SaSint {
10186    if threads < 0
10187        || fs < 0
10188        || sa.len()
10189            < t.len()
10190                .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
10191    {
10192        return -1;
10193    }
10194
10195    if t.len() <= 1 {
10196        if t.len() == 1 {
10197            sa[0] = 0;
10198        }
10199        return 0;
10200    }
10201
10202    main_int(t, sa, k, fs, threads)
10203}
10204
10205fn build_bwt(
10206    t: &[u16],
10207    u: &mut [u16],
10208    a: &mut [SaSint],
10209    fs: SaSint,
10210    freq: Option<&mut [SaSint]>,
10211    threads: SaSint,
10212) -> SaSint {
10213    if fs < 0
10214        || threads < 0
10215        || u.len() < t.len()
10216        || a.len()
10217            < t.len()
10218                .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
10219        || freq.as_ref().is_some_and(|freq| freq.len() < ALPHABET_SIZE)
10220    {
10221        return -1;
10222    }
10223    if t.len() <= 1 {
10224        fill_freq(t, freq);
10225        if t.len() == 1 {
10226            u[0] = t[0];
10227        }
10228        return t.len() as SaSint;
10229    }
10230
10231    let n = t.len();
10232    let mut index = main_16u_alloc(t, a, LIBSAIS_FLAGS_BWT, 0, None, fs, freq, threads);
10233    if index >= 0 {
10234        index += 1;
10235        u[0] = t[n - 1];
10236        bwt_copy_16u(&mut u[1..], a, index - 1);
10237        bwt_copy_16u(
10238            &mut u[index as usize..],
10239            &a[index as usize..],
10240            n as SaSint - index,
10241        );
10242    }
10243    index
10244}
10245
10246/// Constructs the Burrows-Wheeler transformed 16-bit string (BWT) of a given 16-bit string.
10247///
10248/// - `t` (`[0..n-1]`): the input 16-bit string.
10249/// - `u` (`[0..n-1]`): the output 16-bit string (can alias `t`).
10250/// - `a` (`[0..n-1+fs]`): the temporary array.
10251/// - `fs`: extra space available at the end of `a` (0 should be enough for most cases).
10252/// - `freq` (`[0..65535]`): optional output symbol frequency table.
10253///
10254/// Returns the primary index on success, -1 or -2 on error.
10255pub fn libsais16_bwt(
10256    t: &[u16],
10257    u: &mut [u16],
10258    a: &mut [SaSint],
10259    fs: SaSint,
10260    freq: Option<&mut [SaSint]>,
10261) -> SaSint {
10262    build_bwt(t, u, a, fs, freq, 1)
10263}
10264
10265fn build_bwt_aux(
10266    t: &[u16],
10267    u: &mut [u16],
10268    a: &mut [SaSint],
10269    fs: SaSint,
10270    freq: Option<&mut [SaSint]>,
10271    r: SaSint,
10272    i: &mut [SaSint],
10273    threads: SaSint,
10274) -> SaSint {
10275    if threads < 0 || r < 2 || (r & (r - 1)) != 0 {
10276        return -1;
10277    }
10278    let samples = if t.is_empty() {
10279        1
10280    } else {
10281        (t.len() - 1) / r as usize + 1
10282    };
10283    if i.len() < samples {
10284        return -1;
10285    }
10286    let n = t.len();
10287    if n <= 1 {
10288        fill_freq(t, freq);
10289        if n == 1 {
10290            u[0] = t[0];
10291        }
10292        i[0] = n as SaSint;
10293        return 0;
10294    }
10295
10296    let index = main_16u_alloc(t, a, LIBSAIS_FLAGS_BWT, r, Some(i), fs, freq, threads);
10297    if index == 0 {
10298        u[0] = t[n - 1];
10299        bwt_copy_16u(&mut u[1..], a, i[0] - 1);
10300        bwt_copy_16u(
10301            &mut u[i[0] as usize..],
10302            &a[i[0] as usize..],
10303            n as SaSint - i[0],
10304        );
10305    }
10306    index
10307}
10308
10309/// Constructs the Burrows-Wheeler transformed 16-bit string (BWT) of a given 16-bit string with auxiliary indexes.
10310///
10311/// - `t` (`[0..n-1]`): the input 16-bit string.
10312/// - `u` (`[0..n-1]`): the output 16-bit string (can alias `t`).
10313/// - `a` (`[0..n-1+fs]`): the temporary array.
10314/// - `fs`: extra space available at the end of `a` (0 should be enough for most cases).
10315/// - `freq` (`[0..65535]`): optional output symbol frequency table.
10316/// - `r`: sampling rate for the auxiliary indexes (must be a power of two).
10317/// - `i` (`[0..(n-1)/r]`): output auxiliary indexes.
10318///
10319/// Returns 0 on success, -1 or -2 on error.
10320pub fn libsais16_bwt_aux(
10321    t: &[u16],
10322    u: &mut [u16],
10323    a: &mut [SaSint],
10324    fs: SaSint,
10325    freq: Option<&mut [SaSint]>,
10326    r: SaSint,
10327    i: &mut [SaSint],
10328) -> SaSint {
10329    build_bwt_aux(t, u, a, fs, freq, r, i, 1)
10330}
10331
10332/// Constructs the Burrows-Wheeler transformed 16-bit string (BWT) of a given 16-bit string using a libsais16 context.
10333///
10334/// - `ctx`: the libsais16 context.
10335/// - `t` (`[0..n-1]`): the input 16-bit string.
10336/// - `u` (`[0..n-1]`): the output 16-bit string (can alias `t`).
10337/// - `a` (`[0..n-1+fs]`): the temporary array.
10338/// - `fs`: extra space available at the end of `a` (0 should be enough for most cases).
10339/// - `freq` (`[0..65535]`): optional output symbol frequency table.
10340///
10341/// Returns the primary index on success, -1 or -2 on error.
10342pub fn libsais16_bwt_ctx(
10343    ctx: &mut Context,
10344    t: &[u16],
10345    u: &mut [u16],
10346    a: &mut [SaSint],
10347    fs: SaSint,
10348    freq: Option<&mut [SaSint]>,
10349) -> SaSint {
10350    if fs < 0
10351        || u.len() < t.len()
10352        || a.len()
10353            < t.len()
10354                .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
10355        || freq.as_ref().is_some_and(|freq| freq.len() < ALPHABET_SIZE)
10356    {
10357        return -1;
10358    }
10359    if t.len() <= 1 {
10360        fill_freq(t, freq);
10361        if t.len() == 1 {
10362            u[0] = t[0];
10363        }
10364        return t.len() as SaSint;
10365    }
10366
10367    let n = t.len();
10368    let mut index = main_16u_ctx(ctx, t, a, LIBSAIS_FLAGS_BWT, 0, None, fs, freq);
10369    if index >= 0 {
10370        index += 1;
10371        u[0] = t[n - 1];
10372        bwt_copy_16u(&mut u[1..], a, index - 1);
10373        bwt_copy_16u(
10374            &mut u[index as usize..],
10375            &a[index as usize..],
10376            n as SaSint - index,
10377        );
10378    }
10379    index
10380}
10381
10382/// Constructs the BWT of a given 16-bit string with auxiliary indexes using a libsais16 context.
10383///
10384/// - `ctx`: the libsais16 context.
10385/// - `t` (`[0..n-1]`): the input 16-bit string.
10386/// - `u` (`[0..n-1]`): the output 16-bit string (can alias `t`).
10387/// - `a` (`[0..n-1+fs]`): the temporary array.
10388/// - `fs`: extra space available at the end of `a` (0 should be enough for most cases).
10389/// - `freq` (`[0..65535]`): optional output symbol frequency table.
10390/// - `r`: sampling rate for the auxiliary indexes (must be a power of two).
10391/// - `i` (`[0..(n-1)/r]`): output auxiliary indexes.
10392///
10393/// Returns 0 on success, -1 or -2 on error.
10394pub fn libsais16_bwt_aux_ctx(
10395    ctx: &mut Context,
10396    t: &[u16],
10397    u: &mut [u16],
10398    a: &mut [SaSint],
10399    fs: SaSint,
10400    freq: Option<&mut [SaSint]>,
10401    r: SaSint,
10402    i: &mut [SaSint],
10403) -> SaSint {
10404    if fs < 0 || r < 2 || (r & (r - 1)) != 0 {
10405        return -1;
10406    }
10407    let samples = if t.is_empty() {
10408        1
10409    } else {
10410        (t.len() - 1) / r as usize + 1
10411    };
10412    if u.len() < t.len()
10413        || a.len()
10414            < t.len()
10415                .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
10416        || i.len() < samples
10417        || freq.as_ref().is_some_and(|freq| freq.len() < ALPHABET_SIZE)
10418    {
10419        return -1;
10420    }
10421    if t.len() <= 1 {
10422        fill_freq(t, freq);
10423        if t.len() == 1 {
10424            u[0] = t[0];
10425        }
10426        i[0] = t.len() as SaSint;
10427        return 0;
10428    }
10429
10430    let n = t.len();
10431    let index = main_16u_ctx(ctx, t, a, LIBSAIS_FLAGS_BWT, r, Some(i), fs, freq);
10432    if index == 0 {
10433        u[0] = t[n - 1];
10434        bwt_copy_16u(&mut u[1..], a, i[0] - 1);
10435        bwt_copy_16u(
10436            &mut u[i[0] as usize..],
10437            &a[i[0] as usize..],
10438            n as SaSint - i[0],
10439        );
10440    }
10441    index
10442}
10443
10444/// Constructs the Burrows-Wheeler transformed 16-bit string (BWT) of a given 16-bit string in parallel using OpenMP-style threading.
10445///
10446/// - `t` (`[0..n-1]`): the input 16-bit string.
10447/// - `u` (`[0..n-1]`): the output 16-bit string (can alias `t`).
10448/// - `a` (`[0..n-1+fs]`): the temporary array.
10449/// - `fs`: extra space available at the end of `a` (0 should be enough for most cases).
10450/// - `freq` (`[0..65535]`): optional output symbol frequency table.
10451/// - `threads`: number of worker threads (can be 0 for the implementation default).
10452///
10453/// Returns the primary index on success, -1 or -2 on error.
10454pub fn libsais16_bwt_omp(
10455    t: &[u16],
10456    u: &mut [u16],
10457    a: &mut [SaSint],
10458    fs: SaSint,
10459    freq: Option<&mut [SaSint]>,
10460    threads: SaSint,
10461) -> SaSint {
10462    if threads < 0 {
10463        -1
10464    } else {
10465        build_bwt(t, u, a, fs, freq, threads)
10466    }
10467}
10468
10469/// Constructs the BWT of a given 16-bit string with auxiliary indexes in parallel using OpenMP-style threading.
10470///
10471/// - `t` (`[0..n-1]`): the input 16-bit string.
10472/// - `u` (`[0..n-1]`): the output 16-bit string (can alias `t`).
10473/// - `a` (`[0..n-1+fs]`): the temporary array.
10474/// - `fs`: extra space available at the end of `a` (0 should be enough for most cases).
10475/// - `freq` (`[0..65535]`): optional output symbol frequency table.
10476/// - `r`: sampling rate for the auxiliary indexes (must be a power of two).
10477/// - `i` (`[0..(n-1)/r]`): output auxiliary indexes.
10478/// - `threads`: number of worker threads (can be 0 for the implementation default).
10479///
10480/// Returns 0 on success, -1 or -2 on error.
10481pub fn libsais16_bwt_aux_omp(
10482    t: &[u16],
10483    u: &mut [u16],
10484    a: &mut [SaSint],
10485    fs: SaSint,
10486    freq: Option<&mut [SaSint]>,
10487    r: SaSint,
10488    i: &mut [SaSint],
10489    threads: SaSint,
10490) -> SaSint {
10491    if threads < 0 {
10492        -1
10493    } else {
10494        build_bwt_aux(t, u, a, fs, freq, r, i, threads)
10495    }
10496}
10497
10498fn validate_unbwt_aux(
10499    t: &[u16],
10500    u: &[u16],
10501    a: &[SaSint],
10502    freq: Option<&[SaSint]>,
10503    r: SaSint,
10504    i: &[SaSint],
10505) -> SaSint {
10506    let n = t.len();
10507    if u.len() < n
10508        || a.len() < n
10509        || freq.is_some_and(|freq| freq.len() < ALPHABET_SIZE)
10510        || ((r != n as SaSint) && (r < 2 || (r & (r - 1)) != 0))
10511        || i.is_empty()
10512    {
10513        return -1;
10514    }
10515    if n <= 1 {
10516        return if i[0] == n as SaSint { 0 } else { -1 };
10517    }
10518
10519    let samples = (n - 1) / r as usize + 1;
10520    if i.len() < samples {
10521        return -1;
10522    }
10523
10524    for &index in &i[..samples] {
10525        if index <= 0 || index as usize > n {
10526            return -1;
10527        }
10528    }
10529    0
10530}
10531
10532fn unbwt_compute_histogram(t: &[u16], count: &mut [usize]) {
10533    for &symbol in t {
10534        count[symbol as usize] += 1;
10535    }
10536}
10537
10538fn unbwt_shift(n: usize) -> usize {
10539    let mut shift = 0usize;
10540    while (n >> shift) > (1usize << UNBWT_FASTBITS) {
10541        shift += 1;
10542    }
10543    shift
10544}
10545
10546fn unbwt_calculate_fastbits(bucket2: &mut [usize], fastbits: &mut [u16], shift: usize) {
10547    let mut v = 0usize;
10548    let mut sum = 1usize;
10549    for (w, bucket) in bucket2.iter_mut().enumerate().take(ALPHABET_SIZE) {
10550        let prev = sum;
10551        sum += *bucket;
10552        *bucket = prev;
10553        if prev != sum {
10554            while v <= ((sum - 1) >> shift) {
10555                fastbits[v] = w as u16;
10556                v += 1;
10557            }
10558        }
10559    }
10560}
10561
10562fn unbwt_calculate_p(t: &[u16], p: &mut [usize], bucket2: &mut [usize], index: usize) {
10563    for row in 0..index {
10564        let symbol = t[row] as usize;
10565        p[bucket2[symbol]] = row;
10566        bucket2[symbol] += 1;
10567    }
10568
10569    for row in index + 1..=t.len() {
10570        let symbol = t[row - 1] as usize;
10571        p[bucket2[symbol]] = row;
10572        bucket2[symbol] += 1;
10573    }
10574}
10575
10576#[allow(dead_code, non_snake_case)]
10577fn unbwt_calculate_P(
10578    t: &[u16],
10579    p: &mut [usize],
10580    bucket2: &mut [usize],
10581    index: usize,
10582    block_start: usize,
10583    block_end: usize,
10584) {
10585    let first_end = index.min(block_end);
10586    for row in block_start..first_end {
10587        let symbol = t[row] as usize;
10588        p[bucket2[symbol]] = row;
10589        bucket2[symbol] += 1;
10590    }
10591
10592    let second_start = block_start.max(index) + 1;
10593    for row in second_start..=block_end {
10594        let symbol = t[row - 1] as usize;
10595        p[bucket2[symbol]] = row;
10596        bucket2[symbol] += 1;
10597    }
10598}
10599
10600fn unbwt_init_single(
10601    t: &[u16],
10602    p: &mut [usize],
10603    freq: Option<&[SaSint]>,
10604    i: &[SaSint],
10605    bucket2: &mut [usize],
10606    fastbits: &mut [u16],
10607) {
10608    let shift = unbwt_shift(t.len());
10609    if let Some(freq) = freq {
10610        for c in 0..ALPHABET_SIZE {
10611            bucket2[c] = freq[c] as usize;
10612        }
10613    } else {
10614        bucket2.fill(0);
10615        unbwt_compute_histogram(t, bucket2);
10616    }
10617
10618    unbwt_calculate_fastbits(bucket2, fastbits, shift);
10619    unbwt_calculate_p(t, p, bucket2, i[0] as usize);
10620}
10621
10622#[allow(dead_code)]
10623fn unbwt_init_parallel(
10624    t: &[u16],
10625    p: &mut [usize],
10626    freq: Option<&[SaSint]>,
10627    i: &[SaSint],
10628    bucket2: &mut [usize],
10629    fastbits: &mut [u16],
10630    buckets: &mut [usize],
10631    threads: SaSint,
10632) {
10633    let n = t.len();
10634    let available_threads = buckets.len() / ALPHABET_SIZE;
10635    let num_threads = if threads > 1 && n >= 65_536 && available_threads > 1 {
10636        usize::try_from(threads)
10637            .expect("threads must be non-negative")
10638            .min(available_threads)
10639            .max(1)
10640    } else {
10641        1
10642    };
10643
10644    if num_threads == 1 {
10645        unbwt_init_single(t, p, freq, i, bucket2, fastbits);
10646        return;
10647    }
10648
10649    let index = usize::try_from(i[0]).expect("primary index must be non-negative");
10650    let shift = unbwt_shift(n);
10651    let block_stride = (n / num_threads) & !15usize;
10652
10653    for thread in 0..num_threads {
10654        let block_start = thread * block_stride;
10655        let block_size = if thread + 1 < num_threads {
10656            block_stride
10657        } else {
10658            n - block_start
10659        };
10660        let local = &mut buckets[thread * ALPHABET_SIZE..(thread + 1) * ALPHABET_SIZE];
10661        local.fill(0);
10662        unbwt_compute_histogram(&t[block_start..block_start + block_size], local);
10663    }
10664
10665    bucket2.fill(0);
10666    for thread in 0..num_threads {
10667        let local = &mut buckets[thread * ALPHABET_SIZE..(thread + 1) * ALPHABET_SIZE];
10668        for c in 0..ALPHABET_SIZE {
10669            let a = bucket2[c];
10670            let b = local[c];
10671            bucket2[c] = a + b;
10672            local[c] = a;
10673        }
10674    }
10675
10676    unbwt_calculate_fastbits(bucket2, fastbits, shift);
10677
10678    for thread in 0..num_threads {
10679        let block_start = thread * block_stride;
10680        let block_size = if thread + 1 < num_threads {
10681            block_stride
10682        } else {
10683            n - block_start
10684        };
10685        let local = &mut buckets[thread * ALPHABET_SIZE..(thread + 1) * ALPHABET_SIZE];
10686        for c in 0..ALPHABET_SIZE {
10687            local[c] += bucket2[c];
10688        }
10689        unbwt_calculate_P(t, p, local, index, block_start, block_start + block_size);
10690    }
10691
10692    let last_local = &buckets[(num_threads - 1) * ALPHABET_SIZE..num_threads * ALPHABET_SIZE];
10693    bucket2.copy_from_slice(last_local);
10694}
10695
10696fn unbwt_decode_symbol(
10697    p0: usize,
10698    p: &[usize],
10699    bucket2: &[usize],
10700    fastbits: &[u16],
10701    shift: usize,
10702) -> (u16, usize) {
10703    let mut c0 = fastbits[p0 >> shift] as usize;
10704    if bucket2[c0] <= p0 {
10705        while bucket2[c0] <= p0 {
10706            c0 += 1;
10707        }
10708    }
10709    (c0 as u16, p[p0])
10710}
10711
10712#[allow(dead_code)]
10713fn unbwt_decode_1(
10714    u: &mut [u16],
10715    p: &[usize],
10716    bucket2: &[usize],
10717    fastbits: &[u16],
10718    shift: usize,
10719    i0: &mut usize,
10720    k: usize,
10721) {
10722    let mut cursors = [*i0];
10723    unbwt_decode_lanes::<1>(u, p, bucket2, fastbits, shift, k, &mut cursors, k);
10724    *i0 = cursors[0];
10725}
10726
10727#[allow(dead_code)]
10728fn unbwt_decode_2(
10729    u: &mut [u16],
10730    p: &[usize],
10731    bucket2: &[usize],
10732    fastbits: &[u16],
10733    shift: usize,
10734    r: usize,
10735    i0: &mut usize,
10736    i1: &mut usize,
10737    k: usize,
10738) {
10739    let mut cursors = [*i0, *i1];
10740    unbwt_decode_lanes::<2>(u, p, bucket2, fastbits, shift, r, &mut cursors, k);
10741    *i0 = cursors[0];
10742    *i1 = cursors[1];
10743}
10744
10745#[allow(dead_code)]
10746fn unbwt_decode_3(
10747    u: &mut [u16],
10748    p: &[usize],
10749    bucket2: &[usize],
10750    fastbits: &[u16],
10751    shift: usize,
10752    r: usize,
10753    i0: &mut usize,
10754    i1: &mut usize,
10755    i2: &mut usize,
10756    k: usize,
10757) {
10758    let mut cursors = [*i0, *i1, *i2];
10759    unbwt_decode_lanes::<3>(u, p, bucket2, fastbits, shift, r, &mut cursors, k);
10760    *i0 = cursors[0];
10761    *i1 = cursors[1];
10762    *i2 = cursors[2];
10763}
10764
10765#[allow(dead_code)]
10766fn unbwt_decode_4(
10767    u: &mut [u16],
10768    p: &[usize],
10769    bucket2: &[usize],
10770    fastbits: &[u16],
10771    shift: usize,
10772    r: usize,
10773    i0: &mut usize,
10774    i1: &mut usize,
10775    i2: &mut usize,
10776    i3: &mut usize,
10777    k: usize,
10778) {
10779    let mut cursors = [*i0, *i1, *i2, *i3];
10780    unbwt_decode_lanes::<4>(u, p, bucket2, fastbits, shift, r, &mut cursors, k);
10781    *i0 = cursors[0];
10782    *i1 = cursors[1];
10783    *i2 = cursors[2];
10784    *i3 = cursors[3];
10785}
10786
10787#[allow(dead_code)]
10788fn unbwt_decode_5(
10789    u: &mut [u16],
10790    p: &[usize],
10791    bucket2: &[usize],
10792    fastbits: &[u16],
10793    shift: usize,
10794    r: usize,
10795    cursors: &mut [usize; 5],
10796    k: usize,
10797) {
10798    unbwt_decode_lanes::<5>(u, p, bucket2, fastbits, shift, r, cursors, k);
10799}
10800
10801#[allow(dead_code)]
10802fn unbwt_decode_6(
10803    u: &mut [u16],
10804    p: &[usize],
10805    bucket2: &[usize],
10806    fastbits: &[u16],
10807    shift: usize,
10808    r: usize,
10809    cursors: &mut [usize; 6],
10810    k: usize,
10811) {
10812    unbwt_decode_lanes::<6>(u, p, bucket2, fastbits, shift, r, cursors, k);
10813}
10814
10815#[allow(dead_code)]
10816fn unbwt_decode_7(
10817    u: &mut [u16],
10818    p: &[usize],
10819    bucket2: &[usize],
10820    fastbits: &[u16],
10821    shift: usize,
10822    r: usize,
10823    cursors: &mut [usize; 7],
10824    k: usize,
10825) {
10826    unbwt_decode_lanes::<7>(u, p, bucket2, fastbits, shift, r, cursors, k);
10827}
10828
10829#[allow(dead_code)]
10830fn unbwt_decode_8(
10831    u: &mut [u16],
10832    p: &[usize],
10833    bucket2: &[usize],
10834    fastbits: &[u16],
10835    shift: usize,
10836    r: usize,
10837    cursors: &mut [usize; 8],
10838    k: usize,
10839) {
10840    unbwt_decode_lanes::<8>(u, p, bucket2, fastbits, shift, r, cursors, k);
10841}
10842
10843fn unbwt_decode(
10844    u: &mut [u16],
10845    p: &[usize],
10846    n: usize,
10847    r: usize,
10848    i: &[SaSint],
10849    bucket2: &[usize],
10850    fastbits: &[u16],
10851) {
10852    let shift = unbwt_shift(n);
10853    let blocks = 1 + (n - 1) / r;
10854    let remainder = n - r * (blocks - 1);
10855    unbwt_decode_blocks(u, p, r, i, bucket2, fastbits, shift, blocks, remainder);
10856}
10857
10858fn unbwt_decode_blocks(
10859    u: &mut [u16],
10860    p: &[usize],
10861    r: usize,
10862    i: &[SaSint],
10863    bucket2: &[usize],
10864    fastbits: &[u16],
10865    shift: usize,
10866    blocks: usize,
10867    remainder: usize,
10868) {
10869    let mut blocks_left = blocks;
10870    let mut i_offset = 0usize;
10871    let mut u_offset = 0usize;
10872
10873    while blocks_left > 8 {
10874        let mut cursors = [
10875            i[i_offset] as usize,
10876            i[i_offset + 1] as usize,
10877            i[i_offset + 2] as usize,
10878            i[i_offset + 3] as usize,
10879            i[i_offset + 4] as usize,
10880            i[i_offset + 5] as usize,
10881            i[i_offset + 6] as usize,
10882            i[i_offset + 7] as usize,
10883        ];
10884        unbwt_decode_lanes::<8>(
10885            &mut u[u_offset..],
10886            p,
10887            bucket2,
10888            fastbits,
10889            shift,
10890            r,
10891            &mut cursors,
10892            r,
10893        );
10894        i_offset += 8;
10895        blocks_left -= 8;
10896        u_offset += 8 * r;
10897    }
10898
10899    match blocks_left {
10900        1 => {
10901            let mut cursors = [i[i_offset] as usize];
10902            unbwt_decode_lanes::<1>(
10903                &mut u[u_offset..],
10904                p,
10905                bucket2,
10906                fastbits,
10907                shift,
10908                r,
10909                &mut cursors,
10910                remainder,
10911            );
10912        }
10913        2 => {
10914            let mut cursors = [i[i_offset] as usize, i[i_offset + 1] as usize];
10915            unbwt_decode_lanes::<2>(
10916                &mut u[u_offset..],
10917                p,
10918                bucket2,
10919                fastbits,
10920                shift,
10921                r,
10922                &mut cursors,
10923                remainder,
10924            );
10925            let mut first = [cursors[0]];
10926            unbwt_decode_lanes::<1>(
10927                &mut u[u_offset + remainder..],
10928                p,
10929                bucket2,
10930                fastbits,
10931                shift,
10932                r,
10933                &mut first,
10934                r - remainder,
10935            );
10936        }
10937        3 => {
10938            let mut cursors = [
10939                i[i_offset] as usize,
10940                i[i_offset + 1] as usize,
10941                i[i_offset + 2] as usize,
10942            ];
10943            unbwt_decode_lanes::<3>(
10944                &mut u[u_offset..],
10945                p,
10946                bucket2,
10947                fastbits,
10948                shift,
10949                r,
10950                &mut cursors,
10951                remainder,
10952            );
10953            let mut first = [cursors[0], cursors[1]];
10954            unbwt_decode_lanes::<2>(
10955                &mut u[u_offset + remainder..],
10956                p,
10957                bucket2,
10958                fastbits,
10959                shift,
10960                r,
10961                &mut first,
10962                r - remainder,
10963            );
10964        }
10965        4 => {
10966            let mut cursors = [
10967                i[i_offset] as usize,
10968                i[i_offset + 1] as usize,
10969                i[i_offset + 2] as usize,
10970                i[i_offset + 3] as usize,
10971            ];
10972            unbwt_decode_lanes::<4>(
10973                &mut u[u_offset..],
10974                p,
10975                bucket2,
10976                fastbits,
10977                shift,
10978                r,
10979                &mut cursors,
10980                remainder,
10981            );
10982            let mut first = [cursors[0], cursors[1], cursors[2]];
10983            unbwt_decode_lanes::<3>(
10984                &mut u[u_offset + remainder..],
10985                p,
10986                bucket2,
10987                fastbits,
10988                shift,
10989                r,
10990                &mut first,
10991                r - remainder,
10992            );
10993        }
10994        5 => {
10995            let mut cursors = [
10996                i[i_offset] as usize,
10997                i[i_offset + 1] as usize,
10998                i[i_offset + 2] as usize,
10999                i[i_offset + 3] as usize,
11000                i[i_offset + 4] as usize,
11001            ];
11002            unbwt_decode_lanes::<5>(
11003                &mut u[u_offset..],
11004                p,
11005                bucket2,
11006                fastbits,
11007                shift,
11008                r,
11009                &mut cursors,
11010                remainder,
11011            );
11012            let mut first = [cursors[0], cursors[1], cursors[2], cursors[3]];
11013            unbwt_decode_lanes::<4>(
11014                &mut u[u_offset + remainder..],
11015                p,
11016                bucket2,
11017                fastbits,
11018                shift,
11019                r,
11020                &mut first,
11021                r - remainder,
11022            );
11023        }
11024        6 => {
11025            let mut cursors = [
11026                i[i_offset] as usize,
11027                i[i_offset + 1] as usize,
11028                i[i_offset + 2] as usize,
11029                i[i_offset + 3] as usize,
11030                i[i_offset + 4] as usize,
11031                i[i_offset + 5] as usize,
11032            ];
11033            unbwt_decode_lanes::<6>(
11034                &mut u[u_offset..],
11035                p,
11036                bucket2,
11037                fastbits,
11038                shift,
11039                r,
11040                &mut cursors,
11041                remainder,
11042            );
11043            let mut first = [cursors[0], cursors[1], cursors[2], cursors[3], cursors[4]];
11044            unbwt_decode_lanes::<5>(
11045                &mut u[u_offset + remainder..],
11046                p,
11047                bucket2,
11048                fastbits,
11049                shift,
11050                r,
11051                &mut first,
11052                r - remainder,
11053            );
11054        }
11055        7 => {
11056            let mut cursors = [
11057                i[i_offset] as usize,
11058                i[i_offset + 1] as usize,
11059                i[i_offset + 2] as usize,
11060                i[i_offset + 3] as usize,
11061                i[i_offset + 4] as usize,
11062                i[i_offset + 5] as usize,
11063                i[i_offset + 6] as usize,
11064            ];
11065            unbwt_decode_lanes::<7>(
11066                &mut u[u_offset..],
11067                p,
11068                bucket2,
11069                fastbits,
11070                shift,
11071                r,
11072                &mut cursors,
11073                remainder,
11074            );
11075            let mut first = [
11076                cursors[0], cursors[1], cursors[2], cursors[3], cursors[4], cursors[5],
11077            ];
11078            unbwt_decode_lanes::<6>(
11079                &mut u[u_offset + remainder..],
11080                p,
11081                bucket2,
11082                fastbits,
11083                shift,
11084                r,
11085                &mut first,
11086                r - remainder,
11087            );
11088        }
11089        _ => {
11090            let mut cursors = [
11091                i[i_offset] as usize,
11092                i[i_offset + 1] as usize,
11093                i[i_offset + 2] as usize,
11094                i[i_offset + 3] as usize,
11095                i[i_offset + 4] as usize,
11096                i[i_offset + 5] as usize,
11097                i[i_offset + 6] as usize,
11098                i[i_offset + 7] as usize,
11099            ];
11100            unbwt_decode_lanes::<8>(
11101                &mut u[u_offset..],
11102                p,
11103                bucket2,
11104                fastbits,
11105                shift,
11106                r,
11107                &mut cursors,
11108                remainder,
11109            );
11110            let mut first = [
11111                cursors[0], cursors[1], cursors[2], cursors[3], cursors[4], cursors[5], cursors[6],
11112            ];
11113            unbwt_decode_lanes::<7>(
11114                &mut u[u_offset + remainder..],
11115                p,
11116                bucket2,
11117                fastbits,
11118                shift,
11119                r,
11120                &mut first,
11121                r - remainder,
11122            );
11123        }
11124    }
11125}
11126
11127fn unbwt_decode_omp(
11128    u: &mut [u16],
11129    p: &[usize],
11130    n: usize,
11131    r: usize,
11132    i: &[SaSint],
11133    bucket2: &[usize],
11134    fastbits: &[u16],
11135    threads: SaSint,
11136) {
11137    let blocks = 1 + (n - 1) / r;
11138    let remainder = n - r * (blocks - 1);
11139    let num_threads = if threads > 1 && n >= 65_536 {
11140        usize::try_from(threads)
11141            .expect("threads must be non-negative")
11142            .min(blocks)
11143            .max(1)
11144    } else {
11145        1
11146    };
11147
11148    if num_threads == 1 {
11149        unbwt_decode(u, p, n, r, i, bucket2, fastbits);
11150        return;
11151    }
11152
11153    let shift = unbwt_shift(n);
11154    let block_stride = blocks / num_threads;
11155    let block_remainder = blocks % num_threads;
11156    let u_ptr = SyncMutPtr::new(u);
11157    run_rayon_with_threads(num_threads, || {
11158        (0..num_threads).into_par_iter().for_each(|thread| {
11159            let block_count = block_stride + usize::from(thread < block_remainder);
11160            let block_start = block_stride * thread + thread.min(block_remainder);
11161            let tail = if thread + 1 < num_threads {
11162                r
11163            } else {
11164                remainder
11165            };
11166            let u = unsafe { u_ptr.as_slice() };
11167            unbwt_decode_blocks(
11168                &mut u[r * block_start..],
11169                p,
11170                r,
11171                &i[block_start..],
11172                bucket2,
11173                fastbits,
11174                shift,
11175                block_count,
11176                tail,
11177            );
11178        });
11179    });
11180}
11181
11182fn unbwt_decode_lanes<const LANES: usize>(
11183    u: &mut [u16],
11184    p: &[usize],
11185    bucket2: &[usize],
11186    fastbits: &[u16],
11187    shift: usize,
11188    r: usize,
11189    cursors: &mut [usize; LANES],
11190    k: usize,
11191) {
11192    for pos in 0..k {
11193        for lane in 0..LANES {
11194            let (symbol, next) = unbwt_decode_symbol(cursors[lane], p, bucket2, fastbits, shift);
11195            cursors[lane] = next;
11196            u[lane * r + pos] = symbol;
11197        }
11198    }
11199}
11200
11201fn unbwt_core(
11202    t: &[u16],
11203    u: &mut [u16],
11204    a: &mut [SaSint],
11205    freq: Option<&[SaSint]>,
11206    r: SaSint,
11207    i: &[SaSint],
11208) -> SaSint {
11209    let n = t.len();
11210    let shift = unbwt_shift(n);
11211    let mut bucket2 = vec![0usize; ALPHABET_SIZE];
11212    let mut fastbits = vec![0u16; 1 + (n >> shift)];
11213
11214    unbwt_core_with_buffers(t, u, a, freq, r, i, &mut bucket2, &mut fastbits, 1)
11215}
11216
11217fn unbwt_core_with_buffers(
11218    t: &[u16],
11219    u: &mut [u16],
11220    a: &mut [SaSint],
11221    freq: Option<&[SaSint]>,
11222    r: SaSint,
11223    i: &[SaSint],
11224    bucket2: &mut [usize],
11225    fastbits: &mut [u16],
11226    threads: SaSint,
11227) -> SaSint {
11228    let n = t.len();
11229    let shift = unbwt_shift(n);
11230    if bucket2.len() < ALPHABET_SIZE || fastbits.len() < 1 + (n >> shift) {
11231        return -2;
11232    }
11233
11234    let mut p = vec![0usize; n + 1];
11235    unbwt_init_single(
11236        t,
11237        &mut p,
11238        freq,
11239        i,
11240        &mut bucket2[..ALPHABET_SIZE],
11241        &mut fastbits[..1 + (n >> shift)],
11242    );
11243    unbwt_decode_omp(
11244        u,
11245        &p,
11246        n,
11247        r as usize,
11248        i,
11249        &bucket2[..ALPHABET_SIZE],
11250        &fastbits[..1 + (n >> shift)],
11251        threads,
11252    );
11253
11254    for (dst, &src) in a.iter_mut().zip(p.iter().skip(1)) {
11255        *dst = src as SaSint;
11256    }
11257    0
11258}
11259
11260fn inverse_bwt(
11261    t: &[u16],
11262    u: &mut [u16],
11263    a: &mut [SaSint],
11264    freq: Option<&[SaSint]>,
11265    primary: SaSint,
11266) -> SaSint {
11267    let n = t.len();
11268    let i = [primary];
11269    let rc = validate_unbwt_aux(t, u, a, freq, n as SaSint, &i);
11270    if rc != 0 {
11271        return rc;
11272    }
11273    if n <= 1 {
11274        if n == 1 {
11275            u[0] = t[0];
11276        }
11277        return 0;
11278    }
11279    unbwt_core(t, u, a, freq, n as SaSint, &i)
11280}
11281
11282/// Reconstructs the original 16-bit string from a given BWT and primary index.
11283///
11284/// - `t` (`[0..n-1]`): the input 16-bit string.
11285/// - `u` (`[0..n-1]`): the output 16-bit string (can alias `t`).
11286/// - `a` (`[0..n]`): the temporary array (must have length `n + 1`).
11287/// - `freq` (`[0..65535]`): optional input symbol frequency table.
11288/// - `i`: the primary index.
11289///
11290/// Returns 0 on success, -1 or -2 on error.
11291pub fn libsais16_unbwt(
11292    t: &[u16],
11293    u: &mut [u16],
11294    a: &mut [SaSint],
11295    freq: Option<&[SaSint]>,
11296    i: SaSint,
11297) -> SaSint {
11298    inverse_bwt(t, u, a, freq, i)
11299}
11300
11301/// Reconstructs the original 16-bit string from a given BWT and primary index using a libsais16 reverse-BWT context.
11302///
11303/// - `ctx`: the libsais16 reverse-BWT context.
11304/// - `t` (`[0..n-1]`): the input 16-bit string.
11305/// - `u` (`[0..n-1]`): the output 16-bit string (can alias `t`).
11306/// - `a` (`[0..n]`): the temporary array (must have length `n + 1`).
11307/// - `freq` (`[0..65535]`): optional input symbol frequency table.
11308/// - `i`: the primary index.
11309///
11310/// Returns 0 on success, -1 or -2 on error.
11311pub fn libsais16_unbwt_ctx(
11312    ctx: &mut UnbwtContext,
11313    t: &[u16],
11314    u: &mut [u16],
11315    a: &mut [SaSint],
11316    freq: Option<&[SaSint]>,
11317    i: SaSint,
11318) -> SaSint {
11319    libsais16_unbwt_aux_ctx(ctx, t, u, a, freq, t.len() as SaSint, &[i])
11320}
11321
11322/// Reconstructs the original 16-bit string from a given BWT with auxiliary indexes.
11323///
11324/// - `t` (`[0..n-1]`): the input 16-bit string.
11325/// - `u` (`[0..n-1]`): the output 16-bit string (can alias `t`).
11326/// - `a` (`[0..n]`): the temporary array (must have length `n + 1`).
11327/// - `freq` (`[0..65535]`): optional input symbol frequency table.
11328/// - `r`: sampling rate for the auxiliary indexes (must be a power of two).
11329/// - `i` (`[0..(n-1)/r]`): input auxiliary indexes.
11330///
11331/// Returns 0 on success, -1 or -2 on error.
11332pub fn libsais16_unbwt_aux(
11333    t: &[u16],
11334    u: &mut [u16],
11335    a: &mut [SaSint],
11336    freq: Option<&[SaSint]>,
11337    r: SaSint,
11338    i: &[SaSint],
11339) -> SaSint {
11340    let rc = validate_unbwt_aux(t, u, a, freq, r, i);
11341    if rc != 0 {
11342        return rc;
11343    }
11344    if t.len() <= 1 {
11345        if t.len() == 1 {
11346            u[0] = t[0];
11347        }
11348        return 0;
11349    }
11350    unbwt_core(t, u, a, freq, r, i)
11351}
11352
11353/// Reconstructs the original 16-bit string from a given BWT with auxiliary indexes using a libsais16 reverse-BWT context.
11354///
11355/// - `ctx`: the libsais16 reverse-BWT context.
11356/// - `t` (`[0..n-1]`): the input 16-bit string.
11357/// - `u` (`[0..n-1]`): the output 16-bit string (can alias `t`).
11358/// - `a` (`[0..n]`): the temporary array (must have length `n + 1`).
11359/// - `freq` (`[0..65535]`): optional input symbol frequency table.
11360/// - `r`: sampling rate for the auxiliary indexes (must be a power of two).
11361/// - `i` (`[0..(n-1)/r]`): input auxiliary indexes.
11362///
11363/// Returns 0 on success, -1 or -2 on error.
11364pub fn libsais16_unbwt_aux_ctx(
11365    ctx: &mut UnbwtContext,
11366    t: &[u16],
11367    u: &mut [u16],
11368    a: &mut [SaSint],
11369    freq: Option<&[SaSint]>,
11370    r: SaSint,
11371    i: &[SaSint],
11372) -> SaSint {
11373    let rc = validate_unbwt_aux(t, u, a, freq, r, i);
11374    if rc != 0 {
11375        return rc;
11376    }
11377    if t.len() <= 1 {
11378        if t.len() == 1 {
11379            u[0] = t[0];
11380        }
11381        return 0;
11382    }
11383    unbwt_core_with_buffers(
11384        t,
11385        u,
11386        a,
11387        freq,
11388        r,
11389        i,
11390        &mut ctx.bucket2,
11391        &mut ctx.fastbits,
11392        ctx.threads,
11393    )
11394}
11395
11396/// Reconstructs the original 16-bit string from a given BWT and primary index in parallel using OpenMP-style threading.
11397///
11398/// - `t` (`[0..n-1]`): the input 16-bit string.
11399/// - `u` (`[0..n-1]`): the output 16-bit string (can alias `t`).
11400/// - `a` (`[0..n]`): the temporary array (must have length `n + 1`).
11401/// - `freq` (`[0..65535]`): optional input symbol frequency table.
11402/// - `i`: the primary index.
11403/// - `threads`: number of worker threads (can be 0 for the implementation default).
11404///
11405/// Returns 0 on success, -1 or -2 on error.
11406pub fn libsais16_unbwt_omp(
11407    t: &[u16],
11408    u: &mut [u16],
11409    a: &mut [SaSint],
11410    freq: Option<&[SaSint]>,
11411    i: SaSint,
11412    threads: SaSint,
11413) -> SaSint {
11414    if threads < 0 {
11415        -1
11416    } else {
11417        let primary = [i];
11418        libsais16_unbwt_aux_omp(t, u, a, freq, t.len() as SaSint, &primary, threads)
11419    }
11420}
11421
11422/// Reconstructs the original 16-bit string from a given BWT with auxiliary indexes in parallel using OpenMP-style threading.
11423///
11424/// - `t` (`[0..n-1]`): the input 16-bit string.
11425/// - `u` (`[0..n-1]`): the output 16-bit string (can alias `t`).
11426/// - `a` (`[0..n]`): the temporary array (must have length `n + 1`).
11427/// - `freq` (`[0..65535]`): optional input symbol frequency table.
11428/// - `r`: sampling rate for the auxiliary indexes (must be a power of two).
11429/// - `i` (`[0..(n-1)/r]`): input auxiliary indexes.
11430/// - `threads`: number of worker threads (can be 0 for the implementation default).
11431///
11432/// Returns 0 on success, -1 or -2 on error.
11433pub fn libsais16_unbwt_aux_omp(
11434    t: &[u16],
11435    u: &mut [u16],
11436    a: &mut [SaSint],
11437    freq: Option<&[SaSint]>,
11438    r: SaSint,
11439    i: &[SaSint],
11440    threads: SaSint,
11441) -> SaSint {
11442    if threads < 0 {
11443        -1
11444    } else {
11445        let rc = validate_unbwt_aux(t, u, a, freq, r, i);
11446        if rc != 0 {
11447            return rc;
11448        }
11449        if t.len() <= 1 {
11450            if t.len() == 1 {
11451                u[0] = t[0];
11452            }
11453            return 0;
11454        }
11455        let n = t.len();
11456        let shift = unbwt_shift(n);
11457        let mut bucket2 = vec![0usize; ALPHABET_SIZE];
11458        let mut fastbits = vec![0u16; 1 + (n >> shift)];
11459        unbwt_core_with_buffers(
11460            t,
11461            u,
11462            a,
11463            freq,
11464            r,
11465            i,
11466            &mut bucket2,
11467            &mut fastbits,
11468            normalize_threads(threads),
11469        )
11470    }
11471}
11472
11473/// Constructs the permuted longest common prefix array (PLCP) of a given 16-bit string and suffix array.
11474///
11475/// - `t` (`[0..n-1]`): the input 16-bit string.
11476/// - `sa` (`[0..n-1]`): the input suffix array.
11477/// - `plcp` (`[0..n-1]`): the output permuted longest common prefix array.
11478///
11479/// Returns 0 on success, -1 on error.
11480pub fn libsais16_plcp(t: &[u16], sa: &[SaSint], plcp: &mut [SaSint]) -> SaSint {
11481    compute_plcp(t, sa, plcp, false)
11482}
11483
11484/// Constructs the PLCP of a given 16-bit string set and generalized suffix array (GSA).
11485///
11486/// - `t` (`[0..n-1]`): the input 16-bit string set using 0 as separators (`t[n-1]` must be 0).
11487/// - `sa` (`[0..n-1]`): the input generalized suffix array.
11488/// - `plcp` (`[0..n-1]`): the output permuted longest common prefix array.
11489///
11490/// Returns 0 on success, -1 on error.
11491pub fn libsais16_plcp_gsa(t: &[u16], sa: &[SaSint], plcp: &mut [SaSint]) -> SaSint {
11492    if t.last().copied().unwrap_or(0) != 0 {
11493        -1
11494    } else {
11495        compute_plcp(t, sa, plcp, true)
11496    }
11497}
11498
11499fn compute_plcp(t: &[u16], sa: &[SaSint], plcp: &mut [SaSint], gsa: bool) -> SaSint {
11500    if sa.len() != t.len() || plcp.len() != t.len() {
11501        return -1;
11502    }
11503    if t.len() <= 1 {
11504        if t.len() == 1 {
11505            plcp[0] = 0;
11506        }
11507        return 0;
11508    }
11509
11510    if compute_phi(sa, plcp) != 0 {
11511        return -1;
11512    }
11513
11514    compute_plcp_from_phi(t, plcp, gsa)
11515}
11516
11517fn compute_phi(sa: &[SaSint], plcp: &mut [SaSint]) -> SaSint {
11518    let n = sa.len();
11519    let mut previous = n as SaSint;
11520    for &suffix_value in sa {
11521        let Some(suffix) = suffix_index(suffix_value, n) else {
11522            return -1;
11523        };
11524        plcp[suffix] = previous;
11525        previous = suffix_value;
11526    }
11527    0
11528}
11529
11530fn compute_plcp_from_phi(t: &[u16], plcp: &mut [SaSint], gsa: bool) -> SaSint {
11531    let n = t.len();
11532    let mut l = 0usize;
11533    for i in 0..t.len() {
11534        let previous = plcp[i];
11535        if previous == n as SaSint {
11536            plcp[i] = 0;
11537            l = 0;
11538            continue;
11539        }
11540
11541        let Some(prev) = suffix_index(previous, n) else {
11542            return -1;
11543        };
11544
11545        while i + l < t.len()
11546            && prev + l < t.len()
11547            && t[i + l] == t[prev + l]
11548            && (!gsa || t[i + l] != 0)
11549        {
11550            l += 1;
11551        }
11552        plcp[i] = l as SaSint;
11553        l = l.saturating_sub(1);
11554    }
11555    0
11556}
11557
11558fn compute_phi_omp(sa: &[SaSint], plcp: &mut [SaSint], n: SaSint, threads: SaSint) -> SaSint {
11559    let n_usize = n as usize;
11560    if threads == 1 || n < 65_536 {
11561        return compute_phi(&sa[..n_usize], &mut plcp[..n_usize]);
11562    }
11563
11564    let block_stride = (n / threads) & !15;
11565    for thread in 0..threads {
11566        let block_start = thread * block_stride;
11567        let block_size = if thread < threads - 1 {
11568            block_stride
11569        } else {
11570            n - block_start
11571        };
11572        let start = block_start as usize;
11573        let end = (block_start + block_size) as usize;
11574        let mut previous = if start > 0 { sa[start - 1] } else { n };
11575        for &suffix_value in &sa[start..end] {
11576            let Some(suffix) = suffix_index(suffix_value, n_usize) else {
11577                return -1;
11578            };
11579            plcp[suffix] = previous;
11580            previous = suffix_value;
11581        }
11582    }
11583    0
11584}
11585
11586fn compute_plcp_omp(t: &[u16], plcp: &mut [SaSint], n: SaSint, threads: SaSint) -> SaSint {
11587    if threads == 1 || n < 65_536 {
11588        let n = n as usize;
11589        return compute_plcp_from_phi(&t[..n], &mut plcp[..n], false);
11590    }
11591
11592    let block_stride = (n / threads) & !15;
11593    for thread in 0..threads {
11594        let block_start = thread * block_stride;
11595        let block_size = if thread < threads - 1 {
11596            block_stride
11597        } else {
11598            n - block_start
11599        };
11600        let rc = compute_plcp_range(
11601            t,
11602            plcp,
11603            n as usize,
11604            block_start as isize,
11605            block_size as isize,
11606            false,
11607        );
11608        if rc != 0 {
11609            return rc;
11610        }
11611    }
11612    0
11613}
11614
11615fn compute_plcp_range(
11616    t: &[u16],
11617    plcp: &mut [SaSint],
11618    n: usize,
11619    omp_block_start: isize,
11620    omp_block_size: isize,
11621    gsa: bool,
11622) -> SaSint {
11623    let mut l = 0usize;
11624    let end = (omp_block_start + omp_block_size) as usize;
11625    for i in omp_block_start as usize..end {
11626        let previous = plcp[i];
11627        if previous == n as SaSint {
11628            plcp[i] = 0;
11629            l = 0;
11630            continue;
11631        }
11632
11633        let Some(prev) = suffix_index(previous, n) else {
11634            return -1;
11635        };
11636
11637        while i + l < t.len()
11638            && prev + l < t.len()
11639            && t[i + l] == t[prev + l]
11640            && (!gsa || t[i + l] != 0)
11641        {
11642            l += 1;
11643        }
11644        plcp[i] = l as SaSint;
11645        l = l.saturating_sub(1);
11646    }
11647    0
11648}
11649
11650fn compute_plcp_gsa(
11651    t: &[u16],
11652    plcp: &mut [SaSint],
11653    omp_block_start: isize,
11654    omp_block_size: isize,
11655) -> SaSint {
11656    let n = t.len();
11657    let mut l = 0usize;
11658    let end = (omp_block_start + omp_block_size) as usize;
11659    for i in omp_block_start as usize..end {
11660        let previous = plcp[i];
11661        if previous == n as SaSint {
11662            plcp[i] = 0;
11663            l = 0;
11664            continue;
11665        }
11666
11667        let Some(prev) = suffix_index(previous, n) else {
11668            return -1;
11669        };
11670
11671        while i + l < t.len() && prev + l < t.len() && t[i + l] == t[prev + l] && t[i + l] != 0 {
11672            l += 1;
11673        }
11674        plcp[i] = l as SaSint;
11675        l = l.saturating_sub(1);
11676    }
11677    0
11678}
11679
11680fn compute_plcp_gsa_omp(t: &[u16], plcp: &mut [SaSint], n: SaSint, threads: SaSint) -> SaSint {
11681    if threads == 1 || n < 65_536 {
11682        return compute_plcp_gsa(t, plcp, 0, n as isize);
11683    }
11684
11685    let block_stride = (n / threads) & !15;
11686    for thread in 0..threads {
11687        let block_start = thread * block_stride;
11688        let block_size = if thread < threads - 1 {
11689            block_stride
11690        } else {
11691            n - block_start
11692        };
11693        let rc = compute_plcp_gsa(t, plcp, block_start as isize, block_size as isize);
11694        if rc != 0 {
11695            return rc;
11696        }
11697    }
11698    0
11699}
11700
11701fn compute_lcp(
11702    plcp: &[SaSint],
11703    sa: &[SaSint],
11704    lcp: &mut [SaSint],
11705    omp_block_start: isize,
11706    omp_block_size: isize,
11707) -> SaSint {
11708    let end = (omp_block_start + omp_block_size) as usize;
11709    for row in omp_block_start as usize..end {
11710        let Some(suffix) = suffix_index(sa[row], plcp.len()) else {
11711            return -1;
11712        };
11713        lcp[row] = plcp[suffix];
11714    }
11715    0
11716}
11717
11718fn compute_lcp_omp(
11719    plcp: &[SaSint],
11720    sa: &[SaSint],
11721    lcp: &mut [SaSint],
11722    n: SaSint,
11723    threads: SaSint,
11724) -> SaSint {
11725    if threads == 1 || n < 65_536 {
11726        return compute_lcp(plcp, sa, lcp, 0, n as isize);
11727    }
11728
11729    let block_stride = (n / threads) & !15;
11730    for thread in 0..threads {
11731        let block_start = thread * block_stride;
11732        let block_size = if thread < threads - 1 {
11733            block_stride
11734        } else {
11735            n - block_start
11736        };
11737        let rc = compute_lcp(plcp, sa, lcp, block_start as isize, block_size as isize);
11738        if rc != 0 {
11739            return rc;
11740        }
11741    }
11742    0
11743}
11744
11745/// Constructs the longest common prefix array (LCP) from a PLCP and suffix array.
11746///
11747/// - `plcp` (`[0..n-1]`): the input permuted longest common prefix array.
11748/// - `sa` (`[0..n-1]`): the input suffix array or generalized suffix array (GSA).
11749/// - `lcp` (`[0..n-1]`): the output longest common prefix array (can alias `sa`).
11750///
11751/// Returns 0 on success, -1 on error.
11752pub fn libsais16_lcp(plcp: &[SaSint], sa: &[SaSint], lcp: &mut [SaSint]) -> SaSint {
11753    if plcp.len() != sa.len() || lcp.len() != sa.len() {
11754        return -1;
11755    }
11756    for (row, &suffix) in sa.iter().enumerate() {
11757        let Some(suffix) = suffix_index(suffix, plcp.len()) else {
11758            return -1;
11759        };
11760        lcp[row] = plcp[suffix];
11761    }
11762    0
11763}
11764
11765fn suffix_index(value: SaSint, len: usize) -> Option<usize> {
11766    usize::try_from(value).ok().filter(|&index| index < len)
11767}
11768
11769/// Constructs the PLCP of a given 16-bit string and suffix array in parallel using OpenMP-style threading.
11770///
11771/// - `t` (`[0..n-1]`): the input 16-bit string.
11772/// - `sa` (`[0..n-1]`): the input suffix array.
11773/// - `plcp` (`[0..n-1]`): the output permuted longest common prefix array.
11774/// - `threads`: number of worker threads (can be 0 for the implementation default).
11775///
11776/// Returns 0 on success, -1 on error.
11777pub fn libsais16_plcp_omp(
11778    t: &[u16],
11779    sa: &[SaSint],
11780    plcp: &mut [SaSint],
11781    threads: SaSint,
11782) -> SaSint {
11783    if threads < 0 {
11784        return -1;
11785    }
11786    if sa.len() != t.len() || plcp.len() != t.len() {
11787        return -1;
11788    }
11789    if t.len() <= 1 {
11790        if t.len() == 1 {
11791            plcp[0] = 0;
11792        }
11793        return 0;
11794    }
11795
11796    let n = t.len() as SaSint;
11797    let threads = normalize_threads(threads);
11798    if compute_phi_omp(sa, plcp, n, threads) != 0 {
11799        return -1;
11800    }
11801    compute_plcp_omp(t, plcp, n, threads)
11802}
11803
11804/// Constructs the PLCP of a given 16-bit string set and GSA in parallel using OpenMP-style threading.
11805///
11806/// - `t` (`[0..n-1]`): the input 16-bit string set using 0 as separators (`t[n-1]` must be 0).
11807/// - `sa` (`[0..n-1]`): the input generalized suffix array.
11808/// - `plcp` (`[0..n-1]`): the output permuted longest common prefix array.
11809/// - `threads`: number of worker threads (can be 0 for the implementation default).
11810///
11811/// Returns 0 on success, -1 on error.
11812pub fn libsais16_plcp_gsa_omp(
11813    t: &[u16],
11814    sa: &[SaSint],
11815    plcp: &mut [SaSint],
11816    threads: SaSint,
11817) -> SaSint {
11818    if threads < 0 {
11819        return -1;
11820    }
11821    if t.last().copied().unwrap_or(0) != 0 {
11822        return -1;
11823    }
11824    if sa.len() != t.len() || plcp.len() != t.len() {
11825        return -1;
11826    }
11827    if t.len() <= 1 {
11828        if t.len() == 1 {
11829            plcp[0] = 0;
11830        }
11831        return 0;
11832    }
11833
11834    let n = t.len() as SaSint;
11835    let threads = normalize_threads(threads);
11836    if compute_phi_omp(sa, plcp, n, threads) != 0 {
11837        return -1;
11838    }
11839    compute_plcp_gsa_omp(t, plcp, n, threads)
11840}
11841
11842/// Constructs the LCP from a PLCP and suffix array in parallel using OpenMP-style threading.
11843///
11844/// - `plcp` (`[0..n-1]`): the input permuted longest common prefix array.
11845/// - `sa` (`[0..n-1]`): the input suffix array or generalized suffix array (GSA).
11846/// - `lcp` (`[0..n-1]`): the output longest common prefix array (can alias `sa`).
11847/// - `threads`: number of worker threads (can be 0 for the implementation default).
11848///
11849/// Returns 0 on success, -1 on error.
11850pub fn libsais16_lcp_omp(
11851    plcp: &[SaSint],
11852    sa: &[SaSint],
11853    lcp: &mut [SaSint],
11854    threads: SaSint,
11855) -> SaSint {
11856    if threads < 0 {
11857        return -1;
11858    }
11859    if plcp.len() != sa.len() || lcp.len() != sa.len() {
11860        return -1;
11861    }
11862
11863    compute_lcp_omp(
11864        plcp,
11865        sa,
11866        lcp,
11867        sa.len() as SaSint,
11868        normalize_threads(threads),
11869    )
11870}
11871
11872#[cfg(all(test, feature = "upstream-c"))]
11873mod tests {
11874    use super::*;
11875
11876    unsafe extern "C" {
11877        fn probe_public_libsais16(t: *const u16, sa: *mut SaSint, n: SaSint, fs: SaSint) -> SaSint;
11878        fn probe_public_libsais16_freq(
11879            t: *const u16,
11880            sa: *mut SaSint,
11881            n: SaSint,
11882            fs: SaSint,
11883            freq: *mut SaSint,
11884        ) -> SaSint;
11885        fn probe_public_libsais16_gsa(
11886            t: *const u16,
11887            sa: *mut SaSint,
11888            n: SaSint,
11889            fs: SaSint,
11890        ) -> SaSint;
11891        fn probe_public_libsais16_gsa_freq(
11892            t: *const u16,
11893            sa: *mut SaSint,
11894            n: SaSint,
11895            fs: SaSint,
11896            freq: *mut SaSint,
11897        ) -> SaSint;
11898        fn probe_public_libsais16_int(
11899            t: *mut SaSint,
11900            sa: *mut SaSint,
11901            n: SaSint,
11902            k: SaSint,
11903            fs: SaSint,
11904        ) -> SaSint;
11905        fn probe_libsais16_main_32s_entry(
11906            t: *mut SaSint,
11907            sa: *mut SaSint,
11908            n: SaSint,
11909            k: SaSint,
11910            fs: SaSint,
11911            threads: SaSint,
11912        ) -> SaSint;
11913        fn probe_libsais16_final_sorting_scan_left_to_right_32s(
11914            t: *const SaSint,
11915            sa: *mut SaSint,
11916            induction_bucket: *mut SaSint,
11917            omp_block_start: SaSint,
11918            omp_block_size: SaSint,
11919        );
11920        fn probe_libsais16_final_sorting_scan_right_to_left_32s(
11921            t: *const SaSint,
11922            sa: *mut SaSint,
11923            induction_bucket: *mut SaSint,
11924            omp_block_start: SaSint,
11925            omp_block_size: SaSint,
11926        );
11927        fn probe_libsais16_clear_lms_suffixes_omp(
11928            sa: *mut SaSint,
11929            n: SaSint,
11930            k: SaSint,
11931            bucket_start: *mut SaSint,
11932            bucket_end: *mut SaSint,
11933            threads: SaSint,
11934        );
11935        fn probe_libsais16_flip_suffix_markers_omp(sa: *mut SaSint, l: SaSint, threads: SaSint);
11936        fn probe_libsais16_induce_final_order_32s_6k(
11937            t: *const SaSint,
11938            sa: *mut SaSint,
11939            n: SaSint,
11940            k: SaSint,
11941            buckets: *mut SaSint,
11942            threads: SaSint,
11943        );
11944        fn probe_libsais16_induce_final_order_32s_4k(
11945            t: *const SaSint,
11946            sa: *mut SaSint,
11947            n: SaSint,
11948            k: SaSint,
11949            buckets: *mut SaSint,
11950            threads: SaSint,
11951        );
11952        fn probe_libsais16_induce_final_order_32s_2k(
11953            t: *const SaSint,
11954            sa: *mut SaSint,
11955            n: SaSint,
11956            k: SaSint,
11957            buckets: *mut SaSint,
11958            threads: SaSint,
11959        );
11960        fn probe_libsais16_induce_final_order_32s_1k(
11961            t: *const SaSint,
11962            sa: *mut SaSint,
11963            n: SaSint,
11964            k: SaSint,
11965            buckets: *mut SaSint,
11966            threads: SaSint,
11967        );
11968        fn probe_libsais16_induce_partial_order_32s_6k_omp(
11969            t: *const SaSint,
11970            sa: *mut SaSint,
11971            n: SaSint,
11972            k: SaSint,
11973            buckets: *mut SaSint,
11974            first_lms_suffix: SaSint,
11975            left_suffixes_count: SaSint,
11976            threads: SaSint,
11977        );
11978        fn probe_libsais16_induce_partial_order_32s_4k_omp(
11979            t: *const SaSint,
11980            sa: *mut SaSint,
11981            n: SaSint,
11982            k: SaSint,
11983            buckets: *mut SaSint,
11984            threads: SaSint,
11985        );
11986        fn probe_libsais16_induce_partial_order_32s_2k_omp(
11987            t: *const SaSint,
11988            sa: *mut SaSint,
11989            n: SaSint,
11990            k: SaSint,
11991            buckets: *mut SaSint,
11992            threads: SaSint,
11993        );
11994        fn probe_libsais16_induce_partial_order_32s_1k_omp(
11995            t: *const SaSint,
11996            sa: *mut SaSint,
11997            n: SaSint,
11998            k: SaSint,
11999            buckets: *mut SaSint,
12000            threads: SaSint,
12001        );
12002        fn probe_libsais16_induce_partial_order_16u_omp(
12003            t: *const u16,
12004            sa: *mut SaSint,
12005            n: SaSint,
12006            k: SaSint,
12007            flags: SaSint,
12008            buckets: *mut SaSint,
12009            first_lms_suffix: SaSint,
12010            left_suffixes_count: SaSint,
12011            threads: SaSint,
12012        );
12013        fn probe_libsais16_induce_final_order_16u_omp(
12014            t: *const u16,
12015            sa: *mut SaSint,
12016            n: SaSint,
12017            k: SaSint,
12018            flags: SaSint,
12019            r: SaSint,
12020            i: *mut SaSint,
12021            buckets: *mut SaSint,
12022            threads: SaSint,
12023        ) -> SaSint;
12024        fn probe_public_libsais16_bwt(
12025            t: *const u16,
12026            u: *mut u16,
12027            a: *mut SaSint,
12028            n: SaSint,
12029            fs: SaSint,
12030        ) -> SaSint;
12031        fn probe_public_libsais16_bwt_freq(
12032            t: *const u16,
12033            u: *mut u16,
12034            a: *mut SaSint,
12035            n: SaSint,
12036            fs: SaSint,
12037            freq: *mut SaSint,
12038        ) -> SaSint;
12039        fn probe_public_libsais16_bwt_aux(
12040            t: *const u16,
12041            u: *mut u16,
12042            a: *mut SaSint,
12043            n: SaSint,
12044            fs: SaSint,
12045            r: SaSint,
12046            i: *mut SaSint,
12047        ) -> SaSint;
12048        fn probe_public_libsais16_bwt_aux_freq(
12049            t: *const u16,
12050            u: *mut u16,
12051            a: *mut SaSint,
12052            n: SaSint,
12053            fs: SaSint,
12054            freq: *mut SaSint,
12055            r: SaSint,
12056            i: *mut SaSint,
12057        ) -> SaSint;
12058        fn probe_public_libsais16_unbwt(
12059            t: *const u16,
12060            u: *mut u16,
12061            a: *mut SaSint,
12062            n: SaSint,
12063            i: SaSint,
12064        ) -> SaSint;
12065        fn probe_public_libsais16_unbwt_freq(
12066            t: *const u16,
12067            u: *mut u16,
12068            a: *mut SaSint,
12069            n: SaSint,
12070            freq: *const SaSint,
12071            i: SaSint,
12072        ) -> SaSint;
12073        fn probe_public_libsais16_unbwt_aux(
12074            t: *const u16,
12075            u: *mut u16,
12076            a: *mut SaSint,
12077            n: SaSint,
12078            r: SaSint,
12079            i: *const SaSint,
12080        ) -> SaSint;
12081        fn probe_public_libsais16_unbwt_aux_freq(
12082            t: *const u16,
12083            u: *mut u16,
12084            a: *mut SaSint,
12085            n: SaSint,
12086            freq: *const SaSint,
12087            r: SaSint,
12088            i: *const SaSint,
12089        ) -> SaSint;
12090        fn probe_public_libsais16_plcp(
12091            t: *const u16,
12092            sa: *const SaSint,
12093            plcp: *mut SaSint,
12094            n: SaSint,
12095        ) -> SaSint;
12096        fn probe_public_libsais16_plcp_gsa(
12097            t: *const u16,
12098            sa: *const SaSint,
12099            plcp: *mut SaSint,
12100            n: SaSint,
12101        ) -> SaSint;
12102        fn probe_public_libsais16_lcp(
12103            plcp: *const SaSint,
12104            sa: *const SaSint,
12105            lcp: *mut SaSint,
12106            n: SaSint,
12107        ) -> SaSint;
12108        fn probe_libsais16_gather_lms_suffixes_16u(
12109            t: *const u16,
12110            sa: *mut SaSint,
12111            n: SaSint,
12112            m: SaSint,
12113            omp_block_start: SaSint,
12114            omp_block_size: SaSint,
12115        );
12116        fn probe_libsais16_count_and_gather_lms_suffixes_16u(
12117            t: *const u16,
12118            sa: *mut SaSint,
12119            n: SaSint,
12120            buckets: *mut SaSint,
12121            omp_block_start: SaSint,
12122            omp_block_size: SaSint,
12123        ) -> SaSint;
12124        fn probe_libsais16_initialize_buckets_start_and_end_16u(
12125            buckets: *mut SaSint,
12126            freq: *mut SaSint,
12127        ) -> SaSint;
12128        fn probe_libsais16_initialize_buckets_for_lms_suffixes_radix_sort_16u(
12129            t: *const u16,
12130            buckets: *mut SaSint,
12131            first_lms_suffix: SaSint,
12132        ) -> SaSint;
12133        fn probe_libsais16_radix_sort_lms_suffixes_16u(
12134            t: *const u16,
12135            sa: *mut SaSint,
12136            induction_bucket: *mut SaSint,
12137            omp_block_start: SaSint,
12138            omp_block_size: SaSint,
12139        );
12140        fn probe_libsais16_initialize_buckets_for_partial_sorting_16u(
12141            t: *const u16,
12142            buckets: *mut SaSint,
12143            first_lms_suffix: SaSint,
12144            left_suffixes_count: SaSint,
12145        );
12146        fn probe_libsais16_partial_sorting_scan_left_to_right_16u(
12147            t: *const u16,
12148            sa: *mut SaSint,
12149            buckets: *mut SaSint,
12150            d: SaSint,
12151            omp_block_start: SaSint,
12152            omp_block_size: SaSint,
12153        ) -> SaSint;
12154        fn probe_libsais16_partial_sorting_scan_right_to_left_16u(
12155            t: *const u16,
12156            sa: *mut SaSint,
12157            buckets: *mut SaSint,
12158            d: SaSint,
12159            omp_block_start: SaSint,
12160            omp_block_size: SaSint,
12161        ) -> SaSint;
12162        fn probe_libsais16_partial_gsa_scan_right_to_left_16u(
12163            t: *const u16,
12164            sa: *mut SaSint,
12165            buckets: *mut SaSint,
12166            d: SaSint,
12167            omp_block_start: SaSint,
12168            omp_block_size: SaSint,
12169        ) -> SaSint;
12170        fn probe_libsais16_partial_sorting_shift_markers_16u_omp(
12171            sa: *mut SaSint,
12172            n: SaSint,
12173            buckets: *const SaSint,
12174            threads: SaSint,
12175        );
12176        fn probe_libsais16_final_sorting_scan_left_to_right_16u(
12177            t: *const u16,
12178            sa: *mut SaSint,
12179            induction_bucket: *mut SaSint,
12180            omp_block_start: SaSint,
12181            omp_block_size: SaSint,
12182        );
12183        fn probe_libsais16_final_sorting_scan_right_to_left_16u(
12184            t: *const u16,
12185            sa: *mut SaSint,
12186            induction_bucket: *mut SaSint,
12187            omp_block_start: SaSint,
12188            omp_block_size: SaSint,
12189        );
12190        fn probe_libsais16_final_gsa_scan_right_to_left_16u(
12191            t: *const u16,
12192            sa: *mut SaSint,
12193            induction_bucket: *mut SaSint,
12194            omp_block_start: SaSint,
12195            omp_block_size: SaSint,
12196        );
12197        fn probe_libsais16_final_bwt_scan_left_to_right_16u(
12198            t: *const u16,
12199            sa: *mut SaSint,
12200            induction_bucket: *mut SaSint,
12201            omp_block_start: SaSint,
12202            omp_block_size: SaSint,
12203        );
12204        fn probe_libsais16_final_bwt_scan_right_to_left_16u(
12205            t: *const u16,
12206            sa: *mut SaSint,
12207            induction_bucket: *mut SaSint,
12208            omp_block_start: SaSint,
12209            omp_block_size: SaSint,
12210        ) -> SaSint;
12211        fn probe_libsais16_final_bwt_aux_scan_left_to_right_16u(
12212            t: *const u16,
12213            sa: *mut SaSint,
12214            rm: SaSint,
12215            i_sample: *mut SaSint,
12216            induction_bucket: *mut SaSint,
12217            omp_block_start: SaSint,
12218            omp_block_size: SaSint,
12219        );
12220        fn probe_libsais16_final_bwt_aux_scan_right_to_left_16u(
12221            t: *const u16,
12222            sa: *mut SaSint,
12223            rm: SaSint,
12224            i_sample: *mut SaSint,
12225            induction_bucket: *mut SaSint,
12226            omp_block_start: SaSint,
12227            omp_block_size: SaSint,
12228        );
12229        fn probe_libsais16_renumber_lms_suffixes_16u(
12230            sa: *mut SaSint,
12231            m: SaSint,
12232            name: SaSint,
12233            omp_block_start: SaSint,
12234            omp_block_size: SaSint,
12235        ) -> SaSint;
12236        fn probe_libsais16_place_lms_suffixes_interval_16u(
12237            sa: *mut SaSint,
12238            n: SaSint,
12239            m: SaSint,
12240            flags: SaSint,
12241            buckets: *mut SaSint,
12242        );
12243        fn probe_libsais16_bwt_copy_16u(u: *mut u16, a: *mut SaSint, n: SaSint);
12244        fn probe_libsais16_gather_lms_suffixes_16u_omp(
12245            t: *const u16,
12246            sa: *mut SaSint,
12247            n: SaSint,
12248            threads: SaSint,
12249        );
12250        fn probe_libsais16_count_and_gather_lms_suffixes_16u_omp(
12251            t: *const u16,
12252            sa: *mut SaSint,
12253            n: SaSint,
12254            buckets: *mut SaSint,
12255            threads: SaSint,
12256        ) -> SaSint;
12257        fn probe_libsais16_radix_sort_lms_suffixes_16u_omp(
12258            t: *const u16,
12259            sa: *mut SaSint,
12260            n: SaSint,
12261            m: SaSint,
12262            flags: SaSint,
12263            buckets: *mut SaSint,
12264            threads: SaSint,
12265        );
12266        fn probe_libsais16_partial_sorting_scan_left_to_right_16u_omp(
12267            t: *const u16,
12268            sa: *mut SaSint,
12269            n: SaSint,
12270            k: SaSint,
12271            buckets: *mut SaSint,
12272            left_suffixes_count: SaSint,
12273            d: SaSint,
12274            threads: SaSint,
12275        ) -> SaSint;
12276        fn probe_libsais16_partial_sorting_scan_right_to_left_16u_omp(
12277            t: *const u16,
12278            sa: *mut SaSint,
12279            n: SaSint,
12280            k: SaSint,
12281            buckets: *mut SaSint,
12282            first_lms_suffix: SaSint,
12283            left_suffixes_count: SaSint,
12284            d: SaSint,
12285            threads: SaSint,
12286        );
12287        fn probe_libsais16_partial_gsa_scan_right_to_left_16u_omp(
12288            t: *const u16,
12289            sa: *mut SaSint,
12290            n: SaSint,
12291            k: SaSint,
12292            buckets: *mut SaSint,
12293            first_lms_suffix: SaSint,
12294            left_suffixes_count: SaSint,
12295            d: SaSint,
12296            threads: SaSint,
12297        );
12298        fn probe_libsais16_renumber_lms_suffixes_16u_omp(
12299            sa: *mut SaSint,
12300            m: SaSint,
12301            threads: SaSint,
12302        ) -> SaSint;
12303        fn probe_libsais16_final_bwt_scan_left_to_right_16u_omp(
12304            t: *const u16,
12305            sa: *mut SaSint,
12306            n: SaSint,
12307            k: SaSint,
12308            induction_bucket: *mut SaSint,
12309            threads: SaSint,
12310        );
12311        fn probe_libsais16_final_bwt_aux_scan_left_to_right_16u_omp(
12312            t: *const u16,
12313            sa: *mut SaSint,
12314            n: SaSint,
12315            k: SaSint,
12316            rm: SaSint,
12317            i_sample: *mut SaSint,
12318            induction_bucket: *mut SaSint,
12319            threads: SaSint,
12320        );
12321        fn probe_libsais16_final_sorting_scan_left_to_right_16u_omp(
12322            t: *const u16,
12323            sa: *mut SaSint,
12324            n: SaSint,
12325            k: SaSint,
12326            induction_bucket: *mut SaSint,
12327            threads: SaSint,
12328        );
12329        fn probe_libsais16_final_bwt_scan_right_to_left_16u_omp(
12330            t: *const u16,
12331            sa: *mut SaSint,
12332            n: SaSint,
12333            k: SaSint,
12334            induction_bucket: *mut SaSint,
12335            threads: SaSint,
12336        ) -> SaSint;
12337        fn probe_libsais16_final_bwt_aux_scan_right_to_left_16u_omp(
12338            t: *const u16,
12339            sa: *mut SaSint,
12340            n: SaSint,
12341            k: SaSint,
12342            rm: SaSint,
12343            i_sample: *mut SaSint,
12344            induction_bucket: *mut SaSint,
12345            threads: SaSint,
12346        );
12347        fn probe_libsais16_final_sorting_scan_right_to_left_16u_omp(
12348            t: *const u16,
12349            sa: *mut SaSint,
12350            omp_block_start: SaSint,
12351            omp_block_size: SaSint,
12352            k: SaSint,
12353            induction_bucket: *mut SaSint,
12354            threads: SaSint,
12355        );
12356        fn probe_libsais16_final_gsa_scan_right_to_left_16u_omp(
12357            t: *const u16,
12358            sa: *mut SaSint,
12359            omp_block_start: SaSint,
12360            omp_block_size: SaSint,
12361            k: SaSint,
12362            induction_bucket: *mut SaSint,
12363            threads: SaSint,
12364        );
12365        fn probe_libsais16_bwt_copy_16u_omp(
12366            u: *mut u16,
12367            a: *mut SaSint,
12368            n: SaSint,
12369            threads: SaSint,
12370        );
12371        fn probe_libsais16_gather_marked_lms_suffixes(
12372            sa: *mut SaSint,
12373            m: SaSint,
12374            l: SaSint,
12375            omp_block_start: SaSint,
12376            omp_block_size: SaSint,
12377        ) -> SaSint;
12378        fn probe_libsais16_gather_marked_lms_suffixes_omp(
12379            sa: *mut SaSint,
12380            n: SaSint,
12381            m: SaSint,
12382            fs: SaSint,
12383            threads: SaSint,
12384        );
12385        fn probe_libsais16_renumber_and_gather_lms_suffixes_omp(
12386            sa: *mut SaSint,
12387            n: SaSint,
12388            m: SaSint,
12389            fs: SaSint,
12390            threads: SaSint,
12391        ) -> SaSint;
12392        fn probe_libsais16_reconstruct_lms_suffixes(
12393            sa: *mut SaSint,
12394            n: SaSint,
12395            m: SaSint,
12396            omp_block_start: SaSint,
12397            omp_block_size: SaSint,
12398        );
12399        fn probe_libsais16_reconstruct_lms_suffixes_omp(
12400            sa: *mut SaSint,
12401            n: SaSint,
12402            m: SaSint,
12403            threads: SaSint,
12404        );
12405        fn probe_libsais16_renumber_distinct_lms_suffixes_32s_4k(
12406            sa: *mut SaSint,
12407            m: SaSint,
12408            name: SaSint,
12409            omp_block_start: SaSint,
12410            omp_block_size: SaSint,
12411        ) -> SaSint;
12412        fn probe_libsais16_mark_distinct_lms_suffixes_32s(
12413            sa: *mut SaSint,
12414            m: SaSint,
12415            omp_block_start: SaSint,
12416            omp_block_size: SaSint,
12417        );
12418        fn probe_libsais16_clamp_lms_suffixes_length_32s(
12419            sa: *mut SaSint,
12420            m: SaSint,
12421            omp_block_start: SaSint,
12422            omp_block_size: SaSint,
12423        );
12424        fn probe_libsais16_renumber_distinct_lms_suffixes_32s_4k_omp(
12425            sa: *mut SaSint,
12426            m: SaSint,
12427            threads: SaSint,
12428        ) -> SaSint;
12429        fn probe_libsais16_mark_distinct_lms_suffixes_32s_omp(
12430            sa: *mut SaSint,
12431            n: SaSint,
12432            m: SaSint,
12433            threads: SaSint,
12434        );
12435        fn probe_libsais16_clamp_lms_suffixes_length_32s_omp(
12436            sa: *mut SaSint,
12437            n: SaSint,
12438            m: SaSint,
12439            threads: SaSint,
12440        );
12441        fn probe_libsais16_renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
12442            sa: *mut SaSint,
12443            n: SaSint,
12444            m: SaSint,
12445            threads: SaSint,
12446        ) -> SaSint;
12447        fn probe_libsais16_renumber_unique_and_nonunique_lms_suffixes_32s(
12448            t: *mut SaSint,
12449            sa: *mut SaSint,
12450            m: SaSint,
12451            f: SaSint,
12452            omp_block_start: SaSint,
12453            omp_block_size: SaSint,
12454        ) -> SaSint;
12455        fn probe_libsais16_compact_unique_and_nonunique_lms_suffixes_32s(
12456            sa: *mut SaSint,
12457            m: SaSint,
12458            pl: *mut SaSint,
12459            pr: *mut SaSint,
12460            omp_block_start: SaSint,
12461            omp_block_size: SaSint,
12462        );
12463        fn probe_libsais16_renumber_unique_and_nonunique_lms_suffixes_32s_omp(
12464            t: *mut SaSint,
12465            sa: *mut SaSint,
12466            m: SaSint,
12467            threads: SaSint,
12468        ) -> SaSint;
12469        fn probe_libsais16_compact_unique_and_nonunique_lms_suffixes_32s_omp(
12470            sa: *mut SaSint,
12471            n: SaSint,
12472            m: SaSint,
12473            fs: SaSint,
12474            f: SaSint,
12475            threads: SaSint,
12476        );
12477        fn probe_libsais16_compact_lms_suffixes_32s_omp(
12478            t: *mut SaSint,
12479            sa: *mut SaSint,
12480            n: SaSint,
12481            m: SaSint,
12482            fs: SaSint,
12483            threads: SaSint,
12484        ) -> SaSint;
12485        fn probe_libsais16_merge_unique_lms_suffixes_32s(
12486            t: *mut SaSint,
12487            sa: *mut SaSint,
12488            n: SaSint,
12489            m: SaSint,
12490            l: SaSint,
12491            omp_block_start: SaSint,
12492            omp_block_size: SaSint,
12493        );
12494        fn probe_libsais16_merge_nonunique_lms_suffixes_32s(
12495            sa: *mut SaSint,
12496            n: SaSint,
12497            m: SaSint,
12498            l: SaSint,
12499            omp_block_start: SaSint,
12500            omp_block_size: SaSint,
12501        );
12502        fn probe_libsais16_merge_unique_lms_suffixes_32s_omp(
12503            t: *mut SaSint,
12504            sa: *mut SaSint,
12505            n: SaSint,
12506            m: SaSint,
12507            threads: SaSint,
12508        );
12509        fn probe_libsais16_merge_nonunique_lms_suffixes_32s_omp(
12510            sa: *mut SaSint,
12511            n: SaSint,
12512            m: SaSint,
12513            f: SaSint,
12514            threads: SaSint,
12515        );
12516        fn probe_libsais16_merge_compacted_lms_suffixes_32s_omp(
12517            t: *mut SaSint,
12518            sa: *mut SaSint,
12519            n: SaSint,
12520            m: SaSint,
12521            f: SaSint,
12522            threads: SaSint,
12523        );
12524        fn probe_libsais16_radix_sort_lms_suffixes_32s_6k(
12525            t: *const SaSint,
12526            sa: *mut SaSint,
12527            induction_bucket: *mut SaSint,
12528            omp_block_start: SaSint,
12529            omp_block_size: SaSint,
12530        );
12531        fn probe_libsais16_radix_sort_lms_suffixes_32s_2k(
12532            t: *const SaSint,
12533            sa: *mut SaSint,
12534            induction_bucket: *mut SaSint,
12535            omp_block_start: SaSint,
12536            omp_block_size: SaSint,
12537        );
12538        fn probe_libsais16_radix_sort_lms_suffixes_32s_6k_omp(
12539            t: *const SaSint,
12540            sa: *mut SaSint,
12541            n: SaSint,
12542            m: SaSint,
12543            induction_bucket: *mut SaSint,
12544            threads: SaSint,
12545        );
12546        fn probe_libsais16_radix_sort_lms_suffixes_32s_2k_omp(
12547            t: *const SaSint,
12548            sa: *mut SaSint,
12549            n: SaSint,
12550            m: SaSint,
12551            induction_bucket: *mut SaSint,
12552            threads: SaSint,
12553        );
12554        fn probe_libsais16_radix_sort_lms_suffixes_32s_1k(
12555            t: *const SaSint,
12556            sa: *mut SaSint,
12557            n: SaSint,
12558            buckets: *mut SaSint,
12559        ) -> SaSint;
12560        fn probe_libsais16_radix_sort_set_markers_32s_6k(
12561            sa: *mut SaSint,
12562            induction_bucket: *mut SaSint,
12563            omp_block_start: SaSint,
12564            omp_block_size: SaSint,
12565        );
12566        fn probe_libsais16_radix_sort_set_markers_32s_4k(
12567            sa: *mut SaSint,
12568            induction_bucket: *mut SaSint,
12569            omp_block_start: SaSint,
12570            omp_block_size: SaSint,
12571        );
12572        fn probe_libsais16_radix_sort_set_markers_32s_6k_omp(
12573            sa: *mut SaSint,
12574            k: SaSint,
12575            induction_bucket: *mut SaSint,
12576            threads: SaSint,
12577        );
12578        fn probe_libsais16_radix_sort_set_markers_32s_4k_omp(
12579            sa: *mut SaSint,
12580            k: SaSint,
12581            induction_bucket: *mut SaSint,
12582            threads: SaSint,
12583        );
12584        fn probe_libsais16_place_lms_suffixes_histogram_32s_6k(
12585            sa: *mut SaSint,
12586            n: SaSint,
12587            k: SaSint,
12588            m: SaSint,
12589            buckets: *const SaSint,
12590        );
12591        fn probe_libsais16_place_lms_suffixes_histogram_32s_4k(
12592            sa: *mut SaSint,
12593            n: SaSint,
12594            k: SaSint,
12595            m: SaSint,
12596            buckets: *const SaSint,
12597        );
12598        fn probe_libsais16_place_lms_suffixes_histogram_32s_2k(
12599            sa: *mut SaSint,
12600            n: SaSint,
12601            k: SaSint,
12602            m: SaSint,
12603            buckets: *const SaSint,
12604        );
12605        fn probe_libsais16_gather_lms_suffixes_32s(
12606            t: *const SaSint,
12607            sa: *mut SaSint,
12608            n: SaSint,
12609        ) -> SaSint;
12610        fn probe_libsais16_gather_compacted_lms_suffixes_32s(
12611            t: *const SaSint,
12612            sa: *mut SaSint,
12613            n: SaSint,
12614        ) -> SaSint;
12615        fn probe_libsais16_count_lms_suffixes_32s_2k(
12616            t: *const SaSint,
12617            n: SaSint,
12618            k: SaSint,
12619            buckets: *mut SaSint,
12620        );
12621        fn probe_libsais16_count_and_gather_lms_suffixes_32s_4k(
12622            t: *const SaSint,
12623            sa: *mut SaSint,
12624            n: SaSint,
12625            k: SaSint,
12626            buckets: *mut SaSint,
12627            omp_block_start: SaSint,
12628            omp_block_size: SaSint,
12629        ) -> SaSint;
12630        fn probe_libsais16_count_and_gather_lms_suffixes_32s_4k_omp(
12631            t: *const SaSint,
12632            sa: *mut SaSint,
12633            n: SaSint,
12634            k: SaSint,
12635            buckets: *mut SaSint,
12636            local_buckets: SaSint,
12637            threads: SaSint,
12638        ) -> SaSint;
12639        fn probe_libsais16_count_suffixes_32s(
12640            t: *const SaSint,
12641            n: SaSint,
12642            k: SaSint,
12643            buckets: *mut SaSint,
12644        );
12645        fn probe_libsais16_initialize_buckets_start_and_end_32s_6k(k: SaSint, buckets: *mut SaSint);
12646        fn probe_libsais16_initialize_buckets_start_and_end_32s_4k(k: SaSint, buckets: *mut SaSint);
12647        fn probe_libsais16_initialize_buckets_end_32s_2k(k: SaSint, buckets: *mut SaSint);
12648        fn probe_libsais16_initialize_buckets_start_and_end_32s_2k(k: SaSint, buckets: *mut SaSint);
12649        fn probe_libsais16_initialize_buckets_start_32s_1k(k: SaSint, buckets: *mut SaSint);
12650        fn probe_libsais16_initialize_buckets_end_32s_1k(k: SaSint, buckets: *mut SaSint);
12651        fn probe_libsais16_initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
12652            t: *const SaSint,
12653            k: SaSint,
12654            buckets: *mut SaSint,
12655            first_lms_suffix: SaSint,
12656        );
12657        fn probe_libsais16_initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
12658            t: *const SaSint,
12659            k: SaSint,
12660            buckets: *mut SaSint,
12661            first_lms_suffix: SaSint,
12662        ) -> SaSint;
12663        fn probe_libsais16_initialize_buckets_for_radix_and_partial_sorting_32s_4k(
12664            t: *const SaSint,
12665            k: SaSint,
12666            buckets: *mut SaSint,
12667            first_lms_suffix: SaSint,
12668        );
12669        fn probe_libsais16_place_lms_suffixes_interval_32s_4k(
12670            sa: *mut SaSint,
12671            n: SaSint,
12672            k: SaSint,
12673            m: SaSint,
12674            buckets: *const SaSint,
12675        );
12676        fn probe_libsais16_place_lms_suffixes_interval_32s_2k(
12677            sa: *mut SaSint,
12678            n: SaSint,
12679            k: SaSint,
12680            m: SaSint,
12681            buckets: *const SaSint,
12682        );
12683        fn probe_libsais16_place_lms_suffixes_interval_32s_1k(
12684            t: *const SaSint,
12685            sa: *mut SaSint,
12686            k: SaSint,
12687            m: SaSint,
12688            buckets: *mut SaSint,
12689        );
12690        fn probe_libsais16_renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(
12691            t: *mut SaSint,
12692            sa: *mut SaSint,
12693            n: SaSint,
12694            m: SaSint,
12695            threads: SaSint,
12696        ) -> SaSint;
12697        fn probe_libsais16_partial_sorting_shift_markers_32s_6k_omp(
12698            sa: *mut SaSint,
12699            k: SaSint,
12700            buckets: *const SaSint,
12701            threads: SaSint,
12702        );
12703        fn probe_libsais16_partial_sorting_shift_markers_32s_4k(sa: *mut SaSint, n: SaSint);
12704        fn probe_libsais16_partial_sorting_shift_buckets_32s_6k(k: SaSint, buckets: *mut SaSint);
12705        fn probe_libsais16_partial_sorting_scan_left_to_right_32s_6k(
12706            t: *const SaSint,
12707            sa: *mut SaSint,
12708            buckets: *mut SaSint,
12709            d: SaSint,
12710            omp_block_start: SaSint,
12711            omp_block_size: SaSint,
12712        ) -> SaSint;
12713        fn probe_libsais16_partial_sorting_scan_left_to_right_32s_4k(
12714            t: *const SaSint,
12715            sa: *mut SaSint,
12716            k: SaSint,
12717            buckets: *mut SaSint,
12718            d: SaSint,
12719            omp_block_start: SaSint,
12720            omp_block_size: SaSint,
12721        ) -> SaSint;
12722        fn probe_libsais16_partial_sorting_scan_left_to_right_32s_1k(
12723            t: *const SaSint,
12724            sa: *mut SaSint,
12725            buckets: *mut SaSint,
12726            omp_block_start: SaSint,
12727            omp_block_size: SaSint,
12728        );
12729        fn probe_libsais16_partial_sorting_scan_left_to_right_32s_6k_omp(
12730            t: *const SaSint,
12731            sa: *mut SaSint,
12732            n: SaSint,
12733            buckets: *mut SaSint,
12734            left_suffixes_count: SaSint,
12735            d: SaSint,
12736            threads: SaSint,
12737        ) -> SaSint;
12738        fn probe_libsais16_partial_sorting_scan_left_to_right_32s_4k_omp(
12739            t: *const SaSint,
12740            sa: *mut SaSint,
12741            n: SaSint,
12742            k: SaSint,
12743            buckets: *mut SaSint,
12744            d: SaSint,
12745            threads: SaSint,
12746        ) -> SaSint;
12747        fn probe_libsais16_partial_sorting_scan_left_to_right_32s_1k_omp(
12748            t: *const SaSint,
12749            sa: *mut SaSint,
12750            n: SaSint,
12751            buckets: *mut SaSint,
12752            threads: SaSint,
12753        );
12754        fn probe_libsais16_partial_sorting_scan_right_to_left_32s_6k(
12755            t: *const SaSint,
12756            sa: *mut SaSint,
12757            buckets: *mut SaSint,
12758            d: SaSint,
12759            omp_block_start: SaSint,
12760            omp_block_size: SaSint,
12761        ) -> SaSint;
12762        fn probe_libsais16_partial_sorting_scan_right_to_left_32s_4k(
12763            t: *const SaSint,
12764            sa: *mut SaSint,
12765            k: SaSint,
12766            buckets: *mut SaSint,
12767            d: SaSint,
12768            omp_block_start: SaSint,
12769            omp_block_size: SaSint,
12770        ) -> SaSint;
12771        fn probe_libsais16_partial_sorting_scan_right_to_left_32s_1k(
12772            t: *const SaSint,
12773            sa: *mut SaSint,
12774            buckets: *mut SaSint,
12775            omp_block_start: SaSint,
12776            omp_block_size: SaSint,
12777        );
12778        fn probe_libsais16_partial_sorting_scan_right_to_left_32s_6k_omp(
12779            t: *const SaSint,
12780            sa: *mut SaSint,
12781            n: SaSint,
12782            buckets: *mut SaSint,
12783            first_lms_suffix: SaSint,
12784            left_suffixes_count: SaSint,
12785            d: SaSint,
12786            threads: SaSint,
12787        ) -> SaSint;
12788        fn probe_libsais16_partial_sorting_scan_right_to_left_32s_4k_omp(
12789            t: *const SaSint,
12790            sa: *mut SaSint,
12791            n: SaSint,
12792            k: SaSint,
12793            buckets: *mut SaSint,
12794            d: SaSint,
12795            threads: SaSint,
12796        ) -> SaSint;
12797        fn probe_libsais16_partial_sorting_scan_right_to_left_32s_1k_omp(
12798            t: *const SaSint,
12799            sa: *mut SaSint,
12800            n: SaSint,
12801            buckets: *mut SaSint,
12802            threads: SaSint,
12803        );
12804        fn probe_libsais16_partial_sorting_gather_lms_suffixes_32s_4k(
12805            sa: *mut SaSint,
12806            omp_block_start: SaSint,
12807            omp_block_size: SaSint,
12808        ) -> SaSint;
12809        fn probe_libsais16_partial_sorting_gather_lms_suffixes_32s_1k(
12810            sa: *mut SaSint,
12811            omp_block_start: SaSint,
12812            omp_block_size: SaSint,
12813        ) -> SaSint;
12814        fn probe_libsais16_partial_sorting_gather_lms_suffixes_32s_4k_omp(
12815            sa: *mut SaSint,
12816            n: SaSint,
12817            threads: SaSint,
12818        );
12819        fn probe_libsais16_partial_sorting_gather_lms_suffixes_32s_1k_omp(
12820            sa: *mut SaSint,
12821            n: SaSint,
12822            threads: SaSint,
12823        );
12824        fn probe_libsais16_count_and_gather_lms_suffixes_32s_2k(
12825            t: *const SaSint,
12826            sa: *mut SaSint,
12827            n: SaSint,
12828            k: SaSint,
12829            buckets: *mut SaSint,
12830            omp_block_start: SaSint,
12831            omp_block_size: SaSint,
12832        ) -> SaSint;
12833        fn probe_libsais16_count_and_gather_compacted_lms_suffixes_32s_2k(
12834            t: *const SaSint,
12835            sa: *mut SaSint,
12836            n: SaSint,
12837            k: SaSint,
12838            buckets: *mut SaSint,
12839            omp_block_start: SaSint,
12840            omp_block_size: SaSint,
12841        ) -> SaSint;
12842        fn probe_libsais16_count_and_gather_lms_suffixes_32s_2k_omp(
12843            t: *const SaSint,
12844            sa: *mut SaSint,
12845            n: SaSint,
12846            k: SaSint,
12847            buckets: *mut SaSint,
12848            local_buckets: SaSint,
12849            threads: SaSint,
12850        ) -> SaSint;
12851        fn probe_libsais16_count_and_gather_compacted_lms_suffixes_32s_2k_omp(
12852            t: *const SaSint,
12853            sa: *mut SaSint,
12854            n: SaSint,
12855            k: SaSint,
12856            buckets: *mut SaSint,
12857            local_buckets: SaSint,
12858            threads: SaSint,
12859        );
12860        fn probe_libsais16_reconstruct_compacted_lms_suffixes_32s_2k_omp(
12861            t: *mut SaSint,
12862            sa: *mut SaSint,
12863            n: SaSint,
12864            k: SaSint,
12865            m: SaSint,
12866            fs: SaSint,
12867            f: SaSint,
12868            buckets: *mut SaSint,
12869            local_buckets: SaSint,
12870            threads: SaSint,
12871        );
12872        fn probe_libsais16_reconstruct_compacted_lms_suffixes_32s_1k_omp(
12873            t: *mut SaSint,
12874            sa: *mut SaSint,
12875            n: SaSint,
12876            m: SaSint,
12877            fs: SaSint,
12878            f: SaSint,
12879            threads: SaSint,
12880        );
12881    }
12882
12883    fn brute_sa(t: &[u16]) -> Vec<SaSint> {
12884        let mut sa: Vec<_> = (0..t.len() as SaSint).collect();
12885        sa.sort_by(|&a, &b| t[a as usize..].cmp(&t[b as usize..]));
12886        sa
12887    }
12888
12889    #[test]
12890    fn libsais16_gather_lms_suffixes_16u_matches_c() {
12891        let cases: &[&[u16]] = &[
12892            &[2, 1, 3, 1, 2, 0],
12893            &[7, 7, 7, 7, 0],
12894            &[3, 1, 2, 1, 0, 4, 1, 0],
12895            &[9, 1, 9, 1, 9, 0, 2, 2, 0],
12896        ];
12897
12898        for &text in cases {
12899            let n = text.len() as SaSint;
12900            let mut rust_sa = vec![-99; text.len()];
12901            let mut c_sa = rust_sa.clone();
12902
12903            gather_lms_suffixes_16u(text, &mut rust_sa, n, n - 1, 0, n);
12904            unsafe {
12905                probe_libsais16_gather_lms_suffixes_16u(
12906                    text.as_ptr(),
12907                    c_sa.as_mut_ptr(),
12908                    n,
12909                    n - 1,
12910                    0,
12911                    n,
12912                );
12913            }
12914
12915            assert_eq!(rust_sa, c_sa);
12916        }
12917    }
12918
12919    #[test]
12920    fn libsais16_count_and_gather_lms_suffixes_16u_matches_c() {
12921        let cases: &[&[u16]] = &[
12922            &[2, 1, 3, 1, 2, 0],
12923            &[7, 7, 7, 7, 0],
12924            &[3, 1, 2, 1, 0, 4, 1, 0],
12925            &[9, 1, 9, 1, 9, 0, 2, 2, 0],
12926        ];
12927
12928        for &text in cases {
12929            let n = text.len() as SaSint;
12930            let mut rust_sa = vec![-99; text.len()];
12931            let mut c_sa = rust_sa.clone();
12932            let mut rust_buckets = vec![-1; 4 * ALPHABET_SIZE];
12933            let mut c_buckets = rust_buckets.clone();
12934
12935            let rust_m =
12936                count_and_gather_lms_suffixes_16u(text, &mut rust_sa, n, &mut rust_buckets, 0, n);
12937            let c_m = unsafe {
12938                probe_libsais16_count_and_gather_lms_suffixes_16u(
12939                    text.as_ptr(),
12940                    c_sa.as_mut_ptr(),
12941                    n,
12942                    c_buckets.as_mut_ptr(),
12943                    0,
12944                    n,
12945                )
12946            };
12947
12948            assert_eq!(rust_m, c_m);
12949            assert_eq!(rust_sa, c_sa);
12950            assert_eq!(rust_buckets, c_buckets);
12951        }
12952    }
12953
12954    #[test]
12955    fn libsais16_initialize_buckets_start_and_end_16u_matches_c() {
12956        let mut rust_buckets = vec![0; 8 * ALPHABET_SIZE];
12957        for (symbol, counts) in [
12958            (0usize, [1, 0, 0, 2]),
12959            (1, [0, 3, 1, 0]),
12960            (7, [2, 1, 0, 1]),
12961            (1024, [0, 0, 5, 0]),
12962        ] {
12963            for state in 0..4 {
12964                rust_buckets[buckets_index4(symbol, state)] = counts[state];
12965            }
12966        }
12967        let mut c_buckets = rust_buckets.clone();
12968        let mut rust_freq = vec![-1; ALPHABET_SIZE];
12969        let mut c_freq = rust_freq.clone();
12970
12971        let rust_k = initialize_buckets_start_and_end_16u(&mut rust_buckets, Some(&mut rust_freq));
12972        let c_k = unsafe {
12973            probe_libsais16_initialize_buckets_start_and_end_16u(
12974                c_buckets.as_mut_ptr(),
12975                c_freq.as_mut_ptr(),
12976            )
12977        };
12978
12979        assert_eq!(rust_k, c_k);
12980        assert_eq!(rust_buckets, c_buckets);
12981        assert_eq!(rust_freq, c_freq);
12982
12983        let mut rust_buckets_no_freq = vec![0; 8 * ALPHABET_SIZE];
12984        rust_buckets_no_freq[..4 * ALPHABET_SIZE]
12985            .copy_from_slice(&rust_buckets[..4 * ALPHABET_SIZE]);
12986        let mut c_buckets_no_freq = rust_buckets_no_freq.clone();
12987
12988        let rust_k = initialize_buckets_start_and_end_16u(&mut rust_buckets_no_freq, None);
12989        let c_k = unsafe {
12990            probe_libsais16_initialize_buckets_start_and_end_16u(
12991                c_buckets_no_freq.as_mut_ptr(),
12992                std::ptr::null_mut(),
12993            )
12994        };
12995
12996        assert_eq!(rust_k, c_k);
12997        assert_eq!(rust_buckets_no_freq, c_buckets_no_freq);
12998    }
12999
13000    #[test]
13001    fn libsais16_lms_radix_bucket_initialization_matches_c() {
13002        let text = [3, 1, 2, 1, 0, 4, 1, 0];
13003        let n = text.len() as SaSint;
13004        let mut rust_sa = vec![-99; text.len()];
13005        let mut rust_buckets = vec![0; 8 * ALPHABET_SIZE];
13006        let m = count_and_gather_lms_suffixes_16u(
13007            &text,
13008            &mut rust_sa,
13009            n,
13010            &mut rust_buckets[..4 * ALPHABET_SIZE],
13011            0,
13012            n,
13013        );
13014        initialize_buckets_start_and_end_16u(&mut rust_buckets, None);
13015        let first_lms_suffix = rust_sa[(n - m) as usize];
13016
13017        let mut c_buckets = rust_buckets.clone();
13018        let rust_count = initialize_buckets_for_lms_suffixes_radix_sort_16u(
13019            &text,
13020            &mut rust_buckets,
13021            first_lms_suffix,
13022        );
13023        let c_count = unsafe {
13024            probe_libsais16_initialize_buckets_for_lms_suffixes_radix_sort_16u(
13025                text.as_ptr(),
13026                c_buckets.as_mut_ptr(),
13027                first_lms_suffix,
13028            )
13029        };
13030
13031        assert_eq!(rust_count, c_count);
13032        assert_eq!(rust_buckets, c_buckets);
13033    }
13034
13035    #[test]
13036    fn libsais16_radix_sort_lms_suffixes_16u_matches_c() {
13037        let text = [3, 1, 2, 1, 0, 4, 1, 0];
13038        let n = text.len() as SaSint;
13039        let mut rust_sa = vec![-99; text.len()];
13040        let mut rust_buckets = vec![0; 8 * ALPHABET_SIZE];
13041        let m = count_and_gather_lms_suffixes_16u(
13042            &text,
13043            &mut rust_sa,
13044            n,
13045            &mut rust_buckets[..4 * ALPHABET_SIZE],
13046            0,
13047            n,
13048        );
13049        initialize_buckets_start_and_end_16u(&mut rust_buckets, None);
13050        let first_lms_suffix = rust_sa[(n - m) as usize];
13051        initialize_buckets_for_lms_suffixes_radix_sort_16u(
13052            &text,
13053            &mut rust_buckets,
13054            first_lms_suffix,
13055        );
13056
13057        let mut c_sa = rust_sa.clone();
13058        let mut c_buckets = rust_buckets.clone();
13059        {
13060            let induction_bucket = &mut rust_buckets[4 * ALPHABET_SIZE..];
13061            radix_sort_lms_suffixes_16u(&text, &mut rust_sa, induction_bucket, n - m + 1, m - 1);
13062        }
13063        unsafe {
13064            probe_libsais16_radix_sort_lms_suffixes_16u(
13065                text.as_ptr(),
13066                c_sa.as_mut_ptr(),
13067                c_buckets[4 * ALPHABET_SIZE..].as_mut_ptr(),
13068                n - m + 1,
13069                m - 1,
13070            );
13071        }
13072
13073        assert_eq!(rust_sa, c_sa);
13074        assert_eq!(rust_buckets, c_buckets);
13075    }
13076
13077    #[test]
13078    fn libsais16_initialize_buckets_for_partial_sorting_16u_matches_c() {
13079        let text = [3, 1, 2, 1, 0, 4, 1, 0];
13080        let n = text.len() as SaSint;
13081        let mut rust_sa = vec![-99; text.len()];
13082        let mut rust_buckets = vec![0; 8 * ALPHABET_SIZE];
13083        let m = count_and_gather_lms_suffixes_16u(
13084            &text,
13085            &mut rust_sa,
13086            n,
13087            &mut rust_buckets[..4 * ALPHABET_SIZE],
13088            0,
13089            n,
13090        );
13091        initialize_buckets_start_and_end_16u(&mut rust_buckets, None);
13092        let first_lms_suffix = rust_sa[(n - m) as usize];
13093        let left_suffixes_count = initialize_buckets_for_lms_suffixes_radix_sort_16u(
13094            &text,
13095            &mut rust_buckets,
13096            first_lms_suffix,
13097        );
13098        let mut c_buckets = rust_buckets.clone();
13099
13100        initialize_buckets_for_partial_sorting_16u(
13101            &text,
13102            &mut rust_buckets,
13103            first_lms_suffix,
13104            left_suffixes_count,
13105        );
13106        unsafe {
13107            probe_libsais16_initialize_buckets_for_partial_sorting_16u(
13108                text.as_ptr(),
13109                c_buckets.as_mut_ptr(),
13110                first_lms_suffix,
13111                left_suffixes_count,
13112            );
13113        }
13114
13115        assert_eq!(rust_buckets, c_buckets);
13116    }
13117
13118    fn partial_scan_fixture() -> ([u16; 10], Vec<SaSint>, Vec<SaSint>) {
13119        let text = [1, 0, 2, 1, 3, 0, 2, 4, 1, 0];
13120        let mut sa = vec![0; 128];
13121        sa[..5].copy_from_slice(&[3, 5 | SAINT_MIN, 7, 2, 9 | SAINT_MIN]);
13122
13123        let mut buckets = vec![0; 6 * ALPHABET_SIZE];
13124        for v in 0..32 {
13125            buckets[v] = 80 + (v as SaSint) * 4;
13126            buckets[2 * ALPHABET_SIZE + v] = if v % 3 == 0 { 2 } else { 0 };
13127            buckets[4 * ALPHABET_SIZE + v] = 20 + (v as SaSint) * 4;
13128        }
13129
13130        (text, sa, buckets)
13131    }
13132
13133    #[test]
13134    fn libsais16_partial_sorting_scan_left_to_right_16u_matches_c() {
13135        let (text, mut rust_sa, mut rust_buckets) = partial_scan_fixture();
13136        let mut c_sa = rust_sa.clone();
13137        let mut c_buckets = rust_buckets.clone();
13138
13139        let rust_d =
13140            partial_sorting_scan_left_to_right_16u(&text, &mut rust_sa, &mut rust_buckets, 3, 0, 5);
13141        let c_d = unsafe {
13142            probe_libsais16_partial_sorting_scan_left_to_right_16u(
13143                text.as_ptr(),
13144                c_sa.as_mut_ptr(),
13145                c_buckets.as_mut_ptr(),
13146                3,
13147                0,
13148                5,
13149            )
13150        };
13151
13152        assert_eq!(rust_d, c_d);
13153        assert_eq!(rust_sa, c_sa);
13154        assert_eq!(rust_buckets, c_buckets);
13155    }
13156
13157    #[test]
13158    fn libsais16_partial_sorting_scan_right_to_left_16u_matches_c() {
13159        let (text, mut rust_sa, mut rust_buckets) = partial_scan_fixture();
13160        let mut c_sa = rust_sa.clone();
13161        let mut c_buckets = rust_buckets.clone();
13162
13163        let rust_d =
13164            partial_sorting_scan_right_to_left_16u(&text, &mut rust_sa, &mut rust_buckets, 3, 0, 5);
13165        let c_d = unsafe {
13166            probe_libsais16_partial_sorting_scan_right_to_left_16u(
13167                text.as_ptr(),
13168                c_sa.as_mut_ptr(),
13169                c_buckets.as_mut_ptr(),
13170                3,
13171                0,
13172                5,
13173            )
13174        };
13175
13176        assert_eq!(rust_d, c_d);
13177        assert_eq!(rust_sa, c_sa);
13178        assert_eq!(rust_buckets, c_buckets);
13179    }
13180
13181    #[test]
13182    fn libsais16_partial_gsa_scan_right_to_left_16u_matches_c() {
13183        let (text, mut rust_sa, mut rust_buckets) = partial_scan_fixture();
13184        let mut c_sa = rust_sa.clone();
13185        let mut c_buckets = rust_buckets.clone();
13186
13187        let rust_d =
13188            partial_gsa_scan_right_to_left_16u(&text, &mut rust_sa, &mut rust_buckets, 3, 0, 5);
13189        let c_d = unsafe {
13190            probe_libsais16_partial_gsa_scan_right_to_left_16u(
13191                text.as_ptr(),
13192                c_sa.as_mut_ptr(),
13193                c_buckets.as_mut_ptr(),
13194                3,
13195                0,
13196                5,
13197            )
13198        };
13199
13200        assert_eq!(rust_d, c_d);
13201        assert_eq!(rust_sa, c_sa);
13202        assert_eq!(rust_buckets, c_buckets);
13203    }
13204
13205    #[test]
13206    fn libsais16_partial_sorting_shift_markers_16u_matches_c() {
13207        let mut rust_sa = vec![0; 16];
13208        rust_sa[2..6].copy_from_slice(&[1, 2 | SAINT_MIN, 3 | SAINT_MIN, 4]);
13209        rust_sa[8..12].copy_from_slice(&[5 | SAINT_MIN, 6, 7 | SAINT_MIN, 8]);
13210        let mut c_sa = rust_sa.clone();
13211
13212        let mut buckets = vec![0; 6 * ALPHABET_SIZE];
13213        buckets[0] = 2;
13214        buckets[2] = 8;
13215        buckets[4 * ALPHABET_SIZE + 2] = 6;
13216        buckets[4 * ALPHABET_SIZE + 4] = 12;
13217
13218        let n = rust_sa.len() as SaSint;
13219        partial_sorting_shift_markers_16u_omp(&mut rust_sa, n, &buckets, 1);
13220        unsafe {
13221            probe_libsais16_partial_sorting_shift_markers_16u_omp(
13222                c_sa.as_mut_ptr(),
13223                c_sa.len() as SaSint,
13224                buckets.as_ptr(),
13225                1,
13226            );
13227        }
13228
13229        assert_eq!(rust_sa, c_sa);
13230    }
13231
13232    #[test]
13233    fn libsais16_partial_left_to_right_16u_block_omp_uses_cache_pipeline() {
13234        let block_size = 65_536usize;
13235        let k = 512usize;
13236        let text: Vec<u16> = (0..block_size + 2)
13237            .map(|i| 1 + ((i * 17 + i / 7) % (k - 1)) as u16)
13238            .collect();
13239        let sa_len = block_size + 2 * k * 100;
13240        let mut base_sa = vec![0; sa_len];
13241        for (i, slot) in base_sa.iter_mut().take(block_size).enumerate() {
13242            *slot = (i + 2) as SaSint;
13243        }
13244        let mut base_buckets = vec![0; 8 * ALPHABET_SIZE];
13245        for v in 0..2 * k {
13246            base_buckets[4 * ALPHABET_SIZE + v] = (block_size + v * 100) as SaSint;
13247        }
13248
13249        let mut scalar_sa = base_sa.clone();
13250        let mut threaded_sa = base_sa;
13251        let mut scalar_buckets = base_buckets.clone();
13252        let mut threaded_buckets = base_buckets;
13253        let mut thread_state = alloc_thread_state(4).unwrap();
13254        let scalar_d = partial_sorting_scan_left_to_right_16u(
13255            &text,
13256            &mut scalar_sa,
13257            &mut scalar_buckets,
13258            0,
13259            0,
13260            block_size as SaSint,
13261        );
13262        let threaded_d = partial_sorting_scan_left_to_right_16u_block_omp(
13263            &text,
13264            &mut threaded_sa,
13265            k as SaSint,
13266            &mut threaded_buckets,
13267            0,
13268            0,
13269            block_size as SaSint,
13270            4,
13271            &mut thread_state,
13272        );
13273
13274        assert_eq!(threaded_d, scalar_d);
13275        assert_eq!(threaded_sa, scalar_sa);
13276        assert_eq!(threaded_buckets, scalar_buckets);
13277    }
13278
13279    #[test]
13280    fn libsais16_partial_left_to_right_16u_omp_uses_block_pipeline() {
13281        let block_size = 65_536usize;
13282        let k = 512usize;
13283        let text: Vec<u16> = (0..block_size + 2)
13284            .map(|i| 1 + ((i * 17 + i / 7) % (k - 1)) as u16)
13285            .collect();
13286        let sa_len = block_size + 2 * k * 100;
13287        let mut base_sa = vec![0; sa_len];
13288        for (i, slot) in base_sa.iter_mut().take(block_size).enumerate() {
13289            let value = (i + 2) as SaSint;
13290            *slot = if i % 17 == 0 {
13291                value | SAINT_MIN
13292            } else {
13293                value
13294            };
13295        }
13296        let mut base_buckets = vec![0; 8 * ALPHABET_SIZE];
13297        for v in 0..2 * k {
13298            base_buckets[4 * ALPHABET_SIZE + v] = (block_size + v * 100) as SaSint;
13299            base_buckets[2 * ALPHABET_SIZE + v] = if v % 5 == 0 { 3 } else { 0 };
13300        }
13301
13302        let mut scalar_sa = base_sa.clone();
13303        let mut threaded_sa = base_sa;
13304        let mut scalar_buckets = base_buckets.clone();
13305        let mut threaded_buckets = base_buckets;
13306        let scalar_d = partial_sorting_scan_left_to_right_16u_omp(
13307            &text,
13308            &mut scalar_sa,
13309            text.len() as SaSint,
13310            k as SaSint,
13311            &mut scalar_buckets,
13312            block_size as SaSint,
13313            7,
13314            1,
13315        );
13316        let threaded_d = partial_sorting_scan_left_to_right_16u_omp(
13317            &text,
13318            &mut threaded_sa,
13319            text.len() as SaSint,
13320            k as SaSint,
13321            &mut threaded_buckets,
13322            block_size as SaSint,
13323            7,
13324            4,
13325        );
13326
13327        assert_eq!(threaded_d, scalar_d);
13328        assert_eq!(threaded_sa, scalar_sa);
13329        assert_eq!(threaded_buckets, scalar_buckets);
13330    }
13331
13332    #[test]
13333    fn libsais16_partial_right_to_left_16u_block_omp_uses_cache_pipeline() {
13334        let block_size = 65_536usize;
13335        let k = 512usize;
13336        let width = 2 * k;
13337        let block_start = width * 200 + 1024;
13338        let text: Vec<u16> = (0..block_size + 2)
13339            .map(|i| 1 + ((i * 17 + i / 7) % (k - 1)) as u16)
13340            .collect();
13341        let sa_len = block_start + block_size + 1;
13342        let mut base_sa = vec![0; sa_len];
13343        for i in 0..block_size {
13344            let value = (i + 2) as SaSint;
13345            base_sa[block_start + i] = if i % 17 == 0 {
13346                value | SAINT_MIN
13347            } else {
13348                value
13349            };
13350        }
13351        let mut base_buckets = vec![0; 8 * ALPHABET_SIZE];
13352        for v in 0..width {
13353            base_buckets[v] = ((v + 1) * 200) as SaSint;
13354            base_buckets[2 * ALPHABET_SIZE + v] = if v % 5 == 0 { 3 } else { 0 };
13355        }
13356
13357        let mut scalar_sa = base_sa.clone();
13358        let mut threaded_sa = base_sa.clone();
13359        let mut scalar_buckets = base_buckets.clone();
13360        let mut threaded_buckets = base_buckets.clone();
13361        let mut thread_state = alloc_thread_state(4).unwrap();
13362        let scalar_d = partial_sorting_scan_right_to_left_16u(
13363            &text,
13364            &mut scalar_sa,
13365            &mut scalar_buckets,
13366            7,
13367            block_start as SaSint,
13368            block_size as SaSint,
13369        );
13370        let threaded_d = partial_sorting_scan_right_to_left_16u_block_omp(
13371            &text,
13372            &mut threaded_sa,
13373            k as SaSint,
13374            &mut threaded_buckets,
13375            7,
13376            block_start as SaSint,
13377            block_size as SaSint,
13378            4,
13379            &mut thread_state,
13380        );
13381        assert_eq!(threaded_d, scalar_d);
13382        assert_eq!(threaded_sa, scalar_sa);
13383        assert_eq!(threaded_buckets, scalar_buckets);
13384
13385        let mut scalar_sa = base_sa;
13386        let mut threaded_sa = scalar_sa.clone();
13387        let mut scalar_buckets = base_buckets.clone();
13388        let mut threaded_buckets = base_buckets;
13389        let scalar_d = partial_gsa_scan_right_to_left_16u(
13390            &text,
13391            &mut scalar_sa,
13392            &mut scalar_buckets,
13393            7,
13394            block_start as SaSint,
13395            block_size as SaSint,
13396        );
13397        let threaded_d = partial_gsa_scan_right_to_left_16u_block_omp(
13398            &text,
13399            &mut threaded_sa,
13400            k as SaSint,
13401            &mut threaded_buckets,
13402            7,
13403            block_start as SaSint,
13404            block_size as SaSint,
13405            4,
13406            &mut thread_state,
13407        );
13408        assert_eq!(threaded_d, scalar_d);
13409        assert_eq!(threaded_sa, scalar_sa);
13410        assert_eq!(threaded_buckets, scalar_buckets);
13411    }
13412
13413    #[test]
13414    fn libsais16_partial_right_to_left_16u_omp_uses_block_pipeline() {
13415        let block_size = 65_536usize;
13416        let k = 512usize;
13417        let width = 2 * k;
13418        let block_start = width * 200 + 1024;
13419        let text: Vec<u16> = (0..block_size + 2)
13420            .map(|i| 1 + ((i * 17 + i / 7) % (k - 1)) as u16)
13421            .collect();
13422        let sa_len = block_start + block_size + 1;
13423        let n = sa_len as SaSint;
13424        let first_lms_suffix = n - (block_start + block_size) as SaSint;
13425        let left_suffixes_count = block_start as SaSint - 1;
13426        let mut base_sa = vec![0; sa_len];
13427        for i in 0..block_size {
13428            let value = (i + 2) as SaSint;
13429            base_sa[block_start + i] = if i % 17 == 0 {
13430                value | SAINT_MIN
13431            } else {
13432                value
13433            };
13434        }
13435        let mut base_buckets = vec![0; 8 * ALPHABET_SIZE];
13436        for v in 0..width {
13437            base_buckets[v] = ((v + 1) * 200) as SaSint;
13438            base_buckets[2 * ALPHABET_SIZE + v] = if v % 5 == 0 { 3 } else { 0 };
13439        }
13440
13441        let mut scalar_sa = base_sa.clone();
13442        let mut threaded_sa = base_sa.clone();
13443        let mut scalar_buckets = base_buckets.clone();
13444        let mut threaded_buckets = base_buckets.clone();
13445        partial_sorting_scan_right_to_left_16u_omp(
13446            &text,
13447            &mut scalar_sa,
13448            n,
13449            k as SaSint,
13450            &mut scalar_buckets,
13451            first_lms_suffix,
13452            left_suffixes_count,
13453            7,
13454            1,
13455        );
13456        partial_sorting_scan_right_to_left_16u_omp(
13457            &text,
13458            &mut threaded_sa,
13459            n,
13460            k as SaSint,
13461            &mut threaded_buckets,
13462            first_lms_suffix,
13463            left_suffixes_count,
13464            7,
13465            4,
13466        );
13467        assert_eq!(threaded_sa, scalar_sa);
13468        assert_eq!(threaded_buckets, scalar_buckets);
13469
13470        let mut scalar_sa = base_sa;
13471        let mut threaded_sa = scalar_sa.clone();
13472        let mut scalar_buckets = base_buckets.clone();
13473        let mut threaded_buckets = base_buckets;
13474        partial_gsa_scan_right_to_left_16u_omp(
13475            &text,
13476            &mut scalar_sa,
13477            n,
13478            k as SaSint,
13479            &mut scalar_buckets,
13480            first_lms_suffix,
13481            left_suffixes_count,
13482            7,
13483            1,
13484        );
13485        partial_gsa_scan_right_to_left_16u_omp(
13486            &text,
13487            &mut threaded_sa,
13488            n,
13489            k as SaSint,
13490            &mut threaded_buckets,
13491            first_lms_suffix,
13492            left_suffixes_count,
13493            7,
13494            4,
13495        );
13496        assert_eq!(threaded_sa, scalar_sa);
13497        assert_eq!(threaded_buckets, scalar_buckets);
13498    }
13499
13500    fn final_scan_fixture() -> ([u16; 10], Vec<SaSint>, Vec<SaSint>) {
13501        let text = [1, 0, 2, 1, 3, 0, 2, 4, 1, 0];
13502        let mut sa = vec![0; 96];
13503        sa[..6].copy_from_slice(&[3, 0, 5 | SAINT_MIN, 7, 2, 9 | SAINT_MIN]);
13504
13505        let mut induction_bucket = vec![0; ALPHABET_SIZE];
13506        for c in 0..8 {
13507            induction_bucket[c] = 24 + (c as SaSint) * 6;
13508        }
13509
13510        (text, sa, induction_bucket)
13511    }
13512
13513    fn final_order_buckets(induction_bucket: &[SaSint]) -> Vec<SaSint> {
13514        let mut buckets = vec![0; 8 * ALPHABET_SIZE];
13515        buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE].copy_from_slice(induction_bucket);
13516        buckets[7 * ALPHABET_SIZE..8 * ALPHABET_SIZE].copy_from_slice(induction_bucket);
13517        buckets
13518    }
13519
13520    #[test]
13521    fn libsais16_final_sorting_scan_left_to_right_16u_matches_c() {
13522        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
13523        let mut c_sa = rust_sa.clone();
13524        let mut c_bucket = rust_bucket.clone();
13525
13526        final_sorting_scan_left_to_right_16u(&text, &mut rust_sa, &mut rust_bucket, 0, 6);
13527        unsafe {
13528            probe_libsais16_final_sorting_scan_left_to_right_16u(
13529                text.as_ptr(),
13530                c_sa.as_mut_ptr(),
13531                c_bucket.as_mut_ptr(),
13532                0,
13533                6,
13534            );
13535        }
13536
13537        assert_eq!(rust_sa, c_sa);
13538        assert_eq!(rust_bucket, c_bucket);
13539    }
13540
13541    #[test]
13542    fn libsais16_final_sorting_scan_right_to_left_16u_matches_c() {
13543        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
13544        let mut c_sa = rust_sa.clone();
13545        let mut c_bucket = rust_bucket.clone();
13546
13547        final_sorting_scan_right_to_left_16u(&text, &mut rust_sa, &mut rust_bucket, 0, 6);
13548        unsafe {
13549            probe_libsais16_final_sorting_scan_right_to_left_16u(
13550                text.as_ptr(),
13551                c_sa.as_mut_ptr(),
13552                c_bucket.as_mut_ptr(),
13553                0,
13554                6,
13555            );
13556        }
13557
13558        assert_eq!(rust_sa, c_sa);
13559        assert_eq!(rust_bucket, c_bucket);
13560    }
13561
13562    #[test]
13563    fn libsais16_final_gsa_scan_right_to_left_16u_matches_c() {
13564        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
13565        let mut c_sa = rust_sa.clone();
13566        let mut c_bucket = rust_bucket.clone();
13567
13568        final_gsa_scan_right_to_left_16u(&text, &mut rust_sa, &mut rust_bucket, 0, 6);
13569        unsafe {
13570            probe_libsais16_final_gsa_scan_right_to_left_16u(
13571                text.as_ptr(),
13572                c_sa.as_mut_ptr(),
13573                c_bucket.as_mut_ptr(),
13574                0,
13575                6,
13576            );
13577        }
13578
13579        assert_eq!(rust_sa, c_sa);
13580        assert_eq!(rust_bucket, c_bucket);
13581    }
13582
13583    #[test]
13584    fn libsais16_final_sorting_32s_helpers_behave_like_upstream_shapes() {
13585        let t = vec![0, 1, 2, 1, 0, 1, 2, 1, 0];
13586
13587        let mut rust_sa = vec![1, 0, 0];
13588        let mut rust_bucket = vec![0, 1, 3];
13589        let mut c_sa = rust_sa.clone();
13590        let mut c_bucket = rust_bucket.clone();
13591        final_sorting_scan_left_to_right_32s(&t, &mut rust_sa, &mut rust_bucket, 0, 1);
13592        unsafe {
13593            probe_libsais16_final_sorting_scan_left_to_right_32s(
13594                t.as_ptr(),
13595                c_sa.as_mut_ptr(),
13596                c_bucket.as_mut_ptr(),
13597                0,
13598                1,
13599            );
13600        }
13601        assert_eq!(rust_sa, c_sa);
13602        assert_eq!(rust_bucket, c_bucket);
13603
13604        let mut rust_sa = vec![0, 2, 0];
13605        let mut rust_bucket = vec![1, 2, 3];
13606        let mut c_sa = rust_sa.clone();
13607        let mut c_bucket = rust_bucket.clone();
13608        final_sorting_scan_right_to_left_32s(&t, &mut rust_sa, &mut rust_bucket, 0, 2);
13609        unsafe {
13610            probe_libsais16_final_sorting_scan_right_to_left_32s(
13611                t.as_ptr(),
13612                c_sa.as_mut_ptr(),
13613                c_bucket.as_mut_ptr(),
13614                0,
13615                2,
13616            );
13617        }
13618        assert_eq!(rust_sa, c_sa);
13619        assert_eq!(rust_bucket, c_bucket);
13620
13621        let mut sa = vec![1, 2, 0, 0];
13622        let mut induction_bucket = vec![0, 1, 3];
13623        let mut cache = vec![ThreadCache::default(); PER_THREAD_CACHE_SIZE];
13624        final_sorting_scan_left_to_right_32s_block_omp(
13625            &t,
13626            &mut sa,
13627            &mut induction_bucket,
13628            &mut cache,
13629            0,
13630            2,
13631            2,
13632        );
13633        assert_eq!(sa[0] & SAINT_MAX, 0);
13634        assert_eq!(sa[1] & SAINT_MAX, 1);
13635        assert_eq!(induction_bucket[0], 1);
13636        assert_eq!(induction_bucket[1], 2);
13637
13638        let mut sa = vec![0, 2, 0, 0];
13639        let mut induction_bucket = vec![1, 2, 3];
13640        let mut cache = vec![ThreadCache::default(); PER_THREAD_CACHE_SIZE];
13641        final_sorting_scan_right_to_left_32s_block_omp(
13642            &t,
13643            &mut sa,
13644            &mut induction_bucket,
13645            &mut cache,
13646            0,
13647            2,
13648            2,
13649        );
13650        assert_eq!(sa[1] & SAINT_MAX, 1);
13651        assert_eq!(induction_bucket[1], 1);
13652    }
13653
13654    #[test]
13655    fn libsais16_final_left_to_right_16u_block_omp_uses_cache_pipeline() {
13656        let block_size = 65_536usize;
13657        let k = 512usize;
13658        let text: Vec<u16> = (0..=block_size).map(|i| 1 + (i % (k - 1)) as u16).collect();
13659        let sa_len = block_size + k * 200;
13660        let mut base_sa = vec![0; sa_len];
13661        for (i, slot) in base_sa.iter_mut().take(block_size).enumerate() {
13662            *slot = (i + 1) as SaSint;
13663        }
13664        let mut base_bucket = vec![0; k];
13665        for c in 0..k {
13666            base_bucket[c] = (block_size + c * 200) as SaSint;
13667        }
13668
13669        let mut scalar_sa = base_sa.clone();
13670        let mut threaded_sa = base_sa.clone();
13671        let mut scalar_bucket = base_bucket.clone();
13672        let mut threaded_bucket = base_bucket.clone();
13673        let mut thread_state = alloc_thread_state(4).unwrap();
13674        final_bwt_scan_left_to_right_16u(
13675            &text,
13676            &mut scalar_sa,
13677            &mut scalar_bucket,
13678            0,
13679            block_size as SaSint,
13680        );
13681        final_bwt_scan_left_to_right_16u_block_omp(
13682            &text,
13683            &mut threaded_sa,
13684            k as SaSint,
13685            &mut threaded_bucket,
13686            0,
13687            block_size as SaSint,
13688            4,
13689            &mut thread_state,
13690        );
13691        assert_eq!(threaded_sa, scalar_sa);
13692        assert_eq!(threaded_bucket, scalar_bucket);
13693
13694        let rm = 3;
13695        let mut scalar_sa = base_sa.clone();
13696        let mut threaded_sa = base_sa.clone();
13697        let mut scalar_bucket = base_bucket.clone();
13698        let mut threaded_bucket = base_bucket.clone();
13699        let mut scalar_i = vec![-1; (block_size / (rm as usize + 1)) + 2];
13700        let mut threaded_i = scalar_i.clone();
13701        final_bwt_aux_scan_left_to_right_16u(
13702            &text,
13703            &mut scalar_sa,
13704            rm,
13705            &mut scalar_i,
13706            &mut scalar_bucket,
13707            0,
13708            block_size as SaSint,
13709        );
13710        final_bwt_aux_scan_left_to_right_16u_block_omp(
13711            &text,
13712            &mut threaded_sa,
13713            k as SaSint,
13714            rm,
13715            &mut threaded_i,
13716            &mut threaded_bucket,
13717            0,
13718            block_size as SaSint,
13719            4,
13720            &mut thread_state,
13721        );
13722        assert_eq!(threaded_sa, scalar_sa);
13723        assert_eq!(threaded_i, scalar_i);
13724        assert_eq!(threaded_bucket, scalar_bucket);
13725
13726        let mut scalar_sa = base_sa;
13727        let mut threaded_sa = scalar_sa.clone();
13728        let mut scalar_bucket = base_bucket.clone();
13729        let mut threaded_bucket = base_bucket;
13730        final_sorting_scan_left_to_right_16u(
13731            &text,
13732            &mut scalar_sa,
13733            &mut scalar_bucket,
13734            0,
13735            block_size as SaSint,
13736        );
13737        final_sorting_scan_left_to_right_16u_block_omp(
13738            &text,
13739            &mut threaded_sa,
13740            k as SaSint,
13741            &mut threaded_bucket,
13742            0,
13743            block_size as SaSint,
13744            4,
13745            &mut thread_state,
13746        );
13747        assert_eq!(threaded_sa, scalar_sa);
13748        assert_eq!(threaded_bucket, scalar_bucket);
13749    }
13750
13751    #[test]
13752    fn libsais16_final_right_to_left_16u_block_omp_uses_cache_pipeline() {
13753        let block_size = 65_536usize;
13754        let k = 512usize;
13755        let block_start = k * 200 + 1024;
13756        let text: Vec<u16> = (0..=block_size + 1)
13757            .map(|i| 1 + (i % (k - 1)) as u16)
13758            .collect();
13759        let sa_len = block_start + block_size + 1;
13760        let mut base_sa = vec![0; sa_len];
13761        for i in 0..block_size {
13762            base_sa[block_start + i] = (i + 1) as SaSint;
13763        }
13764        let mut base_bucket = vec![0; k];
13765        for c in 0..k {
13766            base_bucket[c] = ((c + 1) * 200) as SaSint;
13767        }
13768
13769        let mut scalar_sa = base_sa.clone();
13770        let mut threaded_sa = base_sa.clone();
13771        let mut scalar_bucket = base_bucket.clone();
13772        let mut threaded_bucket = base_bucket.clone();
13773        let mut thread_state = alloc_thread_state(4).unwrap();
13774        final_bwt_scan_right_to_left_16u(
13775            &text,
13776            &mut scalar_sa,
13777            &mut scalar_bucket,
13778            block_start as SaSint,
13779            block_size as SaSint,
13780        );
13781        final_bwt_scan_right_to_left_16u_block_omp(
13782            &text,
13783            &mut threaded_sa,
13784            k as SaSint,
13785            &mut threaded_bucket,
13786            block_start as SaSint,
13787            block_size as SaSint,
13788            4,
13789            &mut thread_state,
13790        );
13791        assert_eq!(threaded_sa, scalar_sa);
13792        assert_eq!(threaded_bucket, scalar_bucket);
13793
13794        let rm = 3;
13795        let mut scalar_sa = base_sa.clone();
13796        let mut threaded_sa = base_sa.clone();
13797        let mut scalar_bucket = base_bucket.clone();
13798        let mut threaded_bucket = base_bucket.clone();
13799        let mut scalar_i = vec![-1; (block_size / (rm as usize + 1)) + 2];
13800        let mut threaded_i = scalar_i.clone();
13801        final_bwt_aux_scan_right_to_left_16u(
13802            &text,
13803            &mut scalar_sa,
13804            rm,
13805            &mut scalar_i,
13806            &mut scalar_bucket,
13807            block_start as SaSint,
13808            block_size as SaSint,
13809        );
13810        final_bwt_aux_scan_right_to_left_16u_block_omp(
13811            &text,
13812            &mut threaded_sa,
13813            k as SaSint,
13814            rm,
13815            &mut threaded_i,
13816            &mut threaded_bucket,
13817            block_start as SaSint,
13818            block_size as SaSint,
13819            4,
13820            &mut thread_state,
13821        );
13822        assert_eq!(threaded_sa, scalar_sa);
13823        assert_eq!(threaded_i, scalar_i);
13824        assert_eq!(threaded_bucket, scalar_bucket);
13825
13826        let mut scalar_sa = base_sa.clone();
13827        let mut threaded_sa = base_sa.clone();
13828        let mut scalar_bucket = base_bucket.clone();
13829        let mut threaded_bucket = base_bucket.clone();
13830        final_sorting_scan_right_to_left_16u(
13831            &text,
13832            &mut scalar_sa,
13833            &mut scalar_bucket,
13834            block_start as SaSint,
13835            block_size as SaSint,
13836        );
13837        final_sorting_scan_right_to_left_16u_block_omp(
13838            &text,
13839            &mut threaded_sa,
13840            k as SaSint,
13841            &mut threaded_bucket,
13842            block_start as SaSint,
13843            block_size as SaSint,
13844            4,
13845            &mut thread_state,
13846        );
13847        assert_eq!(threaded_sa, scalar_sa);
13848        assert_eq!(threaded_bucket, scalar_bucket);
13849
13850        let mut scalar_sa = base_sa;
13851        let mut threaded_sa = scalar_sa.clone();
13852        let mut scalar_bucket = base_bucket.clone();
13853        let mut threaded_bucket = base_bucket;
13854        final_gsa_scan_right_to_left_16u(
13855            &text,
13856            &mut scalar_sa,
13857            &mut scalar_bucket,
13858            block_start as SaSint,
13859            block_size as SaSint,
13860        );
13861        final_gsa_scan_right_to_left_16u_block_omp(
13862            &text,
13863            &mut threaded_sa,
13864            k as SaSint,
13865            &mut threaded_bucket,
13866            block_start as SaSint,
13867            block_size as SaSint,
13868            4,
13869            &mut thread_state,
13870        );
13871        assert_eq!(threaded_sa, scalar_sa);
13872        assert_eq!(threaded_bucket, scalar_bucket);
13873    }
13874
13875    #[test]
13876    fn libsais16_clear_lms_suffixes_omp_zeroes_requested_bucket_ranges() {
13877        let mut rust_sa = vec![5, 4, 3, 2, 1, 9];
13878        let mut c_sa = rust_sa.clone();
13879        let n = rust_sa.len() as SaSint;
13880        let mut bucket_start = vec![1, 4, 5];
13881        let mut bucket_end = vec![3, 5, 5];
13882
13883        clear_lms_suffixes_omp(&mut rust_sa, n, 3, &bucket_start, &bucket_end, 2);
13884        unsafe {
13885            probe_libsais16_clear_lms_suffixes_omp(
13886                c_sa.as_mut_ptr(),
13887                n,
13888                3,
13889                bucket_start.as_mut_ptr(),
13890                bucket_end.as_mut_ptr(),
13891                2,
13892            );
13893        }
13894
13895        assert_eq!(rust_sa, c_sa);
13896    }
13897
13898    #[test]
13899    fn libsais16_partial_order_wrapper_helpers_match_manual_sequence() {
13900        let mut rust_sa = vec![1, 2, 3, 4];
13901        let mut c_sa = rust_sa.clone();
13902        flip_suffix_markers_omp(&mut rust_sa, 3, 2);
13903        unsafe {
13904            probe_libsais16_flip_suffix_markers_omp(c_sa.as_mut_ptr(), 3, 2);
13905        }
13906        assert_eq!(rust_sa, c_sa);
13907
13908        let t = vec![0, 1, 2, 1, 0, 1, 2, 1, 0];
13909        let n = t.len() as SaSint;
13910        let k = 3;
13911        let mut wrapped_sa = vec![0; t.len()];
13912        let mut wrapped_buckets = vec![0; k as usize];
13913        let mut wrapped_state = alloc_thread_state(1).unwrap();
13914        induce_partial_order_32s_1k_omp(
13915            &t,
13916            &mut wrapped_sa,
13917            n,
13918            k,
13919            &mut wrapped_buckets,
13920            1,
13921            &mut wrapped_state,
13922        );
13923
13924        let mut manual_sa = vec![0; t.len()];
13925        let mut manual_buckets = vec![0; k as usize];
13926        let mut manual_state = alloc_thread_state(1).unwrap();
13927        count_suffixes_32s(&t, n, k, &mut manual_buckets);
13928        initialize_buckets_start_32s_1k(k, &mut manual_buckets);
13929        partial_sorting_scan_left_to_right_32s_1k_omp(
13930            &t,
13931            &mut manual_sa,
13932            n,
13933            &mut manual_buckets,
13934            1,
13935            &mut manual_state,
13936        );
13937        count_suffixes_32s(&t, n, k, &mut manual_buckets);
13938        initialize_buckets_end_32s_1k(k, &mut manual_buckets);
13939        partial_sorting_scan_right_to_left_32s_1k_omp(
13940            &t,
13941            &mut manual_sa,
13942            n,
13943            &mut manual_buckets,
13944            1,
13945            &mut manual_state,
13946        );
13947        partial_sorting_gather_lms_suffixes_32s_1k_omp(&mut manual_sa, n, 1, &mut manual_state);
13948
13949        assert_eq!(wrapped_sa, manual_sa);
13950        assert_eq!(wrapped_buckets, manual_buckets);
13951    }
13952
13953    #[test]
13954    fn libsais16_induce_partial_order_32s_wrappers_match_c() {
13955        let t = make_main_32s_stress_text(128, 24);
13956        let n = t.len() as SaSint;
13957        let k = 24;
13958        let threads = 1;
13959
13960        let mut rust_sa = vec![0; t.len()];
13961        let mut rust_buckets = vec![0; 6 * k as usize];
13962        let mut rust_state = alloc_thread_state(threads).unwrap();
13963        let m = count_and_gather_lms_suffixes_32s_4k_omp(
13964            &t,
13965            &mut rust_sa,
13966            n,
13967            k,
13968            &mut rust_buckets,
13969            1,
13970            threads,
13971            &mut rust_state,
13972        );
13973        assert!(m > 1);
13974        rust_sa[..(n - m) as usize].fill(0);
13975        let first_lms_suffix = rust_sa[(n - m) as usize];
13976        let left_suffixes_count = initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
13977            &t,
13978            k,
13979            &mut rust_buckets,
13980            first_lms_suffix,
13981        );
13982        let (_, induction_bucket) = rust_buckets.split_at_mut(4 * k as usize);
13983        radix_sort_lms_suffixes_32s_6k_omp(&t, &mut rust_sa, n, m, induction_bucket, threads);
13984        radix_sort_set_markers_32s_6k_omp(&mut rust_sa, k, induction_bucket, threads);
13985        initialize_buckets_for_partial_sorting_32s_6k(
13986            &t,
13987            k,
13988            &mut rust_buckets,
13989            first_lms_suffix,
13990            left_suffixes_count,
13991        );
13992        let mut c_sa = rust_sa.clone();
13993        let mut c_buckets = rust_buckets.clone();
13994        induce_partial_order_32s_6k_omp(
13995            &t,
13996            &mut rust_sa,
13997            n,
13998            k,
13999            &mut rust_buckets,
14000            first_lms_suffix,
14001            left_suffixes_count,
14002            threads,
14003            &mut rust_state,
14004        );
14005        unsafe {
14006            probe_libsais16_induce_partial_order_32s_6k_omp(
14007                t.as_ptr(),
14008                c_sa.as_mut_ptr(),
14009                n,
14010                k,
14011                c_buckets.as_mut_ptr(),
14012                first_lms_suffix,
14013                left_suffixes_count,
14014                threads,
14015            );
14016        }
14017        assert_eq!(rust_sa, c_sa);
14018        assert_eq!(rust_buckets, c_buckets);
14019
14020        let mut rust_sa = vec![0; t.len()];
14021        let mut rust_buckets = vec![0; 4 * k as usize];
14022        let mut rust_state = alloc_thread_state(threads).unwrap();
14023        let m = count_and_gather_lms_suffixes_32s_2k_omp(
14024            &t,
14025            &mut rust_sa,
14026            n,
14027            k,
14028            &mut rust_buckets,
14029            1,
14030            threads,
14031            &mut rust_state,
14032        );
14033        assert!(m > 1);
14034        let first_lms_suffix = rust_sa[(n - m) as usize];
14035        initialize_buckets_for_radix_and_partial_sorting_32s_4k(
14036            &t,
14037            k,
14038            &mut rust_buckets,
14039            first_lms_suffix,
14040        );
14041        let (_, induction_bucket) = rust_buckets.split_at_mut(1);
14042        radix_sort_lms_suffixes_32s_2k_omp(&t, &mut rust_sa, n, m, induction_bucket, threads);
14043        radix_sort_set_markers_32s_4k_omp(&mut rust_sa, k, induction_bucket, threads);
14044        place_lms_suffixes_interval_32s_4k(&mut rust_sa, n, k, m - 1, &rust_buckets);
14045        let mut c_sa = rust_sa.clone();
14046        let mut c_buckets = rust_buckets.clone();
14047        induce_partial_order_32s_4k_omp(
14048            &t,
14049            &mut rust_sa,
14050            n,
14051            k,
14052            &mut rust_buckets,
14053            threads,
14054            &mut rust_state,
14055        );
14056        unsafe {
14057            probe_libsais16_induce_partial_order_32s_4k_omp(
14058                t.as_ptr(),
14059                c_sa.as_mut_ptr(),
14060                n,
14061                k,
14062                c_buckets.as_mut_ptr(),
14063                threads,
14064            );
14065        }
14066        assert_eq!(rust_sa, c_sa);
14067        assert_eq!(rust_buckets, c_buckets);
14068
14069        let mut rust_sa = vec![0; t.len()];
14070        let mut rust_buckets = vec![0; 2 * k as usize];
14071        let mut rust_state = alloc_thread_state(threads).unwrap();
14072        let m = count_and_gather_lms_suffixes_32s_2k_omp(
14073            &t,
14074            &mut rust_sa,
14075            n,
14076            k,
14077            &mut rust_buckets,
14078            1,
14079            threads,
14080            &mut rust_state,
14081        );
14082        assert!(m > 1);
14083        let first_lms_suffix = rust_sa[(n - m) as usize];
14084        initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
14085            &t,
14086            k,
14087            &mut rust_buckets,
14088            first_lms_suffix,
14089        );
14090        let (_, induction_bucket) = rust_buckets.split_at_mut(1);
14091        radix_sort_lms_suffixes_32s_2k_omp(&t, &mut rust_sa, n, m, induction_bucket, threads);
14092        place_lms_suffixes_interval_32s_2k(&mut rust_sa, n, k, m - 1, &rust_buckets);
14093        initialize_buckets_start_and_end_32s_2k(k, &mut rust_buckets);
14094        let mut c_sa = rust_sa.clone();
14095        let mut c_buckets = rust_buckets.clone();
14096        induce_partial_order_32s_2k_omp(
14097            &t,
14098            &mut rust_sa,
14099            n,
14100            k,
14101            &mut rust_buckets,
14102            threads,
14103            &mut rust_state,
14104        );
14105        unsafe {
14106            probe_libsais16_induce_partial_order_32s_2k_omp(
14107                t.as_ptr(),
14108                c_sa.as_mut_ptr(),
14109                n,
14110                k,
14111                c_buckets.as_mut_ptr(),
14112                threads,
14113            );
14114        }
14115        assert_eq!(rust_sa, c_sa);
14116        assert_eq!(rust_buckets, c_buckets);
14117
14118        let mut rust_sa = vec![0; t.len()];
14119        let mut rust_buckets = vec![0; k as usize];
14120        let mut rust_state = alloc_thread_state(threads).unwrap();
14121        count_suffixes_32s(&t, n, k, &mut rust_buckets);
14122        initialize_buckets_end_32s_1k(k, &mut rust_buckets);
14123        let m = radix_sort_lms_suffixes_32s_1k(&t, &mut rust_sa, n, &mut rust_buckets);
14124        assert!(m > 1);
14125        let mut c_sa = rust_sa.clone();
14126        let mut c_buckets = rust_buckets.clone();
14127        induce_partial_order_32s_1k_omp(
14128            &t,
14129            &mut rust_sa,
14130            n,
14131            k,
14132            &mut rust_buckets,
14133            threads,
14134            &mut rust_state,
14135        );
14136        unsafe {
14137            probe_libsais16_induce_partial_order_32s_1k_omp(
14138                t.as_ptr(),
14139                c_sa.as_mut_ptr(),
14140                n,
14141                k,
14142                c_buckets.as_mut_ptr(),
14143                threads,
14144            );
14145        }
14146        assert_eq!(rust_sa, c_sa);
14147        assert_eq!(rust_buckets, c_buckets);
14148    }
14149
14150    #[test]
14151    fn libsais16_induce_partial_order_16u_omp_matches_c() {
14152        let text = [3, 1, 2, 1, 0, 4, 1, 0];
14153        let n = text.len() as SaSint;
14154        let flags = 0;
14155        let threads = 1;
14156        let mut rust_sa = vec![0; text.len()];
14157        let mut rust_buckets = vec![0; 8 * ALPHABET_SIZE];
14158
14159        let m = count_and_gather_lms_suffixes_16u_omp(
14160            &text,
14161            &mut rust_sa,
14162            n,
14163            &mut rust_buckets[..4 * ALPHABET_SIZE],
14164            threads,
14165            &mut [],
14166        );
14167        let k = initialize_buckets_start_and_end_16u(&mut rust_buckets, None);
14168        assert!(m > 0);
14169        let first_lms_suffix = rust_sa[(n - m) as usize];
14170        let left_suffixes_count = initialize_buckets_for_lms_suffixes_radix_sort_16u(
14171            &text,
14172            &mut rust_buckets,
14173            first_lms_suffix,
14174        );
14175        radix_sort_lms_suffixes_16u_omp(
14176            &text,
14177            &mut rust_sa,
14178            n,
14179            m,
14180            flags,
14181            &mut rust_buckets,
14182            threads,
14183            &mut [],
14184        );
14185        initialize_buckets_for_partial_sorting_16u(
14186            &text,
14187            &mut rust_buckets,
14188            first_lms_suffix,
14189            left_suffixes_count,
14190        );
14191
14192        let mut c_sa = rust_sa.clone();
14193        let mut c_buckets = rust_buckets.clone();
14194        induce_partial_order_16u_omp(
14195            &text,
14196            &mut rust_sa,
14197            n,
14198            k,
14199            flags,
14200            &mut rust_buckets,
14201            first_lms_suffix,
14202            left_suffixes_count,
14203            threads,
14204        );
14205        unsafe {
14206            probe_libsais16_induce_partial_order_16u_omp(
14207                text.as_ptr(),
14208                c_sa.as_mut_ptr(),
14209                n,
14210                k,
14211                flags,
14212                c_buckets.as_mut_ptr(),
14213                first_lms_suffix,
14214                left_suffixes_count,
14215                threads,
14216            );
14217        }
14218
14219        assert_eq!(rust_sa, c_sa);
14220        assert_eq!(rust_buckets, c_buckets);
14221    }
14222
14223    fn final_order_32s_fixture() -> (Vec<SaSint>, Vec<SaSint>) {
14224        (
14225            vec![0, 1, 2, 1, 0, 1, 2, 1, 0],
14226            vec![1, 0, 2, 0, 0, 0, 0, 0, 0],
14227        )
14228    }
14229
14230    fn seed_final_order_bucket_sections(buckets: &mut [SaSint], k: usize, branch_k: usize) {
14231        let left = [0, 1, 3];
14232        let right = [1, 2, 3];
14233        let left_section = match branch_k {
14234            6 => 4 * k,
14235            4 => 2 * k,
14236            2 => k,
14237            _ => 0,
14238        };
14239        let right_section = match branch_k {
14240            6 => 5 * k,
14241            4 => 3 * k,
14242            2 => 0,
14243            _ => 0,
14244        };
14245        buckets[left_section..left_section + k].copy_from_slice(&left);
14246        buckets[right_section..right_section + k].copy_from_slice(&right);
14247    }
14248
14249    #[test]
14250    fn libsais16_induce_final_order_32s_wrappers_match_c() {
14251        let (t, sa) = final_order_32s_fixture();
14252        let n = t.len() as SaSint;
14253        let k = 3;
14254        let threads = 1;
14255
14256        let mut rust_sa = sa.clone();
14257        let mut rust_buckets = vec![0; 6 * k as usize];
14258        seed_final_order_bucket_sections(&mut rust_buckets, k as usize, 6);
14259        let mut c_sa = rust_sa.clone();
14260        let mut c_buckets = rust_buckets.clone();
14261        let mut rust_state = alloc_thread_state(threads).unwrap();
14262        induce_final_order_32s_6k(
14263            &t,
14264            &mut rust_sa,
14265            n,
14266            k,
14267            &mut rust_buckets,
14268            threads,
14269            &mut rust_state,
14270        );
14271        unsafe {
14272            probe_libsais16_induce_final_order_32s_6k(
14273                t.as_ptr(),
14274                c_sa.as_mut_ptr(),
14275                n,
14276                k,
14277                c_buckets.as_mut_ptr(),
14278                threads,
14279            );
14280        }
14281        assert_eq!(rust_sa, c_sa);
14282        assert_eq!(rust_buckets, c_buckets);
14283
14284        let mut rust_sa = sa.clone();
14285        let mut rust_buckets = vec![0; 4 * k as usize];
14286        seed_final_order_bucket_sections(&mut rust_buckets, k as usize, 4);
14287        let mut c_sa = rust_sa.clone();
14288        let mut c_buckets = rust_buckets.clone();
14289        let mut rust_state = alloc_thread_state(threads).unwrap();
14290        induce_final_order_32s_4k(
14291            &t,
14292            &mut rust_sa,
14293            n,
14294            k,
14295            &mut rust_buckets,
14296            threads,
14297            &mut rust_state,
14298        );
14299        unsafe {
14300            probe_libsais16_induce_final_order_32s_4k(
14301                t.as_ptr(),
14302                c_sa.as_mut_ptr(),
14303                n,
14304                k,
14305                c_buckets.as_mut_ptr(),
14306                threads,
14307            );
14308        }
14309        assert_eq!(rust_sa, c_sa);
14310        assert_eq!(rust_buckets, c_buckets);
14311
14312        let mut rust_sa = sa.clone();
14313        let mut rust_buckets = vec![0; 2 * k as usize];
14314        seed_final_order_bucket_sections(&mut rust_buckets, k as usize, 2);
14315        let mut c_sa = rust_sa.clone();
14316        let mut c_buckets = rust_buckets.clone();
14317        let mut rust_state = alloc_thread_state(threads).unwrap();
14318        induce_final_order_32s_2k(
14319            &t,
14320            &mut rust_sa,
14321            n,
14322            k,
14323            &mut rust_buckets,
14324            threads,
14325            &mut rust_state,
14326        );
14327        unsafe {
14328            probe_libsais16_induce_final_order_32s_2k(
14329                t.as_ptr(),
14330                c_sa.as_mut_ptr(),
14331                n,
14332                k,
14333                c_buckets.as_mut_ptr(),
14334                threads,
14335            );
14336        }
14337        assert_eq!(rust_sa, c_sa);
14338        assert_eq!(rust_buckets, c_buckets);
14339
14340        let mut rust_sa = sa;
14341        let mut rust_buckets = vec![0; k as usize];
14342        let mut c_sa = rust_sa.clone();
14343        let mut c_buckets = rust_buckets.clone();
14344        let mut rust_state = alloc_thread_state(threads).unwrap();
14345        induce_final_order_32s_1k(
14346            &t,
14347            &mut rust_sa,
14348            n,
14349            k,
14350            &mut rust_buckets,
14351            threads,
14352            &mut rust_state,
14353        );
14354        unsafe {
14355            probe_libsais16_induce_final_order_32s_1k(
14356                t.as_ptr(),
14357                c_sa.as_mut_ptr(),
14358                n,
14359                k,
14360                c_buckets.as_mut_ptr(),
14361                threads,
14362            );
14363        }
14364        assert_eq!(rust_sa, c_sa);
14365        assert_eq!(rust_buckets, c_buckets);
14366    }
14367
14368    #[test]
14369    fn libsais16_induce_final_order_16u_omp_matches_manual_sequence() {
14370        let (text, mut wrapped_sa, induction_bucket) = final_scan_fixture();
14371        let mut wrapped_buckets = final_order_buckets(&induction_bucket);
14372        let mut c_sa = wrapped_sa.clone();
14373        let mut c_buckets = wrapped_buckets.clone();
14374        let mut wrapped_state = alloc_thread_state(1).unwrap();
14375        let wrapped_index = induce_final_order_16u_omp(
14376            &text,
14377            &mut wrapped_sa,
14378            text.len() as SaSint,
14379            8,
14380            0,
14381            0,
14382            None,
14383            &mut wrapped_buckets,
14384            1,
14385            &mut wrapped_state,
14386        );
14387        let c_index = unsafe {
14388            probe_libsais16_induce_final_order_16u_omp(
14389                text.as_ptr(),
14390                c_sa.as_mut_ptr(),
14391                text.len() as SaSint,
14392                8,
14393                0,
14394                0,
14395                std::ptr::null_mut(),
14396                c_buckets.as_mut_ptr(),
14397                1,
14398            )
14399        };
14400
14401        let (text, mut manual_sa, induction_bucket) = final_scan_fixture();
14402        let mut manual_buckets = final_order_buckets(&induction_bucket);
14403        {
14404            let (left_buckets, right_tail) = manual_buckets.split_at_mut(7 * ALPHABET_SIZE);
14405            final_sorting_scan_left_to_right_16u_omp(
14406                &text,
14407                &mut manual_sa,
14408                text.len() as SaSint,
14409                8,
14410                &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE],
14411                1,
14412            );
14413            final_sorting_scan_right_to_left_16u_omp(
14414                &text,
14415                &mut manual_sa,
14416                0,
14417                text.len() as SaSint,
14418                8,
14419                &mut right_tail[..ALPHABET_SIZE],
14420                1,
14421            );
14422        }
14423
14424        assert_eq!(wrapped_index, 0);
14425        assert_eq!(wrapped_index, c_index);
14426        assert_eq!(wrapped_sa, manual_sa);
14427        assert_eq!(wrapped_sa, c_sa);
14428        assert_eq!(wrapped_buckets, manual_buckets);
14429        assert_eq!(wrapped_buckets, c_buckets);
14430
14431        let (text, mut wrapped_sa, induction_bucket) = final_scan_fixture();
14432        let mut wrapped_buckets = final_order_buckets(&induction_bucket);
14433        let mut c_sa = wrapped_sa.clone();
14434        let mut c_buckets = wrapped_buckets.clone();
14435        let mut wrapped_state = alloc_thread_state(1).unwrap();
14436        let wrapped_index = induce_final_order_16u_omp(
14437            &text,
14438            &mut wrapped_sa,
14439            text.len() as SaSint,
14440            8,
14441            LIBSAIS_FLAGS_BWT,
14442            0,
14443            None,
14444            &mut wrapped_buckets,
14445            1,
14446            &mut wrapped_state,
14447        );
14448        let c_index = unsafe {
14449            probe_libsais16_induce_final_order_16u_omp(
14450                text.as_ptr(),
14451                c_sa.as_mut_ptr(),
14452                text.len() as SaSint,
14453                8,
14454                LIBSAIS_FLAGS_BWT,
14455                0,
14456                std::ptr::null_mut(),
14457                c_buckets.as_mut_ptr(),
14458                1,
14459            )
14460        };
14461
14462        let (text, mut manual_sa, induction_bucket) = final_scan_fixture();
14463        let mut manual_buckets = final_order_buckets(&induction_bucket);
14464        let manual_index = {
14465            let (left_buckets, right_tail) = manual_buckets.split_at_mut(7 * ALPHABET_SIZE);
14466            final_bwt_scan_left_to_right_16u_omp(
14467                &text,
14468                &mut manual_sa,
14469                text.len() as SaSint,
14470                8,
14471                &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE],
14472                1,
14473            );
14474            final_bwt_scan_right_to_left_16u_omp(
14475                &text,
14476                &mut manual_sa,
14477                text.len() as SaSint,
14478                8,
14479                &mut right_tail[..ALPHABET_SIZE],
14480                1,
14481            )
14482        };
14483
14484        assert_eq!(wrapped_index, manual_index);
14485        assert_eq!(wrapped_index, c_index);
14486        assert_eq!(wrapped_sa, manual_sa);
14487        assert_eq!(wrapped_sa, c_sa);
14488        assert_eq!(wrapped_buckets, manual_buckets);
14489        assert_eq!(wrapped_buckets, c_buckets);
14490
14491        let (text, mut wrapped_sa, induction_bucket) = final_scan_fixture();
14492        let mut wrapped_buckets = final_order_buckets(&induction_bucket);
14493        let mut c_sa = wrapped_sa.clone();
14494        let mut c_buckets = wrapped_buckets.clone();
14495        let mut wrapped_state = alloc_thread_state(1).unwrap();
14496        let mut wrapped_i = vec![-1; 8];
14497        let mut c_i = wrapped_i.clone();
14498        let wrapped_index = induce_final_order_16u_omp(
14499            &text,
14500            &mut wrapped_sa,
14501            text.len() as SaSint,
14502            8,
14503            LIBSAIS_FLAGS_BWT,
14504            2,
14505            Some(&mut wrapped_i),
14506            &mut wrapped_buckets,
14507            1,
14508            &mut wrapped_state,
14509        );
14510        let c_index = unsafe {
14511            probe_libsais16_induce_final_order_16u_omp(
14512                text.as_ptr(),
14513                c_sa.as_mut_ptr(),
14514                text.len() as SaSint,
14515                8,
14516                LIBSAIS_FLAGS_BWT,
14517                2,
14518                c_i.as_mut_ptr(),
14519                c_buckets.as_mut_ptr(),
14520                1,
14521            )
14522        };
14523
14524        let (text, mut manual_sa, induction_bucket) = final_scan_fixture();
14525        let mut manual_buckets = final_order_buckets(&induction_bucket);
14526        let mut manual_i = vec![-1; 8];
14527        {
14528            let (left_buckets, right_tail) = manual_buckets.split_at_mut(7 * ALPHABET_SIZE);
14529            final_bwt_aux_scan_left_to_right_16u_omp(
14530                &text,
14531                &mut manual_sa,
14532                text.len() as SaSint,
14533                8,
14534                1,
14535                &mut manual_i,
14536                &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE],
14537                1,
14538            );
14539            final_bwt_aux_scan_right_to_left_16u_omp(
14540                &text,
14541                &mut manual_sa,
14542                text.len() as SaSint,
14543                8,
14544                1,
14545                &mut manual_i,
14546                &mut right_tail[..ALPHABET_SIZE],
14547                1,
14548            );
14549        }
14550
14551        assert_eq!(wrapped_index, 0);
14552        assert_eq!(wrapped_index, c_index);
14553        assert_eq!(wrapped_sa, manual_sa);
14554        assert_eq!(wrapped_sa, c_sa);
14555        assert_eq!(wrapped_buckets, manual_buckets);
14556        assert_eq!(wrapped_buckets, c_buckets);
14557        assert_eq!(wrapped_i, manual_i);
14558        assert_eq!(wrapped_i, c_i);
14559    }
14560
14561    #[test]
14562    fn libsais16_main_16u_matches_public_c_suffix_array_paths() {
14563        let text = [3, 1, 4, 1, 5, 9, 0, 2];
14564        let n = text.len() as SaSint;
14565        let fs = 32;
14566        let mut rust_sa = vec![0; text.len() + fs as usize];
14567        let mut rust_buckets = vec![0; 8 * ALPHABET_SIZE];
14568        let mut rust_freq = vec![0; ALPHABET_SIZE];
14569        let mut rust_state = alloc_thread_state(1).unwrap();
14570        let rust_index = main_16u(
14571            &text,
14572            &mut rust_sa,
14573            n,
14574            &mut rust_buckets,
14575            0,
14576            0,
14577            None,
14578            fs,
14579            Some(&mut rust_freq),
14580            1,
14581            &mut rust_state,
14582        );
14583
14584        let mut c_sa = vec![0; text.len() + fs as usize];
14585        let mut c_freq = vec![0; ALPHABET_SIZE];
14586        let c_index = unsafe {
14587            probe_public_libsais16_freq(
14588                text.as_ptr(),
14589                c_sa.as_mut_ptr(),
14590                n,
14591                fs,
14592                c_freq.as_mut_ptr(),
14593            )
14594        };
14595
14596        assert_eq!(rust_index, c_index);
14597        assert_eq!(&rust_sa[..text.len()], &c_sa[..text.len()]);
14598        assert_eq!(rust_freq, c_freq);
14599
14600        let text = [2, 1, 0, 2, 0];
14601        let n = text.len() as SaSint;
14602        let fs = 24;
14603        let mut rust_sa = vec![0; text.len() + fs as usize];
14604        let mut rust_buckets = vec![0; 8 * ALPHABET_SIZE];
14605        let mut rust_freq = vec![0; ALPHABET_SIZE];
14606        let mut rust_state = alloc_thread_state(1).unwrap();
14607        let rust_index = main_16u(
14608            &text,
14609            &mut rust_sa,
14610            n,
14611            &mut rust_buckets,
14612            LIBSAIS_FLAGS_GSA,
14613            0,
14614            None,
14615            fs,
14616            Some(&mut rust_freq),
14617            1,
14618            &mut rust_state,
14619        );
14620
14621        let mut c_sa = vec![0; text.len() + fs as usize];
14622        let mut c_freq = vec![0; ALPHABET_SIZE];
14623        let c_index = unsafe {
14624            probe_public_libsais16_gsa_freq(
14625                text.as_ptr(),
14626                c_sa.as_mut_ptr(),
14627                n,
14628                fs,
14629                c_freq.as_mut_ptr(),
14630            )
14631        };
14632
14633        assert_eq!(rust_index, c_index);
14634        assert_eq!(&rust_sa[..text.len()], &c_sa[..text.len()]);
14635        assert_eq!(rust_freq, c_freq);
14636    }
14637
14638    fn make_main_32s_stress_text(len: usize, alphabet: SaSint) -> Vec<SaSint> {
14639        let mut state: u32 = 0x1357_9bdf;
14640        let mut t = Vec::with_capacity(len + 1);
14641
14642        for i in 0..len {
14643            state = state.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
14644            let mut value = ((state >> 16) % (alphabet as u32 - 1)) as SaSint + 1;
14645            if i % 17 < 8 {
14646                value = ((i / 17) as SaSint % 11) + 1;
14647            }
14648            if i % 29 < 10 {
14649                value = (((i / 29) as SaSint * 3) % 19) + 1;
14650            }
14651            if i % 64 >= 48 {
14652                value = t[i - 48];
14653            }
14654            t.push(value);
14655        }
14656
14657        t.push(0);
14658        t
14659    }
14660
14661    fn make_recursive_main_32s_text(repeats: usize) -> Vec<SaSint> {
14662        let motif = [9, 4, 9, 2, 9, 4, 9, 1];
14663        let mut t = Vec::with_capacity(repeats * motif.len() + 1);
14664        for _ in 0..repeats {
14665            t.extend_from_slice(&motif);
14666        }
14667        t.push(0);
14668        t
14669    }
14670
14671    fn assert_main_32s_entry_matches_c(mut t: Vec<SaSint>, k: SaSint, fs: SaSint) {
14672        let n = t.len() as SaSint;
14673        let threads = 1;
14674        let mut sa = vec![0; t.len() + fs as usize];
14675        let initial_t = t.clone();
14676        let initial_sa = sa.clone();
14677
14678        let c_result = unsafe {
14679            probe_libsais16_main_32s_entry(t.as_mut_ptr(), sa.as_mut_ptr(), n, k, fs, threads)
14680        };
14681        let c_t = t.clone();
14682        let c_sa = sa.clone();
14683
14684        t.copy_from_slice(&initial_t);
14685        sa.copy_from_slice(&initial_sa);
14686
14687        let mut thread_state = alloc_thread_state(threads).unwrap();
14688        let rust_result = main_32s_entry(
14689            t.as_mut_ptr(),
14690            &mut sa,
14691            n,
14692            k,
14693            fs,
14694            threads,
14695            &mut thread_state,
14696        );
14697
14698        assert_eq!(rust_result, c_result);
14699        assert_eq!(t, c_t);
14700        assert_eq!(sa, c_sa);
14701    }
14702
14703    #[test]
14704    fn libsais16_main_32s_entry_matches_c_for_local_32s_paths() {
14705        assert_main_32s_entry_matches_c(make_main_32s_stress_text(1024, 300), 300, 2048);
14706        assert_main_32s_entry_matches_c(make_main_32s_stress_text(1024, 400), 400, 2048);
14707        assert_main_32s_entry_matches_c(make_main_32s_stress_text(1024, 700), 700, 2048);
14708        assert_main_32s_entry_matches_c(make_main_32s_stress_text(1024, 1501), 1501, 2048);
14709        assert_main_32s_entry_matches_c(make_recursive_main_32s_text(24), 300, 0);
14710        assert_main_32s_entry_matches_c(make_recursive_main_32s_text(24), 1501, 0);
14711    }
14712
14713    #[test]
14714    fn libsais16_final_bwt_scan_left_to_right_16u_matches_c() {
14715        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
14716        let mut c_sa = rust_sa.clone();
14717        let mut c_bucket = rust_bucket.clone();
14718
14719        final_bwt_scan_left_to_right_16u(&text, &mut rust_sa, &mut rust_bucket, 0, 6);
14720        unsafe {
14721            probe_libsais16_final_bwt_scan_left_to_right_16u(
14722                text.as_ptr(),
14723                c_sa.as_mut_ptr(),
14724                c_bucket.as_mut_ptr(),
14725                0,
14726                6,
14727            );
14728        }
14729
14730        assert_eq!(rust_sa, c_sa);
14731        assert_eq!(rust_bucket, c_bucket);
14732    }
14733
14734    #[test]
14735    fn libsais16_final_bwt_scan_right_to_left_16u_matches_c() {
14736        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
14737        let mut c_sa = rust_sa.clone();
14738        let mut c_bucket = rust_bucket.clone();
14739
14740        let rust_index =
14741            final_bwt_scan_right_to_left_16u(&text, &mut rust_sa, &mut rust_bucket, 0, 6);
14742        let c_index = unsafe {
14743            probe_libsais16_final_bwt_scan_right_to_left_16u(
14744                text.as_ptr(),
14745                c_sa.as_mut_ptr(),
14746                c_bucket.as_mut_ptr(),
14747                0,
14748                6,
14749            )
14750        };
14751
14752        assert_eq!(rust_index, c_index);
14753        assert_eq!(rust_sa, c_sa);
14754        assert_eq!(rust_bucket, c_bucket);
14755    }
14756
14757    #[test]
14758    fn libsais16_final_bwt_aux_scan_left_to_right_16u_matches_c() {
14759        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
14760        let mut c_sa = rust_sa.clone();
14761        let mut c_bucket = rust_bucket.clone();
14762        let mut rust_i = vec![-1; 8];
14763        let mut c_i = rust_i.clone();
14764
14765        final_bwt_aux_scan_left_to_right_16u(
14766            &text,
14767            &mut rust_sa,
14768            1,
14769            &mut rust_i,
14770            &mut rust_bucket,
14771            0,
14772            6,
14773        );
14774        unsafe {
14775            probe_libsais16_final_bwt_aux_scan_left_to_right_16u(
14776                text.as_ptr(),
14777                c_sa.as_mut_ptr(),
14778                1,
14779                c_i.as_mut_ptr(),
14780                c_bucket.as_mut_ptr(),
14781                0,
14782                6,
14783            );
14784        }
14785
14786        assert_eq!(rust_sa, c_sa);
14787        assert_eq!(rust_bucket, c_bucket);
14788        assert_eq!(rust_i, c_i);
14789    }
14790
14791    #[test]
14792    fn libsais16_final_bwt_aux_scan_right_to_left_16u_matches_c() {
14793        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
14794        let mut c_sa = rust_sa.clone();
14795        let mut c_bucket = rust_bucket.clone();
14796        let mut rust_i = vec![-1; 8];
14797        let mut c_i = rust_i.clone();
14798
14799        final_bwt_aux_scan_right_to_left_16u(
14800            &text,
14801            &mut rust_sa,
14802            1,
14803            &mut rust_i,
14804            &mut rust_bucket,
14805            0,
14806            6,
14807        );
14808        unsafe {
14809            probe_libsais16_final_bwt_aux_scan_right_to_left_16u(
14810                text.as_ptr(),
14811                c_sa.as_mut_ptr(),
14812                1,
14813                c_i.as_mut_ptr(),
14814                c_bucket.as_mut_ptr(),
14815                0,
14816                6,
14817            );
14818        }
14819
14820        assert_eq!(rust_sa, c_sa);
14821        assert_eq!(rust_bucket, c_bucket);
14822        assert_eq!(rust_i, c_i);
14823    }
14824
14825    #[test]
14826    fn libsais16_renumber_lms_suffixes_16u_matches_c() {
14827        let m = 6;
14828        let mut rust_sa = vec![0; 20];
14829        rust_sa[..m].copy_from_slice(&[2, 4 | SAINT_MIN, 6, 8 | SAINT_MIN, 10, 12 | SAINT_MIN]);
14830        let mut c_sa = rust_sa.clone();
14831
14832        let rust_name = renumber_lms_suffixes_16u(&mut rust_sa, m as SaSint, 5, 0, m as SaSint);
14833        let c_name = unsafe {
14834            probe_libsais16_renumber_lms_suffixes_16u(
14835                c_sa.as_mut_ptr(),
14836                m as SaSint,
14837                5,
14838                0,
14839                m as SaSint,
14840            )
14841        };
14842
14843        assert_eq!(rust_name, c_name);
14844        assert_eq!(rust_sa, c_sa);
14845    }
14846
14847    fn lms_interval_fixture() -> (Vec<SaSint>, Vec<SaSint>) {
14848        let mut sa = vec![-7; 16];
14849        sa[4..8].copy_from_slice(&[41, 42, 61, 62]);
14850
14851        let mut buckets = vec![0; 8 * ALPHABET_SIZE];
14852        buckets[buckets_index2(2, 1)] = 0;
14853        buckets[buckets_index2(3, 1)] = 2;
14854        buckets[buckets_index2(4, 1)] = 2;
14855        buckets[buckets_index2(5, 1)] = 2;
14856        buckets[buckets_index2(6, 1)] = 4;
14857        buckets[buckets_index2(7, 1)] = 4;
14858        buckets[7 * ALPHABET_SIZE + 2] = 6;
14859        buckets[7 * ALPHABET_SIZE + 5] = 12;
14860
14861        (sa, buckets)
14862    }
14863
14864    #[test]
14865    fn libsais16_place_lms_suffixes_interval_16u_matches_c() {
14866        for flags in [0, LIBSAIS_FLAGS_GSA] {
14867            let (mut rust_sa, mut rust_buckets) = lms_interval_fixture();
14868            let mut c_sa = rust_sa.clone();
14869            let mut c_buckets = rust_buckets.clone();
14870
14871            place_lms_suffixes_interval_16u(&mut rust_sa, 16, 8, flags, &mut rust_buckets);
14872            unsafe {
14873                probe_libsais16_place_lms_suffixes_interval_16u(
14874                    c_sa.as_mut_ptr(),
14875                    16,
14876                    8,
14877                    flags,
14878                    c_buckets.as_mut_ptr(),
14879                );
14880            }
14881
14882            assert_eq!(rust_sa, c_sa);
14883            assert_eq!(rust_buckets, c_buckets);
14884        }
14885    }
14886
14887    #[test]
14888    fn libsais16_bwt_copy_16u_matches_c() {
14889        let mut a = vec![0, 1, 65535, 65536, -1, -2, 70000, 17, 131071, -65536];
14890        let mut rust_u = vec![999; a.len()];
14891        let mut c_u = rust_u.clone();
14892
14893        bwt_copy_16u(&mut rust_u, &a, a.len() as SaSint);
14894        unsafe {
14895            probe_libsais16_bwt_copy_16u(c_u.as_mut_ptr(), a.as_mut_ptr(), a.len() as SaSint);
14896        }
14897
14898        assert_eq!(rust_u, c_u);
14899    }
14900
14901    #[test]
14902    fn libsais16_early_omp_wrappers_match_c() {
14903        let text = [3, 1, 2, 1, 0, 4, 1, 0];
14904        let n = text.len() as SaSint;
14905
14906        let mut rust_sa = vec![-99; text.len()];
14907        let mut c_sa = rust_sa.clone();
14908        gather_lms_suffixes_16u_omp(&text, &mut rust_sa, n, 1, &mut []);
14909        unsafe {
14910            probe_libsais16_gather_lms_suffixes_16u_omp(text.as_ptr(), c_sa.as_mut_ptr(), n, 1);
14911        }
14912        assert_eq!(rust_sa, c_sa);
14913
14914        let mut rust_sa = vec![-99; text.len()];
14915        let mut c_sa = rust_sa.clone();
14916        let mut rust_buckets = vec![-1; 4 * ALPHABET_SIZE];
14917        let mut c_buckets = rust_buckets.clone();
14918        let rust_m = count_and_gather_lms_suffixes_16u_omp(
14919            &text,
14920            &mut rust_sa,
14921            n,
14922            &mut rust_buckets,
14923            1,
14924            &mut [],
14925        );
14926        let c_m = unsafe {
14927            probe_libsais16_count_and_gather_lms_suffixes_16u_omp(
14928                text.as_ptr(),
14929                c_sa.as_mut_ptr(),
14930                n,
14931                c_buckets.as_mut_ptr(),
14932                1,
14933            )
14934        };
14935        assert_eq!(rust_m, c_m);
14936        assert_eq!(rust_sa, c_sa);
14937        assert_eq!(rust_buckets, c_buckets);
14938
14939        let mut rust_buckets = vec![0; 8 * ALPHABET_SIZE];
14940        let m = count_and_gather_lms_suffixes_16u(
14941            &text,
14942            &mut rust_sa,
14943            n,
14944            &mut rust_buckets[..4 * ALPHABET_SIZE],
14945            0,
14946            n,
14947        );
14948        initialize_buckets_start_and_end_16u(&mut rust_buckets, None);
14949        let first_lms_suffix = rust_sa[(n - m) as usize];
14950        initialize_buckets_for_lms_suffixes_radix_sort_16u(
14951            &text,
14952            &mut rust_buckets,
14953            first_lms_suffix,
14954        );
14955        let mut c_sa = rust_sa.clone();
14956        let mut c_buckets = rust_buckets.clone();
14957        radix_sort_lms_suffixes_16u_omp(
14958            &text,
14959            &mut rust_sa,
14960            n,
14961            m,
14962            0,
14963            &mut rust_buckets,
14964            1,
14965            &mut [],
14966        );
14967        unsafe {
14968            probe_libsais16_radix_sort_lms_suffixes_16u_omp(
14969                text.as_ptr(),
14970                c_sa.as_mut_ptr(),
14971                n,
14972                m,
14973                0,
14974                c_buckets.as_mut_ptr(),
14975                1,
14976            );
14977        }
14978        assert_eq!(rust_sa, c_sa);
14979        assert_eq!(rust_buckets, c_buckets);
14980    }
14981
14982    #[test]
14983    fn libsais16_early_omp_wrappers_use_block_partition_for_large_inputs() {
14984        let n = 65_600usize;
14985        let text: Vec<u16> = (0..n)
14986            .map(|i| 1 + ((i * 37 + i / 17) % 509) as u16)
14987            .collect();
14988
14989        let mut gathered_threaded = vec![-99; n];
14990        let mut gathered_scalar = vec![-99; n];
14991        let mut thread_state = alloc_thread_state(4).unwrap();
14992        let mut count_sa = vec![-99; n];
14993        let mut count_buckets = vec![0; 4 * ALPHABET_SIZE];
14994        count_and_gather_lms_suffixes_16u_omp(
14995            &text,
14996            &mut count_sa,
14997            n as SaSint,
14998            &mut count_buckets,
14999            4,
15000            &mut thread_state,
15001        );
15002        gather_lms_suffixes_16u_omp(
15003            &text,
15004            &mut gathered_threaded,
15005            n as SaSint,
15006            4,
15007            &mut thread_state,
15008        );
15009        gather_lms_suffixes_16u(
15010            &text,
15011            &mut gathered_scalar,
15012            n as SaSint,
15013            n as SaSint - 1,
15014            0,
15015            n as SaSint,
15016        );
15017        assert_eq!(gathered_threaded, gathered_scalar);
15018
15019        let mut sa_threaded = vec![-99; n];
15020        let mut sa_scalar = vec![-99; n];
15021        let mut buckets_threaded = vec![0; 4 * ALPHABET_SIZE];
15022        let mut buckets_scalar = vec![0; 4 * ALPHABET_SIZE];
15023        let m_threaded = count_and_gather_lms_suffixes_16u_omp(
15024            &text,
15025            &mut sa_threaded,
15026            n as SaSint,
15027            &mut buckets_threaded,
15028            4,
15029            &mut thread_state,
15030        );
15031        let m_scalar = count_and_gather_lms_suffixes_16u(
15032            &text,
15033            &mut sa_scalar,
15034            n as SaSint,
15035            &mut buckets_scalar,
15036            0,
15037            n as SaSint,
15038        );
15039        assert_eq!(m_threaded, m_scalar);
15040        assert_eq!(
15041            &sa_threaded[n - m_threaded as usize..],
15042            &sa_scalar[n - m_scalar as usize..]
15043        );
15044        assert_eq!(buckets_threaded, buckets_scalar);
15045    }
15046
15047    #[test]
15048    fn libsais16_late_omp_wrappers_match_c() {
15049        let m = 6;
15050        let mut rust_sa = vec![0; 20];
15051        rust_sa[..m].copy_from_slice(&[2, 4 | SAINT_MIN, 6, 8 | SAINT_MIN, 10, 12 | SAINT_MIN]);
15052        let mut c_sa = rust_sa.clone();
15053        let mut rust_thread_state = alloc_thread_state(1).unwrap();
15054        let rust_name =
15055            renumber_lms_suffixes_16u_omp(&mut rust_sa, m as SaSint, 1, &mut rust_thread_state);
15056        let c_name = unsafe {
15057            probe_libsais16_renumber_lms_suffixes_16u_omp(c_sa.as_mut_ptr(), m as SaSint, 1)
15058        };
15059        assert_eq!(rust_name, c_name);
15060        assert_eq!(rust_sa, c_sa);
15061
15062        let mut a = vec![0, 1, 65535, 65536, -1, -2, 70000, 17, 131071, -65536];
15063        let mut rust_u = vec![999; a.len()];
15064        let mut c_u = rust_u.clone();
15065        bwt_copy_16u_omp(&mut rust_u, &a, a.len() as SaSint, 1);
15066        unsafe {
15067            probe_libsais16_bwt_copy_16u_omp(
15068                c_u.as_mut_ptr(),
15069                a.as_mut_ptr(),
15070                a.len() as SaSint,
15071                1,
15072            );
15073        }
15074        assert_eq!(rust_u, c_u);
15075    }
15076
15077    #[test]
15078    fn libsais16_gather_marked_lms_suffixes_matches_c() {
15079        let mut rust_sa = vec![0, 0, 3 | SAINT_MIN, 4, 5 | SAINT_MIN, 6, -7, 8];
15080        let mut c_sa = rust_sa.clone();
15081
15082        let rust_l = gather_marked_lms_suffixes(&mut rust_sa, 2, 8, 0, 4) as SaSint;
15083        let c_l =
15084            unsafe { probe_libsais16_gather_marked_lms_suffixes(c_sa.as_mut_ptr(), 2, 8, 0, 4) };
15085
15086        assert_eq!(rust_l, c_l);
15087        assert_eq!(rust_sa, c_sa);
15088    }
15089
15090    #[test]
15091    fn libsais16_gather_marked_lms_suffixes_omp_matches_c() {
15092        let mut rust_sa = vec![0; 10];
15093        rust_sa[4..8].copy_from_slice(&[2 | SAINT_MIN, 4, 6 | SAINT_MIN, 8]);
15094        let mut c_sa = rust_sa.clone();
15095
15096        let mut rust_thread_state = alloc_thread_state(1).unwrap();
15097        gather_marked_lms_suffixes_omp(&mut rust_sa, 8, 4, 2, 1, &mut rust_thread_state);
15098        unsafe {
15099            probe_libsais16_gather_marked_lms_suffixes_omp(c_sa.as_mut_ptr(), 8, 4, 2, 1);
15100        }
15101
15102        assert_eq!(rust_sa, c_sa);
15103    }
15104
15105    #[test]
15106    fn libsais16_renumber_and_gather_lms_suffixes_omp_matches_c() {
15107        let mut rust_sa = vec![0; 10];
15108        rust_sa[..4].copy_from_slice(&[2, 4 | SAINT_MIN, 6, 8 | SAINT_MIN]);
15109        let mut c_sa = rust_sa.clone();
15110
15111        let mut rust_thread_state = alloc_thread_state(1).unwrap();
15112        let rust_name =
15113            renumber_and_gather_lms_suffixes_omp(&mut rust_sa, 8, 4, 2, 1, &mut rust_thread_state);
15114        let c_name = unsafe {
15115            probe_libsais16_renumber_and_gather_lms_suffixes_omp(c_sa.as_mut_ptr(), 8, 4, 2, 1)
15116        };
15117
15118        assert_eq!(rust_name, c_name);
15119        assert_eq!(rust_sa, c_sa);
15120    }
15121
15122    #[test]
15123    fn libsais16_reconstruct_lms_suffixes_matches_c() {
15124        let mut rust_sa = vec![2, 0, 1, 77, 88, 10, 11, 12];
15125        let mut c_sa = rust_sa.clone();
15126
15127        reconstruct_lms_suffixes(&mut rust_sa, 8, 3, 0, 3);
15128        unsafe {
15129            probe_libsais16_reconstruct_lms_suffixes(c_sa.as_mut_ptr(), 8, 3, 0, 3);
15130        }
15131
15132        assert_eq!(rust_sa, c_sa);
15133
15134        let mut rust_sa = vec![2, 0, 1, 77, 88, 10, 11, 12];
15135        let mut c_sa = rust_sa.clone();
15136        reconstruct_lms_suffixes_omp(&mut rust_sa, 8, 3, 1);
15137        unsafe {
15138            probe_libsais16_reconstruct_lms_suffixes_omp(c_sa.as_mut_ptr(), 8, 3, 1);
15139        }
15140
15141        assert_eq!(rust_sa, c_sa);
15142    }
15143
15144    #[test]
15145    fn libsais16_lms_late_omp_wrappers_use_block_partition() {
15146        let m = 65_536usize;
15147        let mut scalar = vec![0; 2 * m + 8];
15148        for i in 0..m {
15149            let value = (2 * i) as SaSint;
15150            scalar[i] = if i % 7 == 0 { value | SAINT_MIN } else { value };
15151        }
15152        let mut threaded = scalar.clone();
15153
15154        let mut scalar_state = alloc_thread_state(1).unwrap();
15155        let mut threaded_state = alloc_thread_state(4).unwrap();
15156        let scalar_name =
15157            renumber_lms_suffixes_16u_omp(&mut scalar, m as SaSint, 1, &mut scalar_state);
15158        let threaded_name =
15159            renumber_lms_suffixes_16u_omp(&mut threaded, m as SaSint, 4, &mut threaded_state);
15160        assert_eq!(threaded_name, scalar_name);
15161        assert_eq!(threaded, scalar);
15162
15163        let n = 131_072usize;
15164        let m = 65_536usize;
15165        let fs = 128usize;
15166        let mut scalar = vec![0; n + fs];
15167        for i in 0..(n >> 1) {
15168            let value = (i as SaSint + 1) & SAINT_MAX;
15169            scalar[m + i] = if i % 7 == 0 { value | SAINT_MIN } else { value };
15170        }
15171        let marked_count = (0..(n >> 1)).filter(|i| i % 7 == 0).count();
15172        let mut threaded = scalar.clone();
15173
15174        let mut scalar_state = alloc_thread_state(1).unwrap();
15175        let mut threaded_state = alloc_thread_state(4).unwrap();
15176        gather_marked_lms_suffixes_omp(
15177            &mut scalar,
15178            n as SaSint,
15179            m as SaSint,
15180            fs as SaSint,
15181            1,
15182            &mut scalar_state,
15183        );
15184        gather_marked_lms_suffixes_omp(
15185            &mut threaded,
15186            n as SaSint,
15187            m as SaSint,
15188            fs as SaSint,
15189            4,
15190            &mut threaded_state,
15191        );
15192        assert_eq!(
15193            &threaded[n + fs - marked_count..n + fs],
15194            &scalar[n + fs - marked_count..n + fs]
15195        );
15196
15197        let m = 65_536usize;
15198        let n = 2 * m;
15199        let mut scalar = vec![0; n];
15200        for i in 0..m {
15201            scalar[i] = i as SaSint;
15202            scalar[n - m + i] = 1_000_000 + i as SaSint;
15203        }
15204        let mut threaded = scalar.clone();
15205
15206        reconstruct_lms_suffixes_omp(&mut scalar, n as SaSint, m as SaSint, 1);
15207        reconstruct_lms_suffixes_omp(&mut threaded, n as SaSint, m as SaSint, 4);
15208        assert_eq!(threaded, scalar);
15209    }
15210
15211    #[test]
15212    fn libsais16_distinct_lms_helpers_match_c() {
15213        let m = 6;
15214        let mut rust_sa = vec![0; 18];
15215        rust_sa[..m].copy_from_slice(&[
15216            2 | SAINT_MIN,
15217            4 | SAINT_MIN,
15218            6,
15219            8 | SAINT_MIN,
15220            10,
15221            12 | SAINT_MIN,
15222        ]);
15223        let mut c_sa = rust_sa.clone();
15224        let rust_name =
15225            renumber_distinct_lms_suffixes_32s_4k(&mut rust_sa, m as SaSint, 1, 0, m as isize);
15226        let c_name = unsafe {
15227            probe_libsais16_renumber_distinct_lms_suffixes_32s_4k(
15228                c_sa.as_mut_ptr(),
15229                m as SaSint,
15230                1,
15231                0,
15232                m as SaSint,
15233            )
15234        };
15235        assert_eq!(rust_name, c_name);
15236        assert_eq!(rust_sa, c_sa);
15237
15238        let mut rust_sa = vec![0; 12];
15239        rust_sa[m..m + 6].copy_from_slice(&[SAINT_MIN | 1, 0, SAINT_MIN | 2, 0, 3, 0]);
15240        let mut c_sa = rust_sa.clone();
15241        mark_distinct_lms_suffixes_32s(&mut rust_sa, m as SaSint, 0, 6);
15242        unsafe {
15243            probe_libsais16_mark_distinct_lms_suffixes_32s(c_sa.as_mut_ptr(), m as SaSint, 0, 6);
15244        }
15245        assert_eq!(rust_sa, c_sa);
15246
15247        let mut rust_sa = vec![0; 12];
15248        rust_sa[m..m + 6].copy_from_slice(&[SAINT_MIN | 1, 7, SAINT_MIN | 2, 0, -5, 9]);
15249        let mut c_sa = rust_sa.clone();
15250        clamp_lms_suffixes_length_32s(&mut rust_sa, m as SaSint, 0, 6);
15251        unsafe {
15252            probe_libsais16_clamp_lms_suffixes_length_32s(c_sa.as_mut_ptr(), m as SaSint, 0, 6);
15253        }
15254        assert_eq!(rust_sa, c_sa);
15255    }
15256
15257    #[test]
15258    fn libsais16_distinct_lms_omp_wrappers_match_c() {
15259        let n = 12;
15260        let m = 6;
15261        let mut rust_sa = vec![0; 18];
15262        rust_sa[..m].copy_from_slice(&[
15263            2 | SAINT_MIN,
15264            4 | SAINT_MIN,
15265            6,
15266            8 | SAINT_MIN,
15267            10,
15268            12 | SAINT_MIN,
15269        ]);
15270        let mut c_sa = rust_sa.clone();
15271        let mut rust_thread_state = alloc_thread_state(1).unwrap();
15272        let rust_name = renumber_distinct_lms_suffixes_32s_4k_omp(
15273            &mut rust_sa,
15274            m as SaSint,
15275            1,
15276            &mut rust_thread_state,
15277        );
15278        let c_name = unsafe {
15279            probe_libsais16_renumber_distinct_lms_suffixes_32s_4k_omp(
15280                c_sa.as_mut_ptr(),
15281                m as SaSint,
15282                1,
15283            )
15284        };
15285        assert_eq!(rust_name, c_name);
15286        assert_eq!(rust_sa, c_sa);
15287
15288        let mut rust_sa = vec![0; 18];
15289        rust_sa[m..m + 6].copy_from_slice(&[SAINT_MIN | 1, 0, SAINT_MIN | 2, 0, 3, 0]);
15290        let mut c_sa = rust_sa.clone();
15291        mark_distinct_lms_suffixes_32s_omp(&mut rust_sa, n, m as SaSint, 1);
15292        unsafe {
15293            probe_libsais16_mark_distinct_lms_suffixes_32s_omp(
15294                c_sa.as_mut_ptr(),
15295                n,
15296                m as SaSint,
15297                1,
15298            );
15299        }
15300        assert_eq!(rust_sa, c_sa);
15301
15302        let mut rust_sa = vec![0; 18];
15303        rust_sa[m..m + 6].copy_from_slice(&[SAINT_MIN | 1, 7, SAINT_MIN | 2, 0, -5, 9]);
15304        let mut c_sa = rust_sa.clone();
15305        clamp_lms_suffixes_length_32s_omp(&mut rust_sa, n, m as SaSint, 1);
15306        unsafe {
15307            probe_libsais16_clamp_lms_suffixes_length_32s_omp(c_sa.as_mut_ptr(), n, m as SaSint, 1);
15308        }
15309        assert_eq!(rust_sa, c_sa);
15310
15311        let mut rust_sa = vec![0; 18];
15312        rust_sa[..m].copy_from_slice(&[
15313            2 | SAINT_MIN,
15314            4 | SAINT_MIN,
15315            6,
15316            8 | SAINT_MIN,
15317            10,
15318            12 | SAINT_MIN,
15319        ]);
15320        let mut c_sa = rust_sa.clone();
15321        let mut rust_thread_state = alloc_thread_state(1).unwrap();
15322        let rust_name = renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
15323            &mut rust_sa,
15324            n,
15325            m as SaSint,
15326            1,
15327            &mut rust_thread_state,
15328        );
15329        let c_name = unsafe {
15330            probe_libsais16_renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
15331                c_sa.as_mut_ptr(),
15332                n,
15333                m as SaSint,
15334                1,
15335            )
15336        };
15337        assert_eq!(rust_name, c_name);
15338        assert_eq!(rust_sa, c_sa);
15339    }
15340
15341    #[test]
15342    fn libsais16_distinct_lms_omp_wrappers_use_block_partition() {
15343        let m = 65_536usize;
15344        let mut scalar = vec![0; 2 * m];
15345        for i in 0..m {
15346            let value = (2 * i) as SaSint;
15347            scalar[i] = if i % 7 == 0 { value | SAINT_MIN } else { value };
15348        }
15349        let mut threaded = scalar.clone();
15350
15351        let mut scalar_state = alloc_thread_state(1).unwrap();
15352        let mut threaded_state = alloc_thread_state(4).unwrap();
15353        let scalar_name = renumber_distinct_lms_suffixes_32s_4k_omp(
15354            &mut scalar,
15355            m as SaSint,
15356            1,
15357            &mut scalar_state,
15358        );
15359        let threaded_name = renumber_distinct_lms_suffixes_32s_4k_omp(
15360            &mut threaded,
15361            m as SaSint,
15362            4,
15363            &mut threaded_state,
15364        );
15365        assert_eq!(threaded_name, scalar_name);
15366        assert_eq!(threaded, scalar);
15367
15368        let n = 131_072usize;
15369        let m = 65_536usize;
15370        let mut scalar = vec![0; n];
15371        for i in 0..(n >> 1) {
15372            scalar[m + i] = if i % 5 == 0 {
15373                SAINT_MIN | (i as SaSint + 1)
15374            } else if i % 11 == 0 {
15375                0
15376            } else {
15377                i as SaSint + 1
15378            };
15379        }
15380        let mut threaded = scalar.clone();
15381        mark_distinct_lms_suffixes_32s_omp(&mut scalar, n as SaSint, m as SaSint, 1);
15382        mark_distinct_lms_suffixes_32s_omp(&mut threaded, n as SaSint, m as SaSint, 4);
15383        assert_eq!(&threaded[m..n], &scalar[m..n]);
15384
15385        let mut scalar = vec![0; n];
15386        for i in 0..(n >> 1) {
15387            scalar[m + i] = if i % 5 == 0 {
15388                SAINT_MIN | (i as SaSint + 1)
15389            } else {
15390                i as SaSint + 1
15391            };
15392        }
15393        let mut threaded = scalar.clone();
15394        clamp_lms_suffixes_length_32s_omp(&mut scalar, n as SaSint, m as SaSint, 1);
15395        clamp_lms_suffixes_length_32s_omp(&mut threaded, n as SaSint, m as SaSint, 4);
15396        assert_eq!(&threaded[m..n], &scalar[m..n]);
15397    }
15398
15399    #[test]
15400    fn libsais16_unique_nonunique_lms_helpers_match_c() {
15401        let m = 4;
15402        let mut rust_t = vec![0; 12];
15403        let mut rust_sa = vec![0; 12];
15404        rust_sa[..m].copy_from_slice(&[2, 4, 6, 8]);
15405        rust_sa[m + 1] = SAINT_MIN | 11;
15406        rust_sa[m + 2] = 22;
15407        rust_sa[m + 3] = SAINT_MIN | 33;
15408        rust_sa[m + 4] = 44;
15409        let mut c_t = rust_t.clone();
15410        let mut c_sa = rust_sa.clone();
15411
15412        let rust_f = renumber_unique_and_nonunique_lms_suffixes_32s(
15413            &mut rust_t,
15414            &mut rust_sa,
15415            m as SaSint,
15416            0,
15417            0,
15418            m as isize,
15419        );
15420        let c_f = unsafe {
15421            probe_libsais16_renumber_unique_and_nonunique_lms_suffixes_32s(
15422                c_t.as_mut_ptr(),
15423                c_sa.as_mut_ptr(),
15424                m as SaSint,
15425                0,
15426                0,
15427                m as SaSint,
15428            )
15429        };
15430        assert_eq!(rust_f, c_f);
15431        assert_eq!(rust_t, c_t);
15432        assert_eq!(rust_sa, c_sa);
15433
15434        let mut rust_sa = vec![0; 10];
15435        rust_sa[m..m + 4].copy_from_slice(&[SAINT_MIN | 3, 4, SAINT_MIN | 5, 6]);
15436        let mut c_sa = rust_sa.clone();
15437        let mut rust_l = m as isize;
15438        let mut rust_r = 10isize;
15439        let mut c_l = rust_l as SaSint;
15440        let mut c_r = rust_r as SaSint;
15441        compact_unique_and_nonunique_lms_suffixes_32s(
15442            &mut rust_sa,
15443            m as SaSint,
15444            &mut rust_l,
15445            &mut rust_r,
15446            0,
15447            4,
15448        );
15449        unsafe {
15450            probe_libsais16_compact_unique_and_nonunique_lms_suffixes_32s(
15451                c_sa.as_mut_ptr(),
15452                m as SaSint,
15453                &mut c_l,
15454                &mut c_r,
15455                0,
15456                4,
15457            );
15458        }
15459        assert_eq!(rust_l as SaSint, c_l);
15460        assert_eq!(rust_r as SaSint, c_r);
15461        assert_eq!(rust_sa, c_sa);
15462    }
15463
15464    #[test]
15465    fn libsais16_unique_nonunique_lms_omp_wrappers_match_c() {
15466        let n = 8;
15467        let m = 4;
15468        let fs = 4;
15469        let mut rust_t = vec![0; 12];
15470        let mut rust_sa = vec![0; 12];
15471        rust_sa[..m].copy_from_slice(&[2, 4, 6, 8]);
15472        rust_sa[m + 1] = SAINT_MIN | 11;
15473        rust_sa[m + 2] = 22;
15474        rust_sa[m + 3] = SAINT_MIN | 33;
15475        rust_sa[m + 4] = 44;
15476        let mut c_t = rust_t.clone();
15477        let mut c_sa = rust_sa.clone();
15478
15479        let rust_f = renumber_unique_and_nonunique_lms_suffixes_32s_omp(
15480            &mut rust_t,
15481            &mut rust_sa,
15482            m as SaSint,
15483            1,
15484        );
15485        let c_f = unsafe {
15486            probe_libsais16_renumber_unique_and_nonunique_lms_suffixes_32s_omp(
15487                c_t.as_mut_ptr(),
15488                c_sa.as_mut_ptr(),
15489                m as SaSint,
15490                1,
15491            )
15492        };
15493        assert_eq!(rust_f, c_f);
15494        assert_eq!(rust_t, c_t);
15495        assert_eq!(rust_sa, c_sa);
15496
15497        let mut rust_sa = vec![0; 12];
15498        rust_sa[m..m + 4].copy_from_slice(&[SAINT_MIN | 3, 4, SAINT_MIN | 5, 6]);
15499        rust_sa[m - 2..m].copy_from_slice(&[101, 102]);
15500        let mut c_sa = rust_sa.clone();
15501        compact_unique_and_nonunique_lms_suffixes_32s_omp(&mut rust_sa, n, m as SaSint, fs, 2, 1);
15502        unsafe {
15503            probe_libsais16_compact_unique_and_nonunique_lms_suffixes_32s_omp(
15504                c_sa.as_mut_ptr(),
15505                n,
15506                m as SaSint,
15507                fs,
15508                2,
15509                1,
15510            );
15511        }
15512        assert_eq!(rust_sa, c_sa);
15513
15514        let mut rust_t = vec![0; 12];
15515        let mut rust_sa = vec![0; 12];
15516        rust_sa[..m].copy_from_slice(&[2, 4, 6, 8]);
15517        rust_sa[m + 1] = SAINT_MIN | 11;
15518        rust_sa[m + 2] = 22;
15519        rust_sa[m + 3] = SAINT_MIN | 33;
15520        rust_sa[m + 4] = 44;
15521        let mut c_t = rust_t.clone();
15522        let mut c_sa = rust_sa.clone();
15523        let rust_f = compact_lms_suffixes_32s_omp(&mut rust_t, &mut rust_sa, n, m as SaSint, fs, 1);
15524        let c_f = unsafe {
15525            probe_libsais16_compact_lms_suffixes_32s_omp(
15526                c_t.as_mut_ptr(),
15527                c_sa.as_mut_ptr(),
15528                n,
15529                m as SaSint,
15530                fs,
15531                1,
15532            )
15533        };
15534        assert_eq!(rust_f, c_f);
15535        assert_eq!(rust_t, c_t);
15536        assert_eq!(rust_sa, c_sa);
15537    }
15538
15539    #[test]
15540    fn libsais16_unique_nonunique_lms_omp_wrappers_use_block_partition() {
15541        let m = 65_536usize;
15542        let mut scalar_t = vec![0; 2 * m];
15543        let mut scalar_sa = vec![0; 2 * m];
15544        for i in 0..m {
15545            scalar_sa[i] = (2 * i) as SaSint;
15546            scalar_sa[m + i] = if i % 5 == 0 {
15547                SAINT_MIN | (i as SaSint + 3)
15548            } else {
15549                i as SaSint + 3
15550            };
15551        }
15552        let mut threaded_t = scalar_t.clone();
15553        let mut threaded_sa = scalar_sa.clone();
15554
15555        let scalar_f = renumber_unique_and_nonunique_lms_suffixes_32s_omp(
15556            &mut scalar_t,
15557            &mut scalar_sa,
15558            m as SaSint,
15559            1,
15560        );
15561        let threaded_f = renumber_unique_and_nonunique_lms_suffixes_32s_omp(
15562            &mut threaded_t,
15563            &mut threaded_sa,
15564            m as SaSint,
15565            4,
15566        );
15567        assert_eq!(threaded_f, scalar_f);
15568        assert_eq!(threaded_t, scalar_t);
15569        assert_eq!(threaded_sa, scalar_sa);
15570
15571        let n = 131_072usize;
15572        let m = 4_096usize;
15573        let fs = 8_192usize;
15574        let mut scalar_sa = vec![0; n + fs];
15575        for i in 0..(n >> 1) {
15576            scalar_sa[m + i] = if i % 32 == 0 {
15577                SAINT_MIN | (i as SaSint + 1)
15578            } else {
15579                i as SaSint + 1
15580            };
15581        }
15582        let f = 1_024usize;
15583        for i in 0..f {
15584            scalar_sa[m - f + i] = 1_000_000 + i as SaSint;
15585        }
15586        let mut threaded_sa = scalar_sa.clone();
15587
15588        compact_unique_and_nonunique_lms_suffixes_32s_omp(
15589            &mut scalar_sa,
15590            n as SaSint,
15591            m as SaSint,
15592            fs as SaSint,
15593            f as SaSint,
15594            1,
15595        );
15596        compact_unique_and_nonunique_lms_suffixes_32s_omp(
15597            &mut threaded_sa,
15598            n as SaSint,
15599            m as SaSint,
15600            fs as SaSint,
15601            f as SaSint,
15602            4,
15603        );
15604        assert_eq!(&threaded_sa[..m], &scalar_sa[..m]);
15605        assert_eq!(
15606            &threaded_sa[n + fs - m..n + fs],
15607            &scalar_sa[n + fs - m..n + fs]
15608        );
15609    }
15610
15611    #[test]
15612    fn libsais16_merge_lms_helpers_match_c() {
15613        let n = 10;
15614        let m = 3;
15615        let mut rust_t = vec![0; n as usize];
15616        rust_t[1] = SAINT_MIN | 11;
15617        rust_t[3] = SAINT_MIN | 22;
15618        rust_t[7] = SAINT_MIN | 33;
15619        let mut rust_sa = vec![0; n as usize];
15620        rust_sa[6..10].copy_from_slice(&[2, 5, 8, 9]);
15621        let mut c_t = rust_t.clone();
15622        let mut c_sa = rust_sa.clone();
15623        merge_unique_lms_suffixes_32s(&mut rust_t, &mut rust_sa, n, m, 0, 0, n as isize);
15624        unsafe {
15625            probe_libsais16_merge_unique_lms_suffixes_32s(
15626                c_t.as_mut_ptr(),
15627                c_sa.as_mut_ptr(),
15628                n,
15629                m,
15630                0,
15631                0,
15632                n,
15633            );
15634        }
15635        assert_eq!(rust_t, c_t);
15636        assert_eq!(rust_sa, c_sa);
15637
15638        let n = 10;
15639        let m = 5;
15640        let mut rust_sa = vec![9, 0, 8, 0, 0, 7, 31, 32, 33, 34];
15641        let mut c_sa = rust_sa.clone();
15642        merge_nonunique_lms_suffixes_32s(&mut rust_sa, n, m, 2, 0, m as isize);
15643        unsafe {
15644            probe_libsais16_merge_nonunique_lms_suffixes_32s(c_sa.as_mut_ptr(), n, m, 2, 0, m);
15645        }
15646        assert_eq!(rust_sa, c_sa);
15647    }
15648
15649    #[test]
15650    fn libsais16_merge_lms_omp_wrappers_match_c() {
15651        let n = 12;
15652        let m = 4;
15653        let f = 2;
15654        let mut rust_t = vec![0; n as usize];
15655        rust_t[1] = SAINT_MIN | 11;
15656        rust_t[5] = SAINT_MIN | 22;
15657        let mut rust_sa = vec![0; n as usize];
15658        rust_sa[1] = 41;
15659        rust_sa[7..12].copy_from_slice(&[2, 6, 21, 22, 23]);
15660        let mut c_t = rust_t.clone();
15661        let mut c_sa = rust_sa.clone();
15662        merge_unique_lms_suffixes_32s_omp(&mut rust_t, &mut rust_sa, n, m, 1);
15663        unsafe {
15664            probe_libsais16_merge_unique_lms_suffixes_32s_omp(
15665                c_t.as_mut_ptr(),
15666                c_sa.as_mut_ptr(),
15667                n,
15668                m,
15669                1,
15670            );
15671        }
15672        assert_eq!(rust_t, c_t);
15673        assert_eq!(rust_sa, c_sa);
15674
15675        let mut rust_sa = vec![0, 41, 1, 0, 55, 66, 77, 2, 6, 21, 22, 23];
15676        let mut c_sa = rust_sa.clone();
15677        merge_nonunique_lms_suffixes_32s_omp(&mut rust_sa, n, m, f, 1);
15678        unsafe {
15679            probe_libsais16_merge_nonunique_lms_suffixes_32s_omp(c_sa.as_mut_ptr(), n, m, f, 1);
15680        }
15681        assert_eq!(rust_sa, c_sa);
15682
15683        let mut rust_t = vec![0; n as usize];
15684        rust_t[1] = SAINT_MIN | 11;
15685        rust_t[5] = SAINT_MIN | 22;
15686        let mut rust_sa = vec![0; n as usize];
15687        rust_sa[1] = 41;
15688        rust_sa[7..12].copy_from_slice(&[2, 6, 21, 22, 23]);
15689        let mut c_t = rust_t.clone();
15690        let mut c_sa = rust_sa.clone();
15691        merge_compacted_lms_suffixes_32s_omp(&mut rust_t, &mut rust_sa, n, m, f, 1);
15692        unsafe {
15693            probe_libsais16_merge_compacted_lms_suffixes_32s_omp(
15694                c_t.as_mut_ptr(),
15695                c_sa.as_mut_ptr(),
15696                n,
15697                m,
15698                f,
15699                1,
15700            );
15701        }
15702        assert_eq!(rust_t, c_t);
15703        assert_eq!(rust_sa, c_sa);
15704    }
15705
15706    #[test]
15707    fn libsais16_merge_lms_omp_wrappers_use_block_partition() {
15708        let n = 65_536usize;
15709        let m = 10_000usize;
15710        let mut scalar_t = vec![0; n];
15711        for i in (0..n).step_by(17) {
15712            scalar_t[i] = SAINT_MIN | (i as SaSint + 1);
15713        }
15714        let unique_count = scalar_t.iter().filter(|&&value| value < 0).count();
15715        let mut scalar_sa = vec![0; n];
15716        let source = n - m - 1;
15717        for i in 0..=unique_count {
15718            scalar_sa[source + i] = ((i * 13 + 7) % n) as SaSint;
15719        }
15720        let mut threaded_t = scalar_t.clone();
15721        let mut threaded_sa = scalar_sa.clone();
15722
15723        merge_unique_lms_suffixes_32s_omp(
15724            &mut scalar_t,
15725            &mut scalar_sa,
15726            n as SaSint,
15727            m as SaSint,
15728            1,
15729        );
15730        merge_unique_lms_suffixes_32s_omp(
15731            &mut threaded_t,
15732            &mut threaded_sa,
15733            n as SaSint,
15734            m as SaSint,
15735            4,
15736        );
15737        assert_eq!(threaded_t, scalar_t);
15738        assert_eq!(threaded_sa, scalar_sa);
15739
15740        let n = 131_072usize;
15741        let m = 65_536usize;
15742        let f = 100usize;
15743        let mut scalar_sa = vec![1; n];
15744        for i in (0..m).step_by(9) {
15745            scalar_sa[i] = 0;
15746        }
15747        let zero_count = scalar_sa[..m].iter().filter(|&&value| value == 0).count();
15748        let source = n - m - 1 + f;
15749        for i in 0..=zero_count {
15750            scalar_sa[source + i] = 2_000_000 + i as SaSint;
15751        }
15752        let mut threaded_sa = scalar_sa.clone();
15753
15754        merge_nonunique_lms_suffixes_32s_omp(
15755            &mut scalar_sa,
15756            n as SaSint,
15757            m as SaSint,
15758            f as SaSint,
15759            1,
15760        );
15761        merge_nonunique_lms_suffixes_32s_omp(
15762            &mut threaded_sa,
15763            n as SaSint,
15764            m as SaSint,
15765            f as SaSint,
15766            4,
15767        );
15768        assert_eq!(threaded_sa, scalar_sa);
15769    }
15770
15771    #[test]
15772    fn libsais16_radix_sort_lms_suffixes_32s_match_c() {
15773        let t = vec![0, 1, 2, 3, 1, 2, 3, 0];
15774        let mut rust_sa = vec![0, 0, 0, 0, 0, 1, 2, 3];
15775        let mut c_sa = rust_sa.clone();
15776        let mut rust_bucket = vec![0, 6, 7, 8];
15777        let mut c_bucket = rust_bucket.clone();
15778        radix_sort_lms_suffixes_32s_6k(&t, &mut rust_sa, &mut rust_bucket, 5, 3);
15779        unsafe {
15780            probe_libsais16_radix_sort_lms_suffixes_32s_6k(
15781                t.as_ptr(),
15782                c_sa.as_mut_ptr(),
15783                c_bucket.as_mut_ptr(),
15784                5,
15785                3,
15786            );
15787        }
15788        assert_eq!(rust_sa, c_sa);
15789        assert_eq!(rust_bucket, c_bucket);
15790
15791        let mut rust_sa = vec![0, 0, 0, 0, 0, 1, 2, 3];
15792        let mut c_sa = rust_sa.clone();
15793        let mut rust_bucket = vec![0, 0, 6, 0, 7, 0, 8, 0];
15794        let mut c_bucket = rust_bucket.clone();
15795        radix_sort_lms_suffixes_32s_2k(&t, &mut rust_sa, &mut rust_bucket, 5, 3);
15796        unsafe {
15797            probe_libsais16_radix_sort_lms_suffixes_32s_2k(
15798                t.as_ptr(),
15799                c_sa.as_mut_ptr(),
15800                c_bucket.as_mut_ptr(),
15801                5,
15802                3,
15803            );
15804        }
15805        assert_eq!(rust_sa, c_sa);
15806        assert_eq!(rust_bucket, c_bucket);
15807
15808        let mut cache = vec![ThreadCache::default(); 8];
15809        let sa = vec![0, 0, 0, 0, 0, 1, 2, 3];
15810        radix_sort_lms_suffixes_32s_block_gather(&t, &sa, &mut cache, 5, 3);
15811        assert_eq!(cache[5].index, 1);
15812        assert_eq!(cache[5].symbol, 1);
15813        assert_eq!(cache[6].index, 2);
15814        assert_eq!(cache[6].symbol, 2);
15815        assert_eq!(cache[7].index, 3);
15816        assert_eq!(cache[7].symbol, 3);
15817
15818        let mut bucket = vec![0, 6, 7, 8];
15819        radix_sort_lms_suffixes_32s_6k_block_sort(&mut bucket, &mut cache, 5, 3);
15820        assert_eq!(bucket, vec![0, 5, 6, 7]);
15821        assert_eq!(cache[5].symbol, 5);
15822        assert_eq!(cache[6].symbol, 6);
15823        assert_eq!(cache[7].symbol, 7);
15824
15825        let mut cache = vec![ThreadCache::default(); 8];
15826        radix_sort_lms_suffixes_32s_block_gather(&t, &sa, &mut cache, 5, 3);
15827        let mut bucket = vec![0, 0, 6, 0, 7, 0, 8, 0];
15828        radix_sort_lms_suffixes_32s_2k_block_sort(&mut bucket, &mut cache, 5, 3);
15829        assert_eq!(bucket, vec![0, 0, 5, 0, 6, 0, 7, 0]);
15830        assert_eq!(cache[5].symbol, 5);
15831        assert_eq!(cache[6].symbol, 6);
15832        assert_eq!(cache[7].symbol, 7);
15833
15834        let mut rust_sa = vec![0, 0, 0, 0, 0, 1, 2, 3];
15835        let mut c_sa = rust_sa.clone();
15836        let mut rust_bucket = vec![0, 6, 7, 8];
15837        let mut c_bucket = rust_bucket.clone();
15838        radix_sort_lms_suffixes_32s_6k_omp(&t, &mut rust_sa, 8, 4, &mut rust_bucket, 1);
15839        unsafe {
15840            probe_libsais16_radix_sort_lms_suffixes_32s_6k_omp(
15841                t.as_ptr(),
15842                c_sa.as_mut_ptr(),
15843                8,
15844                4,
15845                c_bucket.as_mut_ptr(),
15846                1,
15847            );
15848        }
15849        assert_eq!(rust_sa, c_sa);
15850        assert_eq!(rust_bucket, c_bucket);
15851
15852        let mut rust_sa = vec![0, 0, 0, 0, 0, 1, 2, 3];
15853        let mut c_sa = rust_sa.clone();
15854        let mut rust_bucket = vec![0, 0, 6, 0, 7, 0, 8, 0];
15855        let mut c_bucket = rust_bucket.clone();
15856        radix_sort_lms_suffixes_32s_2k_omp(&t, &mut rust_sa, 8, 4, &mut rust_bucket, 1);
15857        unsafe {
15858            probe_libsais16_radix_sort_lms_suffixes_32s_2k_omp(
15859                t.as_ptr(),
15860                c_sa.as_mut_ptr(),
15861                8,
15862                4,
15863                c_bucket.as_mut_ptr(),
15864                1,
15865            );
15866        }
15867        assert_eq!(rust_sa, c_sa);
15868        assert_eq!(rust_bucket, c_bucket);
15869
15870        let t = vec![2, 1, 3, 1, 0];
15871        let mut rust_sa = vec![0; t.len()];
15872        let mut c_sa = rust_sa.clone();
15873        let mut rust_bucket = vec![0, 2, 4, 5];
15874        let mut c_bucket = rust_bucket.clone();
15875        let rust_m =
15876            radix_sort_lms_suffixes_32s_1k(&t, &mut rust_sa, t.len() as SaSint, &mut rust_bucket);
15877        let c_m = unsafe {
15878            probe_libsais16_radix_sort_lms_suffixes_32s_1k(
15879                t.as_ptr(),
15880                c_sa.as_mut_ptr(),
15881                t.len() as SaSint,
15882                c_bucket.as_mut_ptr(),
15883            )
15884        };
15885        assert_eq!(rust_m, c_m);
15886        assert_eq!(rust_sa, c_sa);
15887        assert_eq!(rust_bucket, c_bucket);
15888    }
15889
15890    #[test]
15891    fn libsais16_radix_sort_set_markers_32s_match_c() {
15892        let mut rust_sa = vec![0; 8];
15893        let mut c_sa = rust_sa.clone();
15894        let mut induction_bucket = vec![1, 3, 5, 7];
15895        radix_sort_set_markers_32s_6k(&mut rust_sa, &induction_bucket, 0, 4);
15896        unsafe {
15897            probe_libsais16_radix_sort_set_markers_32s_6k(
15898                c_sa.as_mut_ptr(),
15899                induction_bucket.as_mut_ptr(),
15900                0,
15901                4,
15902            );
15903        }
15904        assert_eq!(rust_sa, c_sa);
15905
15906        let mut rust_sa = vec![0; 8];
15907        let mut c_sa = rust_sa.clone();
15908        radix_sort_set_markers_32s_6k_omp(&mut rust_sa, 5, &induction_bucket, 1);
15909        unsafe {
15910            probe_libsais16_radix_sort_set_markers_32s_6k_omp(
15911                c_sa.as_mut_ptr(),
15912                5,
15913                induction_bucket.as_mut_ptr(),
15914                1,
15915            );
15916        }
15917        assert_eq!(rust_sa, c_sa);
15918
15919        let mut rust_sa = vec![0; 8];
15920        let mut c_sa = rust_sa.clone();
15921        let mut induction_bucket = vec![1, 0, 3, 0, 5, 0, 7, 0];
15922        radix_sort_set_markers_32s_4k(&mut rust_sa, &induction_bucket, 0, 4);
15923        unsafe {
15924            probe_libsais16_radix_sort_set_markers_32s_4k(
15925                c_sa.as_mut_ptr(),
15926                induction_bucket.as_mut_ptr(),
15927                0,
15928                4,
15929            );
15930        }
15931        assert_eq!(rust_sa, c_sa);
15932
15933        let mut rust_sa = vec![0; 8];
15934        let mut c_sa = rust_sa.clone();
15935        radix_sort_set_markers_32s_4k_omp(&mut rust_sa, 5, &induction_bucket, 1);
15936        unsafe {
15937            probe_libsais16_radix_sort_set_markers_32s_4k_omp(
15938                c_sa.as_mut_ptr(),
15939                5,
15940                induction_bucket.as_mut_ptr(),
15941                1,
15942            );
15943        }
15944        assert_eq!(rust_sa, c_sa);
15945    }
15946
15947    #[test]
15948    fn libsais16_radix_sort_set_markers_32s_omp_partitions_large_inputs() {
15949        let k = 65_600usize;
15950        let induction_bucket_6k: Vec<SaSint> = (0..k).map(|i| i as SaSint).collect();
15951        let mut single = vec![0; k];
15952        let mut threaded = vec![0; k];
15953        radix_sort_set_markers_32s_6k_omp(&mut single, k as SaSint, &induction_bucket_6k, 1);
15954        radix_sort_set_markers_32s_6k_omp(&mut threaded, k as SaSint, &induction_bucket_6k, 4);
15955        assert_eq!(threaded, single);
15956
15957        let mut induction_bucket_4k = vec![0; 2 * k];
15958        for i in 0..k {
15959            induction_bucket_4k[buckets_index2(i, 0)] = i as SaSint;
15960        }
15961        let mut single = vec![0; k];
15962        let mut threaded = vec![0; k];
15963        radix_sort_set_markers_32s_4k_omp(&mut single, k as SaSint, &induction_bucket_4k, 1);
15964        radix_sort_set_markers_32s_4k_omp(&mut threaded, k as SaSint, &induction_bucket_4k, 4);
15965        assert_eq!(threaded, single);
15966    }
15967
15968    #[test]
15969    fn libsais16_partial_sorting_32s_helpers_match_c() {
15970        let k = 3;
15971        let mut rust_sa = vec![0, SAINT_MIN, 2, SAINT_MIN, 4, SAINT_MIN];
15972        let mut c_sa = rust_sa.clone();
15973        let mut buckets = vec![0; 6 * k as usize];
15974        buckets[buckets_index4(1, 0)] = 3;
15975        buckets[buckets_index4(2, 0)] = 6;
15976        buckets[4 * k as usize + buckets_index2(0, 0)] = 0;
15977        buckets[4 * k as usize + buckets_index2(1, 0)] = 1;
15978        partial_sorting_shift_markers_32s_6k_omp(&mut rust_sa, k, &buckets, 1);
15979        unsafe {
15980            probe_libsais16_partial_sorting_shift_markers_32s_6k_omp(
15981                c_sa.as_mut_ptr(),
15982                k,
15983                buckets.as_ptr(),
15984                1,
15985            );
15986        }
15987        assert_eq!(rust_sa, c_sa);
15988
15989        let mut rust_sa = vec![
15990            1 | SUFFIX_GROUP_MARKER,
15991            2,
15992            3 | SUFFIX_GROUP_MARKER,
15993            4 | SUFFIX_GROUP_MARKER,
15994            5,
15995            6,
15996        ];
15997        let mut c_sa = rust_sa.clone();
15998        partial_sorting_shift_markers_32s_4k(&mut rust_sa, 6);
15999        unsafe { probe_libsais16_partial_sorting_shift_markers_32s_4k(c_sa.as_mut_ptr(), 6) };
16000        assert_eq!(rust_sa, c_sa);
16001
16002        let mut rust_buckets = vec![0; 6 * k as usize];
16003        for (i, value) in rust_buckets[4 * k as usize..].iter_mut().enumerate() {
16004            *value = 100 + i as SaSint;
16005        }
16006        let mut c_buckets = rust_buckets.clone();
16007        partial_sorting_shift_buckets_32s_6k(k, &mut rust_buckets);
16008        unsafe { probe_libsais16_partial_sorting_shift_buckets_32s_6k(k, c_buckets.as_mut_ptr()) };
16009        assert_eq!(rust_buckets, c_buckets);
16010
16011        let mut rust_sa = vec![1 | SUFFIX_GROUP_MARKER, -3, 5 | SUFFIX_GROUP_MARKER, -7];
16012        let mut c_sa = rust_sa.clone();
16013        let rust_l = partial_sorting_gather_lms_suffixes_32s_4k(&mut rust_sa, 0, 4);
16014        let c_l = unsafe {
16015            probe_libsais16_partial_sorting_gather_lms_suffixes_32s_4k(c_sa.as_mut_ptr(), 0, 4)
16016        };
16017        assert_eq!(rust_l, c_l);
16018        assert_eq!(rust_sa, c_sa);
16019
16020        let mut rust_sa = vec![1, -3, 5, -7];
16021        let mut c_sa = rust_sa.clone();
16022        let rust_l = partial_sorting_gather_lms_suffixes_32s_1k(&mut rust_sa, 0, 4);
16023        let c_l = unsafe {
16024            probe_libsais16_partial_sorting_gather_lms_suffixes_32s_1k(c_sa.as_mut_ptr(), 0, 4)
16025        };
16026        assert_eq!(rust_l, c_l);
16027        assert_eq!(rust_sa, c_sa);
16028
16029        let mut rust_state = alloc_thread_state(1).unwrap();
16030        let mut rust_sa = vec![1 | SUFFIX_GROUP_MARKER, -3, 5 | SUFFIX_GROUP_MARKER, -7];
16031        let mut c_sa = rust_sa.clone();
16032        partial_sorting_gather_lms_suffixes_32s_4k_omp(&mut rust_sa, 4, 1, &mut rust_state);
16033        unsafe {
16034            probe_libsais16_partial_sorting_gather_lms_suffixes_32s_4k_omp(c_sa.as_mut_ptr(), 4, 1);
16035        }
16036        assert_eq!(rust_sa, c_sa);
16037
16038        let mut rust_state = alloc_thread_state(1).unwrap();
16039        let mut rust_sa = vec![1, -3, 5, -7];
16040        let mut c_sa = rust_sa.clone();
16041        partial_sorting_gather_lms_suffixes_32s_1k_omp(&mut rust_sa, 4, 1, &mut rust_state);
16042        unsafe {
16043            probe_libsais16_partial_sorting_gather_lms_suffixes_32s_1k_omp(c_sa.as_mut_ptr(), 4, 1);
16044        }
16045        assert_eq!(rust_sa, c_sa);
16046    }
16047
16048    #[test]
16049    fn libsais16_partial_sorting_gather_lms_suffixes_32s_omp_uses_block_partition() {
16050        let n = 65_536usize;
16051        let mut base_4k = vec![0; n];
16052        let mut base_1k = vec![0; n];
16053        for i in 0..n {
16054            let value = (i as SaSint + 1) & SAINT_MAX;
16055            base_4k[i] = if i % 7 == 0 {
16056                value | SAINT_MIN | SUFFIX_GROUP_MARKER
16057            } else if i % 11 == 0 {
16058                value | SUFFIX_GROUP_MARKER
16059            } else {
16060                value
16061            };
16062            base_1k[i] = if i % 7 == 0 { value | SAINT_MIN } else { value };
16063        }
16064        let lms_count = base_1k.iter().filter(|&&v| v < 0).count();
16065
16066        let mut scalar = base_4k.clone();
16067        let mut threaded = base_4k;
16068        let mut scalar_state = alloc_thread_state(1).unwrap();
16069        let mut threaded_state = alloc_thread_state(4).unwrap();
16070        partial_sorting_gather_lms_suffixes_32s_4k_omp(
16071            &mut scalar,
16072            n as SaSint,
16073            1,
16074            &mut scalar_state,
16075        );
16076        partial_sorting_gather_lms_suffixes_32s_4k_omp(
16077            &mut threaded,
16078            n as SaSint,
16079            4,
16080            &mut threaded_state,
16081        );
16082        assert_eq!(&threaded[..lms_count], &scalar[..lms_count]);
16083
16084        let mut scalar = base_1k.clone();
16085        let mut threaded = base_1k;
16086        partial_sorting_gather_lms_suffixes_32s_1k_omp(
16087            &mut scalar,
16088            n as SaSint,
16089            1,
16090            &mut scalar_state,
16091        );
16092        partial_sorting_gather_lms_suffixes_32s_1k_omp(
16093            &mut threaded,
16094            n as SaSint,
16095            4,
16096            &mut threaded_state,
16097        );
16098        assert_eq!(&threaded[..lms_count], &scalar[..lms_count]);
16099    }
16100
16101    #[test]
16102    fn libsais16_partial_sorting_32s_block_helpers_behave_like_upstream_shapes() {
16103        let t = vec![0, 1, 2, 1, 0];
16104        let k = 3;
16105
16106        let mut sa = vec![0, 4 | SAINT_MIN, 0];
16107        let mut cache = vec![ThreadCache::default(); sa.len()];
16108        partial_sorting_scan_right_to_left_32s_6k_block_gather(&t, &mut sa, &mut cache, 1, 1);
16109        assert_eq!(cache[1].index, 4 | SAINT_MIN);
16110        assert_eq!(cache[1].symbol, buckets_index4(1, 1) as SaSint);
16111
16112        let mut sa = vec![0, 4 | SUFFIX_GROUP_MARKER, 0];
16113        let mut cache = vec![ThreadCache::default(); sa.len()];
16114        partial_sorting_scan_right_to_left_32s_4k_block_gather(&t, &mut sa, &mut cache, 1, 1);
16115        assert_eq!(sa[1], 0);
16116        assert_eq!(cache[1].index, 4 | SUFFIX_GROUP_MARKER);
16117        assert_eq!(cache[1].symbol, buckets_index2(1, 1) as SaSint);
16118
16119        let mut sa = vec![0, 4, 0];
16120        let mut cache = vec![ThreadCache::default(); sa.len()];
16121        partial_sorting_scan_right_to_left_32s_1k_block_gather(&t, &mut sa, &mut cache, 1, 1);
16122        assert_eq!(sa[1], 0);
16123        assert_eq!(cache[1].index, 3 | SAINT_MIN);
16124        assert_eq!(cache[1].symbol, 1);
16125
16126        let mut sa = vec![4 | SAINT_MIN, 0, 0];
16127        let mut cache = vec![ThreadCache::default(); sa.len()];
16128        partial_sorting_scan_left_to_right_32s_6k_block_gather(&t, &mut sa, &mut cache, 0, 1);
16129        assert_eq!(cache[0].index, 4 | SAINT_MIN);
16130        assert_eq!(cache[0].symbol, buckets_index4(1, 1) as SaSint);
16131
16132        let mut sa = vec![4 | SUFFIX_GROUP_MARKER, 0, 0];
16133        let mut cache = vec![ThreadCache::default(); sa.len()];
16134        partial_sorting_scan_left_to_right_32s_4k_block_gather(&t, &mut sa, &mut cache, 0, 1);
16135        assert_eq!(sa[0], 0);
16136        assert_eq!(cache[0].index, 4 | SUFFIX_GROUP_MARKER);
16137        assert_eq!(cache[0].symbol, buckets_index2(1, 0) as SaSint);
16138
16139        let mut sa = vec![4, 0, 0];
16140        let mut cache = vec![ThreadCache::default(); sa.len()];
16141        partial_sorting_scan_left_to_right_32s_1k_block_gather(&t, &mut sa, &mut cache, 0, 1);
16142        assert_eq!(sa[0], 0);
16143        assert_eq!(cache[0].index, 3);
16144        assert_eq!(cache[0].symbol, 1);
16145
16146        let mut cache = vec![ThreadCache::default(); 3];
16147        cache[1].index = 4 | SAINT_MIN;
16148        cache[1].symbol = buckets_index4(1, 1) as SaSint;
16149        let mut buckets = vec![0; 4 * k];
16150        buckets[buckets_index4(1, 1)] = 2;
16151        let d = partial_sorting_scan_right_to_left_32s_6k_block_sort(
16152            &t,
16153            &mut buckets,
16154            0,
16155            &mut cache,
16156            1,
16157            1,
16158        );
16159        assert_eq!(d, 1);
16160        assert_eq!(cache[1].index, 3 | SAINT_MIN);
16161        assert_eq!(buckets[buckets_index4(1, 1)], 1);
16162        assert_eq!(buckets[buckets_index4(1, 1) + 2], 1);
16163
16164        let mut cache = vec![ThreadCache::default(); 3];
16165        cache[0].index = 4 | SAINT_MIN;
16166        cache[0].symbol = buckets_index4(1, 1) as SaSint;
16167        let mut buckets = vec![0; 4 * k];
16168        buckets[buckets_index4(1, 1)] = 1;
16169        let d = partial_sorting_scan_left_to_right_32s_6k_block_sort(
16170            &t,
16171            &mut buckets,
16172            0,
16173            &mut cache,
16174            0,
16175            1,
16176        );
16177        assert_eq!(d, 1);
16178        assert_eq!(cache[0].index, 3 | SAINT_MIN);
16179        assert_eq!(buckets[buckets_index4(1, 1)], 2);
16180        assert_eq!(buckets[buckets_index4(1, 1) + 2], 1);
16181
16182        let mut cache = vec![ThreadCache::default(); 3];
16183        cache[1].index = 4 | SUFFIX_GROUP_MARKER;
16184        cache[1].symbol = buckets_index2(1, 1) as SaSint;
16185        let mut buckets = vec![0; 4 * k];
16186        buckets[3 * k + 1] = 2;
16187        let d = partial_sorting_scan_right_to_left_32s_4k_block_sort(
16188            &t,
16189            k as SaSint,
16190            &mut buckets,
16191            0,
16192            &mut cache,
16193            1,
16194            1,
16195        );
16196        assert_eq!(d, 1);
16197        assert_eq!(cache[1].symbol, 1);
16198        assert_eq!(buckets[3 * k + 1], 1);
16199
16200        let mut cache = vec![ThreadCache::default(); 3];
16201        cache[0].index = 4 | SUFFIX_GROUP_MARKER;
16202        cache[0].symbol = buckets_index2(1, 0) as SaSint;
16203        let mut buckets = vec![0; 4 * k];
16204        buckets[2 * k + 1] = 1;
16205        let d = partial_sorting_scan_left_to_right_32s_4k_block_sort(
16206            &t,
16207            k as SaSint,
16208            &mut buckets,
16209            0,
16210            &mut cache,
16211            0,
16212            1,
16213        );
16214        assert_eq!(d, 1);
16215        assert_eq!(cache[0].symbol, 1);
16216        assert_eq!(buckets[2 * k + 1], 2);
16217
16218        let mut cache = vec![ThreadCache::default(); 3];
16219        cache[1].index = 4;
16220        cache[1].symbol = 1;
16221        let mut buckets = vec![0; k];
16222        buckets[1] = 2;
16223        partial_sorting_scan_right_to_left_32s_1k_block_sort(&t, &mut buckets, &mut cache, 1, 1);
16224        assert_eq!(cache[1].symbol, 1);
16225        assert_eq!(buckets[1], 1);
16226
16227        let mut cache = vec![ThreadCache::default(); 3];
16228        cache[0].index = 4;
16229        cache[0].symbol = 1;
16230        let mut buckets = vec![0; k];
16231        buckets[1] = 1;
16232        partial_sorting_scan_left_to_right_32s_1k_block_sort(&t, &mut buckets, &mut cache, 0, 1);
16233        assert_eq!(cache[0].symbol, 1);
16234        assert_eq!(buckets[1], 2);
16235    }
16236
16237    #[test]
16238    fn libsais16_partial_sorting_scan_32s_match_c() {
16239        let t = vec![0, 1, 2, 1, 3, 0];
16240        let k = 4;
16241
16242        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 9, 9];
16243        let mut c_sa = rust_sa.clone();
16244        let mut rust_buckets = vec![0; 6 * k as usize];
16245        rust_buckets[buckets_index4(2, 0)] = 4;
16246        rust_buckets[buckets_index4(1, 1)] = 5;
16247        let mut c_buckets = rust_buckets.clone();
16248        let rust_d =
16249            partial_sorting_scan_left_to_right_32s_6k(&t, &mut rust_sa, &mut rust_buckets, 0, 0, 2);
16250        let c_d = unsafe {
16251            probe_libsais16_partial_sorting_scan_left_to_right_32s_6k(
16252                t.as_ptr(),
16253                c_sa.as_mut_ptr(),
16254                c_buckets.as_mut_ptr(),
16255                0,
16256                0,
16257                2,
16258            )
16259        };
16260        assert_eq!(rust_d, c_d);
16261        assert_eq!(rust_sa, c_sa);
16262        assert_eq!(rust_buckets, c_buckets);
16263
16264        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 9, 9];
16265        let mut c_sa = rust_sa.clone();
16266        let mut rust_buckets = vec![0; 4 * k as usize];
16267        rust_buckets[2 * k as usize + 2] = 4;
16268        rust_buckets[2 * k as usize + 1] = 5;
16269        let mut c_buckets = rust_buckets.clone();
16270        let rust_d = partial_sorting_scan_left_to_right_32s_4k(
16271            &t,
16272            &mut rust_sa,
16273            k,
16274            &mut rust_buckets,
16275            0,
16276            0,
16277            2,
16278        );
16279        let c_d = unsafe {
16280            probe_libsais16_partial_sorting_scan_left_to_right_32s_4k(
16281                t.as_ptr(),
16282                c_sa.as_mut_ptr(),
16283                k,
16284                c_buckets.as_mut_ptr(),
16285                0,
16286                0,
16287                2,
16288            )
16289        };
16290        assert_eq!(rust_d, c_d);
16291        assert_eq!(rust_sa, c_sa);
16292        assert_eq!(rust_buckets, c_buckets);
16293
16294        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 9, 9];
16295        let mut c_sa = rust_sa.clone();
16296        let mut rust_buckets = vec![0, 5, 4, 0];
16297        let mut c_buckets = rust_buckets.clone();
16298        partial_sorting_scan_left_to_right_32s_1k(&t, &mut rust_sa, &mut rust_buckets, 0, 2);
16299        unsafe {
16300            probe_libsais16_partial_sorting_scan_left_to_right_32s_1k(
16301                t.as_ptr(),
16302                c_sa.as_mut_ptr(),
16303                c_buckets.as_mut_ptr(),
16304                0,
16305                2,
16306            );
16307        }
16308        assert_eq!(rust_sa, c_sa);
16309        assert_eq!(rust_buckets, c_buckets);
16310
16311        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 9, 9];
16312        let mut c_sa = rust_sa.clone();
16313        let mut rust_buckets = vec![0; 6 * k as usize];
16314        rust_buckets[buckets_index4(2, 0)] = 7;
16315        rust_buckets[buckets_index4(1, 1)] = 6;
16316        let mut c_buckets = rust_buckets.clone();
16317        let rust_d =
16318            partial_sorting_scan_right_to_left_32s_6k(&t, &mut rust_sa, &mut rust_buckets, 0, 0, 2);
16319        let c_d = unsafe {
16320            probe_libsais16_partial_sorting_scan_right_to_left_32s_6k(
16321                t.as_ptr(),
16322                c_sa.as_mut_ptr(),
16323                c_buckets.as_mut_ptr(),
16324                0,
16325                0,
16326                2,
16327            )
16328        };
16329        assert_eq!(rust_d, c_d);
16330        assert_eq!(rust_sa, c_sa);
16331        assert_eq!(rust_buckets, c_buckets);
16332
16333        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 9, 9];
16334        let mut c_sa = rust_sa.clone();
16335        let mut rust_buckets = vec![0; 4 * k as usize];
16336        rust_buckets[3 * k as usize + 2] = 7;
16337        rust_buckets[3 * k as usize + 1] = 6;
16338        let mut c_buckets = rust_buckets.clone();
16339        let rust_d = partial_sorting_scan_right_to_left_32s_4k(
16340            &t,
16341            &mut rust_sa,
16342            k,
16343            &mut rust_buckets,
16344            0,
16345            0,
16346            2,
16347        );
16348        let c_d = unsafe {
16349            probe_libsais16_partial_sorting_scan_right_to_left_32s_4k(
16350                t.as_ptr(),
16351                c_sa.as_mut_ptr(),
16352                k,
16353                c_buckets.as_mut_ptr(),
16354                0,
16355                0,
16356                2,
16357            )
16358        };
16359        assert_eq!(rust_d, c_d);
16360        assert_eq!(rust_sa, c_sa);
16361        assert_eq!(rust_buckets, c_buckets);
16362
16363        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 9, 9];
16364        let mut c_sa = rust_sa.clone();
16365        let mut rust_buckets = vec![0, 6, 7, 0];
16366        let mut c_buckets = rust_buckets.clone();
16367        partial_sorting_scan_right_to_left_32s_1k(&t, &mut rust_sa, &mut rust_buckets, 0, 2);
16368        unsafe {
16369            probe_libsais16_partial_sorting_scan_right_to_left_32s_1k(
16370                t.as_ptr(),
16371                c_sa.as_mut_ptr(),
16372                c_buckets.as_mut_ptr(),
16373                0,
16374                2,
16375            );
16376        }
16377        assert_eq!(rust_sa, c_sa);
16378        assert_eq!(rust_buckets, c_buckets);
16379
16380        let mut state = alloc_thread_state(1).unwrap();
16381        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 7, 9];
16382        let mut c_sa = rust_sa.clone();
16383        let mut rust_buckets = vec![0; 6 * k as usize];
16384        rust_buckets[buckets_index4(2, 0)] = 4;
16385        rust_buckets[buckets_index4(1, 1)] = 5;
16386        rust_buckets[buckets_index4(3, 0)] = 6;
16387        let mut c_buckets = rust_buckets.clone();
16388        let rust_d = partial_sorting_scan_left_to_right_32s_6k_omp(
16389            &t,
16390            &mut rust_sa,
16391            5,
16392            &mut rust_buckets,
16393            2,
16394            0,
16395            1,
16396            &mut state,
16397        );
16398        let c_d = unsafe {
16399            probe_libsais16_partial_sorting_scan_left_to_right_32s_6k_omp(
16400                t.as_ptr(),
16401                c_sa.as_mut_ptr(),
16402                5,
16403                c_buckets.as_mut_ptr(),
16404                2,
16405                0,
16406                1,
16407            )
16408        };
16409        assert_eq!(rust_d, c_d);
16410        assert_eq!(rust_sa, c_sa);
16411        assert_eq!(rust_buckets, c_buckets);
16412
16413        let mut state = alloc_thread_state(1).unwrap();
16414        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 7, 9];
16415        let mut c_sa = rust_sa.clone();
16416        let mut rust_buckets = vec![0; 4 * k as usize];
16417        rust_buckets[2 * k as usize + 2] = 4;
16418        rust_buckets[2 * k as usize + 1] = 5;
16419        rust_buckets[2 * k as usize + 3] = 6;
16420        let mut c_buckets = rust_buckets.clone();
16421        let rust_d = partial_sorting_scan_left_to_right_32s_4k_omp(
16422            &t,
16423            &mut rust_sa,
16424            5,
16425            k,
16426            &mut rust_buckets,
16427            0,
16428            1,
16429            &mut state,
16430        );
16431        let c_d = unsafe {
16432            probe_libsais16_partial_sorting_scan_left_to_right_32s_4k_omp(
16433                t.as_ptr(),
16434                c_sa.as_mut_ptr(),
16435                5,
16436                k,
16437                c_buckets.as_mut_ptr(),
16438                0,
16439                1,
16440            )
16441        };
16442        assert_eq!(rust_d, c_d);
16443        assert_eq!(rust_sa, c_sa);
16444        assert_eq!(rust_buckets, c_buckets);
16445
16446        let mut state = alloc_thread_state(1).unwrap();
16447        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 7, 9];
16448        let mut c_sa = rust_sa.clone();
16449        let mut rust_buckets = vec![0, 5, 4, 6];
16450        let mut c_buckets = rust_buckets.clone();
16451        partial_sorting_scan_left_to_right_32s_1k_omp(
16452            &t,
16453            &mut rust_sa,
16454            5,
16455            &mut rust_buckets,
16456            1,
16457            &mut state,
16458        );
16459        unsafe {
16460            probe_libsais16_partial_sorting_scan_left_to_right_32s_1k_omp(
16461                t.as_ptr(),
16462                c_sa.as_mut_ptr(),
16463                5,
16464                c_buckets.as_mut_ptr(),
16465                1,
16466            );
16467        }
16468        assert_eq!(rust_sa, c_sa);
16469        assert_eq!(rust_buckets, c_buckets);
16470
16471        let mut state = alloc_thread_state(1).unwrap();
16472        let mut rust_sa = vec![0, 0, 3, 4, 9, 9, 9, 9];
16473        let mut c_sa = rust_sa.clone();
16474        let mut rust_buckets = vec![0; 6 * k as usize];
16475        rust_buckets[buckets_index4(2, 0)] = 7;
16476        rust_buckets[buckets_index4(1, 1)] = 6;
16477        let mut c_buckets = rust_buckets.clone();
16478        let rust_d = partial_sorting_scan_right_to_left_32s_6k_omp(
16479            &t,
16480            &mut rust_sa,
16481            5,
16482            &mut rust_buckets,
16483            1,
16484            1,
16485            0,
16486            1,
16487            &mut state,
16488        );
16489        let c_d = unsafe {
16490            probe_libsais16_partial_sorting_scan_right_to_left_32s_6k_omp(
16491                t.as_ptr(),
16492                c_sa.as_mut_ptr(),
16493                5,
16494                c_buckets.as_mut_ptr(),
16495                1,
16496                1,
16497                0,
16498                1,
16499            )
16500        };
16501        assert_eq!(rust_d, c_d);
16502        assert_eq!(rust_sa, c_sa);
16503        assert_eq!(rust_buckets, c_buckets);
16504
16505        let mut state = alloc_thread_state(1).unwrap();
16506        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 9, 9];
16507        let mut c_sa = rust_sa.clone();
16508        let mut rust_buckets = vec![0; 4 * k as usize];
16509        rust_buckets[3 * k as usize + 2] = 7;
16510        rust_buckets[3 * k as usize + 1] = 6;
16511        let mut c_buckets = rust_buckets.clone();
16512        let rust_d = partial_sorting_scan_right_to_left_32s_4k_omp(
16513            &t,
16514            &mut rust_sa,
16515            2,
16516            k,
16517            &mut rust_buckets,
16518            0,
16519            1,
16520            &mut state,
16521        );
16522        let c_d = unsafe {
16523            probe_libsais16_partial_sorting_scan_right_to_left_32s_4k_omp(
16524                t.as_ptr(),
16525                c_sa.as_mut_ptr(),
16526                2,
16527                k,
16528                c_buckets.as_mut_ptr(),
16529                0,
16530                1,
16531            )
16532        };
16533        assert_eq!(rust_d, c_d);
16534        assert_eq!(rust_sa, c_sa);
16535        assert_eq!(rust_buckets, c_buckets);
16536
16537        let mut state = alloc_thread_state(1).unwrap();
16538        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 9, 9];
16539        let mut c_sa = rust_sa.clone();
16540        let mut rust_buckets = vec![0, 6, 7, 0];
16541        let mut c_buckets = rust_buckets.clone();
16542        partial_sorting_scan_right_to_left_32s_1k_omp(
16543            &t,
16544            &mut rust_sa,
16545            2,
16546            &mut rust_buckets,
16547            1,
16548            &mut state,
16549        );
16550        unsafe {
16551            probe_libsais16_partial_sorting_scan_right_to_left_32s_1k_omp(
16552                t.as_ptr(),
16553                c_sa.as_mut_ptr(),
16554                2,
16555                c_buckets.as_mut_ptr(),
16556                1,
16557            );
16558        }
16559        assert_eq!(rust_sa, c_sa);
16560        assert_eq!(rust_buckets, c_buckets);
16561    }
16562
16563    #[test]
16564    fn libsais16_place_lms_suffixes_histogram_32s_match_c() {
16565        let n = 12;
16566        let k = 4;
16567        let m = 4;
16568        let mut rust_sa = vec![101, 102, 103, 104, 9, 9, 9, 9, 9, 9, 9, 9];
16569        let mut c_sa = rust_sa.clone();
16570        let mut buckets = vec![0; 2 * k as usize];
16571        buckets[buckets_index2(1, 0)] = 7;
16572        buckets[buckets_index2(1, 1)] = 2;
16573        buckets[buckets_index2(2, 0)] = 10;
16574        buckets[buckets_index2(2, 1)] = 1;
16575        place_lms_suffixes_histogram_32s_2k(&mut rust_sa, n, k, m, &buckets);
16576        unsafe {
16577            probe_libsais16_place_lms_suffixes_histogram_32s_2k(
16578                c_sa.as_mut_ptr(),
16579                n,
16580                k,
16581                m,
16582                buckets.as_ptr(),
16583            );
16584        }
16585        assert_eq!(rust_sa, c_sa);
16586
16587        let mut rust_sa = vec![101, 102, 103, 104, 9, 9, 9, 9, 9, 9, 9, 9];
16588        let mut c_sa = rust_sa.clone();
16589        let mut buckets = vec![0; 4 * k as usize];
16590        buckets[buckets_index2(1, 1)] = 2;
16591        buckets[buckets_index2(2, 1)] = 1;
16592        buckets[3 * k as usize + 1] = 7;
16593        buckets[3 * k as usize + 2] = 10;
16594        place_lms_suffixes_histogram_32s_4k(&mut rust_sa, n, k, m, &buckets);
16595        unsafe {
16596            probe_libsais16_place_lms_suffixes_histogram_32s_4k(
16597                c_sa.as_mut_ptr(),
16598                n,
16599                k,
16600                m,
16601                buckets.as_ptr(),
16602            );
16603        }
16604        assert_eq!(rust_sa, c_sa);
16605
16606        let mut rust_sa = vec![101, 102, 103, 104, 9, 9, 9, 9, 9, 9, 9, 9];
16607        let mut c_sa = rust_sa.clone();
16608        let mut buckets = vec![0; 6 * k as usize];
16609        buckets[buckets_index4(1, 1)] = 2;
16610        buckets[buckets_index4(2, 1)] = 1;
16611        buckets[5 * k as usize + 1] = 7;
16612        buckets[5 * k as usize + 2] = 10;
16613        place_lms_suffixes_histogram_32s_6k(&mut rust_sa, n, k, m, &buckets);
16614        unsafe {
16615            probe_libsais16_place_lms_suffixes_histogram_32s_6k(
16616                c_sa.as_mut_ptr(),
16617                n,
16618                k,
16619                m,
16620                buckets.as_ptr(),
16621            );
16622        }
16623        assert_eq!(rust_sa, c_sa);
16624    }
16625
16626    #[test]
16627    fn libsais16_count_gather_lms_suffixes_32s_match_c() {
16628        let t = vec![2, 1, 3, 1, 2, 0, 1, 0];
16629        let n = t.len() as SaSint;
16630        let k = 4;
16631
16632        let mut rust_sa = vec![0; t.len()];
16633        let mut c_sa = rust_sa.clone();
16634        let rust_m = gather_lms_suffixes_32s(&t, &mut rust_sa, n);
16635        let c_m =
16636            unsafe { probe_libsais16_gather_lms_suffixes_32s(t.as_ptr(), c_sa.as_mut_ptr(), n) };
16637        assert_eq!(rust_m, c_m);
16638        assert_eq!(rust_sa, c_sa);
16639
16640        let compact_t = vec![2, SAINT_MIN | 1, 3, 1, SAINT_MIN | 2, 0, 1, 0];
16641        let mut rust_sa = vec![0; compact_t.len()];
16642        let mut c_sa = rust_sa.clone();
16643        let rust_m = gather_compacted_lms_suffixes_32s(&compact_t, &mut rust_sa, n);
16644        let c_m = unsafe {
16645            probe_libsais16_gather_compacted_lms_suffixes_32s(
16646                compact_t.as_ptr(),
16647                c_sa.as_mut_ptr(),
16648                n,
16649            )
16650        };
16651        assert_eq!(rust_m, c_m);
16652        assert_eq!(rust_sa, c_sa);
16653
16654        let mut rust_buckets = vec![99; 2 * k as usize];
16655        let mut c_buckets = rust_buckets.clone();
16656        count_lms_suffixes_32s_2k(&t, n, k, &mut rust_buckets);
16657        unsafe {
16658            probe_libsais16_count_lms_suffixes_32s_2k(t.as_ptr(), n, k, c_buckets.as_mut_ptr());
16659        }
16660        assert_eq!(rust_buckets, c_buckets);
16661
16662        let mut rust_sa = vec![0; t.len()];
16663        let mut c_sa = rust_sa.clone();
16664        let mut rust_buckets = vec![0; 2 * k as usize];
16665        let mut c_buckets = rust_buckets.clone();
16666        let rust_m = count_and_gather_lms_suffixes_32s_2k(
16667            &t,
16668            &mut rust_sa,
16669            n,
16670            k,
16671            &mut rust_buckets,
16672            0,
16673            n as isize,
16674        );
16675        let c_m = unsafe {
16676            probe_libsais16_count_and_gather_lms_suffixes_32s_2k(
16677                t.as_ptr(),
16678                c_sa.as_mut_ptr(),
16679                n,
16680                k,
16681                c_buckets.as_mut_ptr(),
16682                0,
16683                n,
16684            )
16685        };
16686        assert_eq!(rust_m, c_m);
16687        assert_eq!(rust_sa, c_sa);
16688        assert_eq!(rust_buckets, c_buckets);
16689
16690        let mut rust_sa = vec![0; compact_t.len()];
16691        let mut c_sa = rust_sa.clone();
16692        let mut rust_buckets = vec![0; 2 * k as usize];
16693        let mut c_buckets = rust_buckets.clone();
16694        let rust_m = count_and_gather_compacted_lms_suffixes_32s_2k(
16695            &compact_t,
16696            &mut rust_sa,
16697            n,
16698            k,
16699            &mut rust_buckets,
16700            0,
16701            n as isize,
16702        );
16703        let c_m = unsafe {
16704            probe_libsais16_count_and_gather_compacted_lms_suffixes_32s_2k(
16705                compact_t.as_ptr(),
16706                c_sa.as_mut_ptr(),
16707                n,
16708                k,
16709                c_buckets.as_mut_ptr(),
16710                0,
16711                n,
16712            )
16713        };
16714        assert_eq!(rust_m, c_m);
16715        assert_eq!(rust_sa, c_sa);
16716        assert_eq!(rust_buckets, c_buckets);
16717    }
16718
16719    #[test]
16720    fn libsais16_small_openmp_leaf_helpers_match_upstream_shapes() {
16721        let sa = [-1, 0, 3, SAINT_MIN, 0, 7, -5];
16722        assert_eq!(count_negative_marked_suffixes(&sa, 1, 5), 1);
16723        assert_eq!(count_zero_marked_suffixes(&sa, 1, 5), 2);
16724
16725        let mut buckets = vec![1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9, 0, 10, 11, 12, 0];
16726        accumulate_counts_s32_4(&mut buckets, 12, 3, 4);
16727        assert_eq!(&buckets[12..15], &[22, 26, 30]);
16728
16729        let mut many = Vec::new();
16730        for bucket in 0..10 {
16731            many.extend([bucket, bucket + 1, bucket + 2, 0]);
16732        }
16733        accumulate_counts_s32(&mut many, 36, 3, 4, 10);
16734        assert_eq!(&many[36..39], &[45, 55, 65]);
16735
16736        let t = [1, SAINT_MIN | 2, 0];
16737        let mut compacted_buckets = vec![0; 6];
16738        count_compacted_lms_suffixes_32s_2k(&t, t.len() as SaSint, 3, &mut compacted_buckets);
16739        assert_eq!(compacted_buckets, vec![1, 0, 1, 0, 0, 1]);
16740
16741        let unique_sa = [0, 2, 4, 6, 0, -10, 20, -30];
16742        assert_eq!(count_unique_suffixes(&unique_sa, 4, 0, 4), 2);
16743
16744        assert_eq!(get_bucket_stride(20_000, 1000, 4), 1024);
16745        assert_eq!(get_bucket_stride(3024, 1001, 4), 1008);
16746        assert_eq!(get_bucket_stride(3000, 1001, 4), 1001);
16747    }
16748
16749    #[test]
16750    fn libsais16_count_gather_lms_suffixes_32s_omp_wrappers_match_c() {
16751        let t = vec![2, 1, 3, 1, 2, 0, 1, 0];
16752        let n = t.len() as SaSint;
16753        let k = 4;
16754        let mut rust_sa = vec![0; t.len()];
16755        let mut c_sa = rust_sa.clone();
16756        let mut rust_buckets = vec![0; 2 * k as usize];
16757        let mut c_buckets = rust_buckets.clone();
16758        let mut rust_state = alloc_thread_state(1).unwrap();
16759        let rust_m = count_and_gather_lms_suffixes_32s_2k_omp(
16760            &t,
16761            &mut rust_sa,
16762            n,
16763            k,
16764            &mut rust_buckets,
16765            0,
16766            1,
16767            &mut rust_state,
16768        );
16769        let c_m = unsafe {
16770            probe_libsais16_count_and_gather_lms_suffixes_32s_2k_omp(
16771                t.as_ptr(),
16772                c_sa.as_mut_ptr(),
16773                n,
16774                k,
16775                c_buckets.as_mut_ptr(),
16776                0,
16777                1,
16778            )
16779        };
16780        assert_eq!(rust_m, c_m);
16781        assert_eq!(rust_sa, c_sa);
16782        assert_eq!(rust_buckets, c_buckets);
16783
16784        let compact_t = vec![2, SAINT_MIN | 1, 3, 1, SAINT_MIN | 2, 0, 1, 0];
16785        let mut rust_sa = vec![0; compact_t.len()];
16786        let mut c_sa = rust_sa.clone();
16787        let mut rust_buckets = vec![0; 2 * k as usize];
16788        let mut c_buckets = rust_buckets.clone();
16789        let mut rust_state = alloc_thread_state(1).unwrap();
16790        count_and_gather_compacted_lms_suffixes_32s_2k_omp(
16791            &compact_t,
16792            &mut rust_sa,
16793            n,
16794            k,
16795            &mut rust_buckets,
16796            0,
16797            1,
16798            &mut rust_state,
16799        );
16800        unsafe {
16801            probe_libsais16_count_and_gather_compacted_lms_suffixes_32s_2k_omp(
16802                compact_t.as_ptr(),
16803                c_sa.as_mut_ptr(),
16804                n,
16805                k,
16806                c_buckets.as_mut_ptr(),
16807                0,
16808                1,
16809            );
16810        }
16811        assert_eq!(rust_sa, c_sa);
16812        assert_eq!(rust_buckets, c_buckets);
16813    }
16814
16815    #[test]
16816    fn libsais16_count_gather_lms_suffixes_32s_4k_match_c() {
16817        let t = vec![2, 1, 3, 1, 2, 0, 1, 0];
16818        let n = t.len() as SaSint;
16819        let k = 4;
16820
16821        let mut rust_buckets = vec![77; 4 * k as usize];
16822        let mut c_buckets = vec![0; 4 * k as usize];
16823        let mut c_sa_for_count = vec![0; t.len()];
16824        count_lms_suffixes_32s_4k(&t, n, k, &mut rust_buckets);
16825        unsafe {
16826            probe_libsais16_count_and_gather_lms_suffixes_32s_4k(
16827                t.as_ptr(),
16828                c_sa_for_count.as_mut_ptr(),
16829                n,
16830                k,
16831                c_buckets.as_mut_ptr(),
16832                0,
16833                n,
16834            );
16835        }
16836        assert_eq!(rust_buckets, c_buckets);
16837
16838        let mut rust_sa = vec![0; t.len()];
16839        let mut c_sa = rust_sa.clone();
16840        let mut rust_buckets = vec![0; 4 * k as usize];
16841        let mut c_buckets = rust_buckets.clone();
16842        let rust_m = count_and_gather_lms_suffixes_32s_4k(
16843            &t,
16844            &mut rust_sa,
16845            n,
16846            k,
16847            &mut rust_buckets,
16848            0,
16849            n as isize,
16850        );
16851        let c_m = unsafe {
16852            probe_libsais16_count_and_gather_lms_suffixes_32s_4k(
16853                t.as_ptr(),
16854                c_sa.as_mut_ptr(),
16855                n,
16856                k,
16857                c_buckets.as_mut_ptr(),
16858                0,
16859                n,
16860            )
16861        };
16862        assert_eq!(rust_m, c_m);
16863        assert_eq!(rust_sa, c_sa);
16864        assert_eq!(rust_buckets, c_buckets);
16865
16866        let mut rust_sa = vec![0; t.len()];
16867        let mut c_sa = rust_sa.clone();
16868        let mut rust_buckets = vec![0; 4 * k as usize];
16869        let mut c_buckets = rust_buckets.clone();
16870        let mut rust_state = alloc_thread_state(1).unwrap();
16871        let rust_m = count_and_gather_lms_suffixes_32s_4k_omp(
16872            &t,
16873            &mut rust_sa,
16874            n,
16875            k,
16876            &mut rust_buckets,
16877            0,
16878            1,
16879            &mut rust_state,
16880        );
16881        let c_m = unsafe {
16882            probe_libsais16_count_and_gather_lms_suffixes_32s_4k_omp(
16883                t.as_ptr(),
16884                c_sa.as_mut_ptr(),
16885                n,
16886                k,
16887                c_buckets.as_mut_ptr(),
16888                0,
16889                1,
16890            )
16891        };
16892        assert_eq!(rust_m, c_m);
16893        assert_eq!(rust_sa, c_sa);
16894        assert_eq!(rust_buckets, c_buckets);
16895
16896        let mut rust_buckets = vec![91; k as usize];
16897        let mut c_buckets = rust_buckets.clone();
16898        count_suffixes_32s(&t, n, k, &mut rust_buckets);
16899        unsafe {
16900            probe_libsais16_count_suffixes_32s(t.as_ptr(), n, k, c_buckets.as_mut_ptr());
16901        }
16902        assert_eq!(rust_buckets, c_buckets);
16903    }
16904
16905    #[test]
16906    fn libsais16_initialize_buckets_32s_match_c() {
16907        let k = 4;
16908
16909        let base_6k = vec![
16910            1, 2, 0, 1, 0, 1, 2, 0, 3, 0, 1, 1, 2, 1, 0, 0, 9, 9, 9, 9, 8, 8, 8, 8,
16911        ];
16912        let mut rust = base_6k.clone();
16913        let mut c = base_6k.clone();
16914        initialize_buckets_start_and_end_32s_6k(k, &mut rust);
16915        unsafe { probe_libsais16_initialize_buckets_start_and_end_32s_6k(k, c.as_mut_ptr()) };
16916        assert_eq!(rust, c);
16917
16918        let base_4k = vec![1, 2, 0, 1, 3, 0, 2, 1, 9, 9, 9, 9, 8, 8, 8, 8];
16919        let mut rust = base_4k.clone();
16920        let mut c = base_4k.clone();
16921        initialize_buckets_start_and_end_32s_4k(k, &mut rust);
16922        unsafe { probe_libsais16_initialize_buckets_start_and_end_32s_4k(k, c.as_mut_ptr()) };
16923        assert_eq!(rust, c);
16924
16925        let base_2k = vec![1, 2, 0, 1, 3, 0, 2, 1];
16926        let mut rust = base_2k.clone();
16927        let mut c = base_2k.clone();
16928        initialize_buckets_end_32s_2k(k, &mut rust);
16929        unsafe { probe_libsais16_initialize_buckets_end_32s_2k(k, c.as_mut_ptr()) };
16930        assert_eq!(rust, c);
16931
16932        let mut rust = base_2k.clone();
16933        let mut c = base_2k.clone();
16934        initialize_buckets_start_and_end_32s_2k(k, &mut rust);
16935        unsafe { probe_libsais16_initialize_buckets_start_and_end_32s_2k(k, c.as_mut_ptr()) };
16936        assert_eq!(rust, c);
16937
16938        let base_1k = vec![2, 1, 3, 2];
16939        let mut rust = base_1k.clone();
16940        let mut c = base_1k.clone();
16941        initialize_buckets_start_32s_1k(k, &mut rust);
16942        unsafe { probe_libsais16_initialize_buckets_start_32s_1k(k, c.as_mut_ptr()) };
16943        assert_eq!(rust, c);
16944
16945        let mut rust = base_1k.clone();
16946        let mut c = base_1k.clone();
16947        initialize_buckets_end_32s_1k(k, &mut rust);
16948        unsafe { probe_libsais16_initialize_buckets_end_32s_1k(k, c.as_mut_ptr()) };
16949        assert_eq!(rust, c);
16950
16951        let t = vec![2, 1, 3, 1, 2, 0, 1, 0];
16952        let mut rust = vec![1, 2, 0, 1, 3, 0, 2, 1];
16953        let mut c = rust.clone();
16954        initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(&t, k, &mut rust, 4);
16955        unsafe {
16956            probe_libsais16_initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
16957                t.as_ptr(),
16958                k,
16959                c.as_mut_ptr(),
16960                4,
16961            );
16962        }
16963        assert_eq!(rust, c);
16964
16965        let mut rust = vec![
16966            1, 2, 0, 1, 3, 0, 2, 1, 1, 0, 2, 0, 0, 1, 1, 0, 9, 9, 9, 9, 8, 8, 8, 8,
16967        ];
16968        let mut c = rust.clone();
16969        let rust_sum = initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(&t, k, &mut rust, 4);
16970        let c_sum = unsafe {
16971            probe_libsais16_initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
16972                t.as_ptr(),
16973                k,
16974                c.as_mut_ptr(),
16975                4,
16976            )
16977        };
16978        assert_eq!(rust_sum, c_sum);
16979        assert_eq!(rust, c);
16980
16981        let mut rust = base_4k.clone();
16982        let mut c = base_4k;
16983        initialize_buckets_for_radix_and_partial_sorting_32s_4k(&t, k, &mut rust, 4);
16984        unsafe {
16985            probe_libsais16_initialize_buckets_for_radix_and_partial_sorting_32s_4k(
16986                t.as_ptr(),
16987                k,
16988                c.as_mut_ptr(),
16989                4,
16990            );
16991        }
16992        assert_eq!(rust, c);
16993    }
16994
16995    #[test]
16996    fn libsais16_place_lms_suffixes_interval_32s_match_c() {
16997        let n = 12;
16998        let k = 4;
16999        let m = 4;
17000
17001        let mut rust_sa = vec![101, 102, 103, 104, 9, 9, 9, 9, 9, 9, 9, 9];
17002        let mut c_sa = rust_sa.clone();
17003        let mut buckets = vec![0; 4 * k as usize];
17004        buckets[buckets_index2(0, 1)] = 2;
17005        buckets[buckets_index2(1, 1)] = 2;
17006        buckets[buckets_index2(2, 1)] = 3;
17007        buckets[buckets_index2(2, 1) + buckets_index2(1, 0)] = 4;
17008        buckets[3 * k as usize + 1] = 7;
17009        buckets[3 * k as usize + 2] = 10;
17010        place_lms_suffixes_interval_32s_4k(&mut rust_sa, n, k, m, &buckets);
17011        unsafe {
17012            probe_libsais16_place_lms_suffixes_interval_32s_4k(
17013                c_sa.as_mut_ptr(),
17014                n,
17015                k,
17016                m,
17017                buckets.as_ptr(),
17018            );
17019        }
17020        assert_eq!(rust_sa, c_sa);
17021
17022        let mut rust_sa = vec![101, 102, 103, 104, 9, 9, 9, 9, 9, 9, 9, 9];
17023        let mut c_sa = rust_sa.clone();
17024        let mut buckets = vec![0; 2 * k as usize];
17025        buckets[buckets_index2(1, 0)] = 7;
17026        buckets[buckets_index2(0, 1)] = 1;
17027        buckets[buckets_index2(1, 1)] = 1;
17028        buckets[buckets_index2(2, 0)] = 10;
17029        buckets[buckets_index2(2, 1)] = 2;
17030        buckets[buckets_index2(3, 1)] = 3;
17031        place_lms_suffixes_interval_32s_2k(&mut rust_sa, n, k, m, &buckets);
17032        unsafe {
17033            probe_libsais16_place_lms_suffixes_interval_32s_2k(
17034                c_sa.as_mut_ptr(),
17035                n,
17036                k,
17037                m,
17038                buckets.as_ptr(),
17039            );
17040        }
17041        assert_eq!(rust_sa, c_sa);
17042
17043        let t = vec![0, 1, 2, 1, 2, 3, 1, 3, 0, 0, 0, 0];
17044        let mut rust_sa = vec![1, 3, 4, 7, 9, 9, 9, 9, 9, 9, 9, 9];
17045        let mut c_sa = rust_sa.clone();
17046        let rust_buckets = vec![0, 3, 6, 10];
17047        let mut c_buckets = rust_buckets.clone();
17048        place_lms_suffixes_interval_32s_1k(&t, &mut rust_sa, k, m, &rust_buckets);
17049        unsafe {
17050            probe_libsais16_place_lms_suffixes_interval_32s_1k(
17051                t.as_ptr(),
17052                c_sa.as_mut_ptr(),
17053                k,
17054                m,
17055                c_buckets.as_mut_ptr(),
17056            );
17057        }
17058        assert_eq!(rust_sa, c_sa);
17059        assert_eq!(rust_buckets, c_buckets);
17060    }
17061
17062    #[test]
17063    fn libsais16_renumber_and_mark_distinct_lms_suffixes_32s_1k_matches_c() {
17064        let rust_t = vec![2, 1, 3, 1, 2, 0, 1, 0];
17065        let n = rust_t.len() as SaSint;
17066        let mut probe_sa = vec![0; rust_t.len()];
17067        let m = gather_lms_suffixes_32s(&rust_t, &mut probe_sa, n);
17068        let mut rust_sa = vec![0; rust_t.len()];
17069        let mut c_t = rust_t.clone();
17070        let mut c_sa = rust_sa.clone();
17071
17072        let rust_name =
17073            renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(&rust_t, &mut rust_sa, n, m, 1);
17074        let c_name = unsafe {
17075            probe_libsais16_renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(
17076                c_t.as_mut_ptr(),
17077                c_sa.as_mut_ptr(),
17078                n,
17079                m,
17080                1,
17081            )
17082        };
17083        assert_eq!(rust_name, c_name);
17084        assert_eq!(rust_t, c_t);
17085        assert_eq!(rust_sa, c_sa);
17086    }
17087
17088    #[test]
17089    fn libsais16_reconstruct_compacted_lms_suffixes_32s_match_c() {
17090        let n = 8;
17091        let k = 4;
17092        let fs = 0;
17093        let f = 0;
17094        let mut m_probe_sa = vec![0; n as usize];
17095        let m = gather_lms_suffixes_32s(&[2, 1, 3, 1, 2, 0, 1, 0], &mut m_probe_sa, n);
17096
17097        let mut rust_t = vec![2, 1, 3, 1, 2, 0, 1, 0];
17098        let mut c_t = rust_t.clone();
17099        let mut rust_sa = vec![0; n as usize];
17100        let mut c_sa = rust_sa.clone();
17101        let mut rust_buckets = vec![0; 2 * k as usize];
17102        let mut c_buckets = rust_buckets.clone();
17103        let mut rust_thread_state = alloc_thread_state(1).unwrap();
17104        reconstruct_compacted_lms_suffixes_32s_2k_omp(
17105            &mut rust_t,
17106            &mut rust_sa,
17107            n,
17108            k,
17109            m,
17110            fs,
17111            f,
17112            &mut rust_buckets,
17113            0,
17114            1,
17115            &mut rust_thread_state,
17116        );
17117        unsafe {
17118            probe_libsais16_reconstruct_compacted_lms_suffixes_32s_2k_omp(
17119                c_t.as_mut_ptr(),
17120                c_sa.as_mut_ptr(),
17121                n,
17122                k,
17123                m,
17124                fs,
17125                f,
17126                c_buckets.as_mut_ptr(),
17127                0,
17128                1,
17129            );
17130        }
17131        assert_eq!(rust_t, c_t);
17132        assert_eq!(rust_sa, c_sa);
17133        assert_eq!(rust_buckets, c_buckets);
17134
17135        let mut rust_t = vec![2, 1, 3, 1, 2, 0, 1, 0];
17136        let mut c_t = rust_t.clone();
17137        let mut rust_sa = vec![0; n as usize];
17138        let mut c_sa = rust_sa.clone();
17139        reconstruct_compacted_lms_suffixes_32s_1k_omp(&mut rust_t, &mut rust_sa, n, m, fs, f, 1);
17140        unsafe {
17141            probe_libsais16_reconstruct_compacted_lms_suffixes_32s_1k_omp(
17142                c_t.as_mut_ptr(),
17143                c_sa.as_mut_ptr(),
17144                n,
17145                m,
17146                fs,
17147                f,
17148                1,
17149            );
17150        }
17151        assert_eq!(rust_t, c_t);
17152        assert_eq!(rust_sa, c_sa);
17153    }
17154
17155    #[test]
17156    fn libsais16_partial_omp_wrappers_match_c() {
17157        let (text, mut rust_sa, mut rust_buckets) = partial_scan_fixture();
17158        let mut c_sa = rust_sa.clone();
17159        let mut c_buckets = rust_buckets.clone();
17160
17161        let rust_d = partial_sorting_scan_left_to_right_16u_omp(
17162            &text,
17163            &mut rust_sa,
17164            text.len() as SaSint,
17165            8,
17166            &mut rust_buckets,
17167            5,
17168            3,
17169            1,
17170        );
17171        let c_d = unsafe {
17172            probe_libsais16_partial_sorting_scan_left_to_right_16u_omp(
17173                text.as_ptr(),
17174                c_sa.as_mut_ptr(),
17175                text.len() as SaSint,
17176                8,
17177                c_buckets.as_mut_ptr(),
17178                5,
17179                3,
17180                1,
17181            )
17182        };
17183        assert_eq!(rust_d, c_d);
17184        assert_eq!(rust_sa, c_sa);
17185        assert_eq!(rust_buckets, c_buckets);
17186
17187        let (text, mut rust_sa, mut rust_buckets) = partial_scan_fixture();
17188        rust_sa[6..10].copy_from_slice(&[3, 5 | SAINT_MIN, 7, 9 | SAINT_MIN]);
17189        let mut c_sa = rust_sa.clone();
17190        let mut c_buckets = rust_buckets.clone();
17191        partial_sorting_scan_right_to_left_16u_omp(
17192            &text,
17193            &mut rust_sa,
17194            text.len() as SaSint,
17195            8,
17196            &mut rust_buckets,
17197            0,
17198            5,
17199            3,
17200            1,
17201        );
17202        unsafe {
17203            probe_libsais16_partial_sorting_scan_right_to_left_16u_omp(
17204                text.as_ptr(),
17205                c_sa.as_mut_ptr(),
17206                text.len() as SaSint,
17207                8,
17208                c_buckets.as_mut_ptr(),
17209                0,
17210                5,
17211                3,
17212                1,
17213            );
17214        }
17215        assert_eq!(rust_sa, c_sa);
17216        assert_eq!(rust_buckets, c_buckets);
17217
17218        let (text, mut rust_sa, mut rust_buckets) = partial_scan_fixture();
17219        rust_sa[6..10].copy_from_slice(&[3, 5 | SAINT_MIN, 7, 9 | SAINT_MIN]);
17220        let mut c_sa = rust_sa.clone();
17221        let mut c_buckets = rust_buckets.clone();
17222        partial_gsa_scan_right_to_left_16u_omp(
17223            &text,
17224            &mut rust_sa,
17225            text.len() as SaSint,
17226            8,
17227            &mut rust_buckets,
17228            0,
17229            5,
17230            3,
17231            1,
17232        );
17233        unsafe {
17234            probe_libsais16_partial_gsa_scan_right_to_left_16u_omp(
17235                text.as_ptr(),
17236                c_sa.as_mut_ptr(),
17237                text.len() as SaSint,
17238                8,
17239                c_buckets.as_mut_ptr(),
17240                0,
17241                5,
17242                3,
17243                1,
17244            );
17245        }
17246        assert_eq!(rust_sa, c_sa);
17247        assert_eq!(rust_buckets, c_buckets);
17248    }
17249
17250    #[test]
17251    fn libsais16_final_omp_wrappers_match_c() {
17252        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
17253        let mut c_sa = rust_sa.clone();
17254        let mut c_bucket = rust_bucket.clone();
17255        final_bwt_scan_left_to_right_16u_omp(
17256            &text,
17257            &mut rust_sa,
17258            text.len() as SaSint,
17259            8,
17260            &mut rust_bucket,
17261            1,
17262        );
17263        unsafe {
17264            probe_libsais16_final_bwt_scan_left_to_right_16u_omp(
17265                text.as_ptr(),
17266                c_sa.as_mut_ptr(),
17267                text.len() as SaSint,
17268                8,
17269                c_bucket.as_mut_ptr(),
17270                1,
17271            );
17272        }
17273        assert_eq!(rust_sa, c_sa);
17274        assert_eq!(rust_bucket, c_bucket);
17275
17276        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
17277        let mut c_sa = rust_sa.clone();
17278        let mut c_bucket = rust_bucket.clone();
17279        let mut rust_i = vec![-1; 8];
17280        let mut c_i = rust_i.clone();
17281        final_bwt_aux_scan_left_to_right_16u_omp(
17282            &text,
17283            &mut rust_sa,
17284            text.len() as SaSint,
17285            8,
17286            1,
17287            &mut rust_i,
17288            &mut rust_bucket,
17289            1,
17290        );
17291        unsafe {
17292            probe_libsais16_final_bwt_aux_scan_left_to_right_16u_omp(
17293                text.as_ptr(),
17294                c_sa.as_mut_ptr(),
17295                text.len() as SaSint,
17296                8,
17297                1,
17298                c_i.as_mut_ptr(),
17299                c_bucket.as_mut_ptr(),
17300                1,
17301            );
17302        }
17303        assert_eq!(rust_sa, c_sa);
17304        assert_eq!(rust_bucket, c_bucket);
17305        assert_eq!(rust_i, c_i);
17306
17307        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
17308        let mut c_sa = rust_sa.clone();
17309        let mut c_bucket = rust_bucket.clone();
17310        final_sorting_scan_left_to_right_16u_omp(
17311            &text,
17312            &mut rust_sa,
17313            text.len() as SaSint,
17314            8,
17315            &mut rust_bucket,
17316            1,
17317        );
17318        unsafe {
17319            probe_libsais16_final_sorting_scan_left_to_right_16u_omp(
17320                text.as_ptr(),
17321                c_sa.as_mut_ptr(),
17322                text.len() as SaSint,
17323                8,
17324                c_bucket.as_mut_ptr(),
17325                1,
17326            );
17327        }
17328        assert_eq!(rust_sa, c_sa);
17329        assert_eq!(rust_bucket, c_bucket);
17330
17331        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
17332        let mut c_sa = rust_sa.clone();
17333        let mut c_bucket = rust_bucket.clone();
17334        let rust_index = final_bwt_scan_right_to_left_16u_omp(
17335            &text,
17336            &mut rust_sa,
17337            text.len() as SaSint,
17338            8,
17339            &mut rust_bucket,
17340            1,
17341        );
17342        let c_index = unsafe {
17343            probe_libsais16_final_bwt_scan_right_to_left_16u_omp(
17344                text.as_ptr(),
17345                c_sa.as_mut_ptr(),
17346                text.len() as SaSint,
17347                8,
17348                c_bucket.as_mut_ptr(),
17349                1,
17350            )
17351        };
17352        assert_eq!(rust_index, c_index);
17353        assert_eq!(rust_sa, c_sa);
17354        assert_eq!(rust_bucket, c_bucket);
17355
17356        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
17357        let mut c_sa = rust_sa.clone();
17358        let mut c_bucket = rust_bucket.clone();
17359        let mut rust_i = vec![-1; 8];
17360        let mut c_i = rust_i.clone();
17361        final_bwt_aux_scan_right_to_left_16u_omp(
17362            &text,
17363            &mut rust_sa,
17364            text.len() as SaSint,
17365            8,
17366            1,
17367            &mut rust_i,
17368            &mut rust_bucket,
17369            1,
17370        );
17371        unsafe {
17372            probe_libsais16_final_bwt_aux_scan_right_to_left_16u_omp(
17373                text.as_ptr(),
17374                c_sa.as_mut_ptr(),
17375                text.len() as SaSint,
17376                8,
17377                1,
17378                c_i.as_mut_ptr(),
17379                c_bucket.as_mut_ptr(),
17380                1,
17381            );
17382        }
17383        assert_eq!(rust_sa, c_sa);
17384        assert_eq!(rust_bucket, c_bucket);
17385        assert_eq!(rust_i, c_i);
17386
17387        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
17388        let mut c_sa = rust_sa.clone();
17389        let mut c_bucket = rust_bucket.clone();
17390        final_sorting_scan_right_to_left_16u_omp(&text, &mut rust_sa, 0, 6, 8, &mut rust_bucket, 1);
17391        unsafe {
17392            probe_libsais16_final_sorting_scan_right_to_left_16u_omp(
17393                text.as_ptr(),
17394                c_sa.as_mut_ptr(),
17395                0,
17396                6,
17397                8,
17398                c_bucket.as_mut_ptr(),
17399                1,
17400            );
17401        }
17402        assert_eq!(rust_sa, c_sa);
17403        assert_eq!(rust_bucket, c_bucket);
17404
17405        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
17406        let mut c_sa = rust_sa.clone();
17407        let mut c_bucket = rust_bucket.clone();
17408        final_gsa_scan_right_to_left_16u_omp(&text, &mut rust_sa, 0, 6, 8, &mut rust_bucket, 1);
17409        unsafe {
17410            probe_libsais16_final_gsa_scan_right_to_left_16u_omp(
17411                text.as_ptr(),
17412                c_sa.as_mut_ptr(),
17413                0,
17414                6,
17415                8,
17416                c_bucket.as_mut_ptr(),
17417                1,
17418            );
17419        }
17420        assert_eq!(rust_sa, c_sa);
17421        assert_eq!(rust_bucket, c_bucket);
17422    }
17423
17424    #[test]
17425    fn libsais16_matches_bruteforce() {
17426        let t = [3, 1, 4, 1, 5, 9, 0, 2];
17427        let mut sa = vec![0; t.len()];
17428        let mut freq = vec![0; ALPHABET_SIZE];
17429        assert_eq!(libsais16(&t, &mut sa, 0, Some(&mut freq)), 0);
17430        assert_eq!(sa, brute_sa(&t));
17431        assert_eq!(freq[1], 2);
17432        assert_eq!(freq[9], 1);
17433    }
17434
17435    #[test]
17436    fn libsais16_bwt_round_trips() {
17437        let t = [2, 1, 3, 1, 2, 4, 1, 0];
17438        let mut bwt = vec![0; t.len()];
17439        let mut work = vec![0; t.len()];
17440        let primary = libsais16_bwt(&t, &mut bwt, &mut work, 0, None);
17441        assert!(primary > 0);
17442
17443        let mut restored = vec![0; t.len()];
17444        assert_eq!(
17445            libsais16_unbwt(&bwt, &mut restored, &mut work, None, primary),
17446            0
17447        );
17448        assert_eq!(restored, t);
17449    }
17450
17451    #[test]
17452    fn libsais16_plcp_lcp_are_consistent() {
17453        let t = [2, 1, 2, 1, 0];
17454        let sa = brute_sa(&t);
17455        let mut plcp = vec![0; t.len()];
17456        let mut lcp = vec![0; t.len()];
17457        assert_eq!(libsais16_plcp(&t, &sa, &mut plcp), 0);
17458        assert_eq!(libsais16_lcp(&plcp, &sa, &mut lcp), 0);
17459        assert_eq!(lcp[0], 0);
17460
17461        let mut named_plcp = vec![0; t.len()];
17462        assert_eq!(
17463            compute_phi_omp(&sa, &mut named_plcp, t.len() as SaSint, 1),
17464            0
17465        );
17466        assert_eq!(
17467            compute_plcp_omp(&t, &mut named_plcp, t.len() as SaSint, 1),
17468            0
17469        );
17470        assert_eq!(named_plcp, plcp);
17471
17472        let mut named_lcp = vec![0; t.len()];
17473        assert_eq!(
17474            compute_lcp_omp(&named_plcp, &sa, &mut named_lcp, t.len() as SaSint, 1),
17475            0
17476        );
17477        assert_eq!(named_lcp, lcp);
17478
17479        let mut gsa_plcp = vec![0; t.len()];
17480        let mut named_gsa_plcp = vec![0; t.len()];
17481        assert_eq!(libsais16_plcp_gsa(&t, &sa, &mut gsa_plcp), 0);
17482        assert_eq!(
17483            compute_phi_omp(&sa, &mut named_gsa_plcp, t.len() as SaSint, 1),
17484            0
17485        );
17486        assert_eq!(
17487            compute_plcp_gsa_omp(&t, &mut named_gsa_plcp, t.len() as SaSint, 1),
17488            0
17489        );
17490        assert_eq!(named_gsa_plcp, gsa_plcp);
17491    }
17492
17493    #[test]
17494    fn libsais16_bwt_copy_16u_omp_uses_block_partition_for_large_inputs() {
17495        let n = 65_600usize;
17496        let a: Vec<SaSint> = (0..n).map(|i| (i * 17) as SaSint).collect();
17497        let mut threaded = vec![0; n];
17498        let mut sequential = vec![0; n];
17499
17500        bwt_copy_16u_omp(&mut threaded, &a, n as SaSint, 4);
17501        bwt_copy_16u(&mut sequential, &a, n as SaSint);
17502
17503        assert_eq!(threaded, sequential);
17504    }
17505
17506    #[test]
17507    fn libsais16_plcp_lcp_omp_wrappers_match_single_thread_on_large_inputs() {
17508        let n = 65_600usize;
17509        let text: Vec<u16> = (0..n).map(|i| 1 + (i % 251) as u16).collect();
17510        let sa: Vec<SaSint> = (0..n as SaSint).collect();
17511
17512        let mut plcp_single = vec![0; n];
17513        let mut plcp_threaded = vec![0; n];
17514        assert_eq!(compute_phi_omp(&sa, &mut plcp_single, n as SaSint, 1), 0);
17515        assert_eq!(compute_phi_omp(&sa, &mut plcp_threaded, n as SaSint, 4), 0);
17516        assert_eq!(plcp_threaded, plcp_single);
17517
17518        assert_eq!(compute_plcp_omp(&text, &mut plcp_single, n as SaSint, 1), 0);
17519        assert_eq!(
17520            compute_plcp_omp(&text, &mut plcp_threaded, n as SaSint, 4),
17521            0
17522        );
17523        assert_eq!(plcp_threaded, plcp_single);
17524
17525        let mut lcp_single = vec![0; n];
17526        let mut lcp_threaded = vec![0; n];
17527        assert_eq!(
17528            compute_lcp_omp(&plcp_single, &sa, &mut lcp_single, n as SaSint, 1),
17529            0
17530        );
17531        assert_eq!(
17532            compute_lcp_omp(&plcp_threaded, &sa, &mut lcp_threaded, n as SaSint, 4),
17533            0
17534        );
17535        assert_eq!(lcp_threaded, lcp_single);
17536    }
17537
17538    #[test]
17539    fn libsais16_context_allocates_upstream_shaped_buffers() {
17540        let ctx = create_ctx().unwrap();
17541        assert_eq!(ctx.threads, 1);
17542        assert_eq!(ctx.buckets.len(), 8 * ALPHABET_SIZE);
17543        assert!(ctx.thread_state.is_none());
17544
17545        let ctx = create_ctx_omp(2).unwrap();
17546        assert_eq!(ctx.threads, 2);
17547        assert_eq!(ctx.buckets.len(), 8 * ALPHABET_SIZE);
17548        let thread_state = ctx.thread_state.as_ref().unwrap();
17549        assert_eq!(thread_state.len(), 2);
17550        assert_eq!(thread_state[0].buckets.len(), 4 * ALPHABET_SIZE);
17551        assert_eq!(thread_state[0].cache_entries, PER_THREAD_CACHE_SIZE);
17552
17553        let ctx = create_ctx_omp(0).unwrap();
17554        assert_eq!(ctx.threads, 1);
17555        assert!(ctx.thread_state.is_none());
17556    }
17557
17558    #[test]
17559    fn libsais16_unbwt_context_allocates_upstream_shaped_buffers() {
17560        let ctx = unbwt_create_ctx().unwrap();
17561        assert_eq!(ctx.threads, 1);
17562        assert_eq!(ctx.bucket2.len(), ALPHABET_SIZE);
17563        assert_eq!(ctx.fastbits.len(), 1 + (1 << UNBWT_FASTBITS));
17564        assert!(ctx.buckets.is_none());
17565
17566        let ctx = unbwt_create_ctx_omp(3).unwrap();
17567        assert_eq!(ctx.threads, 3);
17568        assert_eq!(ctx.bucket2.len(), ALPHABET_SIZE);
17569        assert_eq!(ctx.fastbits.len(), 1 + (1 << UNBWT_FASTBITS));
17570        assert_eq!(ctx.buckets.as_ref().unwrap().len(), 3 * ALPHABET_SIZE);
17571    }
17572
17573    #[test]
17574    fn libsais16_named_unbwt_helpers_follow_decode_shapes() {
17575        let t = [0, 1, 2];
17576        let mut p = vec![usize::MAX; 4];
17577        let mut bucket2 = vec![0; ALPHABET_SIZE];
17578        bucket2[0] = 1;
17579        bucket2[1] = 2;
17580        bucket2[2] = 3;
17581        unbwt_calculate_P(&t, &mut p, &mut bucket2, 2, 1, 3);
17582        assert_eq!(p[2], 1);
17583        assert_eq!(p[3], 3);
17584
17585        let p = [1usize, 2, 0];
17586        let mut bucket2 = vec![3; ALPHABET_SIZE];
17587        bucket2[0] = 1;
17588        bucket2[1] = 2;
17589        bucket2[2] = 3;
17590        let fastbits = vec![0; 3];
17591
17592        let mut u = vec![99; 3];
17593        let mut i0 = 0;
17594        unbwt_decode_1(&mut u, &p, &bucket2, &fastbits, 0, &mut i0, 3);
17595        assert_eq!(u, vec![0, 1, 2]);
17596        assert_eq!(i0, 0);
17597
17598        let mut u = vec![99; 6];
17599        let (mut i0, mut i1) = (0, 1);
17600        unbwt_decode_2(&mut u, &p, &bucket2, &fastbits, 0, 3, &mut i0, &mut i1, 2);
17601        assert_eq!(&u[..2], &[0, 1]);
17602        assert_eq!(&u[3..5], &[1, 2]);
17603        assert_eq!((i0, i1), (2, 0));
17604
17605        let mut u = vec![99; 8];
17606        let mut cursors = [0; 8];
17607        unbwt_decode_8(&mut u, &p, &bucket2, &fastbits, 0, 1, &mut cursors, 1);
17608        assert_eq!(u, vec![0; 8]);
17609        assert_eq!(cursors, [1; 8]);
17610    }
17611
17612    #[test]
17613    fn libsais16_unbwt_init_parallel_uses_block_partition() {
17614        let n = 70_003usize;
17615        let t: Vec<u16> = (0..n)
17616            .map(|i| ((i.wrapping_mul(37).wrapping_add(i >> 3)) % 251) as u16)
17617            .collect();
17618        let i = [12_345];
17619
17620        let mut single_p = vec![0; n + 1];
17621        let mut threaded_p = vec![0; n + 1];
17622        let mut single_bucket2 = vec![0; ALPHABET_SIZE];
17623        let mut threaded_bucket2 = vec![0; ALPHABET_SIZE];
17624        let mut single_fastbits = vec![0; 1 + (1 << UNBWT_FASTBITS)];
17625        let mut threaded_fastbits = vec![0; 1 + (1 << UNBWT_FASTBITS)];
17626        let mut buckets = vec![0; 4 * ALPHABET_SIZE];
17627
17628        unbwt_init_single(
17629            &t,
17630            &mut single_p,
17631            None,
17632            &i,
17633            &mut single_bucket2,
17634            &mut single_fastbits,
17635        );
17636        unbwt_init_parallel(
17637            &t,
17638            &mut threaded_p,
17639            None,
17640            &i,
17641            &mut threaded_bucket2,
17642            &mut threaded_fastbits,
17643            &mut buckets,
17644            4,
17645        );
17646
17647        assert_eq!(threaded_p, single_p);
17648        assert_eq!(threaded_bucket2, single_bucket2);
17649        assert_eq!(threaded_fastbits, single_fastbits);
17650    }
17651
17652    fn assert_libsais16_matches_c(text: &[u16]) {
17653        let mut rust_sa = vec![0; text.len()];
17654        let mut c_sa = vec![0; text.len()];
17655
17656        let rust_rc = libsais16(text, &mut rust_sa, 0, None);
17657        let c_rc = unsafe {
17658            probe_public_libsais16(text.as_ptr(), c_sa.as_mut_ptr(), text.len() as SaSint, 0)
17659        };
17660
17661        assert_eq!(rust_rc, c_rc);
17662        assert_eq!(rust_sa, c_sa);
17663    }
17664
17665    fn assert_libsais16_gsa_matches_c(text: &[u16]) {
17666        let mut rust_sa = vec![0; text.len()];
17667        let mut c_sa = vec![0; text.len()];
17668
17669        let rust_rc = libsais16_gsa(text, &mut rust_sa, 0, None);
17670        let c_rc = unsafe {
17671            probe_public_libsais16_gsa(text.as_ptr(), c_sa.as_mut_ptr(), text.len() as SaSint, 0)
17672        };
17673
17674        assert_eq!(rust_rc, c_rc);
17675        assert_eq!(rust_sa, c_sa);
17676    }
17677
17678    fn assert_libsais16_int_matches_c(text: &[SaSint], k: SaSint) {
17679        let mut rust_t = text.to_vec();
17680        let mut c_t = text.to_vec();
17681        let mut rust_sa = vec![0; text.len()];
17682        let mut c_sa = vec![0; text.len()];
17683
17684        let rust_rc = libsais16_int(&mut rust_t, &mut rust_sa, k, 0);
17685        let c_rc = unsafe {
17686            probe_public_libsais16_int(
17687                c_t.as_mut_ptr(),
17688                c_sa.as_mut_ptr(),
17689                c_t.len() as SaSint,
17690                k,
17691                0,
17692            )
17693        };
17694
17695        assert_eq!(rust_rc, c_rc);
17696        assert_eq!(rust_t, c_t);
17697        assert_eq!(rust_sa, c_sa);
17698    }
17699
17700    fn assert_libsais16_bwt_matches_c(text: &[u16]) {
17701        let mut rust_u = vec![0; text.len()];
17702        let mut rust_a = vec![0; text.len()];
17703        let mut c_u = vec![0; text.len()];
17704        let mut c_a = vec![0; text.len()];
17705
17706        let rust_rc = libsais16_bwt(text, &mut rust_u, &mut rust_a, 0, None);
17707        let c_rc = unsafe {
17708            probe_public_libsais16_bwt(
17709                text.as_ptr(),
17710                c_u.as_mut_ptr(),
17711                c_a.as_mut_ptr(),
17712                text.len() as SaSint,
17713                0,
17714            )
17715        };
17716
17717        assert_eq!(rust_rc, c_rc);
17718        assert_eq!(rust_u, c_u);
17719    }
17720
17721    fn assert_libsais16_bwt_aux_matches_c(text: &[u16], r: SaSint) {
17722        let aux_len = if text.is_empty() {
17723            0
17724        } else {
17725            (text.len() - 1) / r as usize + 1
17726        };
17727        let mut rust_u = vec![0; text.len()];
17728        let mut rust_a = vec![0; text.len()];
17729        let mut rust_i = vec![0; aux_len];
17730        let mut c_u = vec![0; text.len()];
17731        let mut c_a = vec![0; text.len()];
17732        let mut c_i = vec![0; aux_len];
17733
17734        let rust_rc = libsais16_bwt_aux(text, &mut rust_u, &mut rust_a, 0, None, r, &mut rust_i);
17735        let c_rc = unsafe {
17736            probe_public_libsais16_bwt_aux(
17737                text.as_ptr(),
17738                c_u.as_mut_ptr(),
17739                c_a.as_mut_ptr(),
17740                text.len() as SaSint,
17741                0,
17742                r,
17743                c_i.as_mut_ptr(),
17744            )
17745        };
17746
17747        assert_eq!(rust_rc, c_rc);
17748        assert_eq!(rust_u, c_u);
17749        assert_eq!(rust_i, c_i);
17750    }
17751
17752    fn assert_libsais16_freq_outputs_match_c(text: &[u16], gsa_text: &[u16]) {
17753        let mut rust_sa = vec![0; text.len()];
17754        let mut c_sa = vec![0; text.len()];
17755        let mut rust_freq = vec![-1; ALPHABET_SIZE];
17756        let mut c_freq = vec![-1; ALPHABET_SIZE];
17757
17758        let rust_rc = libsais16(text, &mut rust_sa, 0, Some(&mut rust_freq));
17759        let c_rc = unsafe {
17760            probe_public_libsais16_freq(
17761                text.as_ptr(),
17762                c_sa.as_mut_ptr(),
17763                text.len() as SaSint,
17764                0,
17765                c_freq.as_mut_ptr(),
17766            )
17767        };
17768        assert_eq!(rust_rc, c_rc);
17769        assert_eq!(rust_sa, c_sa);
17770        assert_eq!(rust_freq, c_freq);
17771
17772        let mut rust_gsa = vec![0; gsa_text.len()];
17773        let mut c_gsa = vec![0; gsa_text.len()];
17774        rust_freq.fill(-1);
17775        c_freq.fill(-1);
17776        let rust_rc = libsais16_gsa(gsa_text, &mut rust_gsa, 0, Some(&mut rust_freq));
17777        let c_rc = unsafe {
17778            probe_public_libsais16_gsa_freq(
17779                gsa_text.as_ptr(),
17780                c_gsa.as_mut_ptr(),
17781                gsa_text.len() as SaSint,
17782                0,
17783                c_freq.as_mut_ptr(),
17784            )
17785        };
17786        assert_eq!(rust_rc, c_rc);
17787        assert_eq!(rust_gsa, c_gsa);
17788        assert_eq!(rust_freq, c_freq);
17789
17790        let mut rust_u = vec![0; text.len()];
17791        let mut rust_a = vec![0; text.len()];
17792        let mut c_u = vec![0; text.len()];
17793        let mut c_a = vec![0; text.len()];
17794        rust_freq.fill(-1);
17795        c_freq.fill(-1);
17796        let rust_rc = libsais16_bwt(text, &mut rust_u, &mut rust_a, 0, Some(&mut rust_freq));
17797        let c_rc = unsafe {
17798            probe_public_libsais16_bwt_freq(
17799                text.as_ptr(),
17800                c_u.as_mut_ptr(),
17801                c_a.as_mut_ptr(),
17802                text.len() as SaSint,
17803                0,
17804                c_freq.as_mut_ptr(),
17805            )
17806        };
17807        assert_eq!(rust_rc, c_rc);
17808        assert_eq!(rust_u, c_u);
17809        assert_eq!(rust_freq, c_freq);
17810
17811        let r = 4;
17812        let aux_len = (text.len() - 1) / r as usize + 1;
17813        let mut rust_i = vec![0; aux_len];
17814        let mut c_i = vec![0; aux_len];
17815        rust_freq.fill(-1);
17816        c_freq.fill(-1);
17817        let rust_rc = libsais16_bwt_aux(
17818            text,
17819            &mut rust_u,
17820            &mut rust_a,
17821            0,
17822            Some(&mut rust_freq),
17823            r,
17824            &mut rust_i,
17825        );
17826        let c_rc = unsafe {
17827            probe_public_libsais16_bwt_aux_freq(
17828                text.as_ptr(),
17829                c_u.as_mut_ptr(),
17830                c_a.as_mut_ptr(),
17831                text.len() as SaSint,
17832                0,
17833                c_freq.as_mut_ptr(),
17834                r,
17835                c_i.as_mut_ptr(),
17836            )
17837        };
17838        assert_eq!(rust_rc, c_rc);
17839        assert_eq!(rust_u, c_u);
17840        assert_eq!(rust_i, c_i);
17841        assert_eq!(rust_freq, c_freq);
17842    }
17843
17844    fn assert_libsais16_unbwt_matches_c(text: &[u16]) {
17845        let mut bwt = vec![0; text.len()];
17846        let mut work = vec![0; text.len()];
17847        let primary = libsais16_bwt(text, &mut bwt, &mut work, 0, None);
17848        assert!(primary >= 0);
17849
17850        let mut rust_u = vec![0; text.len()];
17851        let mut rust_a = vec![0; text.len() + 1];
17852        let mut c_u = vec![0; text.len()];
17853        let mut c_a = vec![0; text.len() + 1];
17854
17855        let rust_rc = libsais16_unbwt(&bwt, &mut rust_u, &mut rust_a, None, primary);
17856        let c_rc = unsafe {
17857            probe_public_libsais16_unbwt(
17858                bwt.as_ptr(),
17859                c_u.as_mut_ptr(),
17860                c_a.as_mut_ptr(),
17861                bwt.len() as SaSint,
17862                primary,
17863            )
17864        };
17865
17866        assert_eq!(rust_rc, c_rc);
17867        assert_eq!(rust_u, c_u);
17868        assert_eq!(rust_u, text);
17869    }
17870
17871    fn assert_libsais16_unbwt_aux_matches_c(text: &[u16], r: SaSint) {
17872        let mut bwt = vec![0; text.len()];
17873        let mut work = vec![0; text.len()];
17874        let mut aux = vec![0; (text.len() - 1) / r as usize + 1];
17875        let bwt_rc = libsais16_bwt_aux(text, &mut bwt, &mut work, 0, None, r, &mut aux);
17876        assert_eq!(bwt_rc, 0);
17877
17878        let mut rust_u = vec![0; text.len()];
17879        let mut rust_a = vec![0; text.len() + 1];
17880        let mut c_u = vec![0; text.len()];
17881        let mut c_a = vec![0; text.len() + 1];
17882
17883        let rust_rc = libsais16_unbwt_aux(&bwt, &mut rust_u, &mut rust_a, None, r, &aux);
17884        let c_rc = unsafe {
17885            probe_public_libsais16_unbwt_aux(
17886                bwt.as_ptr(),
17887                c_u.as_mut_ptr(),
17888                c_a.as_mut_ptr(),
17889                bwt.len() as SaSint,
17890                r,
17891                aux.as_ptr(),
17892            )
17893        };
17894
17895        assert_eq!(rust_rc, c_rc);
17896        assert_eq!(rust_u, c_u);
17897        assert_eq!(rust_u, text);
17898    }
17899
17900    fn assert_libsais16_unbwt_freq_matches_c(text: &[u16]) {
17901        let mut freq = vec![0; ALPHABET_SIZE];
17902        let mut bwt = vec![0; text.len()];
17903        let mut work = vec![0; text.len()];
17904        let primary = libsais16_bwt(text, &mut bwt, &mut work, 0, Some(&mut freq));
17905        assert!(primary >= 0);
17906
17907        let mut rust_u = vec![0; text.len()];
17908        let mut rust_a = vec![0; text.len() + 1];
17909        let mut c_u = vec![0; text.len()];
17910        let mut c_a = vec![0; text.len() + 1];
17911
17912        let rust_rc = libsais16_unbwt(&bwt, &mut rust_u, &mut rust_a, Some(&freq), primary);
17913        let c_rc = unsafe {
17914            probe_public_libsais16_unbwt_freq(
17915                bwt.as_ptr(),
17916                c_u.as_mut_ptr(),
17917                c_a.as_mut_ptr(),
17918                bwt.len() as SaSint,
17919                freq.as_ptr(),
17920                primary,
17921            )
17922        };
17923        assert_eq!(rust_rc, c_rc);
17924        assert_eq!(rust_u, c_u);
17925        assert_eq!(rust_u, text);
17926
17927        let r = 4;
17928        let mut aux = vec![0; (text.len() - 1) / r as usize + 1];
17929        let bwt_rc = libsais16_bwt_aux(text, &mut bwt, &mut work, 0, Some(&mut freq), r, &mut aux);
17930        assert_eq!(bwt_rc, 0);
17931
17932        rust_u.fill(0);
17933        rust_a.fill(0);
17934        c_u.fill(0);
17935        c_a.fill(0);
17936        let rust_rc = libsais16_unbwt_aux(&bwt, &mut rust_u, &mut rust_a, Some(&freq), r, &aux);
17937        let c_rc = unsafe {
17938            probe_public_libsais16_unbwt_aux_freq(
17939                bwt.as_ptr(),
17940                c_u.as_mut_ptr(),
17941                c_a.as_mut_ptr(),
17942                bwt.len() as SaSint,
17943                freq.as_ptr(),
17944                r,
17945                aux.as_ptr(),
17946            )
17947        };
17948        assert_eq!(rust_rc, c_rc);
17949        assert_eq!(rust_u, c_u);
17950        assert_eq!(rust_u, text);
17951    }
17952
17953    fn assert_libsais16_plcp_lcp_matches_c(text: &[u16]) {
17954        let mut sa = vec![0; text.len()];
17955        assert_eq!(libsais16(text, &mut sa, 0, None), 0);
17956
17957        let mut rust_plcp = vec![0; text.len()];
17958        let mut c_plcp = vec![0; text.len()];
17959        let rust_rc = libsais16_plcp(text, &sa, &mut rust_plcp);
17960        let c_rc = unsafe {
17961            probe_public_libsais16_plcp(
17962                text.as_ptr(),
17963                sa.as_ptr(),
17964                c_plcp.as_mut_ptr(),
17965                text.len() as SaSint,
17966            )
17967        };
17968        assert_eq!(rust_rc, c_rc);
17969        assert_eq!(rust_plcp, c_plcp);
17970
17971        let mut rust_lcp = vec![0; text.len()];
17972        let mut c_lcp = vec![0; text.len()];
17973        let rust_rc = libsais16_lcp(&rust_plcp, &sa, &mut rust_lcp);
17974        let c_rc = unsafe {
17975            probe_public_libsais16_lcp(
17976                c_plcp.as_ptr(),
17977                sa.as_ptr(),
17978                c_lcp.as_mut_ptr(),
17979                text.len() as SaSint,
17980            )
17981        };
17982        assert_eq!(rust_rc, c_rc);
17983        assert_eq!(rust_lcp, c_lcp);
17984    }
17985
17986    fn assert_libsais16_plcp_gsa_matches_c(text: &[u16]) {
17987        let mut sa = vec![0; text.len()];
17988        assert_eq!(libsais16_gsa(text, &mut sa, 0, None), 0);
17989
17990        let mut rust_plcp = vec![0; text.len()];
17991        let mut c_plcp = vec![0; text.len()];
17992        let rust_rc = libsais16_plcp_gsa(text, &sa, &mut rust_plcp);
17993        let c_rc = unsafe {
17994            probe_public_libsais16_plcp_gsa(
17995                text.as_ptr(),
17996                sa.as_ptr(),
17997                c_plcp.as_mut_ptr(),
17998                text.len() as SaSint,
17999            )
18000        };
18001        assert_eq!(rust_rc, c_rc);
18002        assert_eq!(rust_plcp, c_plcp);
18003    }
18004
18005    #[test]
18006    fn public_libsais16_matches_upstream_c() {
18007        for text in [
18008            [].as_slice(),
18009            &[1][..],
18010            &[2, 1, 3, 1, 2, 0],
18011            &[2, 1, 3, 1, 2, 4, 1, 0],
18012            &[65_535, 1, 65_534, 1, 0],
18013            &[7, 7, 7, 7, 7, 0],
18014        ] {
18015            assert_libsais16_matches_c(text);
18016        }
18017    }
18018
18019    #[test]
18020    fn public_libsais16_bwt_matches_upstream_c() {
18021        for text in [
18022            [].as_slice(),
18023            &[1][..],
18024            &[2, 1, 3, 1, 2, 0],
18025            &[2, 1, 3, 1, 2, 4, 1, 0],
18026            &[65_535, 1, 65_534, 1, 0],
18027            &[7, 7, 7, 7, 7, 0],
18028        ] {
18029            assert_libsais16_bwt_matches_c(text);
18030        }
18031    }
18032
18033    #[test]
18034    fn public_libsais16_gsa_matches_upstream_c() {
18035        for text in [&[0][..], &[2, 1, 0], &[2, 1, 0, 3, 1, 0], &[7, 7, 0, 7, 0]] {
18036            assert_libsais16_gsa_matches_c(text);
18037        }
18038    }
18039
18040    #[test]
18041    fn public_libsais16_int_matches_upstream_c() {
18042        for (text, k) in [
18043            (&[][..], 0),
18044            (&[0][..], 1),
18045            (&[1, 2, 1, 0][..], 3),
18046            (&[2, 1, 2, 1, 0][..], 3),
18047            (&[3, 3, 3, 2, 1, 0][..], 4),
18048        ] {
18049            assert_libsais16_int_matches_c(text, k);
18050        }
18051    }
18052
18053    #[test]
18054    fn public_libsais16_plcp_lcp_matches_upstream_c() {
18055        for text in [
18056            &[2, 1, 3, 1, 2, 0][..],
18057            &[2, 1, 3, 1, 2, 4, 1, 0],
18058            &[65_535, 1, 65_534, 1, 0],
18059            &[7, 7, 7, 7, 7, 0],
18060        ] {
18061            assert_libsais16_plcp_lcp_matches_c(text);
18062        }
18063    }
18064
18065    #[test]
18066    fn public_libsais16_plcp_gsa_matches_upstream_c() {
18067        for text in [&[0][..], &[2, 1, 0], &[2, 1, 0, 3, 1, 0], &[7, 7, 0, 7, 0]] {
18068            assert_libsais16_plcp_gsa_matches_c(text);
18069        }
18070    }
18071
18072    #[test]
18073    fn public_libsais16_bwt_aux_matches_upstream_c() {
18074        for text in [
18075            &[2, 1, 3, 1, 2, 0][..],
18076            &[2, 1, 3, 1, 2, 4, 1, 0],
18077            &[65_535, 1, 65_534, 1, 0],
18078            &[7, 7, 7, 7, 7, 0],
18079        ] {
18080            assert_libsais16_bwt_aux_matches_c(text, 4);
18081        }
18082    }
18083
18084    #[test]
18085    fn public_libsais16_frequency_outputs_match_upstream_c() {
18086        assert_libsais16_freq_outputs_match_c(&[65_535, 1, 2, 1, 0], &[65_535, 1, 0, 2, 1, 0]);
18087    }
18088
18089    #[test]
18090    fn public_libsais16_unbwt_with_frequency_matches_upstream_c() {
18091        assert_libsais16_unbwt_freq_matches_c(&[65_535, 1, 2, 1, 0]);
18092    }
18093
18094    #[test]
18095    fn public_libsais16_unbwt_matches_upstream_c() {
18096        for text in [
18097            &[1][..],
18098            &[2, 1, 3, 1, 2, 0],
18099            &[2, 1, 3, 1, 2, 4, 1, 0],
18100            &[65_535, 1, 65_534, 1, 0],
18101            &[7, 7, 7, 7, 7, 0],
18102        ] {
18103            assert_libsais16_unbwt_matches_c(text);
18104        }
18105    }
18106
18107    #[test]
18108    fn public_libsais16_unbwt_aux_matches_upstream_c() {
18109        for text in [
18110            &[2, 1, 3, 1, 2, 0][..],
18111            &[2, 1, 3, 1, 2, 4, 1, 0],
18112            &[65_535, 1, 65_534, 1, 0],
18113            &[7, 7, 7, 7, 7, 0],
18114        ] {
18115            assert_libsais16_unbwt_aux_matches_c(text, 4);
18116        }
18117    }
18118
18119    #[test]
18120    fn public_libsais16_unbwt_aux_exercises_decode_dispatch_cases() {
18121        for len in [2usize, 5, 9, 13, 17, 21, 25, 29, 33, 37] {
18122            let text = (0..len)
18123                .map(|i| ((i * 37 + 11) % 65_535 + 1) as u16)
18124                .collect::<Vec<_>>();
18125            assert_libsais16_unbwt_aux_matches_c(&text, 4);
18126        }
18127    }
18128
18129    #[test]
18130    fn libsais16_lcp_helpers_reject_invalid_suffix_entries() {
18131        let text = [2, 1, 2, 1, 0];
18132        let mut plcp = vec![0; text.len()];
18133        let mut lcp = vec![0; text.len()];
18134
18135        assert_eq!(libsais16_plcp(&text, &[0, 1, -1, 3, 4], &mut plcp), -1);
18136        assert_eq!(libsais16_plcp(&text, &[0, 1, 2, 3, 5], &mut plcp), -1);
18137        assert_eq!(libsais16_lcp(&plcp, &[0, 1, -1, 3, 4], &mut lcp), -1);
18138        assert_eq!(libsais16_lcp(&plcp, &[0, 1, 2, 3, 5], &mut lcp), -1);
18139    }
18140
18141    #[test]
18142    fn libsais16_rejects_invalid_public_arguments() {
18143        let text = [2, 1, 3, 1, 2, 0];
18144        let int_text = [1, 2, 1, 0];
18145        let mut int_text_for_short_sa = int_text.to_vec();
18146        let mut int_text_for_negative_fs = int_text.to_vec();
18147        let mut sa = vec![0; text.len() - 1];
18148        let mut int_sa = vec![0; int_text.len() - 1];
18149        let mut full_int_sa = vec![0; int_text.len()];
18150        let mut freq = vec![0; ALPHABET_SIZE - 1];
18151        let mut u = vec![0; text.len() - 1];
18152        let mut a = vec![0; text.len() - 1];
18153        let mut full_u = vec![0; text.len()];
18154        let mut full_a = vec![0; text.len()];
18155        let mut aux = vec![0; 1];
18156
18157        assert_eq!(libsais16(&text, &mut sa, 0, None), -1);
18158        assert_eq!(libsais16(&text, &mut full_a, 0, Some(&mut freq)), -1);
18159        assert_eq!(libsais16_gsa(&[1, 2, 3], &mut full_a[..3], 0, None), -1);
18160        assert_eq!(
18161            libsais16_int(&mut int_text_for_short_sa, &mut int_sa, 3, 0),
18162            -1
18163        );
18164        assert_eq!(
18165            libsais16_int(&mut int_text_for_negative_fs, &mut full_int_sa, 3, -1),
18166            -1
18167        );
18168        assert_eq!(libsais16_bwt(&text, &mut u, &mut full_a, 0, None), -1);
18169        assert_eq!(libsais16_bwt(&text, &mut full_u, &mut a, 0, None), -1);
18170        assert_eq!(
18171            libsais16_bwt_aux(&text, &mut full_u, &mut full_a, 0, None, 0, &mut aux),
18172            -1
18173        );
18174        assert_eq!(
18175            libsais16_bwt_aux(&text, &mut full_u, &mut full_a, 0, None, 3, &mut aux),
18176            -1
18177        );
18178        assert_eq!(
18179            libsais16_bwt_aux(&text, &mut full_u, &mut full_a, 0, None, 4, &mut aux),
18180            -1
18181        );
18182        assert_eq!(create_ctx_omp(-1), None);
18183        assert_eq!(unbwt_create_ctx_omp(-1), None);
18184    }
18185
18186    #[test]
18187    fn libsais16_unbwt_rejects_invalid_public_arguments() {
18188        let text = [2, 1, 3, 1, 2, 0];
18189        let mut bwt = vec![0; text.len()];
18190        let mut work = vec![0; text.len()];
18191        let primary = libsais16_bwt(&text, &mut bwt, &mut work, 0, None);
18192
18193        let mut short_u = vec![0; text.len() - 1];
18194        let mut short_a = vec![0; text.len() - 1];
18195        let mut full_u = vec![0; text.len()];
18196        let mut full_a = vec![0; text.len()];
18197        let short_freq = vec![0; ALPHABET_SIZE - 1];
18198        let short_aux = vec![primary];
18199        let bad_aux = vec![0, 0];
18200        let good_aux = vec![primary, 4];
18201
18202        assert_eq!(
18203            libsais16_unbwt(&bwt, &mut short_u, &mut full_a, None, primary),
18204            -1
18205        );
18206        assert_eq!(
18207            libsais16_unbwt(&bwt, &mut full_u, &mut short_a, None, primary),
18208            -1
18209        );
18210        assert_eq!(
18211            libsais16_unbwt(&bwt, &mut full_u, &mut full_a, Some(&short_freq), primary),
18212            -1
18213        );
18214        assert_eq!(libsais16_unbwt(&bwt, &mut full_u, &mut full_a, None, 0), -1);
18215        assert_eq!(
18216            libsais16_unbwt(
18217                &bwt,
18218                &mut full_u,
18219                &mut full_a,
18220                None,
18221                text.len() as SaSint + 1
18222            ),
18223            -1
18224        );
18225        assert_eq!(
18226            libsais16_unbwt_aux(&bwt, &mut full_u, &mut full_a, None, 0, &good_aux),
18227            -1
18228        );
18229        assert_eq!(
18230            libsais16_unbwt_aux(&bwt, &mut full_u, &mut full_a, None, 3, &good_aux),
18231            -1
18232        );
18233        assert_eq!(
18234            libsais16_unbwt_aux(&bwt, &mut full_u, &mut full_a, None, 4, &short_aux),
18235            -1
18236        );
18237        assert_eq!(
18238            libsais16_unbwt_aux(&bwt, &mut full_u, &mut full_a, None, 4, &bad_aux),
18239            -1
18240        );
18241    }
18242
18243    #[test]
18244    fn libsais16_ctx_rejects_invalid_public_arguments() {
18245        let text = [2, 1, 3, 1, 2, 0];
18246        let mut ctx = create_ctx().unwrap();
18247        let mut sa = vec![0; text.len() - 1];
18248        let mut freq = vec![0; ALPHABET_SIZE - 1];
18249        let mut u = vec![0; text.len() - 1];
18250        let mut a = vec![0; text.len() - 1];
18251        let mut full_u = vec![0; text.len()];
18252        let mut full_a = vec![0; text.len()];
18253        let mut aux = vec![0; 1];
18254
18255        assert_eq!(libsais16_ctx(&mut ctx, &text, &mut sa, 0, None), -1);
18256        assert_eq!(
18257            libsais16_ctx(&mut ctx, &text, &mut full_a, 0, Some(&mut freq)),
18258            -1
18259        );
18260        assert_eq!(
18261            libsais16_gsa_ctx(&mut ctx, &[1, 2, 3], &mut full_a[..3], 0, None),
18262            -1
18263        );
18264        assert_eq!(
18265            libsais16_bwt_ctx(&mut ctx, &text, &mut u, &mut full_a, 0, None),
18266            -1
18267        );
18268        assert_eq!(
18269            libsais16_bwt_ctx(&mut ctx, &text, &mut full_u, &mut a, 0, None),
18270            -1
18271        );
18272        assert_eq!(
18273            libsais16_bwt_aux_ctx(
18274                &mut ctx,
18275                &text,
18276                &mut full_u,
18277                &mut full_a,
18278                0,
18279                None,
18280                0,
18281                &mut aux
18282            ),
18283            -1
18284        );
18285        assert_eq!(
18286            libsais16_bwt_aux_ctx(
18287                &mut ctx,
18288                &text,
18289                &mut full_u,
18290                &mut full_a,
18291                0,
18292                None,
18293                3,
18294                &mut aux
18295            ),
18296            -1
18297        );
18298        assert_eq!(
18299            libsais16_bwt_aux_ctx(
18300                &mut ctx,
18301                &text,
18302                &mut full_u,
18303                &mut full_a,
18304                0,
18305                None,
18306                4,
18307                &mut aux
18308            ),
18309            -1
18310        );
18311
18312        let mut default_ctx = Context::default();
18313        assert_eq!(
18314            libsais16_ctx(&mut default_ctx, &text, &mut full_a, 0, None),
18315            -2
18316        );
18317
18318        let mut bad_bucket_ctx = create_ctx().unwrap();
18319        bad_bucket_ctx.buckets.clear();
18320        assert_eq!(
18321            libsais16_ctx(&mut bad_bucket_ctx, &text, &mut full_a, 0, None),
18322            -2
18323        );
18324
18325        let mut short_thread_state_ctx = create_ctx_omp(2).unwrap();
18326        short_thread_state_ctx
18327            .thread_state
18328            .as_mut()
18329            .unwrap()
18330            .truncate(1);
18331        assert_eq!(
18332            libsais16_ctx(&mut short_thread_state_ctx, &text, &mut full_a, 0, None),
18333            -2
18334        );
18335    }
18336
18337    #[test]
18338    fn libsais16_unbwt_ctx_rejects_invalid_public_arguments() {
18339        let text = [2, 1, 3, 1, 2, 0];
18340        let mut bwt = vec![0; text.len()];
18341        let mut work = vec![0; text.len()];
18342        let primary = libsais16_bwt(&text, &mut bwt, &mut work, 0, None);
18343        let mut ctx = unbwt_create_ctx().unwrap();
18344
18345        let mut short_u = vec![0; text.len() - 1];
18346        let mut short_a = vec![0; text.len() - 1];
18347        let mut full_u = vec![0; text.len()];
18348        let mut full_a = vec![0; text.len()];
18349        let short_freq = vec![0; ALPHABET_SIZE - 1];
18350        let short_aux = vec![primary];
18351        let bad_aux = vec![0, 0];
18352        let good_aux = vec![primary, 4];
18353
18354        assert_eq!(
18355            libsais16_unbwt_ctx(&mut ctx, &bwt, &mut short_u, &mut full_a, None, primary),
18356            -1
18357        );
18358        assert_eq!(
18359            libsais16_unbwt_ctx(&mut ctx, &bwt, &mut full_u, &mut short_a, None, primary),
18360            -1
18361        );
18362        assert_eq!(
18363            libsais16_unbwt_ctx(
18364                &mut ctx,
18365                &bwt,
18366                &mut full_u,
18367                &mut full_a,
18368                Some(&short_freq),
18369                primary
18370            ),
18371            -1
18372        );
18373        assert_eq!(
18374            libsais16_unbwt_ctx(&mut ctx, &bwt, &mut full_u, &mut full_a, None, 0),
18375            -1
18376        );
18377        assert_eq!(
18378            libsais16_unbwt_aux_ctx(&mut ctx, &bwt, &mut full_u, &mut full_a, None, 0, &good_aux),
18379            -1
18380        );
18381        assert_eq!(
18382            libsais16_unbwt_aux_ctx(&mut ctx, &bwt, &mut full_u, &mut full_a, None, 3, &good_aux),
18383            -1
18384        );
18385        assert_eq!(
18386            libsais16_unbwt_aux_ctx(
18387                &mut ctx,
18388                &bwt,
18389                &mut full_u,
18390                &mut full_a,
18391                None,
18392                4,
18393                &short_aux
18394            ),
18395            -1
18396        );
18397        assert_eq!(
18398            libsais16_unbwt_aux_ctx(&mut ctx, &bwt, &mut full_u, &mut full_a, None, 4, &bad_aux),
18399            -1
18400        );
18401    }
18402
18403    #[test]
18404    fn libsais16_context_wrappers_match_direct_calls() {
18405        let text = [2, 1, 3, 1, 2, 0];
18406        let mut ctx = create_ctx().unwrap();
18407
18408        let mut direct_sa = vec![0; text.len()];
18409        let mut ctx_sa = vec![0; text.len()];
18410        assert_eq!(libsais16(&text, &mut direct_sa, 0, None), 0);
18411        assert_eq!(libsais16_ctx(&mut ctx, &text, &mut ctx_sa, 0, None), 0);
18412        assert_eq!(ctx_sa, direct_sa);
18413
18414        let mut direct_bwt = vec![0; text.len()];
18415        let mut direct_work = vec![0; text.len()];
18416        let mut ctx_bwt = vec![0; text.len()];
18417        let mut ctx_work = vec![0; text.len()];
18418        assert_eq!(
18419            libsais16_bwt(&text, &mut direct_bwt, &mut direct_work, 0, None),
18420            libsais16_bwt_ctx(&mut ctx, &text, &mut ctx_bwt, &mut ctx_work, 0, None)
18421        );
18422        assert_eq!(ctx_bwt, direct_bwt);
18423
18424        let mut direct_aux = vec![0; 2];
18425        let mut ctx_aux = vec![0; 2];
18426        assert_eq!(
18427            libsais16_bwt_aux(
18428                &text,
18429                &mut direct_bwt,
18430                &mut direct_work,
18431                0,
18432                None,
18433                4,
18434                &mut direct_aux
18435            ),
18436            libsais16_bwt_aux_ctx(
18437                &mut ctx,
18438                &text,
18439                &mut ctx_bwt,
18440                &mut ctx_work,
18441                0,
18442                None,
18443                4,
18444                &mut ctx_aux
18445            )
18446        );
18447        assert_eq!(ctx_bwt, direct_bwt);
18448        assert_eq!(ctx_aux, direct_aux);
18449    }
18450
18451    #[test]
18452    fn libsais16_unbwt_context_wrappers_match_direct_calls() {
18453        let text = [2, 1, 3, 1, 2, 0];
18454        let mut bwt = vec![0; text.len()];
18455        let mut work = vec![0; text.len()];
18456        let primary = libsais16_bwt(&text, &mut bwt, &mut work, 0, None);
18457
18458        let mut ctx = unbwt_create_ctx().unwrap();
18459        let mut direct = vec![0; text.len()];
18460        let mut direct_work = vec![0; text.len()];
18461        let mut via_ctx = vec![0; text.len()];
18462        let mut ctx_work = vec![0; text.len()];
18463
18464        assert_eq!(
18465            libsais16_unbwt(&bwt, &mut direct, &mut direct_work, None, primary),
18466            0
18467        );
18468        assert_eq!(
18469            libsais16_unbwt_ctx(&mut ctx, &bwt, &mut via_ctx, &mut ctx_work, None, primary),
18470            0
18471        );
18472        assert_eq!(via_ctx, direct);
18473
18474        let mut aux = vec![0; 2];
18475        assert_eq!(
18476            libsais16_bwt_aux(&text, &mut bwt, &mut work, 0, None, 4, &mut aux),
18477            0
18478        );
18479        assert_eq!(
18480            libsais16_unbwt_aux(&bwt, &mut direct, &mut direct_work, None, 4, &aux),
18481            0
18482        );
18483        assert_eq!(
18484            libsais16_unbwt_aux_ctx(&mut ctx, &bwt, &mut via_ctx, &mut ctx_work, None, 4, &aux),
18485            0
18486        );
18487        assert_eq!(via_ctx, direct);
18488    }
18489
18490    #[test]
18491    fn libsais16_ctx_frequency_wrappers_match_direct_calls() {
18492        let text = [2, 1, 3, 1, 2, 0];
18493        let gsa_text = [2, 1, 0, 3, 1, 0];
18494        let mut ctx = create_ctx().unwrap();
18495
18496        let mut direct_sa = vec![0; text.len()];
18497        let mut ctx_sa = vec![0; text.len()];
18498        let mut direct_freq = vec![-1; ALPHABET_SIZE];
18499        let mut ctx_freq = vec![-1; ALPHABET_SIZE];
18500        assert_eq!(
18501            libsais16(&text, &mut direct_sa, 0, Some(&mut direct_freq)),
18502            0
18503        );
18504        assert_eq!(
18505            libsais16_ctx(&mut ctx, &text, &mut ctx_sa, 0, Some(&mut ctx_freq)),
18506            0
18507        );
18508        assert_eq!(ctx_sa, direct_sa);
18509        assert_eq!(ctx_freq, direct_freq);
18510
18511        let mut direct_gsa = vec![0; gsa_text.len()];
18512        let mut ctx_gsa = vec![0; gsa_text.len()];
18513        direct_freq.fill(-1);
18514        ctx_freq.fill(-1);
18515        assert_eq!(
18516            libsais16_gsa(&gsa_text, &mut direct_gsa, 0, Some(&mut direct_freq)),
18517            0
18518        );
18519        assert_eq!(
18520            libsais16_gsa_ctx(&mut ctx, &gsa_text, &mut ctx_gsa, 0, Some(&mut ctx_freq)),
18521            0
18522        );
18523        assert_eq!(ctx_gsa, direct_gsa);
18524        assert_eq!(ctx_freq, direct_freq);
18525
18526        let mut direct_bwt = vec![0; text.len()];
18527        let mut direct_work = vec![0; text.len()];
18528        let mut ctx_bwt = vec![0; text.len()];
18529        let mut ctx_work = vec![0; text.len()];
18530        direct_freq.fill(-1);
18531        ctx_freq.fill(-1);
18532        assert_eq!(
18533            libsais16_bwt(
18534                &text,
18535                &mut direct_bwt,
18536                &mut direct_work,
18537                0,
18538                Some(&mut direct_freq)
18539            ),
18540            libsais16_bwt_ctx(
18541                &mut ctx,
18542                &text,
18543                &mut ctx_bwt,
18544                &mut ctx_work,
18545                0,
18546                Some(&mut ctx_freq)
18547            )
18548        );
18549        assert_eq!(ctx_bwt, direct_bwt);
18550        assert_eq!(ctx_freq, direct_freq);
18551
18552        let mut direct_aux = vec![0; 2];
18553        let mut ctx_aux = vec![0; 2];
18554        direct_freq.fill(-1);
18555        ctx_freq.fill(-1);
18556        assert_eq!(
18557            libsais16_bwt_aux(
18558                &text,
18559                &mut direct_bwt,
18560                &mut direct_work,
18561                0,
18562                Some(&mut direct_freq),
18563                4,
18564                &mut direct_aux
18565            ),
18566            libsais16_bwt_aux_ctx(
18567                &mut ctx,
18568                &text,
18569                &mut ctx_bwt,
18570                &mut ctx_work,
18571                0,
18572                Some(&mut ctx_freq),
18573                4,
18574                &mut ctx_aux
18575            )
18576        );
18577        assert_eq!(ctx_bwt, direct_bwt);
18578        assert_eq!(ctx_aux, direct_aux);
18579        assert_eq!(ctx_freq, direct_freq);
18580    }
18581
18582    #[test]
18583    fn libsais16_unbwt_ctx_frequency_wrappers_match_direct_calls() {
18584        let text = [2, 1, 3, 1, 2, 0];
18585        let mut freq = vec![0; ALPHABET_SIZE];
18586        let mut bwt = vec![0; text.len()];
18587        let mut work = vec![0; text.len()];
18588        let primary = libsais16_bwt(&text, &mut bwt, &mut work, 0, Some(&mut freq));
18589        assert!(primary >= 0);
18590
18591        let mut ctx = unbwt_create_ctx().unwrap();
18592        let mut direct = vec![0; text.len()];
18593        let mut direct_work = vec![0; text.len() + 1];
18594        let mut via_ctx = vec![0; text.len()];
18595        let mut ctx_work = vec![0; text.len() + 1];
18596        assert_eq!(
18597            libsais16_unbwt(&bwt, &mut direct, &mut direct_work, Some(&freq), primary),
18598            libsais16_unbwt_ctx(
18599                &mut ctx,
18600                &bwt,
18601                &mut via_ctx,
18602                &mut ctx_work,
18603                Some(&freq),
18604                primary
18605            )
18606        );
18607        assert_eq!(via_ctx, direct);
18608        assert_eq!(via_ctx, text);
18609
18610        let mut aux = vec![0; (text.len() - 1) / 4 + 1];
18611        assert_eq!(
18612            libsais16_bwt_aux(&text, &mut bwt, &mut work, 0, Some(&mut freq), 4, &mut aux),
18613            0
18614        );
18615        direct.fill(0);
18616        direct_work.fill(0);
18617        via_ctx.fill(0);
18618        ctx_work.fill(0);
18619        assert_eq!(
18620            libsais16_unbwt_aux(&bwt, &mut direct, &mut direct_work, Some(&freq), 4, &aux),
18621            libsais16_unbwt_aux_ctx(
18622                &mut ctx,
18623                &bwt,
18624                &mut via_ctx,
18625                &mut ctx_work,
18626                Some(&freq),
18627                4,
18628                &aux
18629            )
18630        );
18631        assert_eq!(via_ctx, direct);
18632        assert_eq!(via_ctx, text);
18633    }
18634
18635    #[test]
18636    fn libsais16_omp_wrappers_match_direct_calls_and_reject_negative_threads() {
18637        let text = [2, 1, 3, 1, 2, 0];
18638        let gsa_text = [2, 1, 0, 3, 1, 0];
18639        let mut direct_sa = vec![0; text.len()];
18640        let mut omp_sa = vec![0; text.len()];
18641        assert_eq!(libsais16(&text, &mut direct_sa, 0, None), 0);
18642        assert_eq!(libsais16_omp(&text, &mut omp_sa, 0, None, 2), 0);
18643        assert_eq!(omp_sa, direct_sa);
18644        assert_eq!(libsais16_omp(&text, &mut omp_sa, 0, None, -1), -1);
18645
18646        let mut direct_gsa = vec![0; gsa_text.len()];
18647        let mut omp_gsa = vec![0; gsa_text.len()];
18648        assert_eq!(libsais16_gsa(&gsa_text, &mut direct_gsa, 0, None), 0);
18649        assert_eq!(libsais16_gsa_omp(&gsa_text, &mut omp_gsa, 0, None, 2), 0);
18650        assert_eq!(omp_gsa, direct_gsa);
18651        assert_eq!(libsais16_gsa_omp(&gsa_text, &mut omp_gsa, 0, None, -1), -1);
18652
18653        let int_text = [1, 2, 1, 0];
18654        let mut direct_int_text = int_text.to_vec();
18655        let mut omp_int_text = int_text.to_vec();
18656        let mut direct_int_sa = vec![0; int_text.len()];
18657        let mut omp_int_sa = vec![0; int_text.len()];
18658        assert_eq!(
18659            libsais16_int(&mut direct_int_text, &mut direct_int_sa, 3, 0),
18660            0
18661        );
18662        assert_eq!(
18663            libsais16_int_omp(&mut omp_int_text, &mut omp_int_sa, 3, 0, 2),
18664            0
18665        );
18666        assert_eq!(omp_int_text, direct_int_text);
18667        assert_eq!(omp_int_sa, direct_int_sa);
18668        assert_eq!(
18669            libsais16_int_omp(&mut omp_int_text, &mut omp_int_sa, 3, 0, -1),
18670            -1
18671        );
18672
18673        let mut direct_bwt = vec![0; text.len()];
18674        let mut direct_work = vec![0; text.len()];
18675        let mut omp_bwt = vec![0; text.len()];
18676        let mut omp_work = vec![0; text.len()];
18677        assert_eq!(
18678            libsais16_bwt(&text, &mut direct_bwt, &mut direct_work, 0, None),
18679            libsais16_bwt_omp(&text, &mut omp_bwt, &mut omp_work, 0, None, 2)
18680        );
18681        assert_eq!(omp_bwt, direct_bwt);
18682        assert_eq!(
18683            libsais16_bwt_omp(&text, &mut omp_bwt, &mut omp_work, 0, None, -1),
18684            -1
18685        );
18686
18687        let mut direct_aux = vec![0; 2];
18688        let mut omp_aux = vec![0; 2];
18689        assert_eq!(
18690            libsais16_bwt_aux(
18691                &text,
18692                &mut direct_bwt,
18693                &mut direct_work,
18694                0,
18695                None,
18696                4,
18697                &mut direct_aux
18698            ),
18699            libsais16_bwt_aux_omp(
18700                &text,
18701                &mut omp_bwt,
18702                &mut omp_work,
18703                0,
18704                None,
18705                4,
18706                &mut omp_aux,
18707                2
18708            )
18709        );
18710        assert_eq!(omp_bwt, direct_bwt);
18711        assert_eq!(omp_aux, direct_aux);
18712        assert_eq!(
18713            libsais16_bwt_aux_omp(
18714                &text,
18715                &mut omp_bwt,
18716                &mut omp_work,
18717                0,
18718                None,
18719                4,
18720                &mut omp_aux,
18721                -1
18722            ),
18723            -1
18724        );
18725    }
18726
18727    #[test]
18728    fn libsais16_omp_frequency_wrappers_match_direct_calls() {
18729        let text = [2, 1, 3, 1, 2, 0];
18730        let gsa_text = [2, 1, 0, 3, 1, 0];
18731        let mut direct_sa = vec![0; text.len()];
18732        let mut omp_sa = vec![0; text.len()];
18733        let mut direct_freq = vec![-1; ALPHABET_SIZE];
18734        let mut omp_freq = vec![-1; ALPHABET_SIZE];
18735        assert_eq!(
18736            libsais16(&text, &mut direct_sa, 0, Some(&mut direct_freq)),
18737            0
18738        );
18739        assert_eq!(
18740            libsais16_omp(&text, &mut omp_sa, 0, Some(&mut omp_freq), 2),
18741            0
18742        );
18743        assert_eq!(omp_sa, direct_sa);
18744        assert_eq!(omp_freq, direct_freq);
18745
18746        let mut direct_gsa = vec![0; gsa_text.len()];
18747        let mut omp_gsa = vec![0; gsa_text.len()];
18748        direct_freq.fill(-1);
18749        omp_freq.fill(-1);
18750        assert_eq!(
18751            libsais16_gsa(&gsa_text, &mut direct_gsa, 0, Some(&mut direct_freq)),
18752            0
18753        );
18754        assert_eq!(
18755            libsais16_gsa_omp(&gsa_text, &mut omp_gsa, 0, Some(&mut omp_freq), 2),
18756            0
18757        );
18758        assert_eq!(omp_gsa, direct_gsa);
18759        assert_eq!(omp_freq, direct_freq);
18760
18761        let mut direct_bwt = vec![0; text.len()];
18762        let mut direct_work = vec![0; text.len()];
18763        let mut omp_bwt = vec![0; text.len()];
18764        let mut omp_work = vec![0; text.len()];
18765        direct_freq.fill(-1);
18766        omp_freq.fill(-1);
18767        assert_eq!(
18768            libsais16_bwt(
18769                &text,
18770                &mut direct_bwt,
18771                &mut direct_work,
18772                0,
18773                Some(&mut direct_freq)
18774            ),
18775            libsais16_bwt_omp(
18776                &text,
18777                &mut omp_bwt,
18778                &mut omp_work,
18779                0,
18780                Some(&mut omp_freq),
18781                2
18782            )
18783        );
18784        assert_eq!(omp_bwt, direct_bwt);
18785        assert_eq!(omp_freq, direct_freq);
18786
18787        let mut direct_aux = vec![0; 2];
18788        let mut omp_aux = vec![0; 2];
18789        direct_freq.fill(-1);
18790        omp_freq.fill(-1);
18791        assert_eq!(
18792            libsais16_bwt_aux(
18793                &text,
18794                &mut direct_bwt,
18795                &mut direct_work,
18796                0,
18797                Some(&mut direct_freq),
18798                4,
18799                &mut direct_aux
18800            ),
18801            libsais16_bwt_aux_omp(
18802                &text,
18803                &mut omp_bwt,
18804                &mut omp_work,
18805                0,
18806                Some(&mut omp_freq),
18807                4,
18808                &mut omp_aux,
18809                2
18810            )
18811        );
18812        assert_eq!(omp_bwt, direct_bwt);
18813        assert_eq!(omp_aux, direct_aux);
18814        assert_eq!(omp_freq, direct_freq);
18815    }
18816
18817    #[test]
18818    fn libsais16_unbwt_omp_frequency_wrappers_match_direct_calls() {
18819        let text = [2, 1, 3, 1, 2, 0];
18820        let mut freq = vec![0; ALPHABET_SIZE];
18821        let mut bwt = vec![0; text.len()];
18822        let mut work = vec![0; text.len()];
18823        let primary = libsais16_bwt(&text, &mut bwt, &mut work, 0, Some(&mut freq));
18824        assert!(primary >= 0);
18825
18826        let mut direct = vec![0; text.len()];
18827        let mut direct_work = vec![0; text.len() + 1];
18828        let mut omp = vec![0; text.len()];
18829        let mut omp_work = vec![0; text.len() + 1];
18830        assert_eq!(
18831            libsais16_unbwt(&bwt, &mut direct, &mut direct_work, Some(&freq), primary),
18832            libsais16_unbwt_omp(&bwt, &mut omp, &mut omp_work, Some(&freq), primary, 2)
18833        );
18834        assert_eq!(omp, direct);
18835        assert_eq!(omp, text);
18836
18837        let mut aux = vec![0; (text.len() - 1) / 4 + 1];
18838        assert_eq!(
18839            libsais16_bwt_aux(&text, &mut bwt, &mut work, 0, Some(&mut freq), 4, &mut aux),
18840            0
18841        );
18842        direct.fill(0);
18843        direct_work.fill(0);
18844        omp.fill(0);
18845        omp_work.fill(0);
18846        assert_eq!(
18847            libsais16_unbwt_aux(&bwt, &mut direct, &mut direct_work, Some(&freq), 4, &aux),
18848            libsais16_unbwt_aux_omp(&bwt, &mut omp, &mut omp_work, Some(&freq), 4, &aux, 2)
18849        );
18850        assert_eq!(omp, direct);
18851        assert_eq!(omp, text);
18852    }
18853
18854    #[test]
18855    fn libsais16_lcp_and_unbwt_omp_wrappers_match_direct_calls() {
18856        let text = [2, 1, 3, 1, 2, 0];
18857        let mut sa = vec![0; text.len()];
18858        assert_eq!(libsais16(&text, &mut sa, 0, None), 0);
18859
18860        let mut direct_plcp = vec![0; text.len()];
18861        let mut omp_plcp = vec![0; text.len()];
18862        assert_eq!(libsais16_plcp(&text, &sa, &mut direct_plcp), 0);
18863        assert_eq!(libsais16_plcp_omp(&text, &sa, &mut omp_plcp, 2), 0);
18864        assert_eq!(omp_plcp, direct_plcp);
18865        assert_eq!(libsais16_plcp_omp(&text, &sa, &mut omp_plcp, -1), -1);
18866
18867        let gsa_text = [2, 1, 0, 1, 2, 0];
18868        let mut gsa = vec![0; gsa_text.len()];
18869        assert_eq!(libsais16_gsa(&gsa_text, &mut gsa, 0, None), 0);
18870        let mut direct_gsa_plcp = vec![0; gsa_text.len()];
18871        let mut omp_gsa_plcp = vec![0; gsa_text.len()];
18872        assert_eq!(libsais16_plcp_gsa(&gsa_text, &gsa, &mut direct_gsa_plcp), 0);
18873        assert_eq!(
18874            libsais16_plcp_gsa_omp(&gsa_text, &gsa, &mut omp_gsa_plcp, 2),
18875            0
18876        );
18877        assert_eq!(omp_gsa_plcp, direct_gsa_plcp);
18878        assert_eq!(
18879            libsais16_plcp_gsa_omp(&gsa_text, &gsa, &mut omp_gsa_plcp, -1),
18880            -1
18881        );
18882
18883        let mut direct_lcp = vec![0; text.len()];
18884        let mut omp_lcp = vec![0; text.len()];
18885        assert_eq!(libsais16_lcp(&direct_plcp, &sa, &mut direct_lcp), 0);
18886        assert_eq!(libsais16_lcp_omp(&direct_plcp, &sa, &mut omp_lcp, 2), 0);
18887        assert_eq!(omp_lcp, direct_lcp);
18888        assert_eq!(libsais16_lcp_omp(&direct_plcp, &sa, &mut omp_lcp, -1), -1);
18889
18890        let mut bwt = vec![0; text.len()];
18891        let mut work = vec![0; text.len()];
18892        let primary = libsais16_bwt(&text, &mut bwt, &mut work, 0, None);
18893        let mut direct = vec![0; text.len()];
18894        let mut omp = vec![0; text.len()];
18895        let mut direct_work = vec![0; text.len()];
18896        let mut omp_work = vec![0; text.len()];
18897        assert_eq!(
18898            libsais16_unbwt(&bwt, &mut direct, &mut direct_work, None, primary),
18899            0
18900        );
18901        assert_eq!(
18902            libsais16_unbwt_omp(&bwt, &mut omp, &mut omp_work, None, primary, 2),
18903            0
18904        );
18905        assert_eq!(omp, direct);
18906        assert_eq!(
18907            libsais16_unbwt_omp(&bwt, &mut omp, &mut omp_work, None, primary, -1),
18908            -1
18909        );
18910    }
18911}