Skip to main content

libsais_rs/
libsais16x64.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, 64-bit suffix array, BWT, unBWT,
5//! PLCP and LCP entry points (mirroring `libsais16x64.h`).
6
7use std::mem;
8
9use rayon::prelude::*;
10
11use crate::{run_rayon_with_threads, SyncMutPtr};
12
13pub type SaSint = i64;
14pub type SaUint = u64;
15
16pub const ALPHABET_SIZE: usize = 1usize << 16;
17const SAINT_MAX: SaSint = SaSint::MAX;
18const SAINT_MIN: SaSint = SaSint::MIN;
19const SAINT_BIT: u32 = 64;
20const SUFFIX_GROUP_BIT: u32 = SAINT_BIT - 1;
21const SUFFIX_GROUP_MARKER: SaSint = 1_i64 << (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 libsais16x64 context that allows reusing allocated memory with each libsais16x64 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 libsais16x64 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 libsais16x64 context and frees previously allocated memory.
86pub fn free_ctx(_ctx: Context) {}
87
88/// Creates the libsais16x64 reverse-BWT context that allows reusing allocated memory with each `libsais16x64_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 libsais16x64 reverse-BWT context for parallel `libsais16x64_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 libsais16x64 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    for value in &mut sa[..len] {
387        *value ^= SAINT_MIN;
388    }
389}
390
391fn gather_lms_suffixes_32s(t: &[SaSint], sa: &mut [SaSint], n: SaSint) -> SaSint {
392    let mut i = n - 2;
393    let mut m = n - 1;
394    let mut f0 = 1usize;
395    let mut f1: usize;
396    let mut c0 = t[(n - 1) as usize] as isize;
397    let mut c1: isize;
398
399    while i >= 3 {
400        c1 = t[i as usize] as isize;
401        f1 = usize::from(c1 > c0 - f0 as isize);
402        sa[m as usize] = i + 1;
403        m -= (f1 & !f0) as SaSint;
404
405        c0 = t[(i - 1) as usize] as isize;
406        f0 = usize::from(c0 > c1 - f1 as isize);
407        sa[m as usize] = i;
408        m -= (f0 & !f1) as SaSint;
409
410        c1 = t[(i - 2) as usize] as isize;
411        f1 = usize::from(c1 > c0 - f0 as isize);
412        sa[m as usize] = i - 1;
413        m -= (f1 & !f0) as SaSint;
414
415        c0 = t[(i - 3) as usize] as isize;
416        f0 = usize::from(c0 > c1 - f1 as isize);
417        sa[m as usize] = i - 2;
418        m -= (f0 & !f1) as SaSint;
419
420        i -= 4;
421    }
422
423    while i >= 0 {
424        c1 = c0;
425        c0 = t[i as usize] as isize;
426        f1 = f0;
427        f0 = usize::from(c0 > c1 - f1 as isize);
428        sa[m as usize] = i + 1;
429        m -= (f0 & !f1) as SaSint;
430        i -= 1;
431    }
432
433    n - 1 - m
434}
435
436fn gather_compacted_lms_suffixes_32s(t: &[SaSint], sa: &mut [SaSint], n: SaSint) -> SaSint {
437    let mut i = n - 2;
438    let mut m = n - 1;
439    let mut f0 = 1usize;
440    let mut f1: usize;
441    let mut c0 = t[(n - 1) as usize] as isize;
442    let mut c1: isize;
443
444    while i >= 3 {
445        c1 = t[i as usize] as isize;
446        f1 = usize::from(c1 > c0 - f0 as isize);
447        sa[m as usize] = i + 1;
448        m -= (f1 & !f0 & usize::from(c0 >= 0)) as SaSint;
449
450        c0 = t[(i - 1) as usize] as isize;
451        f0 = usize::from(c0 > c1 - f1 as isize);
452        sa[m as usize] = i;
453        m -= (f0 & !f1 & usize::from(c1 >= 0)) as SaSint;
454
455        c1 = t[(i - 2) as usize] as isize;
456        f1 = usize::from(c1 > c0 - f0 as isize);
457        sa[m as usize] = i - 1;
458        m -= (f1 & !f0 & usize::from(c0 >= 0)) as SaSint;
459
460        c0 = t[(i - 3) as usize] as isize;
461        f0 = usize::from(c0 > c1 - f1 as isize);
462        sa[m as usize] = i - 2;
463        m -= (f0 & !f1 & usize::from(c1 >= 0)) as SaSint;
464
465        i -= 4;
466    }
467
468    while i >= 0 {
469        c1 = c0;
470        c0 = t[i as usize] as isize;
471        f1 = f0;
472        f0 = usize::from(c0 > c1 - f1 as isize);
473        sa[m as usize] = i + 1;
474        m -= (f0 & !f1 & usize::from(c1 >= 0)) as SaSint;
475        i -= 1;
476    }
477
478    n - 1 - m
479}
480
481fn count_lms_suffixes_32s_4k(t: &[SaSint], n: SaSint, k: SaSint, buckets: &mut [SaSint]) {
482    buckets[..4 * k as usize].fill(0);
483    let mut i = n - 2;
484    let mut f0 = 1usize;
485    let mut f1: usize;
486    let mut c0 = t[(n - 1) as usize] as isize;
487    let mut c1: isize;
488
489    while i >= 3 {
490        c1 = t[i as usize] as isize;
491        f1 = usize::from(c1 > c0 - f0 as isize);
492        buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
493
494        c0 = t[(i - 1) as usize] as isize;
495        f0 = usize::from(c0 > c1 - f1 as isize);
496        buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
497
498        c1 = t[(i - 2) as usize] as isize;
499        f1 = usize::from(c1 > c0 - f0 as isize);
500        buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
501
502        c0 = t[(i - 3) as usize] as isize;
503        f0 = usize::from(c0 > c1 - f1 as isize);
504        buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
505
506        i -= 4;
507    }
508
509    while i >= 0 {
510        c1 = c0;
511        c0 = t[i as usize] as isize;
512        f1 = f0;
513        f0 = usize::from(c0 > c1 - f1 as isize);
514        buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
515        i -= 1;
516    }
517
518    buckets[buckets_index4(c0 as usize, f0 + f0)] += 1;
519}
520
521fn count_lms_suffixes_32s_2k(t: &[SaSint], n: SaSint, k: SaSint, buckets: &mut [SaSint]) {
522    buckets[..2 * k as usize].fill(0);
523    let mut i = n - 2;
524    let mut f0 = 1usize;
525    let mut f1: usize;
526    let mut c0 = t[(n - 1) as usize] as isize;
527    let mut c1: isize;
528
529    while i >= 3 {
530        c1 = t[i as usize] as isize;
531        f1 = usize::from(c1 > c0 - f0 as isize);
532        buckets[buckets_index2(c0 as usize, f1 & !f0)] += 1;
533
534        c0 = t[(i - 1) as usize] as isize;
535        f0 = usize::from(c0 > c1 - f1 as isize);
536        buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
537
538        c1 = t[(i - 2) as usize] as isize;
539        f1 = usize::from(c1 > c0 - f0 as isize);
540        buckets[buckets_index2(c0 as usize, f1 & !f0)] += 1;
541
542        c0 = t[(i - 3) as usize] as isize;
543        f0 = usize::from(c0 > c1 - f1 as isize);
544        buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
545
546        i -= 4;
547    }
548
549    while i >= 0 {
550        c1 = c0;
551        c0 = t[i as usize] as isize;
552        f1 = f0;
553        f0 = usize::from(c0 > c1 - f1 as isize);
554        buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
555        i -= 1;
556    }
557
558    buckets[buckets_index2(c0 as usize, 0)] += 1;
559}
560
561fn count_compacted_lms_suffixes_32s_2k(t: &[SaSint], n: SaSint, k: SaSint, buckets: &mut [SaSint]) {
562    buckets[..2 * k as usize].fill(0);
563    let mut i = n - 2;
564    let mut f0 = 1usize;
565    let mut f1: usize;
566    let mut c0 = t[(n - 1) as usize] as isize;
567    let mut c1: isize;
568
569    while i >= 3 {
570        c1 = t[i as usize] as isize;
571        f1 = usize::from(c1 > c0 - f0 as isize);
572        buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
573
574        c0 = t[(i - 1) as usize] as isize;
575        f0 = usize::from(c0 > c1 - f1 as isize);
576        buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
577
578        c1 = t[(i - 2) as usize] as isize;
579        f1 = usize::from(c1 > c0 - f0 as isize);
580        buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
581
582        c0 = t[(i - 3) as usize] as isize;
583        f0 = usize::from(c0 > c1 - f1 as isize);
584        buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
585
586        i -= 4;
587    }
588
589    while i >= 0 {
590        c1 = c0;
591        c0 = t[i as usize] as isize;
592        f1 = f0;
593        f0 = usize::from(c0 > c1 - f1 as isize);
594        buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
595        i -= 1;
596    }
597
598    buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, 0)] += 1;
599}
600
601fn get_bucket_stride(free_space: SaSint, bucket_size: SaSint, num_buckets: SaSint) -> SaSint {
602    let bucket_size_1024 = (bucket_size + 1023) & !1023;
603    if free_space / (num_buckets - 1) >= bucket_size_1024 {
604        return bucket_size_1024;
605    }
606    let bucket_size_16 = (bucket_size + 15) & !15;
607    if free_space / (num_buckets - 1) >= bucket_size_16 {
608        return bucket_size_16;
609    }
610    bucket_size
611}
612
613fn count_and_gather_lms_suffixes_32s_4k(
614    t: &[SaSint],
615    sa: &mut [SaSint],
616    n: SaSint,
617    k: SaSint,
618    buckets: &mut [SaSint],
619    omp_block_start: isize,
620    omp_block_size: isize,
621) -> SaSint {
622    buckets[..4 * k as usize].fill(0);
623    let mut m = omp_block_start + omp_block_size - 1;
624
625    if omp_block_size > 0 {
626        let mut j = m + 1;
627        let mut c0 = t[m as usize] as isize;
628        let mut c1 = -1isize;
629        while j < n as isize {
630            c1 = t[j as usize] as isize;
631            if c1 != c0 {
632                break;
633            }
634            j += 1;
635        }
636
637        let mut f0 = usize::from(c0 >= c1);
638        let mut f1: usize;
639        let mut i = m - 1;
640        j = omp_block_start + 64 + 3;
641        while i >= j {
642            c1 = t[i as usize] as isize;
643            f1 = usize::from(c1 > c0 - f0 as isize);
644            sa[m as usize] = (i + 1) as SaSint;
645            m -= (f1 & !f0) as isize;
646            buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
647
648            c0 = t[(i - 1) as usize] as isize;
649            f0 = usize::from(c0 > c1 - f1 as isize);
650            sa[m as usize] = i as SaSint;
651            m -= (f0 & !f1) as isize;
652            buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
653
654            c1 = t[(i - 2) as usize] as isize;
655            f1 = usize::from(c1 > c0 - f0 as isize);
656            sa[m as usize] = (i - 1) as SaSint;
657            m -= (f1 & !f0) as isize;
658            buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
659
660            c0 = t[(i - 3) as usize] as isize;
661            f0 = usize::from(c0 > c1 - f1 as isize);
662            sa[m as usize] = (i - 2) as SaSint;
663            m -= (f0 & !f1) as isize;
664            buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
665
666            i -= 4;
667        }
668
669        j -= 64 + 3;
670        while i >= j {
671            c1 = c0;
672            c0 = t[i as usize] as isize;
673            f1 = f0;
674            f0 = usize::from(c0 > c1 - f1 as isize);
675            sa[m as usize] = (i + 1) as SaSint;
676            m -= (f0 & !f1) as isize;
677            buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
678            i -= 1;
679        }
680
681        c1 = if i >= 0 { t[i as usize] as isize } else { -1 };
682        f1 = usize::from(c1 > c0 - f0 as isize);
683        sa[m as usize] = (i + 1) as SaSint;
684        m -= (f1 & !f0) as isize;
685        buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
686    }
687
688    (omp_block_start + omp_block_size - 1 - m) as SaSint
689}
690
691fn count_and_gather_lms_suffixes_32s_2k(
692    t: &[SaSint],
693    sa: &mut [SaSint],
694    n: SaSint,
695    k: SaSint,
696    buckets: &mut [SaSint],
697    omp_block_start: isize,
698    omp_block_size: isize,
699) -> SaSint {
700    buckets[..2 * k as usize].fill(0);
701    let mut m = omp_block_start + omp_block_size - 1;
702
703    if omp_block_size > 0 {
704        let mut j = m + 1;
705        let mut c0 = t[m as usize] as isize;
706        let mut c1 = -1isize;
707        while j < n as isize {
708            c1 = t[j as usize] as isize;
709            if c1 != c0 {
710                break;
711            }
712            j += 1;
713        }
714
715        let mut f0 = usize::from(c0 >= c1);
716        let mut f1: usize;
717        let mut i = m - 1;
718        j = omp_block_start + 64 + 3;
719        while i >= j {
720            c1 = t[i as usize] as isize;
721            f1 = usize::from(c1 > c0 - f0 as isize);
722            sa[m as usize] = (i + 1) as SaSint;
723            m -= (f1 & !f0) as isize;
724            buckets[buckets_index2(c0 as usize, f1 & !f0)] += 1;
725
726            c0 = t[(i - 1) as usize] as isize;
727            f0 = usize::from(c0 > c1 - f1 as isize);
728            sa[m as usize] = i as SaSint;
729            m -= (f0 & !f1) as isize;
730            buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
731
732            c1 = t[(i - 2) as usize] as isize;
733            f1 = usize::from(c1 > c0 - f0 as isize);
734            sa[m as usize] = (i - 1) as SaSint;
735            m -= (f1 & !f0) as isize;
736            buckets[buckets_index2(c0 as usize, f1 & !f0)] += 1;
737
738            c0 = t[(i - 3) as usize] as isize;
739            f0 = usize::from(c0 > c1 - f1 as isize);
740            sa[m as usize] = (i - 2) as SaSint;
741            m -= (f0 & !f1) as isize;
742            buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
743
744            i -= 4;
745        }
746
747        j -= 64 + 3;
748        while i >= j {
749            c1 = c0;
750            c0 = t[i as usize] as isize;
751            f1 = f0;
752            f0 = usize::from(c0 > c1 - f1 as isize);
753            sa[m as usize] = (i + 1) as SaSint;
754            m -= (f0 & !f1) as isize;
755            buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
756            i -= 1;
757        }
758
759        c1 = if i >= 0 { t[i as usize] as isize } else { -1 };
760        f1 = usize::from(c1 > c0 - f0 as isize);
761        sa[m as usize] = (i + 1) as SaSint;
762        m -= (f1 & !f0) as isize;
763        buckets[buckets_index2(c0 as usize, f1 & !f0)] += 1;
764    }
765
766    (omp_block_start + omp_block_size - 1 - m) as SaSint
767}
768
769fn count_and_gather_compacted_lms_suffixes_32s_2k(
770    t: &[SaSint],
771    sa: &mut [SaSint],
772    n: SaSint,
773    k: SaSint,
774    buckets: &mut [SaSint],
775    omp_block_start: isize,
776    omp_block_size: isize,
777) -> SaSint {
778    buckets[..2 * k as usize].fill(0);
779    let mut m = omp_block_start + omp_block_size - 1;
780
781    if omp_block_size > 0 {
782        let mut j = m + 1;
783        let mut c0 = t[m as usize] as isize;
784        let mut c1 = -1isize;
785        while j < n as isize {
786            c1 = t[j as usize] as isize;
787            if c1 != c0 {
788                break;
789            }
790            j += 1;
791        }
792
793        let mut f0 = usize::from(c0 >= c1);
794        let mut f1: usize;
795        let mut i = m - 1;
796        j = omp_block_start + 64 + 3;
797        while i >= j {
798            c1 = t[i as usize] as isize;
799            f1 = usize::from(c1 > c0 - f0 as isize);
800            sa[m as usize] = (i + 1) as SaSint;
801            m -= (f1 & !f0 & usize::from(c0 >= 0)) as isize;
802            buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
803
804            c0 = t[(i - 1) as usize] as isize;
805            f0 = usize::from(c0 > c1 - f1 as isize);
806            sa[m as usize] = i as SaSint;
807            m -= (f0 & !f1 & usize::from(c1 >= 0)) as isize;
808            buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
809
810            c1 = t[(i - 2) as usize] as isize;
811            f1 = usize::from(c1 > c0 - f0 as isize);
812            sa[m as usize] = (i - 1) as SaSint;
813            m -= (f1 & !f0 & usize::from(c0 >= 0)) as isize;
814            buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
815
816            c0 = t[(i - 3) as usize] as isize;
817            f0 = usize::from(c0 > c1 - f1 as isize);
818            sa[m as usize] = (i - 2) as SaSint;
819            m -= (f0 & !f1 & usize::from(c1 >= 0)) as isize;
820            buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
821
822            i -= 4;
823        }
824
825        j -= 64 + 3;
826        while i >= j {
827            c1 = c0;
828            c0 = t[i as usize] as isize;
829            f1 = f0;
830            f0 = usize::from(c0 > c1 - f1 as isize);
831            sa[m as usize] = (i + 1) as SaSint;
832            m -= (f0 & !f1 & usize::from(c1 >= 0)) as isize;
833            buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
834            i -= 1;
835        }
836
837        c1 = if i >= 0 { t[i as usize] as isize } else { -1 };
838        f1 = usize::from(c1 > c0 - f0 as isize);
839        sa[m as usize] = (i + 1) as SaSint;
840        m -= (f1 & !f0 & usize::from(c0 >= 0)) as isize;
841        buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
842    }
843
844    (omp_block_start + omp_block_size - 1 - m) as SaSint
845}
846
847fn count_and_gather_lms_suffixes_32s_4k_fs_omp(
848    t: &[SaSint],
849    sa: &mut [SaSint],
850    n: SaSint,
851    k: SaSint,
852    buckets: &mut [SaSint],
853    local_buckets: SaSint,
854    threads: SaSint,
855    thread_state: &mut [ThreadState],
856) -> SaSint {
857    if threads == 1 || n < 65_536 {
858        return count_and_gather_lms_suffixes_32s_4k(t, sa, n, k, buckets, 0, n as isize);
859    }
860
861    let thread_count = threads as usize;
862    let n_usize = n as usize;
863    let bucket_size = 4 * k as usize;
864    let block_stride = (n / threads) & !15;
865    let free_space = if local_buckets != 0 {
866        LIBSAIS_LOCAL_BUFFER_SIZE as SaSint
867    } else {
868        buckets.len() as SaSint
869    };
870    let bucket_stride = get_bucket_stride(free_space, 4 * k, threads) as usize;
871    let workspace_len = bucket_size + bucket_stride * thread_count.saturating_sub(1);
872    let mut workspace = vec![0; workspace_len];
873
874    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
875        let block_start = thread as SaSint * block_stride;
876        let block_size = if thread + 1 < thread_count {
877            block_stride
878        } else {
879            n - block_start
880        };
881        let workspace_end = workspace_len - thread * bucket_stride;
882        let workspace_start = workspace_end - bucket_size;
883        state.count = count_and_gather_lms_suffixes_32s_4k(
884            t,
885            sa,
886            n,
887            k,
888            &mut workspace[workspace_start..workspace_end],
889            block_start as isize,
890            block_size as isize,
891        );
892        state.position = block_start + block_size;
893    }
894
895    let mut m = 0usize;
896    for thread in (0..thread_count).rev() {
897        let count =
898            usize::try_from(thread_state[thread].count).expect("count must be non-negative");
899        m += count;
900        if thread + 1 != thread_count && count > 0 {
901            let src_end = usize::try_from(thread_state[thread].position)
902                .expect("position must be non-negative");
903            let src_start = src_end - count;
904            let dst_start = n_usize - m;
905            sa.copy_within(src_start..src_end, dst_start);
906        }
907    }
908
909    let accumulation_threads = thread_count - 1;
910    let block_stride = (bucket_size / accumulation_threads) & !15usize;
911    for thread in 0..accumulation_threads {
912        let block_start = thread * block_stride;
913        let block_size = if thread + 1 < accumulation_threads {
914            block_stride
915        } else {
916            bucket_size - block_start
917        };
918        accumulate_counts_s32(
919            &mut workspace,
920            block_start,
921            block_size,
922            bucket_stride,
923            accumulation_threads + 1,
924        );
925    }
926
927    buckets[..bucket_size].copy_from_slice(&workspace[..bucket_size]);
928    m as SaSint
929}
930
931fn count_and_gather_lms_suffixes_32s_2k_fs_omp(
932    t: &[SaSint],
933    sa: &mut [SaSint],
934    n: SaSint,
935    k: SaSint,
936    buckets: &mut [SaSint],
937    local_buckets: SaSint,
938    threads: SaSint,
939    thread_state: &mut [ThreadState],
940) -> SaSint {
941    if threads == 1 || n < 65_536 {
942        return count_and_gather_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as isize);
943    }
944
945    let thread_count = threads as usize;
946    let n_usize = n as usize;
947    let bucket_size = 2 * k as usize;
948    let block_stride = (n / threads) & !15;
949    let free_space = if local_buckets != 0 {
950        LIBSAIS_LOCAL_BUFFER_SIZE as SaSint
951    } else {
952        buckets.len() as SaSint
953    };
954    let bucket_stride = get_bucket_stride(free_space, 2 * k, threads) as usize;
955    let workspace_len = bucket_size + bucket_stride * thread_count.saturating_sub(1);
956    let mut workspace = vec![0; workspace_len];
957
958    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
959        let block_start = thread as SaSint * block_stride;
960        let block_size = if thread + 1 < thread_count {
961            block_stride
962        } else {
963            n - block_start
964        };
965        let workspace_end = workspace_len - thread * bucket_stride;
966        let workspace_start = workspace_end - bucket_size;
967        state.count = count_and_gather_lms_suffixes_32s_2k(
968            t,
969            sa,
970            n,
971            k,
972            &mut workspace[workspace_start..workspace_end],
973            block_start as isize,
974            block_size as isize,
975        );
976        state.position = block_start + block_size;
977    }
978
979    let mut m = 0usize;
980    for thread in (0..thread_count).rev() {
981        let count =
982            usize::try_from(thread_state[thread].count).expect("count must be non-negative");
983        m += count;
984        if thread + 1 != thread_count && count > 0 {
985            let src_end = usize::try_from(thread_state[thread].position)
986                .expect("position must be non-negative");
987            let src_start = src_end - count;
988            let dst_start = n_usize - m;
989            sa.copy_within(src_start..src_end, dst_start);
990        }
991    }
992
993    let accumulation_threads = thread_count - 1;
994    let block_stride = (bucket_size / accumulation_threads) & !15usize;
995    for thread in 0..accumulation_threads {
996        let block_start = thread * block_stride;
997        let block_size = if thread + 1 < accumulation_threads {
998            block_stride
999        } else {
1000            bucket_size - block_start
1001        };
1002        accumulate_counts_s32(
1003            &mut workspace,
1004            block_start,
1005            block_size,
1006            bucket_stride,
1007            accumulation_threads + 1,
1008        );
1009    }
1010
1011    buckets[..bucket_size].copy_from_slice(&workspace[..bucket_size]);
1012    m as SaSint
1013}
1014
1015fn count_and_gather_compacted_lms_suffixes_32s_2k_fs_omp(
1016    t: &[SaSint],
1017    sa: &mut [SaSint],
1018    n: SaSint,
1019    k: SaSint,
1020    buckets: &mut [SaSint],
1021    _local_buckets: SaSint,
1022    threads: SaSint,
1023    thread_state: &mut [ThreadState],
1024) {
1025    if threads == 1 || n < 65_536 {
1026        count_and_gather_compacted_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as isize);
1027        return;
1028    }
1029
1030    let thread_count = threads as usize;
1031    let n_usize = n as usize;
1032    let bucket_size = 2 * k as usize;
1033    let block_stride = (n / threads) & !15;
1034    let mut workspaces = vec![vec![0; bucket_size]; thread_count];
1035    let mut gathered_runs = vec![Vec::<SaSint>::new(); thread_count];
1036
1037    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
1038        let block_start = thread as SaSint * block_stride;
1039        let block_size = if thread + 1 < thread_count {
1040            block_stride
1041        } else {
1042            n - block_start
1043        };
1044        let mut temp_sa = vec![0; n_usize + block_size as usize];
1045        state.count = count_and_gather_compacted_lms_suffixes_32s_2k(
1046            t,
1047            &mut temp_sa,
1048            n,
1049            k,
1050            &mut workspaces[thread],
1051            block_start as isize,
1052            block_size as isize,
1053        );
1054        state.position = block_start + block_size;
1055        let count = usize::try_from(state.count).expect("count must be non-negative");
1056        let src_end =
1057            n_usize + usize::try_from(state.position).expect("position must be non-negative");
1058        let src_start = src_end - count;
1059        gathered_runs[thread].extend_from_slice(&temp_sa[src_start..src_end]);
1060    }
1061
1062    let mut suffixes_before = 0usize;
1063    for thread in (0..thread_count).rev() {
1064        let count =
1065            usize::try_from(thread_state[thread].count).expect("count must be non-negative");
1066        suffixes_before += count;
1067        if count > 0 {
1068            let dst_start = n_usize - suffixes_before;
1069            let dst_end = dst_start + count;
1070            sa[dst_start..dst_end].copy_from_slice(&gathered_runs[thread]);
1071        }
1072    }
1073
1074    buckets.fill(0);
1075    for workspace in &workspaces {
1076        for (dst, src) in buckets.iter_mut().zip(workspace.iter()) {
1077            *dst += *src;
1078        }
1079    }
1080}
1081
1082fn count_and_gather_lms_suffixes_32s_4k_nofs_omp(
1083    t: &[SaSint],
1084    sa: &mut [SaSint],
1085    n: SaSint,
1086    k: SaSint,
1087    buckets: &mut [SaSint],
1088    threads: SaSint,
1089) -> SaSint {
1090    if threads > 1 && n >= 65_536 {
1091        count_lms_suffixes_32s_4k(t, n, k, buckets);
1092        gather_lms_suffixes_32s(t, sa, n)
1093    } else {
1094        count_and_gather_lms_suffixes_32s_4k(t, sa, n, k, buckets, 0, n as isize)
1095    }
1096}
1097
1098fn count_and_gather_lms_suffixes_32s_2k_nofs_omp(
1099    t: &[SaSint],
1100    sa: &mut [SaSint],
1101    n: SaSint,
1102    k: SaSint,
1103    buckets: &mut [SaSint],
1104    threads: SaSint,
1105) -> SaSint {
1106    if threads > 1 && n >= 65_536 {
1107        count_lms_suffixes_32s_2k(t, n, k, buckets);
1108        gather_lms_suffixes_32s(t, sa, n)
1109    } else {
1110        count_and_gather_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as isize)
1111    }
1112}
1113
1114fn count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(
1115    t: &[SaSint],
1116    sa: &mut [SaSint],
1117    n: SaSint,
1118    k: SaSint,
1119    buckets: &mut [SaSint],
1120    threads: SaSint,
1121) -> SaSint {
1122    if threads > 1 && n >= 65_536 {
1123        count_compacted_lms_suffixes_32s_2k(t, n, k, buckets);
1124        gather_compacted_lms_suffixes_32s(t, sa, n)
1125    } else {
1126        count_and_gather_compacted_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as isize)
1127    }
1128}
1129
1130fn count_and_gather_lms_suffixes_32s_4k_omp(
1131    t: &[SaSint],
1132    sa: &mut [SaSint],
1133    n: SaSint,
1134    k: SaSint,
1135    buckets: &mut [SaSint],
1136    local_buckets: SaSint,
1137    threads: SaSint,
1138    thread_state: &mut [ThreadState],
1139) -> SaSint {
1140    let free_space = if local_buckets != 0 {
1141        LIBSAIS_LOCAL_BUFFER_SIZE as SaSint
1142    } else {
1143        buckets.len() as SaSint
1144    };
1145    let mut max_threads = (free_space / (((4 * k) + 15) & !15)).min(threads);
1146
1147    if max_threads > 1 && n >= 65_536 && n / k >= 2 {
1148        let thread_cap = n / (16 * k);
1149        if max_threads > thread_cap {
1150            max_threads = thread_cap;
1151        }
1152        count_and_gather_lms_suffixes_32s_4k_fs_omp(
1153            t,
1154            sa,
1155            n,
1156            k,
1157            buckets,
1158            local_buckets,
1159            max_threads.max(2),
1160            thread_state,
1161        )
1162    } else if threads > 1 && n >= 65_536 {
1163        count_lms_suffixes_32s_4k(t, n, k, buckets);
1164        gather_lms_suffixes_32s(t, sa, n)
1165    } else {
1166        count_and_gather_lms_suffixes_32s_4k_nofs_omp(t, sa, n, k, buckets, threads)
1167    }
1168}
1169
1170fn count_and_gather_lms_suffixes_32s_2k_omp(
1171    t: &[SaSint],
1172    sa: &mut [SaSint],
1173    n: SaSint,
1174    k: SaSint,
1175    buckets: &mut [SaSint],
1176    local_buckets: SaSint,
1177    threads: SaSint,
1178    thread_state: &mut [ThreadState],
1179) -> SaSint {
1180    let free_space = if local_buckets != 0 {
1181        LIBSAIS_LOCAL_BUFFER_SIZE as SaSint
1182    } else {
1183        buckets.len() as SaSint
1184    };
1185    let mut max_threads = (free_space / (((2 * k) + 15) & !15)).min(threads);
1186
1187    if max_threads > 1 && n >= 65_536 && n / k >= 2 {
1188        let thread_cap = n / (8 * k);
1189        if max_threads > thread_cap {
1190            max_threads = thread_cap;
1191        }
1192        count_and_gather_lms_suffixes_32s_2k_fs_omp(
1193            t,
1194            sa,
1195            n,
1196            k,
1197            buckets,
1198            local_buckets,
1199            max_threads.max(2),
1200            thread_state,
1201        )
1202    } else if threads > 1 && n >= 65_536 {
1203        count_lms_suffixes_32s_2k(t, n, k, buckets);
1204        gather_lms_suffixes_32s(t, sa, n)
1205    } else {
1206        count_and_gather_lms_suffixes_32s_2k_nofs_omp(t, sa, n, k, buckets, threads)
1207    }
1208}
1209
1210fn count_suffixes_32s(t: &[SaSint], n: SaSint, k: SaSint, buckets: &mut [SaSint]) {
1211    buckets[..k as usize].fill(0);
1212
1213    let mut i = 0usize;
1214    let mut j = (n as usize).saturating_sub(7);
1215    while i < j {
1216        buckets[t[i] as usize] += 1;
1217        buckets[t[i + 1] as usize] += 1;
1218        buckets[t[i + 2] as usize] += 1;
1219        buckets[t[i + 3] as usize] += 1;
1220        buckets[t[i + 4] as usize] += 1;
1221        buckets[t[i + 5] as usize] += 1;
1222        buckets[t[i + 6] as usize] += 1;
1223        buckets[t[i + 7] as usize] += 1;
1224        i += 8;
1225    }
1226
1227    j += 7;
1228    while i < j {
1229        buckets[t[i] as usize] += 1;
1230        i += 1;
1231    }
1232}
1233
1234fn initialize_buckets_start_and_end_32s_6k(k: SaSint, buckets: &mut [SaSint]) {
1235    let k = k as usize;
1236    let mut sum = 0;
1237    for j in 0..k {
1238        let i = buckets_index4(j, 0);
1239        buckets[4 * k + j] = sum;
1240        sum += buckets[i] + buckets[i + 1] + buckets[i + 2] + buckets[i + 3];
1241        buckets[5 * k + j] = sum;
1242    }
1243}
1244
1245fn initialize_buckets_start_and_end_32s_4k(k: SaSint, buckets: &mut [SaSint]) {
1246    let k = k as usize;
1247    let mut sum = 0;
1248    for j in 0..k {
1249        let i = buckets_index2(j, 0);
1250        buckets[2 * k + j] = sum;
1251        sum += buckets[i] + buckets[i + 1];
1252        buckets[3 * k + j] = sum;
1253    }
1254}
1255
1256fn initialize_buckets_end_32s_2k(k: SaSint, buckets: &mut [SaSint]) {
1257    let mut sum0 = 0;
1258    for j in 0..k as usize {
1259        let i = buckets_index2(j, 0);
1260        sum0 += buckets[i] + buckets[i + 1];
1261        buckets[i] = sum0;
1262    }
1263}
1264
1265fn initialize_buckets_start_and_end_32s_2k(k: SaSint, buckets: &mut [SaSint]) {
1266    let k = k as usize;
1267    for j in 0..k {
1268        let i = buckets_index2(j, 0);
1269        buckets[j] = buckets[i];
1270    }
1271    buckets[k] = 0;
1272    buckets.copy_within(0..k - 1, k + 1);
1273}
1274
1275fn initialize_buckets_start_32s_1k(k: SaSint, buckets: &mut [SaSint]) {
1276    let mut sum = 0;
1277    for bucket in buckets.iter_mut().take(k as usize) {
1278        let tmp = *bucket;
1279        *bucket = sum;
1280        sum += tmp;
1281    }
1282}
1283
1284fn initialize_buckets_end_32s_1k(k: SaSint, buckets: &mut [SaSint]) {
1285    let mut sum = 0;
1286    for bucket in buckets.iter_mut().take(k as usize) {
1287        sum += *bucket;
1288        *bucket = sum;
1289    }
1290}
1291
1292fn initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
1293    t: &[SaSint],
1294    k: SaSint,
1295    buckets: &mut [SaSint],
1296    first_lms_suffix: SaSint,
1297) {
1298    buckets[buckets_index2(t[first_lms_suffix as usize] as usize, 0)] += 1;
1299    buckets[buckets_index2(t[first_lms_suffix as usize] as usize, 1)] -= 1;
1300
1301    let mut sum0 = 0;
1302    let mut sum1 = 0;
1303    for j in 0..k as usize {
1304        let i = buckets_index2(j, 0);
1305        sum0 += buckets[i] + buckets[i + 1];
1306        sum1 += buckets[i + 1];
1307        buckets[i] = sum0;
1308        buckets[i + 1] = sum1;
1309    }
1310}
1311
1312fn initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
1313    t: &[SaSint],
1314    k: SaSint,
1315    buckets: &mut [SaSint],
1316    mut first_lms_suffix: SaSint,
1317) -> SaSint {
1318    let mut f0 = 0usize;
1319    let mut c0 = t[first_lms_suffix as usize] as isize;
1320
1321    loop {
1322        first_lms_suffix -= 1;
1323        if first_lms_suffix < 0 {
1324            break;
1325        }
1326        let c1 = c0;
1327        c0 = t[first_lms_suffix as usize] as isize;
1328        let f1 = f0;
1329        f0 = usize::from(c0 > c1 - f1 as isize);
1330        buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] -= 1;
1331    }
1332    buckets[buckets_index4(c0 as usize, f0 + f0)] -= 1;
1333
1334    let mut sum = 0;
1335    for j in 0..k as usize {
1336        let i = buckets_index4(j, 0);
1337        sum += buckets[i + 1] + buckets[i + 3];
1338        buckets[4 * k as usize + j] = sum;
1339    }
1340    sum
1341}
1342
1343fn initialize_buckets_for_partial_sorting_32s_6k(
1344    t: &[SaSint],
1345    k: SaSint,
1346    buckets: &mut [SaSint],
1347    first_lms_suffix: SaSint,
1348    left_suffixes_count: SaSint,
1349) {
1350    let k = k as usize;
1351    let temp_offset = 4 * k;
1352    let first_symbol = t[first_lms_suffix as usize] as usize;
1353    let mut sum0 = left_suffixes_count + 1;
1354    let mut sum1 = 0;
1355    let mut sum2 = 0;
1356
1357    for j in 0..first_symbol {
1358        let i = buckets_index4(j, 0);
1359        let tj = buckets_index2(j, 0);
1360        let ss = buckets[i];
1361        let ls = buckets[i + 1];
1362        let sl = buckets[i + 2];
1363        let ll = buckets[i + 3];
1364
1365        buckets[i] = sum0;
1366        buckets[i + 1] = sum2;
1367        buckets[i + 2] = 0;
1368        buckets[i + 3] = 0;
1369
1370        sum0 += ss + sl;
1371        sum1 += ls;
1372        sum2 += ls + ll;
1373
1374        buckets[temp_offset + tj] = sum0;
1375        buckets[temp_offset + tj + 1] = sum1;
1376    }
1377
1378    sum1 += 1;
1379    for j in first_symbol..k {
1380        let i = buckets_index4(j, 0);
1381        let tj = buckets_index2(j, 0);
1382        let ss = buckets[i];
1383        let ls = buckets[i + 1];
1384        let sl = buckets[i + 2];
1385        let ll = buckets[i + 3];
1386
1387        buckets[i] = sum0;
1388        buckets[i + 1] = sum2;
1389        buckets[i + 2] = 0;
1390        buckets[i + 3] = 0;
1391
1392        sum0 += ss + sl;
1393        sum1 += ls;
1394        sum2 += ls + ll;
1395
1396        buckets[temp_offset + tj] = sum0;
1397        buckets[temp_offset + tj + 1] = sum1;
1398    }
1399}
1400
1401fn initialize_buckets_for_radix_and_partial_sorting_32s_4k(
1402    t: &[SaSint],
1403    k: SaSint,
1404    buckets: &mut [SaSint],
1405    first_lms_suffix: SaSint,
1406) {
1407    let k = k as usize;
1408    buckets[buckets_index2(t[first_lms_suffix as usize] as usize, 0)] += 1;
1409    buckets[buckets_index2(t[first_lms_suffix as usize] as usize, 1)] -= 1;
1410
1411    let mut sum0 = 0;
1412    let mut sum1 = 0;
1413    for j in 0..k {
1414        let i = buckets_index2(j, 0);
1415        buckets[2 * k + j] = sum1;
1416        sum0 += buckets[i + 1];
1417        sum1 += buckets[i] + buckets[i + 1];
1418        buckets[i + 1] = sum0;
1419        buckets[3 * k + j] = sum1;
1420    }
1421}
1422
1423fn count_and_gather_compacted_lms_suffixes_32s_2k_omp(
1424    t: &[SaSint],
1425    sa: &mut [SaSint],
1426    n: SaSint,
1427    k: SaSint,
1428    buckets: &mut [SaSint],
1429    local_buckets: SaSint,
1430    threads: SaSint,
1431    thread_state: &mut [ThreadState],
1432) {
1433    let free_space = if local_buckets != 0 {
1434        LIBSAIS_LOCAL_BUFFER_SIZE as SaSint
1435    } else {
1436        buckets.len() as SaSint
1437    };
1438    let mut max_threads = (free_space / (((2 * k) + 15) & !15)).min(threads);
1439
1440    if local_buckets == 0 && max_threads > 1 && n >= 65_536 && n / k >= 2 {
1441        let thread_cap = n / (8 * k);
1442        if max_threads > thread_cap {
1443            max_threads = thread_cap;
1444        }
1445        count_and_gather_compacted_lms_suffixes_32s_2k_fs_omp(
1446            t,
1447            sa,
1448            n,
1449            k,
1450            buckets,
1451            local_buckets,
1452            max_threads.max(2),
1453            thread_state,
1454        );
1455    } else {
1456        count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(t, sa, n, k, buckets, threads);
1457    }
1458}
1459
1460fn gather_lms_suffixes_16u(
1461    t: &[u16],
1462    sa: &mut [SaSint],
1463    n: SaSint,
1464    mut m: SaSint,
1465    omp_block_start: SaSint,
1466    omp_block_size: SaSint,
1467) {
1468    if omp_block_size > 0 {
1469        let n = n as isize;
1470        let mut i: isize;
1471        let mut j = (omp_block_start + omp_block_size) as isize;
1472        let mut c0 = t[(omp_block_start + omp_block_size - 1) as usize] as isize;
1473        let mut c1 = -1isize;
1474
1475        while j < n {
1476            c1 = t[j as usize] as isize;
1477            if c1 != c0 {
1478                break;
1479            }
1480            j += 1;
1481        }
1482
1483        let mut f0 = usize::from(c0 >= c1);
1484        let mut f1: usize;
1485
1486        i = (omp_block_start + omp_block_size - 2) as isize;
1487        j = (omp_block_start + 3) as isize;
1488        while i >= j {
1489            c1 = t[i as usize] as isize;
1490            f1 = usize::from(c1 > c0 - f0 as isize);
1491            sa[m as usize] = (i + 1) as SaSint;
1492            m -= (f1 & (1 - f0)) as SaSint;
1493
1494            c0 = t[(i - 1) as usize] as isize;
1495            f0 = usize::from(c0 > c1 - f1 as isize);
1496            sa[m as usize] = i as SaSint;
1497            m -= (f0 & (1 - f1)) as SaSint;
1498
1499            c1 = t[(i - 2) as usize] as isize;
1500            f1 = usize::from(c1 > c0 - f0 as isize);
1501            sa[m as usize] = (i - 1) as SaSint;
1502            m -= (f1 & (1 - f0)) as SaSint;
1503
1504            c0 = t[(i - 3) as usize] as isize;
1505            f0 = usize::from(c0 > c1 - f1 as isize);
1506            sa[m as usize] = (i - 2) as SaSint;
1507            m -= (f0 & (1 - f1)) as SaSint;
1508
1509            i -= 4;
1510        }
1511
1512        j -= 3;
1513        while i >= j {
1514            c1 = c0;
1515            c0 = t[i as usize] as isize;
1516            f1 = f0;
1517            f0 = usize::from(c0 > c1 - f1 as isize);
1518            sa[m as usize] = (i + 1) as SaSint;
1519            m -= (f0 & (1 - f1)) as SaSint;
1520            i -= 1;
1521        }
1522
1523        sa[m as usize] = (i + 1) as SaSint;
1524    }
1525}
1526
1527fn count_and_gather_lms_suffixes_16u(
1528    t: &[u16],
1529    sa: &mut [SaSint],
1530    n: SaSint,
1531    buckets: &mut [SaSint],
1532    omp_block_start: SaSint,
1533    omp_block_size: SaSint,
1534) -> SaSint {
1535    buckets[..4 * ALPHABET_SIZE].fill(0);
1536
1537    let mut m = (omp_block_start + omp_block_size - 1) as isize;
1538
1539    if omp_block_size > 0 {
1540        let n = n as isize;
1541        let mut i: isize;
1542        let mut j = m + 1;
1543        let mut c0 = t[m as usize] as isize;
1544        let mut c1 = -1isize;
1545
1546        while j < n {
1547            c1 = t[j as usize] as isize;
1548            if c1 != c0 {
1549                break;
1550            }
1551            j += 1;
1552        }
1553
1554        let mut f0 = usize::from(c0 >= c1);
1555        let mut f1: usize;
1556
1557        i = m - 1;
1558        j = (omp_block_start + 3) as isize;
1559        while i >= j {
1560            c1 = t[i as usize] as isize;
1561            f1 = usize::from(c1 > c0 - f0 as isize);
1562            sa[m as usize] = (i + 1) as SaSint;
1563            m -= (f1 & (1 - f0)) as isize;
1564            buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
1565
1566            c0 = t[(i - 1) as usize] as isize;
1567            f0 = usize::from(c0 > c1 - f1 as isize);
1568            sa[m as usize] = i as SaSint;
1569            m -= (f0 & (1 - f1)) as isize;
1570            buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
1571
1572            c1 = t[(i - 2) as usize] as isize;
1573            f1 = usize::from(c1 > c0 - f0 as isize);
1574            sa[m as usize] = (i - 1) as SaSint;
1575            m -= (f1 & (1 - f0)) as isize;
1576            buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
1577
1578            c0 = t[(i - 3) as usize] as isize;
1579            f0 = usize::from(c0 > c1 - f1 as isize);
1580            sa[m as usize] = (i - 2) as SaSint;
1581            m -= (f0 & (1 - f1)) as isize;
1582            buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
1583
1584            i -= 4;
1585        }
1586
1587        j -= 3;
1588        while i >= j {
1589            c1 = c0;
1590            c0 = t[i as usize] as isize;
1591            f1 = f0;
1592            f0 = usize::from(c0 > c1 - f1 as isize);
1593            sa[m as usize] = (i + 1) as SaSint;
1594            m -= (f0 & (1 - f1)) as isize;
1595            buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
1596            i -= 1;
1597        }
1598
1599        c1 = if i >= 0 { t[i as usize] as isize } else { -1 };
1600        f1 = usize::from(c1 > c0 - f0 as isize);
1601        sa[m as usize] = (i + 1) as SaSint;
1602        m -= (f1 & (1 - f0)) as isize;
1603        buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
1604    }
1605
1606    omp_block_start + omp_block_size - 1 - m as SaSint
1607}
1608
1609fn gather_lms_suffixes_16u_omp(
1610    t: &[u16],
1611    sa: &mut [SaSint],
1612    n: SaSint,
1613    threads: SaSint,
1614    thread_state: &mut [ThreadState],
1615) {
1616    if threads == 1 || n < 65_536 || thread_state.is_empty() {
1617        gather_lms_suffixes_16u(t, sa, n, n - 1, 0, n);
1618        return;
1619    }
1620
1621    let thread_count = threads as usize;
1622    let block_stride = (n / threads) & !15;
1623    let mut suffix_counts_after = vec![0; thread_count];
1624    let mut m = 0;
1625    for thread in (0..thread_count).rev() {
1626        suffix_counts_after[thread] = m;
1627        m += thread_state[thread].m;
1628    }
1629
1630    for thread in 0..thread_count {
1631        let block_start = thread as SaSint * block_stride;
1632        let block_size = if thread + 1 < thread_count {
1633            block_stride
1634        } else {
1635            n - block_start
1636        };
1637        gather_lms_suffixes_16u(
1638            t,
1639            sa,
1640            n,
1641            n - 1 - suffix_counts_after[thread],
1642            block_start,
1643            block_size,
1644        );
1645    }
1646
1647    for thread in 0..thread_count {
1648        if thread_state[thread].m > 0 {
1649            sa[(n - 1 - suffix_counts_after[thread]) as usize] =
1650                thread_state[thread].last_lms_suffix;
1651        }
1652    }
1653}
1654
1655fn count_and_gather_lms_suffixes_16u_omp(
1656    t: &[u16],
1657    sa: &mut [SaSint],
1658    n: SaSint,
1659    buckets: &mut [SaSint],
1660    threads: SaSint,
1661    thread_state: &mut [ThreadState],
1662) -> SaSint {
1663    if threads == 1 || n < 65_536 || thread_state.is_empty() {
1664        return count_and_gather_lms_suffixes_16u(t, sa, n, buckets, 0, n);
1665    }
1666
1667    let thread_count = threads as usize;
1668    let block_stride = (n / threads) & !15;
1669
1670    for thread in 0..thread_count {
1671        let block_start = thread as SaSint * block_stride;
1672        let block_size = if thread + 1 < thread_count {
1673            block_stride
1674        } else {
1675            n - block_start
1676        };
1677        let count = count_and_gather_lms_suffixes_16u(
1678            t,
1679            sa,
1680            n,
1681            &mut thread_state[thread].buckets,
1682            block_start,
1683            block_size,
1684        );
1685        thread_state[thread].m = count;
1686        thread_state[thread].position = block_start + block_size;
1687        if count > 0 {
1688            thread_state[thread].last_lms_suffix = sa[(block_start + block_size - 1) as usize];
1689        }
1690    }
1691
1692    buckets[..4 * ALPHABET_SIZE].fill(0);
1693    let mut m = 0;
1694    for thread in (0..thread_count).rev() {
1695        let position = thread_state[thread].position;
1696        let count = thread_state[thread].m;
1697        m += count;
1698        if thread + 1 != thread_count && count > 0 {
1699            let src_end = position as usize;
1700            let src_start = src_end - count as usize;
1701            let dst_start = (n - m) as usize;
1702            sa.copy_within(src_start..src_end, dst_start);
1703        }
1704        for s in 0..4 * ALPHABET_SIZE {
1705            let a = buckets[s];
1706            let b = thread_state[thread].buckets[s];
1707            buckets[s] = a + b;
1708            thread_state[thread].buckets[s] = a;
1709        }
1710    }
1711
1712    m
1713}
1714
1715fn initialize_buckets_start_and_end_16u(
1716    buckets: &mut [SaSint],
1717    freq: Option<&mut [SaSint]>,
1718) -> SaSint {
1719    let (count_buckets, start_end) = buckets.split_at_mut(6 * ALPHABET_SIZE);
1720    let (bucket_start, bucket_end) = start_end.split_at_mut(ALPHABET_SIZE);
1721
1722    let mut k = -1;
1723    let mut sum = 0;
1724
1725    if let Some(freq) = freq {
1726        for j in 0..ALPHABET_SIZE {
1727            let i = buckets_index4(j, 0);
1728            let total = count_buckets[i]
1729                + count_buckets[i + buckets_index4(0, 1)]
1730                + count_buckets[i + buckets_index4(0, 2)]
1731                + count_buckets[i + buckets_index4(0, 3)];
1732
1733            bucket_start[j] = sum;
1734            sum += total;
1735            bucket_end[j] = sum;
1736            if total > 0 {
1737                k = j as SaSint;
1738            }
1739            freq[j] = total;
1740        }
1741    } else {
1742        for j in 0..ALPHABET_SIZE {
1743            let i = buckets_index4(j, 0);
1744            let total = count_buckets[i]
1745                + count_buckets[i + buckets_index4(0, 1)]
1746                + count_buckets[i + buckets_index4(0, 2)]
1747                + count_buckets[i + buckets_index4(0, 3)];
1748
1749            bucket_start[j] = sum;
1750            sum += total;
1751            bucket_end[j] = sum;
1752            if total > 0 {
1753                k = j as SaSint;
1754            }
1755        }
1756    }
1757
1758    k + 1
1759}
1760
1761fn initialize_buckets_for_lms_suffixes_radix_sort_16u(
1762    t: &[u16],
1763    buckets: &mut [SaSint],
1764    mut first_lms_suffix: SaSint,
1765) -> SaSint {
1766    let mut f0 = 0usize;
1767    let mut c0 = t[first_lms_suffix as usize] as isize;
1768
1769    loop {
1770        first_lms_suffix -= 1;
1771        if first_lms_suffix < 0 {
1772            break;
1773        }
1774
1775        let c1 = c0;
1776        c0 = t[first_lms_suffix as usize] as isize;
1777        let f1 = f0;
1778        f0 = usize::from(c0 > c1 - f1 as isize);
1779        buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] -= 1;
1780    }
1781
1782    buckets[buckets_index4(c0 as usize, f0 + f0)] -= 1;
1783
1784    let (count_buckets, temp_bucket) = buckets.split_at_mut(4 * ALPHABET_SIZE);
1785    let mut sum = 0;
1786    for c in 0..ALPHABET_SIZE {
1787        let i = buckets_index4(c, 0);
1788        let j = buckets_index2(c, 0);
1789        temp_bucket[j + buckets_index2(0, 1)] = sum;
1790        sum += count_buckets[i + buckets_index4(0, 1)] + count_buckets[i + buckets_index4(0, 3)];
1791        temp_bucket[j] = sum;
1792    }
1793
1794    sum
1795}
1796
1797fn radix_sort_lms_suffixes_16u(
1798    t: &[u16],
1799    sa: &mut [SaSint],
1800    induction_bucket: &mut [SaSint],
1801    omp_block_start: SaSint,
1802    omp_block_size: SaSint,
1803) {
1804    let mut i = omp_block_start + omp_block_size - 1;
1805    let mut j = omp_block_start + 64 + 3;
1806    while i >= j {
1807        let p0 = sa[i as usize];
1808        induction_bucket[buckets_index2(t[p0 as usize] as usize, 0)] -= 1;
1809        sa[induction_bucket[buckets_index2(t[p0 as usize] as usize, 0)] as usize] = p0;
1810
1811        let p1 = sa[(i - 1) as usize];
1812        induction_bucket[buckets_index2(t[p1 as usize] as usize, 0)] -= 1;
1813        sa[induction_bucket[buckets_index2(t[p1 as usize] as usize, 0)] as usize] = p1;
1814
1815        let p2 = sa[(i - 2) as usize];
1816        induction_bucket[buckets_index2(t[p2 as usize] as usize, 0)] -= 1;
1817        sa[induction_bucket[buckets_index2(t[p2 as usize] as usize, 0)] as usize] = p2;
1818
1819        let p3 = sa[(i - 3) as usize];
1820        induction_bucket[buckets_index2(t[p3 as usize] as usize, 0)] -= 1;
1821        sa[induction_bucket[buckets_index2(t[p3 as usize] as usize, 0)] as usize] = p3;
1822
1823        i -= 4;
1824    }
1825
1826    j -= 64 + 3;
1827    while i >= j {
1828        let p = sa[i as usize];
1829        induction_bucket[buckets_index2(t[p as usize] as usize, 0)] -= 1;
1830        sa[induction_bucket[buckets_index2(t[p as usize] as usize, 0)] as usize] = p;
1831        i -= 1;
1832    }
1833}
1834
1835fn radix_sort_lms_suffixes_16u_omp(
1836    t: &[u16],
1837    sa: &mut [SaSint],
1838    n: SaSint,
1839    m: SaSint,
1840    flags: SaSint,
1841    buckets: &mut [SaSint],
1842    threads: SaSint,
1843    thread_state: &mut [ThreadState],
1844) {
1845    if (flags & LIBSAIS_FLAGS_GSA) != 0 {
1846        buckets[4 * ALPHABET_SIZE] -= 1;
1847    }
1848    if threads == 1 || n < 65_536 || m < 65_536 || thread_state.is_empty() {
1849        radix_sort_lms_suffixes_16u(t, sa, &mut buckets[4 * ALPHABET_SIZE..], n - m + 1, m - 1);
1850        return;
1851    }
1852
1853    let thread_count = threads as usize;
1854    for thread in 0..thread_count {
1855        let (src_buckets, state_buckets) = (
1856            &buckets[4 * ALPHABET_SIZE..],
1857            &mut thread_state[thread].buckets,
1858        );
1859        for c in 0..ALPHABET_SIZE {
1860            let i = buckets_index2(c, 0);
1861            let j = buckets_index4(c, 1);
1862            state_buckets[i] = src_buckets[i] - state_buckets[j];
1863        }
1864
1865        let mut block_start = 0;
1866        let mut block_size = thread_state[thread].m;
1867        for idx in (thread..thread_count).rev() {
1868            block_start += thread_state[idx].m;
1869        }
1870
1871        if block_start == m && block_size > 0 {
1872            block_start -= 1;
1873            block_size -= 1;
1874        }
1875
1876        radix_sort_lms_suffixes_16u(
1877            t,
1878            sa,
1879            &mut thread_state[thread].buckets,
1880            n - block_start,
1881            block_size,
1882        );
1883    }
1884}
1885
1886fn radix_sort_lms_suffixes_32s_6k(
1887    t: &[SaSint],
1888    sa: &mut [SaSint],
1889    induction_bucket: &mut [SaSint],
1890    omp_block_start: SaSint,
1891    omp_block_size: SaSint,
1892) {
1893    let mut i = omp_block_start + omp_block_size - 1;
1894    let mut j = omp_block_start + 64 + 3;
1895    while i >= j {
1896        let p0 = sa[i as usize];
1897        induction_bucket[t[p0 as usize] as usize] -= 1;
1898        sa[induction_bucket[t[p0 as usize] as usize] as usize] = p0;
1899        let p1 = sa[(i - 1) as usize];
1900        induction_bucket[t[p1 as usize] as usize] -= 1;
1901        sa[induction_bucket[t[p1 as usize] as usize] as usize] = p1;
1902        let p2 = sa[(i - 2) as usize];
1903        induction_bucket[t[p2 as usize] as usize] -= 1;
1904        sa[induction_bucket[t[p2 as usize] as usize] as usize] = p2;
1905        let p3 = sa[(i - 3) as usize];
1906        induction_bucket[t[p3 as usize] as usize] -= 1;
1907        sa[induction_bucket[t[p3 as usize] as usize] as usize] = p3;
1908        i -= 4;
1909    }
1910
1911    j -= 64 + 3;
1912    while i >= j {
1913        let p = sa[i as usize];
1914        induction_bucket[t[p as usize] as usize] -= 1;
1915        sa[induction_bucket[t[p as usize] as usize] as usize] = p;
1916        i -= 1;
1917    }
1918}
1919
1920fn radix_sort_lms_suffixes_32s_2k(
1921    t: &[SaSint],
1922    sa: &mut [SaSint],
1923    induction_bucket: &mut [SaSint],
1924    omp_block_start: SaSint,
1925    omp_block_size: SaSint,
1926) {
1927    let mut i = omp_block_start + omp_block_size - 1;
1928    let mut j = omp_block_start + 64 + 3;
1929    while i >= j {
1930        let p0 = sa[i as usize];
1931        induction_bucket[buckets_index2(t[p0 as usize] as usize, 0)] -= 1;
1932        sa[induction_bucket[buckets_index2(t[p0 as usize] as usize, 0)] as usize] = p0;
1933        let p1 = sa[(i - 1) as usize];
1934        induction_bucket[buckets_index2(t[p1 as usize] as usize, 0)] -= 1;
1935        sa[induction_bucket[buckets_index2(t[p1 as usize] as usize, 0)] as usize] = p1;
1936        let p2 = sa[(i - 2) as usize];
1937        induction_bucket[buckets_index2(t[p2 as usize] as usize, 0)] -= 1;
1938        sa[induction_bucket[buckets_index2(t[p2 as usize] as usize, 0)] as usize] = p2;
1939        let p3 = sa[(i - 3) as usize];
1940        induction_bucket[buckets_index2(t[p3 as usize] as usize, 0)] -= 1;
1941        sa[induction_bucket[buckets_index2(t[p3 as usize] as usize, 0)] as usize] = p3;
1942        i -= 4;
1943    }
1944
1945    j -= 64 + 3;
1946    while i >= j {
1947        let p = sa[i as usize];
1948        induction_bucket[buckets_index2(t[p as usize] as usize, 0)] -= 1;
1949        sa[induction_bucket[buckets_index2(t[p as usize] as usize, 0)] as usize] = p;
1950        i -= 1;
1951    }
1952}
1953
1954fn radix_sort_lms_suffixes_32s_block_gather(
1955    t: &[SaSint],
1956    sa: &[SaSint],
1957    cache: &mut [ThreadCache],
1958    omp_block_start: SaSint,
1959    omp_block_size: SaSint,
1960) {
1961    if omp_block_size <= 0 {
1962        return;
1963    }
1964
1965    let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
1966    let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
1967    let cache_base = if cache.len() >= start + size {
1968        0
1969    } else {
1970        start
1971    };
1972    let mut i = start;
1973    let mut j = if size > 67 { start + size - 67 } else { start };
1974
1975    while i < j {
1976        for current in [i, i + 1, i + 2, i + 3] {
1977            let ci = current - cache_base;
1978            let index = sa[current];
1979            cache[ci].index = index;
1980            cache[ci].symbol = t[index as usize];
1981        }
1982        i += 4;
1983    }
1984
1985    j = if size > 67 { j + 67 } else { start + size };
1986    while i < j {
1987        let ci = i - cache_base;
1988        let index = sa[i];
1989        cache[ci].index = index;
1990        cache[ci].symbol = t[index as usize];
1991        i += 1;
1992    }
1993}
1994
1995fn radix_sort_lms_suffixes_32s_6k_block_sort(
1996    induction_bucket: &mut [SaSint],
1997    cache: &mut [ThreadCache],
1998    omp_block_start: SaSint,
1999    omp_block_size: SaSint,
2000) {
2001    if omp_block_size <= 0 {
2002        return;
2003    }
2004
2005    let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
2006    let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
2007    let cache_base = if cache.len() >= start + size {
2008        0
2009    } else {
2010        start
2011    };
2012    let mut i = start + size - 1;
2013    let mut j = start + 64 + 3;
2014
2015    while i >= j {
2016        for current in [i, i - 1, i - 2, i - 3] {
2017            let ci = current - cache_base;
2018            let v = cache[ci].symbol as usize;
2019            induction_bucket[v] -= 1;
2020            cache[ci].symbol = induction_bucket[v];
2021        }
2022        i -= 4;
2023    }
2024
2025    j -= 64 + 3;
2026    while i >= j {
2027        let ci = i - cache_base;
2028        let v = cache[ci].symbol as usize;
2029        induction_bucket[v] -= 1;
2030        cache[ci].symbol = induction_bucket[v];
2031        if i == 0 {
2032            break;
2033        }
2034        i -= 1;
2035    }
2036}
2037
2038fn radix_sort_lms_suffixes_32s_2k_block_sort(
2039    induction_bucket: &mut [SaSint],
2040    cache: &mut [ThreadCache],
2041    omp_block_start: SaSint,
2042    omp_block_size: SaSint,
2043) {
2044    if omp_block_size <= 0 {
2045        return;
2046    }
2047
2048    let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
2049    let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
2050    let cache_base = if cache.len() >= start + size {
2051        0
2052    } else {
2053        start
2054    };
2055    let mut i = start + size - 1;
2056    let mut j = start + 64 + 3;
2057
2058    while i >= j {
2059        for current in [i, i - 1, i - 2, i - 3] {
2060            let ci = current - cache_base;
2061            let v = buckets_index2(cache[ci].symbol as usize, 0);
2062            induction_bucket[v] -= 1;
2063            cache[ci].symbol = induction_bucket[v];
2064        }
2065        i -= 4;
2066    }
2067
2068    j -= 64 + 3;
2069    while i >= j {
2070        let ci = i - cache_base;
2071        let v = buckets_index2(cache[ci].symbol as usize, 0);
2072        induction_bucket[v] -= 1;
2073        cache[ci].symbol = induction_bucket[v];
2074        if i == 0 {
2075            break;
2076        }
2077        i -= 1;
2078    }
2079}
2080
2081fn radix_sort_lms_suffixes_32s_6k_block_omp(
2082    t: &[SaSint],
2083    sa: &mut [SaSint],
2084    induction_bucket: &mut [SaSint],
2085    cache: &mut [ThreadCache],
2086    block_start: SaSint,
2087    block_size: SaSint,
2088    threads: SaSint,
2089) {
2090    if threads <= 1 || block_size < 16_384 {
2091        radix_sort_lms_suffixes_32s_6k(t, sa, induction_bucket, block_start, block_size);
2092        return;
2093    }
2094
2095    radix_sort_lms_suffixes_32s_block_gather(t, sa, cache, block_start, block_size);
2096    radix_sort_lms_suffixes_32s_6k_block_sort(induction_bucket, cache, block_start, block_size);
2097    place_cached_suffixes(sa, cache, block_start, block_size);
2098}
2099
2100fn radix_sort_lms_suffixes_32s_2k_block_omp(
2101    t: &[SaSint],
2102    sa: &mut [SaSint],
2103    induction_bucket: &mut [SaSint],
2104    cache: &mut [ThreadCache],
2105    block_start: SaSint,
2106    block_size: SaSint,
2107    threads: SaSint,
2108) {
2109    if threads <= 1 || block_size < 16_384 {
2110        radix_sort_lms_suffixes_32s_2k(t, sa, induction_bucket, block_start, block_size);
2111        return;
2112    }
2113
2114    radix_sort_lms_suffixes_32s_block_gather(t, sa, cache, block_start, block_size);
2115    radix_sort_lms_suffixes_32s_2k_block_sort(induction_bucket, cache, block_start, block_size);
2116    place_cached_suffixes(sa, cache, block_start, block_size);
2117}
2118
2119fn radix_sort_lms_suffixes_32s_6k_omp(
2120    t: &[SaSint],
2121    sa: &mut [SaSint],
2122    n: SaSint,
2123    m: SaSint,
2124    induction_bucket: &mut [SaSint],
2125    threads: SaSint,
2126) {
2127    if threads <= 1 || m < 65_536 {
2128        radix_sort_lms_suffixes_32s_6k(t, sa, induction_bucket, n - m + 1, m - 1);
2129        return;
2130    }
2131
2132    let threads_usize = usize::try_from(threads).expect("threads must be positive");
2133    let mut cache = vec![ThreadCache::default(); threads_usize * PER_THREAD_CACHE_SIZE];
2134    let mut block_start = 0usize;
2135    let m_usize = usize::try_from(m).expect("m must be non-negative");
2136    let n_usize = usize::try_from(n).expect("n must be non-negative");
2137    let last = m_usize - 1;
2138
2139    while block_start < last {
2140        let block_end = (block_start + threads_usize * PER_THREAD_CACHE_SIZE).min(last);
2141        radix_sort_lms_suffixes_32s_6k_block_omp(
2142            t,
2143            sa,
2144            induction_bucket,
2145            &mut cache,
2146            (n_usize - block_end) as SaSint,
2147            (block_end - block_start) as SaSint,
2148            threads,
2149        );
2150        block_start = block_end;
2151    }
2152}
2153
2154fn radix_sort_lms_suffixes_32s_2k_omp(
2155    t: &[SaSint],
2156    sa: &mut [SaSint],
2157    n: SaSint,
2158    m: SaSint,
2159    induction_bucket: &mut [SaSint],
2160    threads: SaSint,
2161) {
2162    if threads <= 1 || m < 65_536 {
2163        radix_sort_lms_suffixes_32s_2k(t, sa, induction_bucket, n - m + 1, m - 1);
2164        return;
2165    }
2166
2167    let threads_usize = usize::try_from(threads).expect("threads must be positive");
2168    let mut cache = vec![ThreadCache::default(); threads_usize * PER_THREAD_CACHE_SIZE];
2169    let mut block_start = 0usize;
2170    let m_usize = usize::try_from(m).expect("m must be non-negative");
2171    let n_usize = usize::try_from(n).expect("n must be non-negative");
2172    let last = m_usize - 1;
2173
2174    while block_start < last {
2175        let block_end = (block_start + threads_usize * PER_THREAD_CACHE_SIZE).min(last);
2176        radix_sort_lms_suffixes_32s_2k_block_omp(
2177            t,
2178            sa,
2179            induction_bucket,
2180            &mut cache,
2181            (n_usize - block_end) as SaSint,
2182            (block_end - block_start) as SaSint,
2183            threads,
2184        );
2185        block_start = block_end;
2186    }
2187}
2188
2189fn radix_sort_lms_suffixes_32s_1k(
2190    t: &[SaSint],
2191    sa: &mut [SaSint],
2192    n: SaSint,
2193    buckets: &mut [SaSint],
2194) -> SaSint {
2195    let mut i = n - 2;
2196    let mut m = 0;
2197    let mut f0 = 1usize;
2198    let mut f1: usize;
2199    let mut c0 = t[(n - 1) as usize] as isize;
2200    let mut c1: isize;
2201    let mut c2 = 0isize;
2202
2203    while i >= 64 + 3 {
2204        c1 = t[i as usize] as isize;
2205        f1 = usize::from(c1 > c0 - f0 as isize);
2206        if (f1 & !f0) != 0 {
2207            c2 = c0;
2208            buckets[c2 as usize] -= 1;
2209            sa[buckets[c2 as usize] as usize] = i + 1;
2210            m += 1;
2211        }
2212        c0 = t[(i - 1) as usize] as isize;
2213        f0 = usize::from(c0 > c1 - f1 as isize);
2214        if (f0 & !f1) != 0 {
2215            c2 = c1;
2216            buckets[c2 as usize] -= 1;
2217            sa[buckets[c2 as usize] as usize] = i;
2218            m += 1;
2219        }
2220        c1 = t[(i - 2) as usize] as isize;
2221        f1 = usize::from(c1 > c0 - f0 as isize);
2222        if (f1 & !f0) != 0 {
2223            c2 = c0;
2224            buckets[c2 as usize] -= 1;
2225            sa[buckets[c2 as usize] as usize] = i - 1;
2226            m += 1;
2227        }
2228        c0 = t[(i - 3) as usize] as isize;
2229        f0 = usize::from(c0 > c1 - f1 as isize);
2230        if (f0 & !f1) != 0 {
2231            c2 = c1;
2232            buckets[c2 as usize] -= 1;
2233            sa[buckets[c2 as usize] as usize] = i - 2;
2234            m += 1;
2235        }
2236        i -= 4;
2237    }
2238
2239    while i >= 0 {
2240        c1 = c0;
2241        c0 = t[i as usize] as isize;
2242        f1 = f0;
2243        f0 = usize::from(c0 > c1 - f1 as isize);
2244        if (f0 & !f1) != 0 {
2245            c2 = c1;
2246            buckets[c2 as usize] -= 1;
2247            sa[buckets[c2 as usize] as usize] = i + 1;
2248            m += 1;
2249        }
2250        i -= 1;
2251    }
2252
2253    if m > 1 {
2254        sa[buckets[c2 as usize] as usize] = 0;
2255    }
2256
2257    m
2258}
2259
2260fn radix_sort_set_markers_32s_6k(
2261    sa: &mut [SaSint],
2262    induction_bucket: &[SaSint],
2263    omp_block_start: SaSint,
2264    omp_block_size: SaSint,
2265) {
2266    let mut i = omp_block_start;
2267    let mut j = omp_block_start + omp_block_size - 64 - 3;
2268
2269    while i < j {
2270        sa[induction_bucket[i as usize] as usize] |= SAINT_MIN;
2271        sa[induction_bucket[(i + 1) as usize] as usize] |= SAINT_MIN;
2272        sa[induction_bucket[(i + 2) as usize] as usize] |= SAINT_MIN;
2273        sa[induction_bucket[(i + 3) as usize] as usize] |= SAINT_MIN;
2274        i += 4;
2275    }
2276
2277    j += 64 + 3;
2278    while i < j {
2279        sa[induction_bucket[i as usize] as usize] |= SAINT_MIN;
2280        i += 1;
2281    }
2282}
2283
2284fn radix_sort_set_markers_32s_4k(
2285    sa: &mut [SaSint],
2286    induction_bucket: &[SaSint],
2287    omp_block_start: SaSint,
2288    omp_block_size: SaSint,
2289) {
2290    let mut i = omp_block_start;
2291    let mut j = omp_block_start + omp_block_size - 64 - 3;
2292
2293    while i < j {
2294        sa[induction_bucket[buckets_index2(i as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2295        sa[induction_bucket[buckets_index2((i + 1) as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2296        sa[induction_bucket[buckets_index2((i + 2) as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2297        sa[induction_bucket[buckets_index2((i + 3) as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2298        i += 4;
2299    }
2300
2301    j += 64 + 3;
2302    while i < j {
2303        sa[induction_bucket[buckets_index2(i as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2304        i += 1;
2305    }
2306}
2307
2308fn radix_sort_set_markers_32s_6k_omp(
2309    sa: &mut [SaSint],
2310    k: SaSint,
2311    induction_bucket: &[SaSint],
2312    threads: SaSint,
2313) {
2314    if k <= 1 {
2315        return;
2316    }
2317
2318    if threads <= 1 || k < 65_536 {
2319        radix_sort_set_markers_32s_6k(sa, induction_bucket, 0, k - 1);
2320        return;
2321    }
2322
2323    let threads_usize = usize::try_from(threads).expect("threads must be positive");
2324    let last = usize::try_from(k - 1).expect("k must be positive");
2325    let stride = (last / threads_usize) & !15usize;
2326
2327    {
2328        let sa_ptr = SyncMutPtr::new(sa);
2329        run_rayon_with_threads(threads_usize, || {
2330            (0..threads_usize).into_par_iter().for_each(|thread| {
2331                let start = thread * stride;
2332                let end = if thread + 1 == threads_usize {
2333                    last
2334                } else {
2335                    start + stride
2336                };
2337                if end > start {
2338                    let sa = unsafe { sa_ptr.as_slice() };
2339                    radix_sort_set_markers_32s_6k(
2340                        sa,
2341                        induction_bucket,
2342                        start as SaSint,
2343                        (end - start) as SaSint,
2344                    );
2345                }
2346            });
2347        });
2348    }
2349}
2350
2351fn radix_sort_set_markers_32s_4k_omp(
2352    sa: &mut [SaSint],
2353    k: SaSint,
2354    induction_bucket: &[SaSint],
2355    threads: SaSint,
2356) {
2357    if k <= 1 {
2358        return;
2359    }
2360
2361    if threads <= 1 || k < 65_536 {
2362        radix_sort_set_markers_32s_4k(sa, induction_bucket, 0, k - 1);
2363        return;
2364    }
2365
2366    let threads_usize = usize::try_from(threads).expect("threads must be positive");
2367    let last = usize::try_from(k - 1).expect("k must be positive");
2368    let stride = (last / threads_usize) & !15usize;
2369
2370    {
2371        let sa_ptr = SyncMutPtr::new(sa);
2372        run_rayon_with_threads(threads_usize, || {
2373            (0..threads_usize).into_par_iter().for_each(|thread| {
2374                let start = thread * stride;
2375                let end = if thread + 1 == threads_usize {
2376                    last
2377                } else {
2378                    start + stride
2379                };
2380                if end > start {
2381                    let sa = unsafe { sa_ptr.as_slice() };
2382                    radix_sort_set_markers_32s_4k(
2383                        sa,
2384                        induction_bucket,
2385                        start as SaSint,
2386                        (end - start) as SaSint,
2387                    );
2388                }
2389            });
2390        });
2391    }
2392}
2393
2394fn initialize_buckets_for_partial_sorting_16u(
2395    t: &[u16],
2396    buckets: &mut [SaSint],
2397    first_lms_suffix: SaSint,
2398    left_suffixes_count: SaSint,
2399) {
2400    buckets[buckets_index4(t[first_lms_suffix as usize] as usize, 1)] += 1;
2401
2402    let (front, temp_bucket) = buckets.split_at_mut(4 * ALPHABET_SIZE);
2403    let mut sum0 = left_suffixes_count + 1;
2404    let mut sum1 = 0;
2405
2406    for c in 0..ALPHABET_SIZE {
2407        let i = buckets_index4(c, 0);
2408        let j = buckets_index2(c, 0);
2409
2410        temp_bucket[j + buckets_index2(0, 0)] = sum0;
2411
2412        sum0 += front[i + buckets_index4(0, 0)] + front[i + buckets_index4(0, 2)];
2413        sum1 += front[i + buckets_index4(0, 1)];
2414
2415        front[j + buckets_index2(0, 0)] = sum0;
2416        front[j + buckets_index2(0, 1)] = sum1;
2417    }
2418}
2419
2420fn partial_sorting_shift_markers_32s_6k_omp(
2421    sa: &mut [SaSint],
2422    k: SaSint,
2423    buckets: &[SaSint],
2424    threads: SaSint,
2425) {
2426    let k_usize = usize::try_from(k).expect("k must be non-negative");
2427    let temp_bucket = &buckets[4 * k_usize..];
2428    let thread_count = if threads > 1 && k >= 65536 {
2429        usize::try_from(threads).expect("threads must be positive")
2430    } else {
2431        1
2432    };
2433    let sa_ptr = SyncMutPtr::new(sa);
2434    let buckets_ref: &[SaSint] = buckets;
2435    let temp_bucket_ref: &[SaSint] = temp_bucket;
2436    run_rayon_with_threads(thread_count, || {
2437        (0..thread_count).into_par_iter().for_each(|t| {
2438            let mut c = k_usize as isize - 1 - t as isize;
2439            let sa = unsafe { sa_ptr.as_slice() };
2440            while c >= 1 {
2441                let c_usize = c as usize;
2442                let mut i = buckets_ref[buckets_index4(c_usize, 0)] - 1;
2443                let mut j = temp_bucket_ref[buckets_index2(c_usize - 1, 0)] + 3;
2444                let mut s = SAINT_MIN;
2445
2446                while i >= j {
2447                    let p0 = sa[i as usize];
2448                    let q0 = (p0 & SAINT_MIN) ^ s;
2449                    s ^= q0;
2450                    sa[i as usize] = p0 ^ q0;
2451
2452                    let p1 = sa[(i - 1) as usize];
2453                    let q1 = (p1 & SAINT_MIN) ^ s;
2454                    s ^= q1;
2455                    sa[(i - 1) as usize] = p1 ^ q1;
2456
2457                    let p2 = sa[(i - 2) as usize];
2458                    let q2 = (p2 & SAINT_MIN) ^ s;
2459                    s ^= q2;
2460                    sa[(i - 2) as usize] = p2 ^ q2;
2461
2462                    let p3 = sa[(i - 3) as usize];
2463                    let q3 = (p3 & SAINT_MIN) ^ s;
2464                    s ^= q3;
2465                    sa[(i - 3) as usize] = p3 ^ q3;
2466
2467                    i -= 4;
2468                }
2469
2470                j -= 3;
2471                while i >= j {
2472                    let p = sa[i as usize];
2473                    let q = (p & SAINT_MIN) ^ s;
2474                    s ^= q;
2475                    sa[i as usize] = p ^ q;
2476                    i -= 1;
2477                }
2478
2479                c -= thread_count as isize;
2480            }
2481        });
2482    });
2483}
2484
2485fn partial_sorting_shift_markers_32s_4k(sa: &mut [SaSint], n: SaSint) {
2486    let mut i = n - 1;
2487    let mut s = SUFFIX_GROUP_MARKER;
2488
2489    while i >= 3 {
2490        let p0 = sa[i as usize];
2491        let q0 =
2492            ((p0 & SUFFIX_GROUP_MARKER) ^ s) & (((p0 > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
2493        s ^= q0;
2494        sa[i as usize] = p0 ^ q0;
2495
2496        let p1 = sa[(i - 1) as usize];
2497        let q1 =
2498            ((p1 & SUFFIX_GROUP_MARKER) ^ s) & (((p1 > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
2499        s ^= q1;
2500        sa[(i - 1) as usize] = p1 ^ q1;
2501
2502        let p2 = sa[(i - 2) as usize];
2503        let q2 =
2504            ((p2 & SUFFIX_GROUP_MARKER) ^ s) & (((p2 > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
2505        s ^= q2;
2506        sa[(i - 2) as usize] = p2 ^ q2;
2507
2508        let p3 = sa[(i - 3) as usize];
2509        let q3 =
2510            ((p3 & SUFFIX_GROUP_MARKER) ^ s) & (((p3 > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
2511        s ^= q3;
2512        sa[(i - 3) as usize] = p3 ^ q3;
2513
2514        i -= 4;
2515    }
2516
2517    while i >= 0 {
2518        let p = sa[i as usize];
2519        let q = ((p & SUFFIX_GROUP_MARKER) ^ s) & (((p > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
2520        s ^= q;
2521        sa[i as usize] = p ^ q;
2522        i -= 1;
2523    }
2524}
2525
2526fn partial_sorting_shift_buckets_32s_6k(k: SaSint, buckets: &mut [SaSint]) {
2527    let temp_offset = 4 * k as usize;
2528    let mut i = buckets_index2(0, 0);
2529
2530    while i <= buckets_index2(k as usize - 1, 0) {
2531        buckets[2 * i + buckets_index4(0, 0)] = buckets[temp_offset + i + buckets_index2(0, 0)];
2532        buckets[2 * i + buckets_index4(0, 1)] = buckets[temp_offset + i + buckets_index2(0, 1)];
2533        i += buckets_index2(1, 0);
2534    }
2535}
2536
2537fn partial_sorting_scan_left_to_right_16u(
2538    t: &[u16],
2539    sa: &mut [SaSint],
2540    buckets: &mut [SaSint],
2541    mut d: SaSint,
2542    omp_block_start: SaSint,
2543    omp_block_size: SaSint,
2544) -> SaSint {
2545    let mut i = omp_block_start as isize;
2546    let mut j = (omp_block_start + omp_block_size - 64 - 1) as isize;
2547    while i < j {
2548        let mut p0 = sa[i as usize];
2549        d += SaSint::from(p0 < 0);
2550        p0 &= SAINT_MAX;
2551        let v0 = buckets_index2(
2552            t[(p0 - 1) as usize] as usize,
2553            usize::from(t[(p0 - 2) as usize] >= t[(p0 - 1) as usize]),
2554        );
2555        let mark0 = if buckets[2 * ALPHABET_SIZE + v0] != d {
2556            SAINT_MIN
2557        } else {
2558            0
2559        };
2560        let dst0 = buckets[4 * ALPHABET_SIZE + v0] as usize;
2561        sa[dst0] = (p0 - 1) | mark0;
2562        buckets[4 * ALPHABET_SIZE + v0] += 1;
2563        buckets[2 * ALPHABET_SIZE + v0] = d;
2564
2565        let mut p1 = sa[(i + 1) as usize];
2566        d += SaSint::from(p1 < 0);
2567        p1 &= SAINT_MAX;
2568        let v1 = buckets_index2(
2569            t[(p1 - 1) as usize] as usize,
2570            usize::from(t[(p1 - 2) as usize] >= t[(p1 - 1) as usize]),
2571        );
2572        let mark1 = if buckets[2 * ALPHABET_SIZE + v1] != d {
2573            SAINT_MIN
2574        } else {
2575            0
2576        };
2577        let dst1 = buckets[4 * ALPHABET_SIZE + v1] as usize;
2578        sa[dst1] = (p1 - 1) | mark1;
2579        buckets[4 * ALPHABET_SIZE + v1] += 1;
2580        buckets[2 * ALPHABET_SIZE + v1] = d;
2581
2582        i += 2;
2583    }
2584
2585    j += 64 + 1;
2586    while i < j {
2587        let mut p = sa[i as usize];
2588        d += SaSint::from(p < 0);
2589        p &= SAINT_MAX;
2590        let v = buckets_index2(
2591            t[(p - 1) as usize] as usize,
2592            usize::from(t[(p - 2) as usize] >= t[(p - 1) as usize]),
2593        );
2594        let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
2595            SAINT_MIN
2596        } else {
2597            0
2598        };
2599        let dst = buckets[4 * ALPHABET_SIZE + v] as usize;
2600        sa[dst] = (p - 1) | mark;
2601        buckets[4 * ALPHABET_SIZE + v] += 1;
2602        buckets[2 * ALPHABET_SIZE + v] = d;
2603        i += 1;
2604    }
2605
2606    d
2607}
2608
2609fn partial_sorting_scan_left_to_right_16u_block_prepare(
2610    t: &[u16],
2611    sa: &mut [SaSint],
2612    k: SaSint,
2613    buckets: &mut [SaSint],
2614    cache: &mut [ThreadCache],
2615    omp_block_start: SaSint,
2616    omp_block_size: SaSint,
2617    state: &mut ThreadState,
2618) -> SaSint {
2619    let width = 2 * k as usize;
2620    buckets[..width].fill(0);
2621    buckets[2 * ALPHABET_SIZE..2 * ALPHABET_SIZE + width].fill(0);
2622
2623    let mut count = 0usize;
2624    let mut d = 1;
2625    for i in omp_block_start as usize..(omp_block_start + omp_block_size) as usize {
2626        let mut p = sa[i];
2627        cache[count].index = p;
2628        d += SaSint::from(p < 0);
2629        p &= SAINT_MAX;
2630        let v = buckets_index2(
2631            t[(p - 1) as usize] as usize,
2632            usize::from(t[(p - 2) as usize] >= t[(p - 1) as usize]),
2633        );
2634        cache[count].symbol = v as SaSint;
2635        buckets[v] += 1;
2636        buckets[2 * ALPHABET_SIZE + v] = d;
2637        count += 1;
2638    }
2639    state.cache_entries = count;
2640    d - 1
2641}
2642
2643fn partial_sorting_scan_left_to_right_16u_block_place(
2644    sa: &mut [SaSint],
2645    buckets: &mut [SaSint],
2646    cache: &[ThreadCache],
2647    count: SaSint,
2648    mut d: SaSint,
2649) {
2650    for entry in cache.iter().take(count as usize) {
2651        let mut p = entry.index;
2652        d += SaSint::from(p < 0);
2653        p &= SAINT_MAX;
2654        let v = entry.symbol as usize;
2655        let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
2656            SAINT_MIN
2657        } else {
2658            0
2659        };
2660        let dst = buckets[v] as usize;
2661        sa[dst] = (p - 1) | mark;
2662        buckets[v] += 1;
2663        buckets[2 * ALPHABET_SIZE + v] = d;
2664    }
2665}
2666
2667fn partial_sorting_scan_left_to_right_16u_block_omp(
2668    t: &[u16],
2669    sa: &mut [SaSint],
2670    k: SaSint,
2671    buckets: &mut [SaSint],
2672    d: SaSint,
2673    block_start: SaSint,
2674    block_size: SaSint,
2675    threads: SaSint,
2676    thread_state: &mut [ThreadState],
2677) -> SaSint {
2678    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
2679        usize::try_from(threads)
2680            .expect("threads must be non-negative")
2681            .min(thread_state.len())
2682    } else {
2683        1
2684    };
2685    if thread_count <= 1 {
2686        return partial_sorting_scan_left_to_right_16u(t, sa, buckets, d, block_start, block_size);
2687    }
2688
2689    let bucket_width = 2 * k as usize;
2690    let block_stride = (block_size / thread_count as SaSint) & !15;
2691
2692    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
2693        let local_start = thread as SaSint * block_stride;
2694        let local_size = if thread + 1 < thread_count {
2695            block_stride
2696        } else {
2697            block_size - local_start
2698        };
2699        let mut local_state = ThreadState::default();
2700        state.position = partial_sorting_scan_left_to_right_16u_block_prepare(
2701            t,
2702            sa,
2703            k,
2704            &mut state.buckets,
2705            &mut state.cache,
2706            block_start + local_start,
2707            local_size,
2708            &mut local_state,
2709        );
2710        state.count = local_state.cache_entries as SaSint;
2711    }
2712
2713    let mut next_d = d;
2714    for state in thread_state.iter_mut().take(thread_count) {
2715        for c in 0..bucket_width {
2716            let a = buckets[4 * ALPHABET_SIZE + c];
2717            let b = state.buckets[c];
2718            buckets[4 * ALPHABET_SIZE + c] = a + b;
2719            state.buckets[c] = a;
2720        }
2721
2722        next_d -= 1;
2723        for c in 0..bucket_width {
2724            let a = buckets[2 * ALPHABET_SIZE + c];
2725            let b = state.buckets[2 * ALPHABET_SIZE + c];
2726            let shifted = b + next_d;
2727            buckets[2 * ALPHABET_SIZE + c] = if b > 0 { shifted } else { a };
2728            state.buckets[2 * ALPHABET_SIZE + c] = a;
2729        }
2730        next_d += 1 + state.position;
2731        state.position = next_d - state.position;
2732    }
2733
2734    for state in thread_state.iter_mut().take(thread_count) {
2735        partial_sorting_scan_left_to_right_16u_block_place(
2736            sa,
2737            &mut state.buckets,
2738            &state.cache,
2739            state.count,
2740            state.position,
2741        );
2742    }
2743
2744    next_d
2745}
2746
2747fn partial_sorting_scan_left_to_right_16u_omp(
2748    t: &[u16],
2749    sa: &mut [SaSint],
2750    n: SaSint,
2751    k: SaSint,
2752    buckets: &mut [SaSint],
2753    left_suffixes_count: SaSint,
2754    mut d: SaSint,
2755    threads: SaSint,
2756) -> SaSint {
2757    let v = buckets_index2(
2758        t[(n - 1) as usize] as usize,
2759        usize::from(t[(n - 2) as usize] >= t[(n - 1) as usize]),
2760    );
2761    let dst = buckets[4 * ALPHABET_SIZE + v] as usize;
2762    buckets[4 * ALPHABET_SIZE + v] += 1;
2763    sa[dst] = (n - 1) | SAINT_MIN;
2764    d += 1;
2765    buckets[2 * ALPHABET_SIZE + v] = d;
2766
2767    if threads == 1 || left_suffixes_count < 65536 {
2768        d = partial_sorting_scan_left_to_right_16u(t, sa, buckets, d, 0, left_suffixes_count);
2769    } else {
2770        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
2771        let mut block_start = 0;
2772        while block_start < left_suffixes_count {
2773            if sa[block_start as usize] == 0 {
2774                block_start += 1;
2775            } else {
2776                let mut block_end =
2777                    block_start + threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
2778                if block_end > left_suffixes_count {
2779                    block_end = left_suffixes_count;
2780                }
2781                let mut block_scan_end = block_start + 1;
2782                while block_scan_end < block_end && sa[block_scan_end as usize] != 0 {
2783                    block_scan_end += 1;
2784                }
2785                let block_size = block_scan_end - block_start;
2786
2787                if block_size < 32 {
2788                    while block_start < block_scan_end {
2789                        let mut p = sa[block_start as usize];
2790                        d += SaSint::from(p < 0);
2791                        p &= SAINT_MAX;
2792                        let v = buckets_index2(
2793                            t[(p - 1) as usize] as usize,
2794                            usize::from(t[(p - 2) as usize] >= t[(p - 1) as usize]),
2795                        );
2796                        let dst = buckets[4 * ALPHABET_SIZE + v] as usize;
2797                        buckets[4 * ALPHABET_SIZE + v] += 1;
2798                        let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
2799                            SAINT_MIN
2800                        } else {
2801                            0
2802                        };
2803                        sa[dst] = (p - 1) | mark;
2804                        buckets[2 * ALPHABET_SIZE + v] = d;
2805                        block_start += 1;
2806                    }
2807                } else {
2808                    d = partial_sorting_scan_left_to_right_16u_block_omp(
2809                        t,
2810                        sa,
2811                        k,
2812                        buckets,
2813                        d,
2814                        block_start,
2815                        block_size,
2816                        threads,
2817                        &mut thread_state,
2818                    );
2819                    block_start = block_scan_end;
2820                }
2821            }
2822        }
2823    }
2824    d
2825}
2826
2827fn partial_sorting_scan_right_to_left_16u(
2828    t: &[u16],
2829    sa: &mut [SaSint],
2830    buckets: &mut [SaSint],
2831    mut d: SaSint,
2832    omp_block_start: SaSint,
2833    omp_block_size: SaSint,
2834) -> SaSint {
2835    let mut i = (omp_block_start + omp_block_size - 1) as isize;
2836    let mut j = (omp_block_start + 64 + 1) as isize;
2837    while i >= j {
2838        let mut p0 = sa[i as usize];
2839        d += SaSint::from(p0 < 0);
2840        p0 &= SAINT_MAX;
2841        let v0 = buckets_index2(
2842            t[(p0 - 1) as usize] as usize,
2843            usize::from(t[(p0 - 2) as usize] > t[(p0 - 1) as usize]),
2844        );
2845        let mark0 = if buckets[2 * ALPHABET_SIZE + v0] != d {
2846            SAINT_MIN
2847        } else {
2848            0
2849        };
2850        buckets[v0] -= 1;
2851        sa[buckets[v0] as usize] = (p0 - 1) | mark0;
2852        buckets[2 * ALPHABET_SIZE + v0] = d;
2853
2854        let mut p1 = sa[(i - 1) as usize];
2855        d += SaSint::from(p1 < 0);
2856        p1 &= SAINT_MAX;
2857        let v1 = buckets_index2(
2858            t[(p1 - 1) as usize] as usize,
2859            usize::from(t[(p1 - 2) as usize] > t[(p1 - 1) as usize]),
2860        );
2861        let mark1 = if buckets[2 * ALPHABET_SIZE + v1] != d {
2862            SAINT_MIN
2863        } else {
2864            0
2865        };
2866        buckets[v1] -= 1;
2867        sa[buckets[v1] as usize] = (p1 - 1) | mark1;
2868        buckets[2 * ALPHABET_SIZE + v1] = d;
2869
2870        i -= 2;
2871    }
2872
2873    j -= 64 + 1;
2874    while i >= j {
2875        let mut p = sa[i as usize];
2876        d += SaSint::from(p < 0);
2877        p &= SAINT_MAX;
2878        let v = buckets_index2(
2879            t[(p - 1) as usize] as usize,
2880            usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
2881        );
2882        let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
2883            SAINT_MIN
2884        } else {
2885            0
2886        };
2887        buckets[v] -= 1;
2888        sa[buckets[v] as usize] = (p - 1) | mark;
2889        buckets[2 * ALPHABET_SIZE + v] = d;
2890        i -= 1;
2891    }
2892
2893    d
2894}
2895
2896fn partial_sorting_scan_right_to_left_16u_block_prepare(
2897    t: &[u16],
2898    sa: &mut [SaSint],
2899    k: SaSint,
2900    buckets: &mut [SaSint],
2901    cache: &mut [ThreadCache],
2902    omp_block_start: SaSint,
2903    omp_block_size: SaSint,
2904    state: &mut ThreadState,
2905) -> SaSint {
2906    let width = 2 * k as usize;
2907    buckets[..width].fill(0);
2908    buckets[2 * ALPHABET_SIZE..2 * ALPHABET_SIZE + width].fill(0);
2909
2910    let mut count = 0usize;
2911    let mut d = 1;
2912    for i in (omp_block_start as usize..(omp_block_start + omp_block_size) as usize).rev() {
2913        let mut p = sa[i];
2914        cache[count].index = p;
2915        d += SaSint::from(p < 0);
2916        p &= SAINT_MAX;
2917        let v = buckets_index2(
2918            t[(p - 1) as usize] as usize,
2919            usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
2920        );
2921        cache[count].symbol = v as SaSint;
2922        buckets[v] += 1;
2923        buckets[2 * ALPHABET_SIZE + v] = d;
2924        count += 1;
2925    }
2926    state.cache_entries = count;
2927    d - 1
2928}
2929
2930fn partial_sorting_scan_right_to_left_16u_block_place(
2931    sa: &mut [SaSint],
2932    buckets: &mut [SaSint],
2933    cache: &[ThreadCache],
2934    count: SaSint,
2935    mut d: SaSint,
2936) {
2937    for entry in cache.iter().take(count as usize) {
2938        let mut p = entry.index;
2939        d += SaSint::from(p < 0);
2940        p &= SAINT_MAX;
2941        let v = entry.symbol as usize;
2942        let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
2943            SAINT_MIN
2944        } else {
2945            0
2946        };
2947        buckets[v] -= 1;
2948        sa[buckets[v] as usize] = (p - 1) | mark;
2949        buckets[2 * ALPHABET_SIZE + v] = d;
2950    }
2951}
2952
2953fn partial_gsa_scan_right_to_left_16u_block_place(
2954    sa: &mut [SaSint],
2955    buckets: &mut [SaSint],
2956    cache: &[ThreadCache],
2957    count: SaSint,
2958    mut d: SaSint,
2959) {
2960    for entry in cache.iter().take(count as usize) {
2961        let mut p = entry.index;
2962        d += SaSint::from(p < 0);
2963        p &= SAINT_MAX;
2964        let v = entry.symbol as usize;
2965        if v != 1 {
2966            let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
2967                SAINT_MIN
2968            } else {
2969                0
2970            };
2971            buckets[v] -= 1;
2972            sa[buckets[v] as usize] = (p - 1) | mark;
2973            buckets[2 * ALPHABET_SIZE + v] = d;
2974        }
2975    }
2976}
2977
2978fn partial_sorting_scan_right_to_left_16u_block_omp(
2979    t: &[u16],
2980    sa: &mut [SaSint],
2981    k: SaSint,
2982    buckets: &mut [SaSint],
2983    d: SaSint,
2984    block_start: SaSint,
2985    block_size: SaSint,
2986    threads: SaSint,
2987    thread_state: &mut [ThreadState],
2988) -> SaSint {
2989    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
2990        usize::try_from(threads)
2991            .expect("threads must be non-negative")
2992            .min(thread_state.len())
2993    } else {
2994        1
2995    };
2996    if thread_count <= 1 {
2997        return partial_sorting_scan_right_to_left_16u(t, sa, buckets, d, block_start, block_size);
2998    }
2999
3000    let width = 2 * k as usize;
3001    let distinct_offset = 2 * ALPHABET_SIZE;
3002    let block_stride = (block_size / thread_count as SaSint) & !15;
3003
3004    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
3005        let local_start = thread as SaSint * block_stride;
3006        let local_size = if thread + 1 < thread_count {
3007            block_stride
3008        } else {
3009            block_size - local_start
3010        };
3011        let mut local_state = ThreadState::default();
3012        state.position = partial_sorting_scan_right_to_left_16u_block_prepare(
3013            t,
3014            sa,
3015            k,
3016            &mut state.buckets,
3017            &mut state.cache,
3018            block_start + local_start,
3019            local_size,
3020            &mut local_state,
3021        );
3022        state.count = local_state.cache_entries as SaSint;
3023    }
3024
3025    let mut next_d = d;
3026    for state in thread_state.iter_mut().take(thread_count).rev() {
3027        for c in 0..width {
3028            let a = buckets[c];
3029            let b = state.buckets[c];
3030            buckets[c] = a - b;
3031            state.buckets[c] = a;
3032        }
3033
3034        next_d -= 1;
3035        for c in 0..width {
3036            let offset = distinct_offset + c;
3037            let a = buckets[offset];
3038            let b = state.buckets[offset];
3039            let shifted = b + next_d;
3040            buckets[offset] = if b > 0 { shifted } else { a };
3041            state.buckets[offset] = a;
3042        }
3043        next_d += 1 + state.position;
3044        state.position = next_d - state.position;
3045    }
3046
3047    for state in thread_state.iter_mut().take(thread_count) {
3048        partial_sorting_scan_right_to_left_16u_block_place(
3049            sa,
3050            &mut state.buckets,
3051            &state.cache,
3052            state.count,
3053            state.position,
3054        );
3055    }
3056
3057    next_d
3058}
3059
3060fn partial_sorting_scan_right_to_left_16u_omp(
3061    t: &[u16],
3062    sa: &mut [SaSint],
3063    n: SaSint,
3064    k: SaSint,
3065    buckets: &mut [SaSint],
3066    first_lms_suffix: SaSint,
3067    left_suffixes_count: SaSint,
3068    d: SaSint,
3069    threads: SaSint,
3070) {
3071    let scan_start = left_suffixes_count + 1;
3072    let scan_end = n - first_lms_suffix;
3073
3074    if threads == 1 || scan_end - scan_start < 65536 {
3075        partial_sorting_scan_right_to_left_16u(
3076            t,
3077            sa,
3078            buckets,
3079            d,
3080            scan_start,
3081            scan_end - scan_start,
3082        );
3083    } else {
3084        let mut d = d;
3085        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
3086        let mut block_start = scan_end - 1;
3087        while block_start >= scan_start {
3088            if sa[block_start as usize] == 0 {
3089                block_start -= 1;
3090            } else {
3091                let block_limit = threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
3092                let mut block_max_end = block_start - block_limit;
3093                if block_max_end < scan_start {
3094                    block_max_end = scan_start - 1;
3095                }
3096                let mut block_end = block_start - 1;
3097                while block_end > block_max_end && sa[block_end as usize] != 0 {
3098                    block_end -= 1;
3099                }
3100                let block_size = block_start - block_end;
3101
3102                if block_size < 32 {
3103                    while block_start > block_end {
3104                        let mut p = sa[block_start as usize];
3105                        d += SaSint::from(p < 0);
3106                        p &= SAINT_MAX;
3107                        let v = buckets_index2(
3108                            t[(p - 1) as usize] as usize,
3109                            usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
3110                        );
3111                        let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
3112                            SAINT_MIN
3113                        } else {
3114                            0
3115                        };
3116                        buckets[v] -= 1;
3117                        sa[buckets[v] as usize] = (p - 1) | mark;
3118                        buckets[2 * ALPHABET_SIZE + v] = d;
3119                        block_start -= 1;
3120                    }
3121                } else {
3122                    d = partial_sorting_scan_right_to_left_16u_block_omp(
3123                        t,
3124                        sa,
3125                        k,
3126                        buckets,
3127                        d,
3128                        block_end + 1,
3129                        block_size,
3130                        threads,
3131                        &mut thread_state,
3132                    );
3133                    block_start = block_end;
3134                }
3135            }
3136        }
3137    }
3138}
3139
3140fn partial_sorting_scan_left_to_right_32s_6k(
3141    t: &[SaSint],
3142    sa: &mut [SaSint],
3143    buckets: &mut [SaSint],
3144    mut d: SaSint,
3145    omp_block_start: SaSint,
3146    omp_block_size: SaSint,
3147) -> SaSint {
3148    let mut i = omp_block_start;
3149    let mut j = omp_block_start + omp_block_size - 2 * 64 - 1;
3150
3151    while i < j {
3152        let mut p2 = sa[i as usize];
3153        d += SaSint::from(p2 < 0);
3154        p2 &= SAINT_MAX;
3155        let v2 = buckets_index4(
3156            t[(p2 - 1) as usize] as usize,
3157            usize::from(t[(p2 - 2) as usize] >= t[(p2 - 1) as usize]),
3158        );
3159        let pos2 = buckets[v2] as usize;
3160        buckets[v2] += 1;
3161        sa[pos2] = (p2 - 1) | (((buckets[2 + v2] != d) as SaSint) << (SAINT_BIT - 1));
3162        buckets[2 + v2] = d;
3163
3164        let mut p3 = sa[(i + 1) as usize];
3165        d += SaSint::from(p3 < 0);
3166        p3 &= SAINT_MAX;
3167        let v3 = buckets_index4(
3168            t[(p3 - 1) as usize] as usize,
3169            usize::from(t[(p3 - 2) as usize] >= t[(p3 - 1) as usize]),
3170        );
3171        let pos3 = buckets[v3] as usize;
3172        buckets[v3] += 1;
3173        sa[pos3] = (p3 - 1) | (((buckets[2 + v3] != d) as SaSint) << (SAINT_BIT - 1));
3174        buckets[2 + v3] = d;
3175
3176        i += 2;
3177    }
3178
3179    j += 2 * 64 + 1;
3180    while i < j {
3181        let mut p = sa[i as usize];
3182        d += SaSint::from(p < 0);
3183        p &= SAINT_MAX;
3184        let v = buckets_index4(
3185            t[(p - 1) as usize] as usize,
3186            usize::from(t[(p - 2) as usize] >= t[(p - 1) as usize]),
3187        );
3188        let pos = buckets[v] as usize;
3189        buckets[v] += 1;
3190        sa[pos] = (p - 1) | (((buckets[2 + v] != d) as SaSint) << (SAINT_BIT - 1));
3191        buckets[2 + v] = d;
3192        i += 1;
3193    }
3194
3195    d
3196}
3197
3198fn partial_sorting_scan_left_to_right_32s_4k(
3199    t: &[SaSint],
3200    sa: &mut [SaSint],
3201    k: SaSint,
3202    buckets: &mut [SaSint],
3203    mut d: SaSint,
3204    omp_block_start: SaSint,
3205    omp_block_size: SaSint,
3206) -> SaSint {
3207    let k = k as usize;
3208    let mut i = omp_block_start;
3209    let mut j = omp_block_start + omp_block_size - 2 * 64 - 1;
3210
3211    while i < j {
3212        let mut p0 = sa[i as usize];
3213        sa[i as usize] = p0 & SAINT_MAX;
3214        if p0 > 0 {
3215            sa[i as usize] = 0;
3216            d += p0 >> (SUFFIX_GROUP_BIT - 1);
3217            p0 &= !SUFFIX_GROUP_MARKER;
3218            let v0 = buckets_index2(
3219                t[(p0 - 1) as usize] as usize,
3220                usize::from(t[(p0 - 2) as usize] < t[(p0 - 1) as usize]),
3221            );
3222            let c0 = t[(p0 - 1) as usize] as usize;
3223            let pos0 = buckets[2 * k + c0] as usize;
3224            buckets[2 * k + c0] += 1;
3225            sa[pos0] = (p0 - 1)
3226                | ((usize::from(t[(p0 - 2) as usize] < t[(p0 - 1) as usize]) as SaSint)
3227                    << (SAINT_BIT - 1))
3228                | (((buckets[v0] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3229            buckets[v0] = d;
3230        }
3231
3232        let mut p1 = sa[(i + 1) as usize];
3233        sa[(i + 1) as usize] = p1 & SAINT_MAX;
3234        if p1 > 0 {
3235            sa[(i + 1) as usize] = 0;
3236            d += p1 >> (SUFFIX_GROUP_BIT - 1);
3237            p1 &= !SUFFIX_GROUP_MARKER;
3238            let v1 = buckets_index2(
3239                t[(p1 - 1) as usize] as usize,
3240                usize::from(t[(p1 - 2) as usize] < t[(p1 - 1) as usize]),
3241            );
3242            let c1 = t[(p1 - 1) as usize] as usize;
3243            let pos1 = buckets[2 * k + c1] as usize;
3244            buckets[2 * k + c1] += 1;
3245            sa[pos1] = (p1 - 1)
3246                | ((usize::from(t[(p1 - 2) as usize] < t[(p1 - 1) as usize]) as SaSint)
3247                    << (SAINT_BIT - 1))
3248                | (((buckets[v1] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3249            buckets[v1] = d;
3250        }
3251
3252        i += 2;
3253    }
3254
3255    j += 2 * 64 + 1;
3256    while i < j {
3257        let mut p = sa[i as usize];
3258        sa[i as usize] = p & SAINT_MAX;
3259        if p > 0 {
3260            sa[i as usize] = 0;
3261            d += p >> (SUFFIX_GROUP_BIT - 1);
3262            p &= !SUFFIX_GROUP_MARKER;
3263            let v = buckets_index2(
3264                t[(p - 1) as usize] as usize,
3265                usize::from(t[(p - 2) as usize] < t[(p - 1) as usize]),
3266            );
3267            let c = t[(p - 1) as usize] as usize;
3268            let pos = buckets[2 * k + c] as usize;
3269            buckets[2 * k + c] += 1;
3270            sa[pos] = (p - 1)
3271                | ((usize::from(t[(p - 2) as usize] < t[(p - 1) as usize]) as SaSint)
3272                    << (SAINT_BIT - 1))
3273                | (((buckets[v] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3274            buckets[v] = d;
3275        }
3276        i += 1;
3277    }
3278
3279    d
3280}
3281
3282fn partial_sorting_scan_left_to_right_32s_1k(
3283    t: &[SaSint],
3284    sa: &mut [SaSint],
3285    induction_bucket: &mut [SaSint],
3286    omp_block_start: SaSint,
3287    omp_block_size: SaSint,
3288) {
3289    let mut i = omp_block_start;
3290    let mut j = omp_block_start + omp_block_size - 2 * 64 - 1;
3291
3292    while i < j {
3293        let p0 = sa[i as usize];
3294        sa[i as usize] = p0 & SAINT_MAX;
3295        if p0 > 0 {
3296            sa[i as usize] = 0;
3297            let c0 = t[(p0 - 1) as usize] as usize;
3298            let pos0 = induction_bucket[c0] as usize;
3299            induction_bucket[c0] += 1;
3300            sa[pos0] = (p0 - 1)
3301                | ((usize::from(t[(p0 - 2) as usize] < t[(p0 - 1) as usize]) as SaSint)
3302                    << (SAINT_BIT - 1));
3303        }
3304
3305        let p1 = sa[(i + 1) as usize];
3306        sa[(i + 1) as usize] = p1 & SAINT_MAX;
3307        if p1 > 0 {
3308            sa[(i + 1) as usize] = 0;
3309            let c1 = t[(p1 - 1) as usize] as usize;
3310            let pos1 = induction_bucket[c1] as usize;
3311            induction_bucket[c1] += 1;
3312            sa[pos1] = (p1 - 1)
3313                | ((usize::from(t[(p1 - 2) as usize] < t[(p1 - 1) as usize]) as SaSint)
3314                    << (SAINT_BIT - 1));
3315        }
3316
3317        i += 2;
3318    }
3319
3320    j += 2 * 64 + 1;
3321    while i < j {
3322        let p = sa[i as usize];
3323        sa[i as usize] = p & SAINT_MAX;
3324        if p > 0 {
3325            sa[i as usize] = 0;
3326            let c = t[(p - 1) as usize] as usize;
3327            let pos = induction_bucket[c] as usize;
3328            induction_bucket[c] += 1;
3329            sa[pos] = (p - 1)
3330                | ((usize::from(t[(p - 2) as usize] < t[(p - 1) as usize]) as SaSint)
3331                    << (SAINT_BIT - 1));
3332        }
3333        i += 1;
3334    }
3335}
3336
3337fn partial_sorting_scan_left_to_right_32s_6k_omp(
3338    t: &[SaSint],
3339    sa: &mut [SaSint],
3340    n: SaSint,
3341    buckets: &mut [SaSint],
3342    left_suffixes_count: SaSint,
3343    mut d: SaSint,
3344    threads: SaSint,
3345    _thread_state: &mut [ThreadState],
3346) -> SaSint {
3347    let v = buckets_index4(
3348        t[(n - 1) as usize] as usize,
3349        usize::from(t[(n - 2) as usize] >= t[(n - 1) as usize]),
3350    );
3351    let pos = buckets[v] as usize;
3352    buckets[v] += 1;
3353    sa[pos] = (n - 1) | SAINT_MIN;
3354    d += 1;
3355    buckets[2 + v] = d;
3356
3357    if threads == 1 || left_suffixes_count < 65536 {
3358        d = partial_sorting_scan_left_to_right_32s_6k(t, sa, buckets, d, 0, left_suffixes_count);
3359    } else {
3360        let mut cache = vec![ThreadCache::default(); left_suffixes_count as usize];
3361        let mut block_start = 0;
3362        while block_start < left_suffixes_count {
3363            let mut block_end = block_start + threads * PER_THREAD_CACHE_SIZE as SaSint;
3364            if block_end > left_suffixes_count {
3365                block_end = left_suffixes_count;
3366            }
3367            d = partial_sorting_scan_left_to_right_32s_6k_block_omp(
3368                t,
3369                sa,
3370                buckets,
3371                d,
3372                &mut cache,
3373                block_start,
3374                block_end - block_start,
3375                threads,
3376            );
3377            block_start = block_end;
3378        }
3379    }
3380
3381    d
3382}
3383
3384fn partial_sorting_scan_left_to_right_32s_4k_omp(
3385    t: &[SaSint],
3386    sa: &mut [SaSint],
3387    n: SaSint,
3388    k: SaSint,
3389    buckets: &mut [SaSint],
3390    mut d: SaSint,
3391    threads: SaSint,
3392    _thread_state: &mut [ThreadState],
3393) -> SaSint {
3394    let k_usize = k as usize;
3395    let pos = buckets[2 * k_usize + t[(n - 1) as usize] as usize] as usize;
3396    buckets[2 * k_usize + t[(n - 1) as usize] as usize] += 1;
3397    sa[pos] = (n - 1)
3398        | ((usize::from(t[(n - 2) as usize] < t[(n - 1) as usize]) as SaSint) << (SAINT_BIT - 1))
3399        | SUFFIX_GROUP_MARKER;
3400    d += 1;
3401    buckets[buckets_index2(
3402        t[(n - 1) as usize] as usize,
3403        usize::from(t[(n - 2) as usize] < t[(n - 1) as usize]),
3404    )] = d;
3405
3406    if threads == 1 || n < 65536 {
3407        d = partial_sorting_scan_left_to_right_32s_4k(t, sa, k, buckets, d, 0, n);
3408    } else {
3409        let mut cache = vec![ThreadCache::default(); n as usize];
3410        let mut block_start = 0;
3411        while block_start < n {
3412            let mut block_end = block_start + threads * PER_THREAD_CACHE_SIZE as SaSint;
3413            if block_end > n {
3414                block_end = n;
3415            }
3416            d = partial_sorting_scan_left_to_right_32s_4k_block_omp(
3417                t,
3418                sa,
3419                k,
3420                buckets,
3421                d,
3422                &mut cache,
3423                block_start,
3424                block_end - block_start,
3425                threads,
3426            );
3427            block_start = block_end;
3428        }
3429    }
3430
3431    d
3432}
3433
3434fn partial_sorting_scan_left_to_right_32s_1k_omp(
3435    t: &[SaSint],
3436    sa: &mut [SaSint],
3437    n: SaSint,
3438    buckets: &mut [SaSint],
3439    threads: SaSint,
3440    _thread_state: &mut [ThreadState],
3441) {
3442    let pos = buckets[t[(n - 1) as usize] as usize] as usize;
3443    buckets[t[(n - 1) as usize] as usize] += 1;
3444    sa[pos] = (n - 1)
3445        | ((usize::from(t[(n - 2) as usize] < t[(n - 1) as usize]) as SaSint) << (SAINT_BIT - 1));
3446
3447    if threads == 1 || n < 65536 {
3448        partial_sorting_scan_left_to_right_32s_1k(t, sa, buckets, 0, n);
3449    } else {
3450        let mut cache = vec![ThreadCache::default(); n as usize];
3451        let mut block_start = 0;
3452        while block_start < n {
3453            let mut block_end = block_start + threads * PER_THREAD_CACHE_SIZE as SaSint;
3454            if block_end > n {
3455                block_end = n;
3456            }
3457            partial_sorting_scan_left_to_right_32s_1k_block_omp(
3458                t,
3459                sa,
3460                buckets,
3461                &mut cache,
3462                block_start,
3463                block_end - block_start,
3464                threads,
3465            );
3466            block_start = block_end;
3467        }
3468    }
3469}
3470
3471fn partial_sorting_scan_right_to_left_32s_6k(
3472    t: &[SaSint],
3473    sa: &mut [SaSint],
3474    buckets: &mut [SaSint],
3475    mut d: SaSint,
3476    omp_block_start: SaSint,
3477    omp_block_size: SaSint,
3478) -> SaSint {
3479    if omp_block_size <= 0 {
3480        return d;
3481    }
3482
3483    let mut i = omp_block_start + omp_block_size - 1;
3484    let mut j = omp_block_start + 2 * 64 + 1;
3485
3486    while i >= j {
3487        let mut p2 = sa[i as usize];
3488        d += SaSint::from(p2 < 0);
3489        p2 &= SAINT_MAX;
3490        let v2 = buckets_index4(
3491            t[(p2 - 1) as usize] as usize,
3492            usize::from(t[(p2 - 2) as usize] > t[(p2 - 1) as usize]),
3493        );
3494        buckets[v2] -= 1;
3495        sa[buckets[v2] as usize] =
3496            (p2 - 1) | (((buckets[2 + v2] != d) as SaSint) << (SAINT_BIT - 1));
3497        buckets[2 + v2] = d;
3498
3499        let mut p3 = sa[(i - 1) as usize];
3500        d += SaSint::from(p3 < 0);
3501        p3 &= SAINT_MAX;
3502        let v3 = buckets_index4(
3503            t[(p3 - 1) as usize] as usize,
3504            usize::from(t[(p3 - 2) as usize] > t[(p3 - 1) as usize]),
3505        );
3506        buckets[v3] -= 1;
3507        sa[buckets[v3] as usize] =
3508            (p3 - 1) | (((buckets[2 + v3] != d) as SaSint) << (SAINT_BIT - 1));
3509        buckets[2 + v3] = d;
3510
3511        i -= 2;
3512    }
3513
3514    j -= 2 * 64 + 1;
3515    while i >= j {
3516        let mut p = sa[i as usize];
3517        d += SaSint::from(p < 0);
3518        p &= SAINT_MAX;
3519        let v = buckets_index4(
3520            t[(p - 1) as usize] as usize,
3521            usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
3522        );
3523        buckets[v] -= 1;
3524        sa[buckets[v] as usize] = (p - 1) | (((buckets[2 + v] != d) as SaSint) << (SAINT_BIT - 1));
3525        buckets[2 + v] = d;
3526        i -= 1;
3527    }
3528
3529    d
3530}
3531
3532fn partial_sorting_scan_right_to_left_32s_4k(
3533    t: &[SaSint],
3534    sa: &mut [SaSint],
3535    k: SaSint,
3536    buckets: &mut [SaSint],
3537    mut d: SaSint,
3538    omp_block_start: SaSint,
3539    omp_block_size: SaSint,
3540) -> SaSint {
3541    if omp_block_size <= 0 {
3542        return d;
3543    }
3544
3545    let k = k as usize;
3546    let mut i = omp_block_start + omp_block_size - 1;
3547    let mut j = omp_block_start + 2 * 64 + 1;
3548
3549    while i >= j {
3550        let mut p0 = sa[i as usize];
3551        if p0 > 0 {
3552            sa[i as usize] = 0;
3553            d += p0 >> (SUFFIX_GROUP_BIT - 1);
3554            p0 &= !SUFFIX_GROUP_MARKER;
3555            let v0 = buckets_index2(
3556                t[(p0 - 1) as usize] as usize,
3557                usize::from(t[(p0 - 2) as usize] > t[(p0 - 1) as usize]),
3558            );
3559            let c0 = t[(p0 - 1) as usize] as usize;
3560            buckets[3 * k + c0] -= 1;
3561            sa[buckets[3 * k + c0] as usize] = (p0 - 1)
3562                | ((usize::from(t[(p0 - 2) as usize] > t[(p0 - 1) as usize]) as SaSint)
3563                    << (SAINT_BIT - 1))
3564                | (((buckets[v0] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3565            buckets[v0] = d;
3566        }
3567
3568        let mut p1 = sa[(i - 1) as usize];
3569        if p1 > 0 {
3570            sa[(i - 1) as usize] = 0;
3571            d += p1 >> (SUFFIX_GROUP_BIT - 1);
3572            p1 &= !SUFFIX_GROUP_MARKER;
3573            let v1 = buckets_index2(
3574                t[(p1 - 1) as usize] as usize,
3575                usize::from(t[(p1 - 2) as usize] > t[(p1 - 1) as usize]),
3576            );
3577            let c1 = t[(p1 - 1) as usize] as usize;
3578            buckets[3 * k + c1] -= 1;
3579            sa[buckets[3 * k + c1] as usize] = (p1 - 1)
3580                | ((usize::from(t[(p1 - 2) as usize] > t[(p1 - 1) as usize]) as SaSint)
3581                    << (SAINT_BIT - 1))
3582                | (((buckets[v1] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3583            buckets[v1] = d;
3584        }
3585
3586        i -= 2;
3587    }
3588
3589    j -= 2 * 64 + 1;
3590    while i >= j {
3591        let mut p = sa[i as usize];
3592        if p > 0 {
3593            sa[i as usize] = 0;
3594            d += p >> (SUFFIX_GROUP_BIT - 1);
3595            p &= !SUFFIX_GROUP_MARKER;
3596            let v = buckets_index2(
3597                t[(p - 1) as usize] as usize,
3598                usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
3599            );
3600            let c = t[(p - 1) as usize] as usize;
3601            buckets[3 * k + c] -= 1;
3602            sa[buckets[3 * k + c] as usize] = (p - 1)
3603                | ((usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]) as SaSint)
3604                    << (SAINT_BIT - 1))
3605                | (((buckets[v] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3606            buckets[v] = d;
3607        }
3608        i -= 1;
3609    }
3610
3611    d
3612}
3613
3614fn partial_sorting_scan_right_to_left_32s_1k(
3615    t: &[SaSint],
3616    sa: &mut [SaSint],
3617    induction_bucket: &mut [SaSint],
3618    omp_block_start: SaSint,
3619    omp_block_size: SaSint,
3620) {
3621    if omp_block_size <= 0 {
3622        return;
3623    }
3624
3625    let mut i = omp_block_start + omp_block_size - 1;
3626    let mut j = omp_block_start + 2 * 64 + 1;
3627
3628    while i >= j {
3629        let p0 = sa[i as usize];
3630        if p0 > 0 {
3631            sa[i as usize] = 0;
3632            let c0 = t[(p0 - 1) as usize] as usize;
3633            induction_bucket[c0] -= 1;
3634            sa[induction_bucket[c0] as usize] = (p0 - 1)
3635                | ((usize::from(t[(p0 - 2) as usize] > t[(p0 - 1) as usize]) as SaSint)
3636                    << (SAINT_BIT - 1));
3637        }
3638
3639        let p1 = sa[(i - 1) as usize];
3640        if p1 > 0 {
3641            sa[(i - 1) as usize] = 0;
3642            let c1 = t[(p1 - 1) as usize] as usize;
3643            induction_bucket[c1] -= 1;
3644            sa[induction_bucket[c1] as usize] = (p1 - 1)
3645                | ((usize::from(t[(p1 - 2) as usize] > t[(p1 - 1) as usize]) as SaSint)
3646                    << (SAINT_BIT - 1));
3647        }
3648
3649        i -= 2;
3650    }
3651
3652    j -= 2 * 64 + 1;
3653    while i >= j {
3654        let p = sa[i as usize];
3655        if p > 0 {
3656            sa[i as usize] = 0;
3657            let c = t[(p - 1) as usize] as usize;
3658            induction_bucket[c] -= 1;
3659            sa[induction_bucket[c] as usize] = (p - 1)
3660                | ((usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]) as SaSint)
3661                    << (SAINT_BIT - 1));
3662        }
3663        i -= 1;
3664    }
3665}
3666
3667fn partial_sorting_scan_right_to_left_32s_6k_omp(
3668    t: &[SaSint],
3669    sa: &mut [SaSint],
3670    n: SaSint,
3671    buckets: &mut [SaSint],
3672    first_lms_suffix: SaSint,
3673    left_suffixes_count: SaSint,
3674    mut d: SaSint,
3675    threads: SaSint,
3676    _thread_state: &mut [ThreadState],
3677) -> SaSint {
3678    let scan_start = left_suffixes_count + 1;
3679    let scan_end = n - first_lms_suffix;
3680
3681    if threads == 1 || scan_end - scan_start < 65536 {
3682        d = partial_sorting_scan_right_to_left_32s_6k(
3683            t,
3684            sa,
3685            buckets,
3686            d,
3687            scan_start,
3688            scan_end - scan_start,
3689        );
3690    } else {
3691        let mut cache = vec![ThreadCache::default(); (scan_end - scan_start) as usize];
3692        let mut block_start = scan_end;
3693        while block_start > scan_start {
3694            let block_size =
3695                (block_start - scan_start).min(threads * PER_THREAD_CACHE_SIZE as SaSint);
3696            block_start -= block_size;
3697            d = partial_sorting_scan_right_to_left_32s_6k_block_omp(
3698                t,
3699                sa,
3700                buckets,
3701                d,
3702                &mut cache,
3703                block_start,
3704                block_size,
3705                threads,
3706            );
3707        }
3708    }
3709
3710    d
3711}
3712
3713fn partial_sorting_scan_right_to_left_32s_4k_omp(
3714    t: &[SaSint],
3715    sa: &mut [SaSint],
3716    n: SaSint,
3717    k: SaSint,
3718    buckets: &mut [SaSint],
3719    mut d: SaSint,
3720    threads: SaSint,
3721    _thread_state: &mut [ThreadState],
3722) -> SaSint {
3723    if threads == 1 || n < 65536 {
3724        d = partial_sorting_scan_right_to_left_32s_4k(t, sa, k, buckets, d, 0, n);
3725    } else {
3726        let mut cache = vec![ThreadCache::default(); n as usize];
3727        let mut block_start = n;
3728        while block_start > 0 {
3729            let block_size = block_start.min(threads * PER_THREAD_CACHE_SIZE as SaSint);
3730            block_start -= block_size;
3731            d = partial_sorting_scan_right_to_left_32s_4k_block_omp(
3732                t,
3733                sa,
3734                k,
3735                buckets,
3736                d,
3737                &mut cache,
3738                block_start,
3739                block_size,
3740                threads,
3741            );
3742        }
3743    }
3744
3745    d
3746}
3747
3748fn partial_sorting_scan_right_to_left_32s_1k_omp(
3749    t: &[SaSint],
3750    sa: &mut [SaSint],
3751    n: SaSint,
3752    buckets: &mut [SaSint],
3753    threads: SaSint,
3754    _thread_state: &mut [ThreadState],
3755) {
3756    if threads == 1 || n < 65536 {
3757        partial_sorting_scan_right_to_left_32s_1k(t, sa, buckets, 0, n);
3758    } else {
3759        let mut cache = vec![ThreadCache::default(); n as usize];
3760        let mut block_start = n;
3761        while block_start > 0 {
3762            let block_size = block_start.min(threads * PER_THREAD_CACHE_SIZE as SaSint);
3763            block_start -= block_size;
3764            partial_sorting_scan_right_to_left_32s_1k_block_omp(
3765                t,
3766                sa,
3767                buckets,
3768                &mut cache,
3769                block_start,
3770                block_size,
3771                threads,
3772            );
3773        }
3774    }
3775}
3776
3777fn partial_sorting_scan_left_to_right_32s_6k_block_gather(
3778    t: &[SaSint],
3779    sa: &mut [SaSint],
3780    cache: &mut [ThreadCache],
3781    omp_block_start: SaSint,
3782    omp_block_size: SaSint,
3783) {
3784    let mut i = omp_block_start;
3785    let mut j = omp_block_start + omp_block_size - 64 - 1;
3786
3787    while i < j {
3788        let p0 = sa[i as usize];
3789        cache[i as usize].index = p0;
3790        let p0 = p0 & SAINT_MAX;
3791        cache[i as usize].symbol = if p0 != 0 {
3792            buckets_index4(
3793                t[(p0 - 1) as usize] as usize,
3794                usize::from(t[(p0 - 2) as usize] >= t[(p0 - 1) as usize]),
3795            ) as SaSint
3796        } else {
3797            0
3798        };
3799
3800        let p1 = sa[(i + 1) as usize];
3801        cache[(i + 1) as usize].index = p1;
3802        let p1 = p1 & SAINT_MAX;
3803        cache[(i + 1) as usize].symbol = if p1 != 0 {
3804            buckets_index4(
3805                t[(p1 - 1) as usize] as usize,
3806                usize::from(t[(p1 - 2) as usize] >= t[(p1 - 1) as usize]),
3807            ) as SaSint
3808        } else {
3809            0
3810        };
3811
3812        i += 2;
3813    }
3814
3815    j += 64 + 1;
3816    while i < j {
3817        let p = sa[i as usize];
3818        cache[i as usize].index = p;
3819        let p = p & SAINT_MAX;
3820        cache[i as usize].symbol = if p != 0 {
3821            buckets_index4(
3822                t[(p - 1) as usize] as usize,
3823                usize::from(t[(p - 2) as usize] >= t[(p - 1) as usize]),
3824            ) as SaSint
3825        } else {
3826            0
3827        };
3828        i += 1;
3829    }
3830}
3831
3832fn partial_sorting_scan_left_to_right_32s_4k_block_gather(
3833    t: &[SaSint],
3834    sa: &mut [SaSint],
3835    cache: &mut [ThreadCache],
3836    omp_block_start: SaSint,
3837    omp_block_size: SaSint,
3838) {
3839    let mut i = omp_block_start;
3840    let mut j = omp_block_start + omp_block_size - 64 - 1;
3841
3842    while i < j {
3843        let mut symbol0 = SAINT_MIN;
3844        let mut p0 = sa[i as usize];
3845        if p0 > 0 {
3846            cache[i as usize].index = p0;
3847            p0 &= !SUFFIX_GROUP_MARKER;
3848            symbol0 = buckets_index2(
3849                t[(p0 - 1) as usize] as usize,
3850                usize::from(t[(p0 - 2) as usize] < t[(p0 - 1) as usize]),
3851            ) as SaSint;
3852            p0 = 0;
3853        }
3854        cache[i as usize].symbol = symbol0;
3855        sa[i as usize] = p0 & SAINT_MAX;
3856
3857        let mut symbol1 = SAINT_MIN;
3858        let mut p1 = sa[(i + 1) as usize];
3859        if p1 > 0 {
3860            cache[(i + 1) as usize].index = p1;
3861            p1 &= !SUFFIX_GROUP_MARKER;
3862            symbol1 = buckets_index2(
3863                t[(p1 - 1) as usize] as usize,
3864                usize::from(t[(p1 - 2) as usize] < t[(p1 - 1) as usize]),
3865            ) as SaSint;
3866            p1 = 0;
3867        }
3868        cache[(i + 1) as usize].symbol = symbol1;
3869        sa[(i + 1) as usize] = p1 & SAINT_MAX;
3870
3871        i += 2;
3872    }
3873
3874    j += 64 + 1;
3875    while i < j {
3876        let mut symbol = SAINT_MIN;
3877        let mut p = sa[i as usize];
3878        if p > 0 {
3879            cache[i as usize].index = p;
3880            p &= !SUFFIX_GROUP_MARKER;
3881            symbol = buckets_index2(
3882                t[(p - 1) as usize] as usize,
3883                usize::from(t[(p - 2) as usize] < t[(p - 1) as usize]),
3884            ) as SaSint;
3885            p = 0;
3886        }
3887        cache[i as usize].symbol = symbol;
3888        sa[i as usize] = p & SAINT_MAX;
3889        i += 1;
3890    }
3891}
3892
3893fn partial_sorting_scan_left_to_right_32s_1k_block_gather(
3894    t: &[SaSint],
3895    sa: &mut [SaSint],
3896    cache: &mut [ThreadCache],
3897    omp_block_start: SaSint,
3898    omp_block_size: SaSint,
3899) {
3900    let mut i = omp_block_start;
3901    let mut j = omp_block_start + omp_block_size - 64 - 1;
3902
3903    while i < j {
3904        let mut symbol0 = SAINT_MIN;
3905        let mut p0 = sa[i as usize];
3906        if p0 > 0 {
3907            cache[i as usize].index = (p0 - 1)
3908                | ((usize::from(t[(p0 - 2) as usize] < t[(p0 - 1) as usize]) as SaSint)
3909                    << (SAINT_BIT - 1));
3910            symbol0 = t[(p0 - 1) as usize];
3911            p0 = 0;
3912        }
3913        cache[i as usize].symbol = symbol0;
3914        sa[i as usize] = p0 & SAINT_MAX;
3915
3916        let mut symbol1 = SAINT_MIN;
3917        let mut p1 = sa[(i + 1) as usize];
3918        if p1 > 0 {
3919            cache[(i + 1) as usize].index = (p1 - 1)
3920                | ((usize::from(t[(p1 - 2) as usize] < t[(p1 - 1) as usize]) as SaSint)
3921                    << (SAINT_BIT - 1));
3922            symbol1 = t[(p1 - 1) as usize];
3923            p1 = 0;
3924        }
3925        cache[(i + 1) as usize].symbol = symbol1;
3926        sa[(i + 1) as usize] = p1 & SAINT_MAX;
3927
3928        i += 2;
3929    }
3930
3931    j += 64 + 1;
3932    while i < j {
3933        let mut symbol = SAINT_MIN;
3934        let mut p = sa[i as usize];
3935        if p > 0 {
3936            cache[i as usize].index = (p - 1)
3937                | ((usize::from(t[(p - 2) as usize] < t[(p - 1) as usize]) as SaSint)
3938                    << (SAINT_BIT - 1));
3939            symbol = t[(p - 1) as usize];
3940            p = 0;
3941        }
3942        cache[i as usize].symbol = symbol;
3943        sa[i as usize] = p & SAINT_MAX;
3944        i += 1;
3945    }
3946}
3947
3948fn partial_sorting_scan_right_to_left_32s_6k_block_gather(
3949    t: &[SaSint],
3950    sa: &mut [SaSint],
3951    cache: &mut [ThreadCache],
3952    omp_block_start: SaSint,
3953    omp_block_size: SaSint,
3954) {
3955    let mut i = omp_block_start;
3956    let mut j = omp_block_start + omp_block_size - 64 - 1;
3957
3958    while i < j {
3959        let p0 = sa[i as usize];
3960        cache[i as usize].index = p0;
3961        let p0 = p0 & SAINT_MAX;
3962        cache[i as usize].symbol = if p0 != 0 {
3963            buckets_index4(
3964                t[(p0 - 1) as usize] as usize,
3965                usize::from(t[(p0 - 2) as usize] > t[(p0 - 1) as usize]),
3966            ) as SaSint
3967        } else {
3968            0
3969        };
3970
3971        let p1 = sa[(i + 1) as usize];
3972        cache[(i + 1) as usize].index = p1;
3973        let p1 = p1 & SAINT_MAX;
3974        cache[(i + 1) as usize].symbol = if p1 != 0 {
3975            buckets_index4(
3976                t[(p1 - 1) as usize] as usize,
3977                usize::from(t[(p1 - 2) as usize] > t[(p1 - 1) as usize]),
3978            ) as SaSint
3979        } else {
3980            0
3981        };
3982
3983        i += 2;
3984    }
3985
3986    j += 64 + 1;
3987    while i < j {
3988        let p = sa[i as usize];
3989        cache[i as usize].index = p;
3990        let p = p & SAINT_MAX;
3991        cache[i as usize].symbol = if p != 0 {
3992            buckets_index4(
3993                t[(p - 1) as usize] as usize,
3994                usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
3995            ) as SaSint
3996        } else {
3997            0
3998        };
3999        i += 1;
4000    }
4001}
4002
4003fn partial_sorting_scan_right_to_left_32s_4k_block_gather(
4004    t: &[SaSint],
4005    sa: &mut [SaSint],
4006    cache: &mut [ThreadCache],
4007    omp_block_start: SaSint,
4008    omp_block_size: SaSint,
4009) {
4010    let mut i = omp_block_start;
4011    let mut j = omp_block_start + omp_block_size - 64 - 1;
4012
4013    while i < j {
4014        let mut symbol0 = SAINT_MIN;
4015        let mut p0 = sa[i as usize];
4016        if p0 > 0 {
4017            sa[i as usize] = 0;
4018            cache[i as usize].index = p0;
4019            p0 &= !SUFFIX_GROUP_MARKER;
4020            symbol0 = buckets_index2(
4021                t[(p0 - 1) as usize] as usize,
4022                usize::from(t[(p0 - 2) as usize] > t[(p0 - 1) as usize]),
4023            ) as SaSint;
4024        }
4025        cache[i as usize].symbol = symbol0;
4026
4027        let mut symbol1 = SAINT_MIN;
4028        let mut p1 = sa[(i + 1) as usize];
4029        if p1 > 0 {
4030            sa[(i + 1) as usize] = 0;
4031            cache[(i + 1) as usize].index = p1;
4032            p1 &= !SUFFIX_GROUP_MARKER;
4033            symbol1 = buckets_index2(
4034                t[(p1 - 1) as usize] as usize,
4035                usize::from(t[(p1 - 2) as usize] > t[(p1 - 1) as usize]),
4036            ) as SaSint;
4037        }
4038        cache[(i + 1) as usize].symbol = symbol1;
4039
4040        i += 2;
4041    }
4042
4043    j += 64 + 1;
4044    while i < j {
4045        let mut symbol = SAINT_MIN;
4046        let mut p = sa[i as usize];
4047        if p > 0 {
4048            sa[i as usize] = 0;
4049            cache[i as usize].index = p;
4050            p &= !SUFFIX_GROUP_MARKER;
4051            symbol = buckets_index2(
4052                t[(p - 1) as usize] as usize,
4053                usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
4054            ) as SaSint;
4055        }
4056        cache[i as usize].symbol = symbol;
4057        i += 1;
4058    }
4059}
4060
4061fn partial_sorting_scan_right_to_left_32s_1k_block_gather(
4062    t: &[SaSint],
4063    sa: &mut [SaSint],
4064    cache: &mut [ThreadCache],
4065    omp_block_start: SaSint,
4066    omp_block_size: SaSint,
4067) {
4068    let mut i = omp_block_start;
4069    let mut j = omp_block_start + omp_block_size - 64 - 1;
4070
4071    while i < j {
4072        let mut symbol0 = SAINT_MIN;
4073        let p0 = sa[i as usize];
4074        if p0 > 0 {
4075            sa[i as usize] = 0;
4076            cache[i as usize].index = (p0 - 1)
4077                | ((usize::from(t[(p0 - 2) as usize] > t[(p0 - 1) as usize]) as SaSint)
4078                    << (SAINT_BIT - 1));
4079            symbol0 = t[(p0 - 1) as usize];
4080        }
4081        cache[i as usize].symbol = symbol0;
4082
4083        let mut symbol1 = SAINT_MIN;
4084        let p1 = sa[(i + 1) as usize];
4085        if p1 > 0 {
4086            sa[(i + 1) as usize] = 0;
4087            cache[(i + 1) as usize].index = (p1 - 1)
4088                | ((usize::from(t[(p1 - 2) as usize] > t[(p1 - 1) as usize]) as SaSint)
4089                    << (SAINT_BIT - 1));
4090            symbol1 = t[(p1 - 1) as usize];
4091        }
4092        cache[(i + 1) as usize].symbol = symbol1;
4093
4094        i += 2;
4095    }
4096
4097    j += 64 + 1;
4098    while i < j {
4099        let mut symbol = SAINT_MIN;
4100        let p = sa[i as usize];
4101        if p > 0 {
4102            sa[i as usize] = 0;
4103            cache[i as usize].index = (p - 1)
4104                | ((usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]) as SaSint)
4105                    << (SAINT_BIT - 1));
4106            symbol = t[(p - 1) as usize];
4107        }
4108        cache[i as usize].symbol = symbol;
4109        i += 1;
4110    }
4111}
4112
4113fn partial_sorting_scan_left_to_right_32s_6k_block_sort(
4114    t: &[SaSint],
4115    buckets: &mut [SaSint],
4116    mut d: SaSint,
4117    cache: &mut [ThreadCache],
4118    omp_block_start: SaSint,
4119    omp_block_size: SaSint,
4120) -> SaSint {
4121    let mut i = omp_block_start;
4122    let omp_block_end = omp_block_start + omp_block_size;
4123    let mut j = omp_block_end - 64 - 1;
4124
4125    while i < j {
4126        let v0 = cache[i as usize].symbol as usize;
4127        let p0 = cache[i as usize].index;
4128        d += SaSint::from(p0 < 0);
4129        cache[i as usize].symbol = buckets[v0];
4130        buckets[v0] += 1;
4131        cache[i as usize].index =
4132            (p0 - 1) | (((buckets[2 + v0] != d) as SaSint) << (SAINT_BIT - 1));
4133        buckets[2 + v0] = d;
4134        if cache[i as usize].symbol < omp_block_end {
4135            let s = cache[i as usize].symbol as usize;
4136            let q = cache[i as usize].index & SAINT_MAX;
4137            cache[s].index = cache[i as usize].index;
4138            cache[s].symbol = buckets_index4(
4139                t[(q - 1) as usize] as usize,
4140                usize::from(t[(q - 2) as usize] >= t[(q - 1) as usize]),
4141            ) as SaSint;
4142        }
4143
4144        let v1 = cache[(i + 1) as usize].symbol as usize;
4145        let p1 = cache[(i + 1) as usize].index;
4146        d += SaSint::from(p1 < 0);
4147        cache[(i + 1) as usize].symbol = buckets[v1];
4148        buckets[v1] += 1;
4149        cache[(i + 1) as usize].index =
4150            (p1 - 1) | (((buckets[2 + v1] != d) as SaSint) << (SAINT_BIT - 1));
4151        buckets[2 + v1] = d;
4152        if cache[(i + 1) as usize].symbol < omp_block_end {
4153            let s = cache[(i + 1) as usize].symbol as usize;
4154            let q = cache[(i + 1) as usize].index & SAINT_MAX;
4155            cache[s].index = cache[(i + 1) as usize].index;
4156            cache[s].symbol = buckets_index4(
4157                t[(q - 1) as usize] as usize,
4158                usize::from(t[(q - 2) as usize] >= t[(q - 1) as usize]),
4159            ) as SaSint;
4160        }
4161
4162        i += 2;
4163    }
4164
4165    j += 64 + 1;
4166    while i < j {
4167        let v = cache[i as usize].symbol as usize;
4168        let p = cache[i as usize].index;
4169        d += SaSint::from(p < 0);
4170        cache[i as usize].symbol = buckets[v];
4171        buckets[v] += 1;
4172        cache[i as usize].index = (p - 1) | (((buckets[2 + v] != d) as SaSint) << (SAINT_BIT - 1));
4173        buckets[2 + v] = d;
4174        if cache[i as usize].symbol < omp_block_end {
4175            let s = cache[i as usize].symbol as usize;
4176            let q = cache[i as usize].index & SAINT_MAX;
4177            cache[s].index = cache[i as usize].index;
4178            cache[s].symbol = buckets_index4(
4179                t[(q - 1) as usize] as usize,
4180                usize::from(t[(q - 2) as usize] >= t[(q - 1) as usize]),
4181            ) as SaSint;
4182        }
4183        i += 1;
4184    }
4185
4186    d
4187}
4188
4189fn partial_sorting_scan_left_to_right_32s_4k_block_sort(
4190    t: &[SaSint],
4191    k: SaSint,
4192    buckets: &mut [SaSint],
4193    mut d: SaSint,
4194    cache: &mut [ThreadCache],
4195    omp_block_start: SaSint,
4196    omp_block_size: SaSint,
4197) -> SaSint {
4198    let k = k as usize;
4199    let mut i = omp_block_start;
4200    let omp_block_end = omp_block_start + omp_block_size;
4201    let mut j = omp_block_end - 64 - 1;
4202
4203    while i < j {
4204        for current in [i, i + 1] {
4205            let v = cache[current as usize].symbol;
4206            if v >= 0 {
4207                let p = cache[current as usize].index;
4208                d += p >> (SUFFIX_GROUP_BIT - 1);
4209                let bucket_index = (v >> 1) as usize;
4210                let v_usize = v as usize;
4211                cache[current as usize].symbol = buckets[2 * k + bucket_index];
4212                buckets[2 * k + bucket_index] += 1;
4213                cache[current as usize].index = (p - 1)
4214                    | ((v & 1) << (SAINT_BIT - 1))
4215                    | (((buckets[v_usize] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
4216                buckets[v_usize] = d;
4217                if cache[current as usize].symbol < omp_block_end {
4218                    let ni = cache[current as usize].symbol as usize;
4219                    let mut np = cache[current as usize].index;
4220                    if np > 0 {
4221                        cache[ni].index = np;
4222                        np &= !SUFFIX_GROUP_MARKER;
4223                        cache[ni].symbol = buckets_index2(
4224                            t[(np - 1) as usize] as usize,
4225                            usize::from(t[(np - 2) as usize] < t[(np - 1) as usize]),
4226                        ) as SaSint;
4227                        np = 0;
4228                    }
4229                    cache[current as usize].index = np & SAINT_MAX;
4230                }
4231            }
4232        }
4233        i += 2;
4234    }
4235
4236    j += 64 + 1;
4237    while i < j {
4238        let v = cache[i as usize].symbol;
4239        if v >= 0 {
4240            let p = cache[i as usize].index;
4241            d += p >> (SUFFIX_GROUP_BIT - 1);
4242            let bucket_index = (v >> 1) as usize;
4243            let v_usize = v as usize;
4244            cache[i as usize].symbol = buckets[2 * k + bucket_index];
4245            buckets[2 * k + bucket_index] += 1;
4246            cache[i as usize].index = (p - 1)
4247                | ((v & 1) << (SAINT_BIT - 1))
4248                | (((buckets[v_usize] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
4249            buckets[v_usize] = d;
4250            if cache[i as usize].symbol < omp_block_end {
4251                let ni = cache[i as usize].symbol as usize;
4252                let mut np = cache[i as usize].index;
4253                if np > 0 {
4254                    cache[ni].index = np;
4255                    np &= !SUFFIX_GROUP_MARKER;
4256                    cache[ni].symbol = buckets_index2(
4257                        t[(np - 1) as usize] as usize,
4258                        usize::from(t[(np - 2) as usize] < t[(np - 1) as usize]),
4259                    ) as SaSint;
4260                    np = 0;
4261                }
4262                cache[i as usize].index = np & SAINT_MAX;
4263            }
4264        }
4265        i += 1;
4266    }
4267
4268    d
4269}
4270
4271fn partial_sorting_scan_left_to_right_32s_1k_block_sort(
4272    t: &[SaSint],
4273    induction_bucket: &mut [SaSint],
4274    cache: &mut [ThreadCache],
4275    omp_block_start: SaSint,
4276    omp_block_size: SaSint,
4277) {
4278    let mut i = omp_block_start;
4279    let omp_block_end = omp_block_start + omp_block_size;
4280    let mut j = omp_block_end - 64 - 1;
4281
4282    while i < j {
4283        for current in [i, i + 1] {
4284            let v = cache[current as usize].symbol;
4285            if v >= 0 {
4286                cache[current as usize].symbol = induction_bucket[v as usize];
4287                induction_bucket[v as usize] += 1;
4288                if cache[current as usize].symbol < omp_block_end {
4289                    let ni = cache[current as usize].symbol as usize;
4290                    let mut np = cache[current as usize].index;
4291                    if np > 0 {
4292                        cache[ni].index = (np - 1)
4293                            | ((usize::from(t[(np - 2) as usize] < t[(np - 1) as usize])
4294                                as SaSint)
4295                                << (SAINT_BIT - 1));
4296                        cache[ni].symbol = t[(np - 1) as usize];
4297                        np = 0;
4298                    }
4299                    cache[current as usize].index = np & SAINT_MAX;
4300                }
4301            }
4302        }
4303        i += 2;
4304    }
4305
4306    j = omp_block_end;
4307    while i < j {
4308        let v = cache[i as usize].symbol;
4309        if v >= 0 {
4310            cache[i as usize].symbol = induction_bucket[v as usize];
4311            induction_bucket[v as usize] += 1;
4312            if cache[i as usize].symbol < omp_block_end {
4313                let ni = cache[i as usize].symbol as usize;
4314                let mut np = cache[i as usize].index;
4315                if np > 0 {
4316                    cache[ni].index = (np - 1)
4317                        | ((usize::from(t[(np - 2) as usize] < t[(np - 1) as usize]) as SaSint)
4318                            << (SAINT_BIT - 1));
4319                    cache[ni].symbol = t[(np - 1) as usize];
4320                    np = 0;
4321                }
4322                cache[i as usize].index = np & SAINT_MAX;
4323            }
4324        }
4325        i += 1;
4326    }
4327}
4328
4329fn partial_sorting_scan_right_to_left_32s_6k_block_sort(
4330    t: &[SaSint],
4331    buckets: &mut [SaSint],
4332    mut d: SaSint,
4333    cache: &mut [ThreadCache],
4334    omp_block_start: SaSint,
4335    omp_block_size: SaSint,
4336) -> SaSint {
4337    let mut i = omp_block_start + omp_block_size - 1;
4338    let mut j = omp_block_start + 64 + 1;
4339
4340    while i >= j {
4341        let v0 = cache[i as usize].symbol as usize;
4342        let p0 = cache[i as usize].index;
4343        d += SaSint::from(p0 < 0);
4344        buckets[v0] -= 1;
4345        cache[i as usize].symbol = buckets[v0];
4346        cache[i as usize].index =
4347            (p0 - 1) | (((buckets[2 + v0] != d) as SaSint) << (SAINT_BIT - 1));
4348        buckets[2 + v0] = d;
4349        if cache[i as usize].symbol >= omp_block_start {
4350            let s = cache[i as usize].symbol as usize;
4351            let q = cache[i as usize].index & SAINT_MAX;
4352            cache[s].index = cache[i as usize].index;
4353            cache[s].symbol = buckets_index4(
4354                t[(q - 1) as usize] as usize,
4355                usize::from(t[(q - 2) as usize] > t[(q - 1) as usize]),
4356            ) as SaSint;
4357        }
4358
4359        let v1 = cache[(i - 1) as usize].symbol as usize;
4360        let p1 = cache[(i - 1) as usize].index;
4361        d += SaSint::from(p1 < 0);
4362        buckets[v1] -= 1;
4363        cache[(i - 1) as usize].symbol = buckets[v1];
4364        cache[(i - 1) as usize].index =
4365            (p1 - 1) | (((buckets[2 + v1] != d) as SaSint) << (SAINT_BIT - 1));
4366        buckets[2 + v1] = d;
4367        if cache[(i - 1) as usize].symbol >= omp_block_start {
4368            let s = cache[(i - 1) as usize].symbol as usize;
4369            let q = cache[(i - 1) as usize].index & SAINT_MAX;
4370            cache[s].index = cache[(i - 1) as usize].index;
4371            cache[s].symbol = buckets_index4(
4372                t[(q - 1) as usize] as usize,
4373                usize::from(t[(q - 2) as usize] > t[(q - 1) as usize]),
4374            ) as SaSint;
4375        }
4376
4377        i -= 2;
4378    }
4379
4380    j -= 64 + 1;
4381    while i >= j {
4382        let v = cache[i as usize].symbol as usize;
4383        let p = cache[i as usize].index;
4384        d += SaSint::from(p < 0);
4385        buckets[v] -= 1;
4386        cache[i as usize].symbol = buckets[v];
4387        cache[i as usize].index = (p - 1) | (((buckets[2 + v] != d) as SaSint) << (SAINT_BIT - 1));
4388        buckets[2 + v] = d;
4389        if cache[i as usize].symbol >= omp_block_start {
4390            let s = cache[i as usize].symbol as usize;
4391            let q = cache[i as usize].index & SAINT_MAX;
4392            cache[s].index = cache[i as usize].index;
4393            cache[s].symbol = buckets_index4(
4394                t[(q - 1) as usize] as usize,
4395                usize::from(t[(q - 2) as usize] > t[(q - 1) as usize]),
4396            ) as SaSint;
4397        }
4398        i -= 1;
4399    }
4400
4401    d
4402}
4403
4404fn partial_sorting_scan_right_to_left_32s_4k_block_sort(
4405    t: &[SaSint],
4406    k: SaSint,
4407    buckets: &mut [SaSint],
4408    mut d: SaSint,
4409    cache: &mut [ThreadCache],
4410    omp_block_start: SaSint,
4411    omp_block_size: SaSint,
4412) -> SaSint {
4413    let k = k as usize;
4414    let mut i = omp_block_start + omp_block_size - 1;
4415    let mut j = omp_block_start + 64 + 1;
4416
4417    while i >= j {
4418        for current in [i, i - 1] {
4419            let v = cache[current as usize].symbol;
4420            if v >= 0 {
4421                let p = cache[current as usize].index;
4422                d += p >> (SUFFIX_GROUP_BIT - 1);
4423                let bucket_index = (v >> 1) as usize;
4424                let v_usize = v as usize;
4425                buckets[3 * k + bucket_index] -= 1;
4426                cache[current as usize].symbol = buckets[3 * k + bucket_index];
4427                cache[current as usize].index = (p - 1)
4428                    | ((v & 1) << (SAINT_BIT - 1))
4429                    | (((buckets[v_usize] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
4430                buckets[v_usize] = d;
4431                if cache[current as usize].symbol >= omp_block_start {
4432                    let ni = cache[current as usize].symbol as usize;
4433                    let mut np = cache[current as usize].index;
4434                    if np > 0 {
4435                        cache[current as usize].index = 0;
4436                        cache[ni].index = np;
4437                        np &= !SUFFIX_GROUP_MARKER;
4438                        cache[ni].symbol = buckets_index2(
4439                            t[(np - 1) as usize] as usize,
4440                            usize::from(t[(np - 2) as usize] > t[(np - 1) as usize]),
4441                        ) as SaSint;
4442                    }
4443                }
4444            }
4445        }
4446        i -= 2;
4447    }
4448
4449    j -= 64 + 1;
4450    while i >= j {
4451        let v = cache[i as usize].symbol;
4452        if v >= 0 {
4453            let p = cache[i as usize].index;
4454            d += p >> (SUFFIX_GROUP_BIT - 1);
4455            let bucket_index = (v >> 1) as usize;
4456            let v_usize = v as usize;
4457            buckets[3 * k + bucket_index] -= 1;
4458            cache[i as usize].symbol = buckets[3 * k + bucket_index];
4459            cache[i as usize].index = (p - 1)
4460                | ((v & 1) << (SAINT_BIT - 1))
4461                | (((buckets[v_usize] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
4462            buckets[v_usize] = d;
4463            if cache[i as usize].symbol >= omp_block_start {
4464                let ni = cache[i as usize].symbol as usize;
4465                let mut np = cache[i as usize].index;
4466                if np > 0 {
4467                    cache[i as usize].index = 0;
4468                    cache[ni].index = np;
4469                    np &= !SUFFIX_GROUP_MARKER;
4470                    cache[ni].symbol = buckets_index2(
4471                        t[(np - 1) as usize] as usize,
4472                        usize::from(t[(np - 2) as usize] > t[(np - 1) as usize]),
4473                    ) as SaSint;
4474                }
4475            }
4476        }
4477        i -= 1;
4478    }
4479
4480    d
4481}
4482
4483fn partial_sorting_scan_right_to_left_32s_1k_block_sort(
4484    t: &[SaSint],
4485    induction_bucket: &mut [SaSint],
4486    cache: &mut [ThreadCache],
4487    omp_block_start: SaSint,
4488    omp_block_size: SaSint,
4489) {
4490    let mut i = omp_block_start + omp_block_size - 1;
4491    let mut j = omp_block_start + 64 + 1;
4492
4493    while i >= j {
4494        for current in [i, i - 1] {
4495            let v = cache[current as usize].symbol;
4496            if v >= 0 {
4497                induction_bucket[v as usize] -= 1;
4498                cache[current as usize].symbol = induction_bucket[v as usize];
4499                if cache[current as usize].symbol >= omp_block_start {
4500                    let ni = cache[current as usize].symbol as usize;
4501                    let np = cache[current as usize].index;
4502                    if np > 0 {
4503                        cache[current as usize].index = 0;
4504                        cache[ni].index = (np - 1)
4505                            | ((usize::from(t[(np - 2) as usize] > t[(np - 1) as usize])
4506                                as SaSint)
4507                                << (SAINT_BIT - 1));
4508                        cache[ni].symbol = t[(np - 1) as usize];
4509                    }
4510                }
4511            }
4512        }
4513        i -= 2;
4514    }
4515
4516    j -= 64 + 1;
4517    while i >= j {
4518        let v = cache[i as usize].symbol;
4519        if v >= 0 {
4520            induction_bucket[v as usize] -= 1;
4521            cache[i as usize].symbol = induction_bucket[v as usize];
4522            if cache[i as usize].symbol >= omp_block_start {
4523                let ni = cache[i as usize].symbol as usize;
4524                let np = cache[i as usize].index;
4525                if np > 0 {
4526                    cache[i as usize].index = 0;
4527                    cache[ni].index = (np - 1)
4528                        | ((usize::from(t[(np - 2) as usize] > t[(np - 1) as usize]) as SaSint)
4529                            << (SAINT_BIT - 1));
4530                    cache[ni].symbol = t[(np - 1) as usize];
4531                }
4532            }
4533        }
4534        i -= 1;
4535    }
4536}
4537
4538fn partial_sorting_scan_left_to_right_32s_6k_block_omp(
4539    t: &[SaSint],
4540    sa: &mut [SaSint],
4541    buckets: &mut [SaSint],
4542    d: SaSint,
4543    cache: &mut [ThreadCache],
4544    block_start: SaSint,
4545    block_size: SaSint,
4546    threads: SaSint,
4547) -> SaSint {
4548    if block_size <= 0 {
4549        return d;
4550    }
4551    if threads == 1 || block_size < 16_384 {
4552        return partial_sorting_scan_left_to_right_32s_6k(
4553            t,
4554            sa,
4555            buckets,
4556            d,
4557            block_start,
4558            block_size,
4559        );
4560    }
4561
4562    let threads_usize = usize::try_from(threads)
4563        .expect("threads must be non-negative")
4564        .max(1);
4565    let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
4566    let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
4567    let omp_num_threads = threads_usize.min(block_size_usize.max(1));
4568    let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
4569
4570    {
4571        let sa_ptr = SyncMutPtr::new(sa);
4572        let t_ro: &[SaSint] = t;
4573        let cache_ptr = SyncMutPtr::new(cache);
4574        run_rayon_with_threads(omp_num_threads, || {
4575            (0..omp_num_threads)
4576                .into_par_iter()
4577                .for_each(|omp_thread_num| {
4578                    let mut omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4579                        omp_block_stride
4580                    } else {
4581                        block_size_usize - omp_thread_num * omp_block_stride
4582                    };
4583                    let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
4584                    if omp_block_size == 0 {
4585                        omp_block_size = block_size_usize - (omp_block_start - block_start_usize);
4586                    }
4587                    let cache = unsafe { cache_ptr.as_slice() };
4588                    let sa = unsafe { sa_ptr.as_slice() };
4589                    partial_sorting_scan_left_to_right_32s_6k_block_gather(
4590                        t_ro,
4591                        sa,
4592                        &mut cache[omp_thread_num * omp_block_stride
4593                            ..omp_thread_num * omp_block_stride + omp_block_size],
4594                        omp_block_start as SaSint,
4595                        omp_block_size as SaSint,
4596                    );
4597                });
4598        });
4599    }
4600
4601    let d = partial_sorting_scan_left_to_right_32s_6k_block_sort(
4602        t,
4603        buckets,
4604        d,
4605        &mut cache[..block_size_usize],
4606        block_start,
4607        block_size,
4608    );
4609    place_cached_suffixes(sa, &cache[..block_size_usize], 0, block_size);
4610    d
4611}
4612
4613fn partial_sorting_scan_left_to_right_32s_4k_block_omp(
4614    t: &[SaSint],
4615    sa: &mut [SaSint],
4616    k: SaSint,
4617    buckets: &mut [SaSint],
4618    d: SaSint,
4619    cache: &mut [ThreadCache],
4620    block_start: SaSint,
4621    block_size: SaSint,
4622    threads: SaSint,
4623) -> SaSint {
4624    if block_size <= 0 {
4625        return d;
4626    }
4627    if threads == 1 || block_size < 16_384 {
4628        return partial_sorting_scan_left_to_right_32s_4k(
4629            t,
4630            sa,
4631            k,
4632            buckets,
4633            d,
4634            block_start,
4635            block_size,
4636        );
4637    }
4638
4639    let threads_usize = usize::try_from(threads)
4640        .expect("threads must be non-negative")
4641        .max(1);
4642    let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
4643    let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
4644    let omp_num_threads = threads_usize.min(block_size_usize.max(1));
4645    let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
4646
4647    {
4648        let sa_ptr = SyncMutPtr::new(sa);
4649        let t_ro: &[SaSint] = t;
4650        let cache_ptr = SyncMutPtr::new(cache);
4651        run_rayon_with_threads(omp_num_threads, || {
4652            (0..omp_num_threads)
4653                .into_par_iter()
4654                .for_each(|omp_thread_num| {
4655                    let mut omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4656                        omp_block_stride
4657                    } else {
4658                        block_size_usize - omp_thread_num * omp_block_stride
4659                    };
4660                    let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
4661                    if omp_block_size == 0 {
4662                        omp_block_size = block_size_usize - (omp_block_start - block_start_usize);
4663                    }
4664                    let cache = unsafe { cache_ptr.as_slice() };
4665                    let sa = unsafe { sa_ptr.as_slice() };
4666                    partial_sorting_scan_left_to_right_32s_4k_block_gather(
4667                        t_ro,
4668                        sa,
4669                        &mut cache[omp_thread_num * omp_block_stride
4670                            ..omp_thread_num * omp_block_stride + omp_block_size],
4671                        omp_block_start as SaSint,
4672                        omp_block_size as SaSint,
4673                    );
4674                });
4675        });
4676    }
4677
4678    let cache = &mut cache[..block_size_usize];
4679    let d = partial_sorting_scan_left_to_right_32s_4k_block_sort(
4680        t,
4681        k,
4682        buckets,
4683        d,
4684        cache,
4685        block_start,
4686        block_size,
4687    );
4688    for entry in cache.iter() {
4689        if entry.symbol >= 0 {
4690            sa[entry.symbol as usize] = entry.index;
4691        }
4692    }
4693    d
4694}
4695
4696fn partial_sorting_scan_left_to_right_32s_1k_block_omp(
4697    t: &[SaSint],
4698    sa: &mut [SaSint],
4699    buckets: &mut [SaSint],
4700    cache: &mut [ThreadCache],
4701    block_start: SaSint,
4702    block_size: SaSint,
4703    threads: SaSint,
4704) {
4705    if block_size <= 0 {
4706        return;
4707    }
4708    if threads == 1 || block_size < 16_384 {
4709        partial_sorting_scan_left_to_right_32s_1k(t, sa, buckets, block_start, block_size);
4710        return;
4711    }
4712
4713    let threads_usize = usize::try_from(threads)
4714        .expect("threads must be non-negative")
4715        .max(1);
4716    let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
4717    let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
4718    let omp_num_threads = threads_usize.min(block_size_usize.max(1));
4719    let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
4720
4721    {
4722        let sa_ptr = SyncMutPtr::new(sa);
4723        let t_ro: &[SaSint] = t;
4724        let cache_ptr = SyncMutPtr::new(cache);
4725        run_rayon_with_threads(omp_num_threads, || {
4726            (0..omp_num_threads)
4727                .into_par_iter()
4728                .for_each(|omp_thread_num| {
4729                    let mut omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4730                        omp_block_stride
4731                    } else {
4732                        block_size_usize - omp_thread_num * omp_block_stride
4733                    };
4734                    let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
4735                    if omp_block_size == 0 {
4736                        omp_block_size = block_size_usize - (omp_block_start - block_start_usize);
4737                    }
4738                    let cache = unsafe { cache_ptr.as_slice() };
4739                    let sa = unsafe { sa_ptr.as_slice() };
4740                    partial_sorting_scan_left_to_right_32s_1k_block_gather(
4741                        t_ro,
4742                        sa,
4743                        &mut cache[omp_thread_num * omp_block_stride
4744                            ..omp_thread_num * omp_block_stride + omp_block_size],
4745                        omp_block_start as SaSint,
4746                        omp_block_size as SaSint,
4747                    );
4748                });
4749        });
4750    }
4751
4752    let cache = &mut cache[..block_size_usize];
4753    partial_sorting_scan_left_to_right_32s_1k_block_sort(
4754        t,
4755        buckets,
4756        cache,
4757        block_start,
4758        block_size,
4759    );
4760    compact_and_place_cached_suffixes(sa, cache, block_start, block_size);
4761}
4762
4763fn partial_sorting_scan_right_to_left_32s_6k_block_omp(
4764    t: &[SaSint],
4765    sa: &mut [SaSint],
4766    buckets: &mut [SaSint],
4767    mut d: SaSint,
4768    cache: &mut [ThreadCache],
4769    block_start: SaSint,
4770    block_size: SaSint,
4771    threads: SaSint,
4772) -> SaSint {
4773    if block_size <= 0 {
4774        return d;
4775    }
4776    if threads == 1 || block_size < 16_384 {
4777        return partial_sorting_scan_right_to_left_32s_6k(
4778            t,
4779            sa,
4780            buckets,
4781            d,
4782            block_start,
4783            block_size,
4784        );
4785    }
4786
4787    let threads_usize = usize::try_from(threads)
4788        .expect("threads must be non-negative")
4789        .max(1);
4790    let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
4791    let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
4792    let omp_num_threads = threads_usize.min(block_size_usize.max(1));
4793    let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
4794
4795    {
4796        let sa_ptr = SyncMutPtr::new(sa);
4797        let t_ro: &[SaSint] = t;
4798        let cache_ptr = SyncMutPtr::new(cache);
4799        run_rayon_with_threads(omp_num_threads, || {
4800            (0..omp_num_threads)
4801                .into_par_iter()
4802                .for_each(|omp_thread_num| {
4803                    let mut omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4804                        omp_block_stride
4805                    } else {
4806                        block_size_usize - omp_thread_num * omp_block_stride
4807                    };
4808                    let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
4809                    if omp_block_size == 0 {
4810                        omp_block_size = block_size_usize - (omp_block_start - block_start_usize);
4811                    }
4812                    let cache = unsafe { cache_ptr.as_slice() };
4813                    let sa = unsafe { sa_ptr.as_slice() };
4814                    partial_sorting_scan_right_to_left_32s_6k_block_gather(
4815                        t_ro,
4816                        sa,
4817                        &mut cache[omp_thread_num * omp_block_stride
4818                            ..omp_thread_num * omp_block_stride + omp_block_size],
4819                        omp_block_start as SaSint,
4820                        omp_block_size as SaSint,
4821                    );
4822                });
4823        });
4824    }
4825
4826    d = partial_sorting_scan_right_to_left_32s_6k_block_sort(
4827        t,
4828        buckets,
4829        d,
4830        &mut cache[..block_size_usize],
4831        block_start,
4832        block_size,
4833    );
4834    {
4835        let sa_ptr = SyncMutPtr::new(sa);
4836        let cache_ro: &[ThreadCache] = cache;
4837        run_rayon_with_threads(omp_num_threads, || {
4838            (0..omp_num_threads)
4839                .into_par_iter()
4840                .for_each(|omp_thread_num| {
4841                    let mut omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4842                        omp_block_stride
4843                    } else {
4844                        block_size_usize - omp_thread_num * omp_block_stride
4845                    };
4846                    let cache_start = omp_thread_num * omp_block_stride;
4847                    if omp_block_size == 0 {
4848                        omp_block_size = block_size_usize - cache_start;
4849                    }
4850                    let sa = unsafe { sa_ptr.as_slice() };
4851                    for entry in &cache_ro[cache_start..cache_start + omp_block_size] {
4852                        sa[entry.symbol as usize] = entry.index;
4853                    }
4854                });
4855        });
4856    }
4857    d
4858}
4859
4860fn partial_sorting_scan_right_to_left_32s_4k_block_omp(
4861    t: &[SaSint],
4862    sa: &mut [SaSint],
4863    k: SaSint,
4864    buckets: &mut [SaSint],
4865    mut d: SaSint,
4866    cache: &mut [ThreadCache],
4867    block_start: SaSint,
4868    block_size: SaSint,
4869    threads: SaSint,
4870) -> SaSint {
4871    if block_size <= 0 {
4872        return d;
4873    }
4874    if threads == 1 || block_size < 16_384 {
4875        return partial_sorting_scan_right_to_left_32s_4k(
4876            t,
4877            sa,
4878            k,
4879            buckets,
4880            d,
4881            block_start,
4882            block_size,
4883        );
4884    }
4885
4886    let threads_usize = usize::try_from(threads)
4887        .expect("threads must be non-negative")
4888        .max(1);
4889    let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
4890    let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
4891    let omp_num_threads = threads_usize.min(block_size_usize.max(1));
4892    let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
4893
4894    {
4895        let sa_ptr = SyncMutPtr::new(sa);
4896        let t_ro: &[SaSint] = t;
4897        let cache_ptr = SyncMutPtr::new(cache);
4898        run_rayon_with_threads(omp_num_threads, || {
4899            (0..omp_num_threads)
4900                .into_par_iter()
4901                .for_each(|omp_thread_num| {
4902                    let mut omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4903                        omp_block_stride
4904                    } else {
4905                        block_size_usize - omp_thread_num * omp_block_stride
4906                    };
4907                    let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
4908                    if omp_block_size == 0 {
4909                        omp_block_size = block_size_usize - (omp_block_start - block_start_usize);
4910                    }
4911                    let cache = unsafe { cache_ptr.as_slice() };
4912                    let sa = unsafe { sa_ptr.as_slice() };
4913                    partial_sorting_scan_right_to_left_32s_4k_block_gather(
4914                        t_ro,
4915                        sa,
4916                        &mut cache[omp_thread_num * omp_block_stride
4917                            ..omp_thread_num * omp_block_stride + omp_block_size],
4918                        omp_block_start as SaSint,
4919                        omp_block_size as SaSint,
4920                    );
4921                });
4922        });
4923    }
4924
4925    d = partial_sorting_scan_right_to_left_32s_4k_block_sort(
4926        t,
4927        k,
4928        buckets,
4929        d,
4930        &mut cache[..block_size_usize],
4931        block_start,
4932        block_size,
4933    );
4934    let mut write = 0usize;
4935    for read in 0..block_size_usize {
4936        let entry = cache[read];
4937        if entry.symbol >= 0 {
4938            cache[write] = entry;
4939            write += 1;
4940        }
4941    }
4942    for entry in &cache[..write] {
4943        sa[entry.symbol as usize] = entry.index;
4944    }
4945    d
4946}
4947
4948fn partial_sorting_scan_right_to_left_32s_1k_block_omp(
4949    t: &[SaSint],
4950    sa: &mut [SaSint],
4951    buckets: &mut [SaSint],
4952    cache: &mut [ThreadCache],
4953    block_start: SaSint,
4954    block_size: SaSint,
4955    threads: SaSint,
4956) {
4957    if block_size <= 0 {
4958        return;
4959    }
4960    if threads == 1 || block_size < 16_384 {
4961        partial_sorting_scan_right_to_left_32s_1k(t, sa, buckets, block_start, block_size);
4962        return;
4963    }
4964
4965    let threads_usize = usize::try_from(threads)
4966        .expect("threads must be non-negative")
4967        .max(1);
4968    let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
4969    let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
4970    let omp_num_threads = threads_usize.min(block_size_usize.max(1));
4971    let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
4972
4973    {
4974        let sa_ptr = SyncMutPtr::new(sa);
4975        let t_ro: &[SaSint] = t;
4976        let cache_ptr = SyncMutPtr::new(cache);
4977        run_rayon_with_threads(omp_num_threads, || {
4978            (0..omp_num_threads)
4979                .into_par_iter()
4980                .for_each(|omp_thread_num| {
4981                    let mut omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4982                        omp_block_stride
4983                    } else {
4984                        block_size_usize - omp_thread_num * omp_block_stride
4985                    };
4986                    let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
4987                    if omp_block_size == 0 {
4988                        omp_block_size = block_size_usize - (omp_block_start - block_start_usize);
4989                    }
4990                    let cache = unsafe { cache_ptr.as_slice() };
4991                    let sa = unsafe { sa_ptr.as_slice() };
4992                    partial_sorting_scan_right_to_left_32s_1k_block_gather(
4993                        t_ro,
4994                        sa,
4995                        &mut cache[omp_thread_num * omp_block_stride
4996                            ..omp_thread_num * omp_block_stride + omp_block_size],
4997                        omp_block_start as SaSint,
4998                        omp_block_size as SaSint,
4999                    );
5000                });
5001        });
5002    }
5003
5004    let cache = &mut cache[..block_size_usize];
5005    partial_sorting_scan_right_to_left_32s_1k_block_sort(
5006        t,
5007        buckets,
5008        cache,
5009        block_start,
5010        block_size,
5011    );
5012    compact_and_place_cached_suffixes(sa, cache, block_start, block_size);
5013}
5014
5015fn partial_sorting_gather_lms_suffixes_32s_4k(
5016    sa: &mut [SaSint],
5017    omp_block_start: SaSint,
5018    omp_block_size: SaSint,
5019) -> SaSint {
5020    let mut i = omp_block_start;
5021    let mut j = omp_block_start + omp_block_size - 3;
5022    let mut l = omp_block_start;
5023
5024    while i < j {
5025        let s0 = sa[i as usize] as SaUint;
5026        sa[l as usize] = (s0.wrapping_sub(SUFFIX_GROUP_MARKER as SaUint)
5027            & !(SUFFIX_GROUP_MARKER as SaUint)) as SaSint;
5028        l += SaSint::from((s0 as SaSint) < 0);
5029
5030        let s1 = sa[(i + 1) as usize] as SaUint;
5031        sa[l as usize] = (s1.wrapping_sub(SUFFIX_GROUP_MARKER as SaUint)
5032            & !(SUFFIX_GROUP_MARKER as SaUint)) as SaSint;
5033        l += SaSint::from((s1 as SaSint) < 0);
5034
5035        let s2 = sa[(i + 2) as usize] as SaUint;
5036        sa[l as usize] = (s2.wrapping_sub(SUFFIX_GROUP_MARKER as SaUint)
5037            & !(SUFFIX_GROUP_MARKER as SaUint)) as SaSint;
5038        l += SaSint::from((s2 as SaSint) < 0);
5039
5040        let s3 = sa[(i + 3) as usize] as SaUint;
5041        sa[l as usize] = (s3.wrapping_sub(SUFFIX_GROUP_MARKER as SaUint)
5042            & !(SUFFIX_GROUP_MARKER as SaUint)) as SaSint;
5043        l += SaSint::from((s3 as SaSint) < 0);
5044
5045        i += 4;
5046    }
5047
5048    j += 3;
5049    while i < j {
5050        let s = sa[i as usize] as SaUint;
5051        sa[l as usize] = (s.wrapping_sub(SUFFIX_GROUP_MARKER as SaUint)
5052            & !(SUFFIX_GROUP_MARKER as SaUint)) as SaSint;
5053        l += SaSint::from((s as SaSint) < 0);
5054        i += 1;
5055    }
5056
5057    l
5058}
5059
5060fn partial_sorting_gather_lms_suffixes_32s_1k(
5061    sa: &mut [SaSint],
5062    omp_block_start: SaSint,
5063    omp_block_size: SaSint,
5064) -> SaSint {
5065    let mut i = omp_block_start;
5066    let mut j = omp_block_start + omp_block_size - 3;
5067    let mut l = omp_block_start;
5068
5069    while i < j {
5070        let s0 = sa[i as usize];
5071        sa[l as usize] = s0 & SAINT_MAX;
5072        l += SaSint::from(s0 < 0);
5073
5074        let s1 = sa[(i + 1) as usize];
5075        sa[l as usize] = s1 & SAINT_MAX;
5076        l += SaSint::from(s1 < 0);
5077
5078        let s2 = sa[(i + 2) as usize];
5079        sa[l as usize] = s2 & SAINT_MAX;
5080        l += SaSint::from(s2 < 0);
5081
5082        let s3 = sa[(i + 3) as usize];
5083        sa[l as usize] = s3 & SAINT_MAX;
5084        l += SaSint::from(s3 < 0);
5085
5086        i += 4;
5087    }
5088
5089    j += 3;
5090    while i < j {
5091        let s = sa[i as usize];
5092        sa[l as usize] = s & SAINT_MAX;
5093        l += SaSint::from(s < 0);
5094        i += 1;
5095    }
5096
5097    l
5098}
5099
5100fn partial_sorting_gather_lms_suffixes_32s_4k_omp(
5101    sa: &mut [SaSint],
5102    n: SaSint,
5103    threads: SaSint,
5104    thread_state: &mut [ThreadState],
5105) {
5106    let n_usize = usize::try_from(n).expect("n must be non-negative");
5107    let thread_count = if threads > 1 && n >= 65_536 {
5108        usize::try_from(threads)
5109            .expect("threads must be non-negative")
5110            .min(thread_state.len())
5111            .max(1)
5112    } else {
5113        1
5114    };
5115
5116    if thread_count == 1 {
5117        let _ = partial_sorting_gather_lms_suffixes_32s_4k(sa, 0, n);
5118        return;
5119    }
5120
5121    let block_stride = (n_usize / thread_count) & !15usize;
5122    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
5123        let block_start = thread * block_stride;
5124        let block_size = if thread + 1 < thread_count {
5125            block_stride
5126        } else {
5127            n_usize - block_start
5128        };
5129        state.position = block_start as SaSint;
5130        state.count = partial_sorting_gather_lms_suffixes_32s_4k(
5131            sa,
5132            block_start as SaSint,
5133            block_size as SaSint,
5134        ) - block_start as SaSint;
5135    }
5136
5137    let mut position = 0usize;
5138    for (thread, state) in thread_state.iter().take(thread_count).enumerate() {
5139        let count = usize::try_from(state.count).expect("count must be non-negative");
5140        let src = usize::try_from(state.position).expect("position must be non-negative");
5141        if thread > 0 && count > 0 {
5142            sa.copy_within(src..src + count, position);
5143        }
5144        position += count;
5145    }
5146}
5147
5148fn partial_sorting_gather_lms_suffixes_32s_1k_omp(
5149    sa: &mut [SaSint],
5150    n: SaSint,
5151    threads: SaSint,
5152    thread_state: &mut [ThreadState],
5153) {
5154    let n_usize = usize::try_from(n).expect("n must be non-negative");
5155    let thread_count = if threads > 1 && n >= 65_536 {
5156        usize::try_from(threads)
5157            .expect("threads must be non-negative")
5158            .min(thread_state.len())
5159            .max(1)
5160    } else {
5161        1
5162    };
5163
5164    if thread_count == 1 {
5165        let _ = partial_sorting_gather_lms_suffixes_32s_1k(sa, 0, n);
5166        return;
5167    }
5168
5169    let block_stride = (n_usize / thread_count) & !15usize;
5170    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
5171        let block_start = thread * block_stride;
5172        let block_size = if thread + 1 < thread_count {
5173            block_stride
5174        } else {
5175            n_usize - block_start
5176        };
5177        state.position = block_start as SaSint;
5178        state.count = partial_sorting_gather_lms_suffixes_32s_1k(
5179            sa,
5180            block_start as SaSint,
5181            block_size as SaSint,
5182        ) - block_start as SaSint;
5183    }
5184
5185    let mut position = 0usize;
5186    for (thread, state) in thread_state.iter().take(thread_count).enumerate() {
5187        let count = usize::try_from(state.count).expect("count must be non-negative");
5188        let src = usize::try_from(state.position).expect("position must be non-negative");
5189        if thread > 0 && count > 0 {
5190            sa.copy_within(src..src + count, position);
5191        }
5192        position += count;
5193    }
5194}
5195
5196fn partial_gsa_scan_right_to_left_16u(
5197    t: &[u16],
5198    sa: &mut [SaSint],
5199    buckets: &mut [SaSint],
5200    mut d: SaSint,
5201    omp_block_start: SaSint,
5202    omp_block_size: SaSint,
5203) -> SaSint {
5204    let mut i = (omp_block_start + omp_block_size - 1) as isize;
5205    let mut j = (omp_block_start + 64 + 1) as isize;
5206    while i >= j {
5207        let mut p0 = sa[i as usize];
5208        d += SaSint::from(p0 < 0);
5209        p0 &= SAINT_MAX;
5210        let v0 = buckets_index2(
5211            t[(p0 - 1) as usize] as usize,
5212            usize::from(t[(p0 - 2) as usize] > t[(p0 - 1) as usize]),
5213        );
5214        if v0 != 1 {
5215            let mark0 = if buckets[2 * ALPHABET_SIZE + v0] != d {
5216                SAINT_MIN
5217            } else {
5218                0
5219            };
5220            buckets[v0] -= 1;
5221            sa[buckets[v0] as usize] = (p0 - 1) | mark0;
5222            buckets[2 * ALPHABET_SIZE + v0] = d;
5223        }
5224
5225        let mut p1 = sa[(i - 1) as usize];
5226        d += SaSint::from(p1 < 0);
5227        p1 &= SAINT_MAX;
5228        let v1 = buckets_index2(
5229            t[(p1 - 1) as usize] as usize,
5230            usize::from(t[(p1 - 2) as usize] > t[(p1 - 1) as usize]),
5231        );
5232        if v1 != 1 {
5233            let mark1 = if buckets[2 * ALPHABET_SIZE + v1] != d {
5234                SAINT_MIN
5235            } else {
5236                0
5237            };
5238            buckets[v1] -= 1;
5239            sa[buckets[v1] as usize] = (p1 - 1) | mark1;
5240            buckets[2 * ALPHABET_SIZE + v1] = d;
5241        }
5242
5243        i -= 2;
5244    }
5245
5246    j -= 64 + 1;
5247    while i >= j {
5248        let mut p = sa[i as usize];
5249        d += SaSint::from(p < 0);
5250        p &= SAINT_MAX;
5251        let v = buckets_index2(
5252            t[(p - 1) as usize] as usize,
5253            usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
5254        );
5255        if v != 1 {
5256            let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
5257                SAINT_MIN
5258            } else {
5259                0
5260            };
5261            buckets[v] -= 1;
5262            sa[buckets[v] as usize] = (p - 1) | mark;
5263            buckets[2 * ALPHABET_SIZE + v] = d;
5264        }
5265        i -= 1;
5266    }
5267
5268    d
5269}
5270
5271fn partial_gsa_scan_right_to_left_16u_block_omp(
5272    t: &[u16],
5273    sa: &mut [SaSint],
5274    k: SaSint,
5275    buckets: &mut [SaSint],
5276    d: SaSint,
5277    block_start: SaSint,
5278    block_size: SaSint,
5279    threads: SaSint,
5280    thread_state: &mut [ThreadState],
5281) -> SaSint {
5282    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
5283        usize::try_from(threads)
5284            .expect("threads must be non-negative")
5285            .min(thread_state.len())
5286    } else {
5287        1
5288    };
5289    if thread_count <= 1 {
5290        return partial_gsa_scan_right_to_left_16u(t, sa, buckets, d, block_start, block_size);
5291    }
5292
5293    let width = 2 * k as usize;
5294    let distinct_offset = 2 * ALPHABET_SIZE;
5295    let block_stride = (block_size / thread_count as SaSint) & !15;
5296
5297    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
5298        let local_start = thread as SaSint * block_stride;
5299        let local_size = if thread + 1 < thread_count {
5300            block_stride
5301        } else {
5302            block_size - local_start
5303        };
5304        let mut local_state = ThreadState::default();
5305        state.position = partial_sorting_scan_right_to_left_16u_block_prepare(
5306            t,
5307            sa,
5308            k,
5309            &mut state.buckets,
5310            &mut state.cache,
5311            block_start + local_start,
5312            local_size,
5313            &mut local_state,
5314        );
5315        state.count = local_state.cache_entries as SaSint;
5316    }
5317
5318    let mut next_d = d;
5319    for state in thread_state.iter_mut().take(thread_count).rev() {
5320        for c in 0..width {
5321            let a = buckets[c];
5322            let b = state.buckets[c];
5323            buckets[c] = a - b;
5324            state.buckets[c] = a;
5325        }
5326
5327        next_d -= 1;
5328        for c in 0..width {
5329            let offset = distinct_offset + c;
5330            let a = buckets[offset];
5331            let b = state.buckets[offset];
5332            let shifted = b + next_d;
5333            buckets[offset] = if b > 0 { shifted } else { a };
5334            state.buckets[offset] = a;
5335        }
5336        next_d += 1 + state.position;
5337        state.position = next_d - state.position;
5338    }
5339
5340    for state in thread_state.iter_mut().take(thread_count) {
5341        partial_gsa_scan_right_to_left_16u_block_place(
5342            sa,
5343            &mut state.buckets,
5344            &state.cache,
5345            state.count,
5346            state.position,
5347        );
5348    }
5349
5350    next_d
5351}
5352
5353fn partial_gsa_scan_right_to_left_16u_omp(
5354    t: &[u16],
5355    sa: &mut [SaSint],
5356    n: SaSint,
5357    k: SaSint,
5358    buckets: &mut [SaSint],
5359    first_lms_suffix: SaSint,
5360    left_suffixes_count: SaSint,
5361    d: SaSint,
5362    threads: SaSint,
5363) {
5364    let scan_start = left_suffixes_count + 1;
5365    let scan_end = n - first_lms_suffix;
5366
5367    if threads == 1 || scan_end - scan_start < 65536 {
5368        partial_gsa_scan_right_to_left_16u(t, sa, buckets, d, scan_start, scan_end - scan_start);
5369    } else {
5370        let mut d = d;
5371        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
5372        let mut block_start = scan_end - 1;
5373        while block_start >= scan_start {
5374            if sa[block_start as usize] == 0 {
5375                block_start -= 1;
5376            } else {
5377                let block_limit = threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
5378                let mut block_max_end = block_start - block_limit;
5379                if block_max_end < scan_start {
5380                    block_max_end = scan_start - 1;
5381                }
5382                let mut block_end = block_start - 1;
5383                while block_end > block_max_end && sa[block_end as usize] != 0 {
5384                    block_end -= 1;
5385                }
5386                let block_size = block_start - block_end;
5387
5388                if block_size < 32 {
5389                    while block_start > block_end {
5390                        let mut p = sa[block_start as usize];
5391                        d += SaSint::from(p < 0);
5392                        p &= SAINT_MAX;
5393                        let v = buckets_index2(
5394                            t[(p - 1) as usize] as usize,
5395                            usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
5396                        );
5397                        if v != 1 {
5398                            let mark = if buckets[2 * ALPHABET_SIZE + v] != d {
5399                                SAINT_MIN
5400                            } else {
5401                                0
5402                            };
5403                            buckets[v] -= 1;
5404                            sa[buckets[v] as usize] = (p - 1) | mark;
5405                            buckets[2 * ALPHABET_SIZE + v] = d;
5406                        }
5407                        block_start -= 1;
5408                    }
5409                } else {
5410                    d = partial_gsa_scan_right_to_left_16u_block_omp(
5411                        t,
5412                        sa,
5413                        k,
5414                        buckets,
5415                        d,
5416                        block_end + 1,
5417                        block_size,
5418                        threads,
5419                        &mut thread_state,
5420                    );
5421                    block_start = block_end;
5422                }
5423            }
5424        }
5425    }
5426}
5427
5428fn partial_sorting_shift_markers_16u_omp(
5429    sa: &mut [SaSint],
5430    n: SaSint,
5431    buckets: &[SaSint],
5432    threads: SaSint,
5433) {
5434    let thread_count = if threads > 1 && n >= 65536 {
5435        usize::try_from(threads).expect("threads must be positive")
5436    } else {
5437        1
5438    };
5439    let c_step = buckets_index2(1, 0) as isize;
5440    let c_min = buckets_index2(1, 0) as isize;
5441    let c_max = buckets_index2(ALPHABET_SIZE - 1, 0) as isize;
5442    let sa_ptr = SyncMutPtr::new(sa);
5443    let buckets_ref: &[SaSint] = buckets;
5444    run_rayon_with_threads(thread_count, || {
5445        (0..thread_count).into_par_iter().for_each(|t| {
5446            let mut c = c_max - (t as isize * c_step);
5447            let sa = unsafe { sa_ptr.as_slice() };
5448            while c >= c_min {
5449                let c_usize = c as usize;
5450                let mut s = SAINT_MIN;
5451                let mut i = buckets_ref[4 * ALPHABET_SIZE + c_usize] as isize - 1;
5452                let mut j = buckets_ref[c_usize - buckets_index2(1, 0)] as isize + 3;
5453                while i >= j {
5454                    let p0 = sa[i as usize];
5455                    let q0 = (p0 & SAINT_MIN) ^ s;
5456                    s ^= q0;
5457                    sa[i as usize] = p0 ^ q0;
5458
5459                    let p1 = sa[(i - 1) as usize];
5460                    let q1 = (p1 & SAINT_MIN) ^ s;
5461                    s ^= q1;
5462                    sa[(i - 1) as usize] = p1 ^ q1;
5463
5464                    let p2 = sa[(i - 2) as usize];
5465                    let q2 = (p2 & SAINT_MIN) ^ s;
5466                    s ^= q2;
5467                    sa[(i - 2) as usize] = p2 ^ q2;
5468
5469                    let p3 = sa[(i - 3) as usize];
5470                    let q3 = (p3 & SAINT_MIN) ^ s;
5471                    s ^= q3;
5472                    sa[(i - 3) as usize] = p3 ^ q3;
5473
5474                    i -= 4;
5475                }
5476
5477                j -= 3;
5478                while i >= j {
5479                    let p = sa[i as usize];
5480                    let q = (p & SAINT_MIN) ^ s;
5481                    s ^= q;
5482                    sa[i as usize] = p ^ q;
5483                    i -= 1;
5484                }
5485
5486                c -= c_step * thread_count as isize;
5487            }
5488        });
5489    });
5490}
5491
5492fn induce_partial_order_16u_omp(
5493    t: &[u16],
5494    sa: &mut [SaSint],
5495    n: SaSint,
5496    k: SaSint,
5497    flags: SaSint,
5498    buckets: &mut [SaSint],
5499    first_lms_suffix: SaSint,
5500    left_suffixes_count: SaSint,
5501    threads: SaSint,
5502) {
5503    buckets[2 * ALPHABET_SIZE..4 * ALPHABET_SIZE].fill(0);
5504
5505    if (flags & LIBSAIS_FLAGS_GSA) != 0 {
5506        let marker = 4 * ALPHABET_SIZE + buckets_index2(0, 1);
5507        buckets[marker] = buckets[4 * ALPHABET_SIZE + buckets_index2(1, 1)] - 1;
5508        flip_suffix_markers_omp(sa, buckets[marker], threads);
5509    }
5510
5511    let d = partial_sorting_scan_left_to_right_16u_omp(
5512        t,
5513        sa,
5514        n,
5515        k,
5516        buckets,
5517        left_suffixes_count,
5518        0,
5519        threads,
5520    );
5521    partial_sorting_shift_markers_16u_omp(sa, n, buckets, threads);
5522
5523    if (flags & LIBSAIS_FLAGS_GSA) != 0 {
5524        partial_gsa_scan_right_to_left_16u_omp(
5525            t,
5526            sa,
5527            n,
5528            k,
5529            buckets,
5530            first_lms_suffix,
5531            left_suffixes_count,
5532            d,
5533            threads,
5534        );
5535
5536        if t[first_lms_suffix as usize] == 0 {
5537            let count = (buckets[buckets_index2(1, 1)] - 1) as usize;
5538            sa.copy_within(0..count, 1);
5539            sa[0] = first_lms_suffix | SAINT_MIN;
5540        }
5541
5542        buckets[buckets_index2(0, 1)] = 0;
5543    } else {
5544        partial_sorting_scan_right_to_left_16u_omp(
5545            t,
5546            sa,
5547            n,
5548            k,
5549            buckets,
5550            first_lms_suffix,
5551            left_suffixes_count,
5552            d,
5553            threads,
5554        );
5555    }
5556}
5557
5558fn induce_partial_order_32s_6k_omp(
5559    t: &[SaSint],
5560    sa: &mut [SaSint],
5561    n: SaSint,
5562    k: SaSint,
5563    buckets: &mut [SaSint],
5564    first_lms_suffix: SaSint,
5565    left_suffixes_count: SaSint,
5566    threads: SaSint,
5567    thread_state: &mut [ThreadState],
5568) {
5569    let d = partial_sorting_scan_left_to_right_32s_6k_omp(
5570        t,
5571        sa,
5572        n,
5573        buckets,
5574        left_suffixes_count,
5575        0,
5576        threads,
5577        thread_state,
5578    );
5579    partial_sorting_shift_markers_32s_6k_omp(sa, k, buckets, threads);
5580    partial_sorting_shift_buckets_32s_6k(k, buckets);
5581    partial_sorting_scan_right_to_left_32s_6k_omp(
5582        t,
5583        sa,
5584        n,
5585        buckets,
5586        first_lms_suffix,
5587        left_suffixes_count,
5588        d,
5589        threads,
5590        thread_state,
5591    );
5592}
5593
5594fn induce_partial_order_32s_4k_omp(
5595    t: &[SaSint],
5596    sa: &mut [SaSint],
5597    n: SaSint,
5598    k: SaSint,
5599    buckets: &mut [SaSint],
5600    threads: SaSint,
5601    thread_state: &mut [ThreadState],
5602) {
5603    buckets[..2 * k as usize].fill(0);
5604    let d = partial_sorting_scan_left_to_right_32s_4k_omp(
5605        t,
5606        sa,
5607        n,
5608        k,
5609        buckets,
5610        0,
5611        threads,
5612        thread_state,
5613    );
5614    partial_sorting_shift_markers_32s_4k(sa, n);
5615    partial_sorting_scan_right_to_left_32s_4k_omp(t, sa, n, k, buckets, d, threads, thread_state);
5616    partial_sorting_gather_lms_suffixes_32s_4k_omp(sa, n, threads, thread_state);
5617}
5618
5619fn induce_partial_order_32s_2k_omp(
5620    t: &[SaSint],
5621    sa: &mut [SaSint],
5622    n: SaSint,
5623    k: SaSint,
5624    buckets: &mut [SaSint],
5625    threads: SaSint,
5626    thread_state: &mut [ThreadState],
5627) {
5628    let k = k as usize;
5629    let (left, right) = buckets.split_at_mut(k);
5630    partial_sorting_scan_left_to_right_32s_1k_omp(t, sa, n, right, threads, thread_state);
5631    partial_sorting_scan_right_to_left_32s_1k_omp(t, sa, n, left, threads, thread_state);
5632    partial_sorting_gather_lms_suffixes_32s_1k_omp(sa, n, threads, thread_state);
5633}
5634
5635fn induce_partial_order_32s_1k_omp(
5636    t: &[SaSint],
5637    sa: &mut [SaSint],
5638    n: SaSint,
5639    k: SaSint,
5640    buckets: &mut [SaSint],
5641    threads: SaSint,
5642    thread_state: &mut [ThreadState],
5643) {
5644    count_suffixes_32s(t, n, k, buckets);
5645    initialize_buckets_start_32s_1k(k, buckets);
5646    partial_sorting_scan_left_to_right_32s_1k_omp(t, sa, n, buckets, threads, thread_state);
5647
5648    count_suffixes_32s(t, n, k, buckets);
5649    initialize_buckets_end_32s_1k(k, buckets);
5650    partial_sorting_scan_right_to_left_32s_1k_omp(t, sa, n, buckets, threads, thread_state);
5651
5652    partial_sorting_gather_lms_suffixes_32s_1k_omp(sa, n, threads, thread_state);
5653}
5654
5655fn final_sorting_scan_left_to_right_16u(
5656    t: &[u16],
5657    sa: &mut [SaSint],
5658    induction_bucket: &mut [SaSint],
5659    omp_block_start: SaSint,
5660    omp_block_size: SaSint,
5661) {
5662    let mut i = omp_block_start as isize;
5663    let mut j = (omp_block_start + omp_block_size - 64 - 1) as isize;
5664    while i < j {
5665        final_sorting_ltr_step(t, sa, induction_bucket, i as usize);
5666        final_sorting_ltr_step(t, sa, induction_bucket, (i + 1) as usize);
5667        i += 2;
5668    }
5669    j += 64 + 1;
5670    while i < j {
5671        final_sorting_ltr_step(t, sa, induction_bucket, i as usize);
5672        i += 1;
5673    }
5674}
5675
5676fn final_sorting_scan_right_to_left_16u(
5677    t: &[u16],
5678    sa: &mut [SaSint],
5679    induction_bucket: &mut [SaSint],
5680    omp_block_start: SaSint,
5681    omp_block_size: SaSint,
5682) {
5683    let mut i = (omp_block_start + omp_block_size - 1) as isize;
5684    let mut j = (omp_block_start + 64 + 1) as isize;
5685    while i >= j {
5686        final_sorting_rtl_step(t, sa, induction_bucket, i as usize, false);
5687        final_sorting_rtl_step(t, sa, induction_bucket, (i - 1) as usize, false);
5688        i -= 2;
5689    }
5690    j -= 64 + 1;
5691    while i >= j {
5692        final_sorting_rtl_step(t, sa, induction_bucket, i as usize, false);
5693        i -= 1;
5694    }
5695}
5696
5697fn final_sorting_scan_left_to_right_32s(
5698    t: &[SaSint],
5699    sa: &mut [SaSint],
5700    induction_bucket: &mut [SaSint],
5701    omp_block_start: SaSint,
5702    omp_block_size: SaSint,
5703) {
5704    let mut i = omp_block_start as isize;
5705    let mut j = (omp_block_start + omp_block_size - 2 * 64 - 1) as isize;
5706    while i < j {
5707        for current in [i, i + 1] {
5708            let current = current as usize;
5709            let mut p = sa[current];
5710            sa[current] = p ^ SAINT_MIN;
5711            if p > 0 {
5712                p -= 1;
5713                let p_usize = p as usize;
5714                let bucket = t[p_usize] as usize;
5715                let slot = induction_bucket[bucket] as usize;
5716                sa[slot] = p
5717                    | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
5718                        << (SAINT_BIT - 1));
5719                induction_bucket[bucket] += 1;
5720            }
5721        }
5722        i += 2;
5723    }
5724
5725    j += 2 * 64 + 1;
5726    while i < j {
5727        let current = i as usize;
5728        let mut p = sa[current];
5729        sa[current] = p ^ SAINT_MIN;
5730        if p > 0 {
5731            p -= 1;
5732            let p_usize = p as usize;
5733            let bucket = t[p_usize] as usize;
5734            let slot = induction_bucket[bucket] as usize;
5735            sa[slot] = p
5736                | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
5737                    << (SAINT_BIT - 1));
5738            induction_bucket[bucket] += 1;
5739        }
5740        i += 1;
5741    }
5742}
5743
5744fn final_sorting_scan_left_to_right_32s_block_gather(
5745    t: &[SaSint],
5746    sa: &mut [SaSint],
5747    cache: &mut [ThreadCache],
5748    omp_block_start: SaSint,
5749    omp_block_size: SaSint,
5750) {
5751    if omp_block_size <= 0 {
5752        return;
5753    }
5754
5755    let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5756    let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5757    for offset in 0..size {
5758        let current = start + offset;
5759        let mut symbol = SAINT_MIN;
5760        let mut p = sa[current];
5761        sa[current] = p ^ SAINT_MIN;
5762        if p > 0 {
5763            p -= 1;
5764            let p_usize = p as usize;
5765            cache[offset].index = p
5766                | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
5767                    << (SAINT_BIT - 1));
5768            symbol = t[p_usize];
5769        }
5770        cache[offset].symbol = symbol;
5771    }
5772}
5773
5774fn final_sorting_scan_left_to_right_32s_block_sort(
5775    t: &[SaSint],
5776    induction_bucket: &mut [SaSint],
5777    cache: &mut [ThreadCache],
5778    omp_block_start: SaSint,
5779    omp_block_size: SaSint,
5780) {
5781    if omp_block_size <= 0 {
5782        return;
5783    }
5784
5785    let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5786    let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5787    let block_end = start + size;
5788
5789    for offset in 0..size {
5790        let v = cache[offset].symbol;
5791        if v >= 0 {
5792            let bucket_index = v as usize;
5793            let target = induction_bucket[bucket_index];
5794            cache[offset].symbol = target;
5795            induction_bucket[bucket_index] += 1;
5796            if target >= omp_block_start && target < block_end as SaSint {
5797                let ni = usize::try_from(target - omp_block_start)
5798                    .expect("cache slot must be non-negative");
5799                let mut np = cache[offset].index;
5800                cache[offset].index = np ^ SAINT_MIN;
5801                if np > 0 {
5802                    np -= 1;
5803                    let np_usize = np as usize;
5804                    cache[ni].index = np
5805                        | ((usize::from(t[np_usize - usize::from(np > 0)] < t[np_usize])
5806                            as SaSint)
5807                            << (SAINT_BIT - 1));
5808                    cache[ni].symbol = t[np_usize];
5809                }
5810            }
5811        }
5812    }
5813}
5814
5815fn final_sorting_scan_left_to_right_32s_block_omp(
5816    t: &[SaSint],
5817    sa: &mut [SaSint],
5818    buckets: &mut [SaSint],
5819    cache: &mut [ThreadCache],
5820    block_start: SaSint,
5821    block_size: SaSint,
5822    threads: SaSint,
5823) {
5824    if threads <= 1 || block_size < 16_384 {
5825        final_sorting_scan_left_to_right_32s(t, sa, buckets, block_start, block_size);
5826        return;
5827    }
5828
5829    final_sorting_scan_left_to_right_32s_block_gather(t, sa, cache, block_start, block_size);
5830    final_sorting_scan_left_to_right_32s_block_sort(t, buckets, cache, block_start, block_size);
5831
5832    let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
5833    let threads_usize = usize::try_from(threads.max(1)).expect("threads must be positive");
5834    let omp_num_threads = threads_usize.min(block_size_usize.max(1));
5835    let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
5836    {
5837        let sa_ptr = SyncMutPtr::new(sa);
5838        let cache_ptr = SyncMutPtr::new(cache);
5839        run_rayon_with_threads(omp_num_threads, || {
5840            (0..omp_num_threads)
5841                .into_par_iter()
5842                .for_each(|omp_thread_num| {
5843                    let omp_block_start = omp_thread_num * omp_block_stride;
5844                    let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5845                        omp_block_stride
5846                    } else {
5847                        block_size_usize - omp_block_start
5848                    };
5849                    let sa = unsafe { sa_ptr.as_slice() };
5850                    let cache = unsafe { cache_ptr.as_slice() };
5851                    compact_and_place_cached_suffixes(
5852                        sa,
5853                        cache,
5854                        omp_block_start as SaSint,
5855                        omp_block_size as SaSint,
5856                    );
5857                });
5858        });
5859    }
5860}
5861
5862fn final_sorting_scan_left_to_right_32s_omp(
5863    t: &[SaSint],
5864    sa: &mut [SaSint],
5865    n: SaSint,
5866    induction_bucket: &mut [SaSint],
5867    threads: SaSint,
5868    thread_state: &mut [ThreadState],
5869) {
5870    let last = (n - 1) as usize;
5871    let bucket = t[last] as usize;
5872    let slot = induction_bucket[bucket] as usize;
5873    sa[slot] = (n - 1) | ((usize::from(t[last - 1] < t[last]) as SaSint) << (SAINT_BIT - 1));
5874    induction_bucket[bucket] += 1;
5875
5876    if threads == 1 || n < 65536 || thread_state.is_empty() {
5877        final_sorting_scan_left_to_right_32s(t, sa, induction_bucket, 0, n);
5878        return;
5879    }
5880
5881    let threads_usize = usize::try_from(threads)
5882        .expect("threads must be non-negative")
5883        .max(1);
5884    let block_span = threads_usize * PER_THREAD_CACHE_SIZE;
5885    let mut cache = vec![ThreadCache::default(); block_span];
5886    let mut block_start = 0;
5887    while block_start < n {
5888        let block_end = (block_start + block_span as SaSint).min(n);
5889        final_sorting_scan_left_to_right_32s_block_omp(
5890            t,
5891            sa,
5892            induction_bucket,
5893            &mut cache,
5894            block_start,
5895            block_end - block_start,
5896            threads,
5897        );
5898        block_start = block_end;
5899    }
5900}
5901
5902fn final_sorting_scan_right_to_left_32s(
5903    t: &[SaSint],
5904    sa: &mut [SaSint],
5905    induction_bucket: &mut [SaSint],
5906    omp_block_start: SaSint,
5907    omp_block_size: SaSint,
5908) {
5909    let mut i = (omp_block_start + omp_block_size - 1) as isize;
5910    let mut j = (omp_block_start + 2 * 64 + 1) as isize;
5911    while i >= j {
5912        for current in [i, i - 1] {
5913            let current = current as usize;
5914            let mut p = sa[current];
5915            sa[current] = p & SAINT_MAX;
5916            if p > 0 {
5917                p -= 1;
5918                let p_usize = p as usize;
5919                let bucket = t[p_usize] as usize;
5920                induction_bucket[bucket] -= 1;
5921                let slot = induction_bucket[bucket] as usize;
5922                sa[slot] = p
5923                    | ((usize::from(t[p_usize - usize::from(p > 0)] > t[p_usize]) as SaSint)
5924                        << (SAINT_BIT - 1));
5925            }
5926        }
5927        i -= 2;
5928    }
5929
5930    j -= 2 * 64 + 1;
5931    while i >= j {
5932        let current = i as usize;
5933        let mut p = sa[current];
5934        sa[current] = p & SAINT_MAX;
5935        if p > 0 {
5936            p -= 1;
5937            let p_usize = p as usize;
5938            let bucket = t[p_usize] as usize;
5939            induction_bucket[bucket] -= 1;
5940            let slot = induction_bucket[bucket] as usize;
5941            sa[slot] = p
5942                | ((usize::from(t[p_usize - usize::from(p > 0)] > t[p_usize]) as SaSint)
5943                    << (SAINT_BIT - 1));
5944        }
5945        i -= 1;
5946    }
5947}
5948
5949fn final_sorting_scan_right_to_left_32s_block_gather(
5950    t: &[SaSint],
5951    sa: &mut [SaSint],
5952    cache: &mut [ThreadCache],
5953    omp_block_start: SaSint,
5954    omp_block_size: SaSint,
5955) {
5956    if omp_block_size <= 0 {
5957        return;
5958    }
5959
5960    let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5961    let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5962    for offset in 0..size {
5963        let current = start + offset;
5964        let mut symbol = SAINT_MIN;
5965        let mut p = sa[current];
5966        sa[current] = p & SAINT_MAX;
5967        if p > 0 {
5968            p -= 1;
5969            let p_usize = p as usize;
5970            cache[offset].index = p
5971                | ((usize::from(t[p_usize - usize::from(p > 0)] > t[p_usize]) as SaSint)
5972                    << (SAINT_BIT - 1));
5973            symbol = t[p_usize];
5974        }
5975        cache[offset].symbol = symbol;
5976    }
5977}
5978
5979fn final_sorting_scan_right_to_left_32s_block_sort(
5980    t: &[SaSint],
5981    induction_bucket: &mut [SaSint],
5982    cache: &mut [ThreadCache],
5983    omp_block_start: SaSint,
5984    omp_block_size: SaSint,
5985) {
5986    if omp_block_size <= 0 {
5987        return;
5988    }
5989
5990    let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5991    let block_end = omp_block_start + omp_block_size;
5992    let mut offset = size;
5993
5994    while offset > 0 {
5995        offset -= 1;
5996        let v = cache[offset].symbol;
5997        if v >= 0 {
5998            let bucket_index = v as usize;
5999            induction_bucket[bucket_index] -= 1;
6000            let target = induction_bucket[bucket_index];
6001            cache[offset].symbol = target;
6002            if target >= omp_block_start && target < block_end {
6003                let ni = usize::try_from(target - omp_block_start)
6004                    .expect("cache slot must be non-negative");
6005                let mut np = cache[offset].index;
6006                cache[offset].index = np & SAINT_MAX;
6007                if np > 0 {
6008                    np -= 1;
6009                    let np_usize = np as usize;
6010                    cache[ni].index = np
6011                        | ((usize::from(t[np_usize - usize::from(np > 0)] > t[np_usize])
6012                            as SaSint)
6013                            << (SAINT_BIT - 1));
6014                    cache[ni].symbol = t[np_usize];
6015                }
6016            }
6017        }
6018    }
6019}
6020
6021fn final_sorting_scan_right_to_left_32s_block_omp(
6022    t: &[SaSint],
6023    sa: &mut [SaSint],
6024    buckets: &mut [SaSint],
6025    cache: &mut [ThreadCache],
6026    block_start: SaSint,
6027    block_size: SaSint,
6028    threads: SaSint,
6029) {
6030    if threads <= 1 || block_size < 16_384 {
6031        final_sorting_scan_right_to_left_32s(t, sa, buckets, block_start, block_size);
6032        return;
6033    }
6034
6035    final_sorting_scan_right_to_left_32s_block_gather(t, sa, cache, block_start, block_size);
6036    final_sorting_scan_right_to_left_32s_block_sort(t, buckets, cache, block_start, block_size);
6037
6038    let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
6039    let threads_usize = usize::try_from(threads.max(1)).expect("threads must be positive");
6040    let omp_num_threads = threads_usize.min(block_size_usize.max(1));
6041    let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
6042    {
6043        let sa_ptr = SyncMutPtr::new(sa);
6044        let cache_ptr = SyncMutPtr::new(cache);
6045        run_rayon_with_threads(omp_num_threads, || {
6046            (0..omp_num_threads)
6047                .into_par_iter()
6048                .for_each(|omp_thread_num| {
6049                    let omp_block_start = omp_thread_num * omp_block_stride;
6050                    let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
6051                        omp_block_stride
6052                    } else {
6053                        block_size_usize - omp_block_start
6054                    };
6055                    let sa = unsafe { sa_ptr.as_slice() };
6056                    let cache = unsafe { cache_ptr.as_slice() };
6057                    compact_and_place_cached_suffixes(
6058                        sa,
6059                        cache,
6060                        omp_block_start as SaSint,
6061                        omp_block_size as SaSint,
6062                    );
6063                });
6064        });
6065    }
6066}
6067
6068fn final_sorting_scan_right_to_left_32s_omp(
6069    t: &[SaSint],
6070    sa: &mut [SaSint],
6071    n: SaSint,
6072    induction_bucket: &mut [SaSint],
6073    threads: SaSint,
6074    thread_state: &mut [ThreadState],
6075) {
6076    if threads == 1 || n < 65536 || thread_state.is_empty() {
6077        final_sorting_scan_right_to_left_32s(t, sa, induction_bucket, 0, n);
6078        return;
6079    }
6080
6081    let threads_usize = usize::try_from(threads)
6082        .expect("threads must be non-negative")
6083        .max(1);
6084    let block_span = threads_usize * PER_THREAD_CACHE_SIZE;
6085    let mut cache = vec![ThreadCache::default(); block_span];
6086    let mut block_start = n - 1;
6087    while block_start >= 0 {
6088        let block_end = (block_start - block_span as SaSint).max(-1);
6089        final_sorting_scan_right_to_left_32s_block_omp(
6090            t,
6091            sa,
6092            induction_bucket,
6093            &mut cache,
6094            block_end + 1,
6095            block_start - block_end,
6096            threads,
6097        );
6098        block_start = block_end;
6099    }
6100}
6101
6102fn induce_final_order_32s_6k(
6103    t: &[SaSint],
6104    sa: &mut [SaSint],
6105    n: SaSint,
6106    k: SaSint,
6107    buckets: &mut [SaSint],
6108    threads: SaSint,
6109    thread_state: &mut [ThreadState],
6110) {
6111    let k = k as usize;
6112    final_sorting_scan_left_to_right_32s_omp(
6113        t,
6114        sa,
6115        n,
6116        &mut buckets[4 * k..5 * k],
6117        threads,
6118        thread_state,
6119    );
6120    final_sorting_scan_right_to_left_32s_omp(
6121        t,
6122        sa,
6123        n,
6124        &mut buckets[5 * k..6 * k],
6125        threads,
6126        thread_state,
6127    );
6128}
6129
6130fn induce_final_order_32s_4k(
6131    t: &[SaSint],
6132    sa: &mut [SaSint],
6133    n: SaSint,
6134    k: SaSint,
6135    buckets: &mut [SaSint],
6136    threads: SaSint,
6137    thread_state: &mut [ThreadState],
6138) {
6139    let k = k as usize;
6140    final_sorting_scan_left_to_right_32s_omp(
6141        t,
6142        sa,
6143        n,
6144        &mut buckets[2 * k..3 * k],
6145        threads,
6146        thread_state,
6147    );
6148    final_sorting_scan_right_to_left_32s_omp(
6149        t,
6150        sa,
6151        n,
6152        &mut buckets[3 * k..4 * k],
6153        threads,
6154        thread_state,
6155    );
6156}
6157
6158fn induce_final_order_32s_2k(
6159    t: &[SaSint],
6160    sa: &mut [SaSint],
6161    n: SaSint,
6162    k: SaSint,
6163    buckets: &mut [SaSint],
6164    threads: SaSint,
6165    thread_state: &mut [ThreadState],
6166) {
6167    let k = k as usize;
6168    final_sorting_scan_left_to_right_32s_omp(
6169        t,
6170        sa,
6171        n,
6172        &mut buckets[k..2 * k],
6173        threads,
6174        thread_state,
6175    );
6176    final_sorting_scan_right_to_left_32s_omp(t, sa, n, &mut buckets[..k], threads, thread_state);
6177}
6178
6179fn induce_final_order_32s_1k(
6180    t: &[SaSint],
6181    sa: &mut [SaSint],
6182    n: SaSint,
6183    k: SaSint,
6184    buckets: &mut [SaSint],
6185    threads: SaSint,
6186    thread_state: &mut [ThreadState],
6187) {
6188    count_suffixes_32s(t, n, k, buckets);
6189    initialize_buckets_start_32s_1k(k, buckets);
6190    final_sorting_scan_left_to_right_32s_omp(t, sa, n, buckets, threads, thread_state);
6191
6192    count_suffixes_32s(t, n, k, buckets);
6193    initialize_buckets_end_32s_1k(k, buckets);
6194    final_sorting_scan_right_to_left_32s_omp(t, sa, n, buckets, threads, thread_state);
6195}
6196
6197fn clear_lms_suffixes_omp(
6198    sa: &mut [SaSint],
6199    n: SaSint,
6200    k: SaSint,
6201    bucket_start: &[SaSint],
6202    bucket_end: &[SaSint],
6203    threads: SaSint,
6204) {
6205    let k_usize = usize::try_from(k).expect("k must be non-negative");
6206    let thread_count = if threads > 1 && n >= 65536 {
6207        usize::try_from(threads).expect("threads must be positive")
6208    } else {
6209        1
6210    };
6211    {
6212        let sa_ptr = SyncMutPtr::new(sa);
6213        let bucket_start_ref: &[SaSint] = bucket_start;
6214        let bucket_end_ref: &[SaSint] = bucket_end;
6215        run_rayon_with_threads(thread_count, || {
6216            (0..thread_count).into_par_iter().for_each(|t| {
6217                let mut c = t;
6218                let sa = unsafe { sa_ptr.as_slice() };
6219                while c < k_usize {
6220                    if bucket_end_ref[c] > bucket_start_ref[c] {
6221                        let start = bucket_start_ref[c] as usize;
6222                        let end = bucket_end_ref[c] as usize;
6223                        sa[start..end].fill(0);
6224                    }
6225                    c += thread_count;
6226                }
6227            });
6228        });
6229    }
6230}
6231
6232fn final_gsa_scan_right_to_left_16u(
6233    t: &[u16],
6234    sa: &mut [SaSint],
6235    induction_bucket: &mut [SaSint],
6236    omp_block_start: SaSint,
6237    omp_block_size: SaSint,
6238) {
6239    let mut i = (omp_block_start + omp_block_size - 1) as isize;
6240    let mut j = (omp_block_start + 64 + 1) as isize;
6241    while i >= j {
6242        final_sorting_rtl_step(t, sa, induction_bucket, i as usize, true);
6243        final_sorting_rtl_step(t, sa, induction_bucket, (i - 1) as usize, true);
6244        i -= 2;
6245    }
6246    j -= 64 + 1;
6247    while i >= j {
6248        final_sorting_rtl_step(t, sa, induction_bucket, i as usize, true);
6249        i -= 1;
6250    }
6251}
6252
6253fn final_sorting_ltr_step(
6254    t: &[u16],
6255    sa: &mut [SaSint],
6256    induction_bucket: &mut [SaSint],
6257    index: usize,
6258) {
6259    let mut p = sa[index];
6260    sa[index] = p ^ SAINT_MIN;
6261    if p > 0 {
6262        p -= 1;
6263        let c = t[p as usize] as usize;
6264        let mark = if t[(p - SaSint::from(p > 0)) as usize] < t[p as usize] {
6265            SAINT_MIN
6266        } else {
6267            0
6268        };
6269        let dst = induction_bucket[c] as usize;
6270        sa[dst] = p | mark;
6271        induction_bucket[c] += 1;
6272    }
6273}
6274
6275fn final_sorting_rtl_step(
6276    t: &[u16],
6277    sa: &mut [SaSint],
6278    induction_bucket: &mut [SaSint],
6279    index: usize,
6280    gsa: bool,
6281) {
6282    let mut p = sa[index];
6283    sa[index] = p & SAINT_MAX;
6284    if p > 0 && (!gsa || t[(p - 1) as usize] > 0) {
6285        p -= 1;
6286        let c = t[p as usize] as usize;
6287        let mark = if t[(p - SaSint::from(p > 0)) as usize] > t[p as usize] {
6288            SAINT_MIN
6289        } else {
6290            0
6291        };
6292        induction_bucket[c] -= 1;
6293        sa[induction_bucket[c] as usize] = p | mark;
6294    }
6295}
6296
6297fn final_bwt_scan_left_to_right_16u(
6298    t: &[u16],
6299    sa: &mut [SaSint],
6300    induction_bucket: &mut [SaSint],
6301    omp_block_start: SaSint,
6302    omp_block_size: SaSint,
6303) {
6304    let mut i = omp_block_start as isize;
6305    let mut j = (omp_block_start + omp_block_size - 64 - 1) as isize;
6306    while i < j {
6307        final_bwt_ltr_step(t, sa, induction_bucket, i as usize);
6308        final_bwt_ltr_step(t, sa, induction_bucket, (i + 1) as usize);
6309        i += 2;
6310    }
6311    j += 64 + 1;
6312    while i < j {
6313        final_bwt_ltr_step(t, sa, induction_bucket, i as usize);
6314        i += 1;
6315    }
6316}
6317
6318fn final_bwt_scan_right_to_left_16u(
6319    t: &[u16],
6320    sa: &mut [SaSint],
6321    induction_bucket: &mut [SaSint],
6322    omp_block_start: SaSint,
6323    omp_block_size: SaSint,
6324) -> SaSint {
6325    let mut index = -1;
6326    let mut i = (omp_block_start + omp_block_size - 1) as isize;
6327    let mut j = (omp_block_start + 64 + 1) as isize;
6328    while i >= j {
6329        final_bwt_rtl_step(t, sa, induction_bucket, i as usize, &mut index);
6330        final_bwt_rtl_step(t, sa, induction_bucket, (i - 1) as usize, &mut index);
6331        i -= 2;
6332    }
6333    j -= 64 + 1;
6334    while i >= j {
6335        final_bwt_rtl_step(t, sa, induction_bucket, i as usize, &mut index);
6336        i -= 1;
6337    }
6338    index
6339}
6340
6341fn final_bwt_aux_scan_left_to_right_16u(
6342    t: &[u16],
6343    sa: &mut [SaSint],
6344    rm: SaSint,
6345    i_sample: &mut [SaSint],
6346    induction_bucket: &mut [SaSint],
6347    omp_block_start: SaSint,
6348    omp_block_size: SaSint,
6349) {
6350    let mut i = omp_block_start as isize;
6351    let mut j = (omp_block_start + omp_block_size - 64 - 1) as isize;
6352    while i < j {
6353        final_bwt_aux_ltr_step(t, sa, rm, i_sample, induction_bucket, i as usize);
6354        final_bwt_aux_ltr_step(t, sa, rm, i_sample, induction_bucket, (i + 1) as usize);
6355        i += 2;
6356    }
6357    j += 64 + 1;
6358    while i < j {
6359        final_bwt_aux_ltr_step(t, sa, rm, i_sample, induction_bucket, i as usize);
6360        i += 1;
6361    }
6362}
6363
6364fn final_bwt_aux_scan_right_to_left_16u(
6365    t: &[u16],
6366    sa: &mut [SaSint],
6367    rm: SaSint,
6368    i_sample: &mut [SaSint],
6369    induction_bucket: &mut [SaSint],
6370    omp_block_start: SaSint,
6371    omp_block_size: SaSint,
6372) {
6373    let mut i = (omp_block_start + omp_block_size - 1) as isize;
6374    let mut j = (omp_block_start + 64 + 1) as isize;
6375    while i >= j {
6376        final_bwt_aux_rtl_step(t, sa, rm, i_sample, induction_bucket, i as usize);
6377        final_bwt_aux_rtl_step(t, sa, rm, i_sample, induction_bucket, (i - 1) as usize);
6378        i -= 2;
6379    }
6380    j -= 64 + 1;
6381    while i >= j {
6382        final_bwt_aux_rtl_step(t, sa, rm, i_sample, induction_bucket, i as usize);
6383        i -= 1;
6384    }
6385}
6386
6387fn renumber_lms_suffixes_16u(
6388    sa: &mut [SaSint],
6389    m: SaSint,
6390    mut name: SaSint,
6391    omp_block_start: SaSint,
6392    omp_block_size: SaSint,
6393) -> SaSint {
6394    let mut i = omp_block_start as isize;
6395    let mut j = (omp_block_start + omp_block_size - 64 - 3) as isize;
6396    while i < j {
6397        let p0 = sa[i as usize];
6398        sa[m as usize + ((p0 & SAINT_MAX) >> 1) as usize] = name | SAINT_MIN;
6399        name += SaSint::from(p0 < 0);
6400
6401        let p1 = sa[(i + 1) as usize];
6402        sa[m as usize + ((p1 & SAINT_MAX) >> 1) as usize] = name | SAINT_MIN;
6403        name += SaSint::from(p1 < 0);
6404
6405        let p2 = sa[(i + 2) as usize];
6406        sa[m as usize + ((p2 & SAINT_MAX) >> 1) as usize] = name | SAINT_MIN;
6407        name += SaSint::from(p2 < 0);
6408
6409        let p3 = sa[(i + 3) as usize];
6410        sa[m as usize + ((p3 & SAINT_MAX) >> 1) as usize] = name | SAINT_MIN;
6411        name += SaSint::from(p3 < 0);
6412
6413        i += 4;
6414    }
6415
6416    j += 64 + 3;
6417    while i < j {
6418        let p = sa[i as usize];
6419        sa[m as usize + ((p & SAINT_MAX) >> 1) as usize] = name | SAINT_MIN;
6420        name += SaSint::from(p < 0);
6421        i += 1;
6422    }
6423
6424    name
6425}
6426
6427fn renumber_lms_suffixes_16u_omp(
6428    sa: &mut [SaSint],
6429    m: SaSint,
6430    threads: SaSint,
6431    thread_state: &mut [ThreadState],
6432) -> SaSint {
6433    if threads == 1 || m < 65_536 || thread_state.is_empty() {
6434        return renumber_lms_suffixes_16u(sa, m, 0, 0, m);
6435    }
6436
6437    let thread_count = usize::try_from(threads)
6438        .expect("threads must be non-negative")
6439        .min(thread_state.len());
6440    let block_stride = (m / thread_count as SaSint) & !15;
6441
6442    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
6443        let block_start = thread as SaSint * block_stride;
6444        let block_size = if thread + 1 < thread_count {
6445            block_stride
6446        } else {
6447            m - block_start
6448        };
6449        state.count = count_negative_marked_suffixes(sa, block_start, block_size);
6450    }
6451
6452    let mut name = 0;
6453    for thread in 0..thread_count {
6454        let block_start = thread as SaSint * block_stride;
6455        let block_size = if thread + 1 < thread_count {
6456            block_stride
6457        } else {
6458            m - block_start
6459        };
6460        renumber_lms_suffixes_16u(sa, m, name, block_start, block_size);
6461        name += thread_state[thread].count;
6462    }
6463
6464    name
6465}
6466
6467fn gather_marked_lms_suffixes(
6468    sa: &mut [SaSint],
6469    m: SaSint,
6470    mut l: isize,
6471    omp_block_start: isize,
6472    omp_block_size: isize,
6473) -> isize {
6474    if omp_block_size <= 0 {
6475        return l;
6476    }
6477
6478    l -= 1;
6479    let mut i = m as isize + omp_block_start + omp_block_size - 1;
6480    let mut j = m as isize + omp_block_start + 3;
6481    while i >= j {
6482        let s0 = sa[i as usize];
6483        sa[l as usize] = s0 & SAINT_MAX;
6484        l -= isize::from(s0 < 0);
6485
6486        let s1 = sa[(i - 1) as usize];
6487        sa[l as usize] = s1 & SAINT_MAX;
6488        l -= isize::from(s1 < 0);
6489
6490        let s2 = sa[(i - 2) as usize];
6491        sa[l as usize] = s2 & SAINT_MAX;
6492        l -= isize::from(s2 < 0);
6493
6494        let s3 = sa[(i - 3) as usize];
6495        sa[l as usize] = s3 & SAINT_MAX;
6496        l -= isize::from(s3 < 0);
6497
6498        i -= 4;
6499    }
6500
6501    j -= 3;
6502    while i >= j {
6503        let s = sa[i as usize];
6504        sa[l as usize] = s & SAINT_MAX;
6505        l -= isize::from(s < 0);
6506        i -= 1;
6507    }
6508
6509    l + 1
6510}
6511
6512fn gather_marked_lms_suffixes_omp(
6513    sa: &mut [SaSint],
6514    n: SaSint,
6515    m: SaSint,
6516    fs: SaSint,
6517    threads: SaSint,
6518    thread_state: &mut [ThreadState],
6519) {
6520    let half_n = n >> 1;
6521    if threads == 1 || n < 131_072 || thread_state.is_empty() {
6522        let _ = gather_marked_lms_suffixes(sa, m, (n + fs) as isize, 0, half_n as isize);
6523        return;
6524    }
6525
6526    let thread_count = usize::try_from(threads)
6527        .expect("threads must be non-negative")
6528        .min(thread_state.len());
6529    let block_stride = (half_n / thread_count as SaSint) & !15;
6530
6531    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
6532        let block_start = thread as SaSint * block_stride;
6533        let block_size = if thread + 1 < thread_count {
6534            block_stride
6535        } else {
6536            half_n - block_start
6537        };
6538        let local_end = if thread + 1 < thread_count {
6539            m + block_start + block_size
6540        } else {
6541            n + fs
6542        } as isize;
6543        let gathered_position =
6544            gather_marked_lms_suffixes(sa, m, local_end, block_start as isize, block_size as isize);
6545        state.position = gathered_position as SaSint;
6546        state.count = (local_end - gathered_position) as SaSint;
6547    }
6548
6549    let mut position = (n + fs) as isize;
6550    for thread in (0..thread_count).rev() {
6551        let count =
6552            usize::try_from(thread_state[thread].count).expect("count must be non-negative");
6553        position -= thread_state[thread].count as isize;
6554        if thread + 1 != thread_count && count > 0 {
6555            let src = usize::try_from(thread_state[thread].position)
6556                .expect("position must be non-negative");
6557            let dst = position as usize;
6558            sa.copy_within(src..src + count, dst);
6559        }
6560    }
6561}
6562
6563fn renumber_and_gather_lms_suffixes_omp(
6564    sa: &mut [SaSint],
6565    n: SaSint,
6566    m: SaSint,
6567    fs: SaSint,
6568    threads: SaSint,
6569    thread_state: &mut [ThreadState],
6570) -> SaSint {
6571    let m_usize = m as usize;
6572    let half_n = (n >> 1) as usize;
6573    sa[m_usize..m_usize + half_n].fill(0);
6574
6575    let name = renumber_lms_suffixes_16u_omp(sa, m, threads, thread_state);
6576    if name < m {
6577        gather_marked_lms_suffixes_omp(sa, n, m, fs, threads, thread_state);
6578    } else {
6579        for item in &mut sa[..m_usize] {
6580            *item &= SAINT_MAX;
6581        }
6582    }
6583
6584    name
6585}
6586
6587fn reconstruct_lms_suffixes(
6588    sa: &mut [SaSint],
6589    n: SaSint,
6590    m: SaSint,
6591    omp_block_start: isize,
6592    omp_block_size: isize,
6593) {
6594    if omp_block_size <= 0 {
6595        return;
6596    }
6597
6598    let base = (n - m) as usize;
6599    let mut i = omp_block_start;
6600    let mut j = omp_block_start + omp_block_size - 64 - 3;
6601    while i < j {
6602        let iu = i as usize;
6603        let s0 = sa[iu] as usize;
6604        let s1 = sa[iu + 1] as usize;
6605        let s2 = sa[iu + 2] as usize;
6606        let s3 = sa[iu + 3] as usize;
6607        sa[iu] = sa[base + s0];
6608        sa[iu + 1] = sa[base + s1];
6609        sa[iu + 2] = sa[base + s2];
6610        sa[iu + 3] = sa[base + s3];
6611        i += 4;
6612    }
6613
6614    j += 64 + 3;
6615    while i < j {
6616        let iu = i as usize;
6617        let s = sa[iu] as usize;
6618        sa[iu] = sa[base + s];
6619        i += 1;
6620    }
6621}
6622
6623fn reconstruct_lms_suffixes_omp(sa: &mut [SaSint], n: SaSint, m: SaSint, threads: SaSint) {
6624    if threads == 1 || m < 65_536 {
6625        reconstruct_lms_suffixes(sa, n, m, 0, m as isize);
6626        return;
6627    }
6628
6629    let thread_count = threads as usize;
6630    let block_stride = (m / threads) & !15;
6631    for thread in 0..thread_count {
6632        let block_start = thread as SaSint * block_stride;
6633        let block_size = if thread + 1 < thread_count {
6634            block_stride
6635        } else {
6636            m - block_start
6637        };
6638        reconstruct_lms_suffixes(sa, n, m, block_start as isize, block_size as isize);
6639    }
6640}
6641
6642fn renumber_distinct_lms_suffixes_32s_4k(
6643    sa: &mut [SaSint],
6644    m: SaSint,
6645    mut name: SaSint,
6646    omp_block_start: isize,
6647    omp_block_size: isize,
6648) -> SaSint {
6649    if omp_block_size <= 0 {
6650        return name;
6651    }
6652
6653    let m_usize = m as usize;
6654    let start = omp_block_start as usize;
6655    let size = omp_block_size as usize;
6656    let (sa_head, sam) = sa.split_at_mut(m_usize);
6657    let mut i = start;
6658    let mut j = start + size.saturating_sub(64 + 3);
6659    let mut p3 = 0;
6660
6661    while i < j {
6662        let p0 = sa_head[i];
6663        sa_head[i] = p0 & SAINT_MAX;
6664        sam[(sa_head[i] >> 1) as usize] = name | (p0 & p3 & SAINT_MIN);
6665        name += SaSint::from(p0 < 0);
6666
6667        let p1 = sa_head[i + 1];
6668        sa_head[i + 1] = p1 & SAINT_MAX;
6669        sam[(sa_head[i + 1] >> 1) as usize] = name | (p1 & p0 & SAINT_MIN);
6670        name += SaSint::from(p1 < 0);
6671
6672        let p2 = sa_head[i + 2];
6673        sa_head[i + 2] = p2 & SAINT_MAX;
6674        sam[(sa_head[i + 2] >> 1) as usize] = name | (p2 & p1 & SAINT_MIN);
6675        name += SaSint::from(p2 < 0);
6676
6677        p3 = sa_head[i + 3];
6678        sa_head[i + 3] = p3 & SAINT_MAX;
6679        sam[(sa_head[i + 3] >> 1) as usize] = name | (p3 & p2 & SAINT_MIN);
6680        name += SaSint::from(p3 < 0);
6681
6682        i += 4;
6683    }
6684
6685    j = start + size;
6686    while i < j {
6687        let p2 = p3;
6688        p3 = sa_head[i];
6689        sa_head[i] = p3 & SAINT_MAX;
6690        sam[(sa_head[i] >> 1) as usize] = name | (p3 & p2 & SAINT_MIN);
6691        name += SaSint::from(p3 < 0);
6692        i += 1;
6693    }
6694
6695    name
6696}
6697
6698fn mark_distinct_lms_suffixes_32s(
6699    sa: &mut [SaSint],
6700    m: SaSint,
6701    omp_block_start: isize,
6702    omp_block_size: isize,
6703) {
6704    if omp_block_size <= 0 {
6705        return;
6706    }
6707
6708    let mut i = m as usize + omp_block_start as usize;
6709    let mut j = i + (omp_block_size as usize).saturating_sub(3);
6710    let mut p3 = 0;
6711    while i < j {
6712        let mut p0 = sa[i];
6713        sa[i] = p0 & (p3 | SAINT_MAX);
6714        p0 = if p0 == 0 { p3 } else { p0 };
6715
6716        let mut p1 = sa[i + 1];
6717        sa[i + 1] = p1 & (p0 | SAINT_MAX);
6718        p1 = if p1 == 0 { p0 } else { p1 };
6719
6720        let mut p2 = sa[i + 2];
6721        sa[i + 2] = p2 & (p1 | SAINT_MAX);
6722        p2 = if p2 == 0 { p1 } else { p2 };
6723
6724        p3 = sa[i + 3];
6725        sa[i + 3] = p3 & (p2 | SAINT_MAX);
6726        p3 = if p3 == 0 { p2 } else { p3 };
6727        i += 4;
6728    }
6729
6730    j = m as usize + omp_block_start as usize + omp_block_size as usize;
6731    while i < j {
6732        let p2 = p3;
6733        p3 = sa[i];
6734        sa[i] = p3 & (p2 | SAINT_MAX);
6735        p3 = if p3 == 0 { p2 } else { p3 };
6736        i += 1;
6737    }
6738}
6739
6740fn clamp_lms_suffixes_length_32s(
6741    sa: &mut [SaSint],
6742    m: SaSint,
6743    omp_block_start: isize,
6744    omp_block_size: isize,
6745) {
6746    if omp_block_size <= 0 {
6747        return;
6748    }
6749
6750    let mut i = m as usize + omp_block_start as usize;
6751    let mut j = i + (omp_block_size as usize).saturating_sub(3);
6752    while i < j {
6753        let s0 = sa[i];
6754        sa[i] = if s0 < 0 { s0 } else { 0 } & SAINT_MAX;
6755
6756        let s1 = sa[i + 1];
6757        sa[i + 1] = if s1 < 0 { s1 } else { 0 } & SAINT_MAX;
6758
6759        let s2 = sa[i + 2];
6760        sa[i + 2] = if s2 < 0 { s2 } else { 0 } & SAINT_MAX;
6761
6762        let s3 = sa[i + 3];
6763        sa[i + 3] = if s3 < 0 { s3 } else { 0 } & SAINT_MAX;
6764
6765        i += 4;
6766    }
6767
6768    j = m as usize + omp_block_start as usize + omp_block_size as usize;
6769    while i < j {
6770        let s = sa[i];
6771        sa[i] = if s < 0 { s } else { 0 } & SAINT_MAX;
6772        i += 1;
6773    }
6774}
6775
6776fn renumber_distinct_lms_suffixes_32s_4k_omp(
6777    sa: &mut [SaSint],
6778    m: SaSint,
6779    threads: SaSint,
6780    thread_state: &mut [ThreadState],
6781) -> SaSint {
6782    if threads == 1 || m < 65_536 || thread_state.is_empty() {
6783        return renumber_distinct_lms_suffixes_32s_4k(sa, m, 1, 0, m as isize) - 1;
6784    }
6785
6786    let thread_count = usize::try_from(threads)
6787        .expect("threads must be non-negative")
6788        .min(thread_state.len());
6789    let block_stride = (m / thread_count as SaSint) & !15;
6790
6791    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
6792        let block_start = thread as SaSint * block_stride;
6793        let block_size = if thread + 1 < thread_count {
6794            block_stride
6795        } else {
6796            m - block_start
6797        };
6798        state.count = count_negative_marked_suffixes(sa, block_start, block_size);
6799    }
6800
6801    let mut count = 1;
6802    for thread in 0..thread_count {
6803        let block_start = thread as SaSint * block_stride;
6804        let block_size = if thread + 1 < thread_count {
6805            block_stride
6806        } else {
6807            m - block_start
6808        };
6809        renumber_distinct_lms_suffixes_32s_4k(
6810            sa,
6811            m,
6812            count,
6813            block_start as isize,
6814            block_size as isize,
6815        );
6816        count += thread_state[thread].count;
6817    }
6818
6819    count - 1
6820}
6821
6822fn mark_distinct_lms_suffixes_32s_omp(sa: &mut [SaSint], n: SaSint, m: SaSint, threads: SaSint) {
6823    let half_n = n >> 1;
6824    if threads == 1 || n < 131_072 {
6825        mark_distinct_lms_suffixes_32s(sa, m, 0, half_n as isize);
6826        return;
6827    }
6828
6829    let thread_count = threads as usize;
6830    let block_stride = (half_n / threads) & !15;
6831    for thread in 0..thread_count {
6832        let block_start = thread as SaSint * block_stride;
6833        let block_size = if thread + 1 < thread_count {
6834            block_stride
6835        } else {
6836            half_n - block_start
6837        };
6838        mark_distinct_lms_suffixes_32s(sa, m, block_start as isize, block_size as isize);
6839    }
6840}
6841
6842fn clamp_lms_suffixes_length_32s_omp(sa: &mut [SaSint], n: SaSint, m: SaSint, threads: SaSint) {
6843    let half_n = n >> 1;
6844    if threads == 1 || n < 131_072 {
6845        clamp_lms_suffixes_length_32s(sa, m, 0, half_n as isize);
6846        return;
6847    }
6848
6849    let thread_count = threads as usize;
6850    let block_stride = (half_n / threads) & !15;
6851    for thread in 0..thread_count {
6852        let block_start = thread as SaSint * block_stride;
6853        let block_size = if thread + 1 < thread_count {
6854            block_stride
6855        } else {
6856            half_n - block_start
6857        };
6858        clamp_lms_suffixes_length_32s(sa, m, block_start as isize, block_size as isize);
6859    }
6860}
6861
6862fn renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
6863    sa: &mut [SaSint],
6864    n: SaSint,
6865    m: SaSint,
6866    threads: SaSint,
6867    thread_state: &mut [ThreadState],
6868) -> SaSint {
6869    let m_usize = m as usize;
6870    let half_n = (n >> 1) as usize;
6871    sa[m_usize..m_usize + half_n].fill(0);
6872
6873    let name = renumber_distinct_lms_suffixes_32s_4k_omp(sa, m, threads, thread_state);
6874    if name < m {
6875        mark_distinct_lms_suffixes_32s_omp(sa, n, m, threads);
6876    }
6877
6878    name
6879}
6880
6881fn renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(
6882    t: &[SaSint],
6883    sa: &mut [SaSint],
6884    n: SaSint,
6885    m: SaSint,
6886    threads: SaSint,
6887) -> SaSint {
6888    let m_usize = m as usize;
6889    let n_usize = n as usize;
6890
6891    gather_lms_suffixes_32s(t, sa, n);
6892    sa[m_usize..n_usize - m_usize].fill(0);
6893
6894    let mut i = n - m;
6895    let mut j = n - 1 - 64 - 3;
6896    while i < j {
6897        let s0 = (sa[i as usize] as SaUint >> 1) as usize;
6898        let s1 = (sa[(i + 1) as usize] as SaUint >> 1) as usize;
6899        let s2 = (sa[(i + 2) as usize] as SaUint >> 1) as usize;
6900        let s3 = (sa[(i + 3) as usize] as SaUint >> 1) as usize;
6901        sa[m_usize + s0] = sa[(i + 1) as usize] - sa[i as usize] + 1 + SAINT_MIN;
6902        sa[m_usize + s1] = sa[(i + 2) as usize] - sa[(i + 1) as usize] + 1 + SAINT_MIN;
6903        sa[m_usize + s2] = sa[(i + 3) as usize] - sa[(i + 2) as usize] + 1 + SAINT_MIN;
6904        sa[m_usize + s3] = sa[(i + 4) as usize] - sa[(i + 3) as usize] + 1 + SAINT_MIN;
6905        i += 4;
6906    }
6907
6908    j += 64 + 3;
6909    while i < j {
6910        let s = (sa[i as usize] as SaUint >> 1) as usize;
6911        sa[m_usize + s] = sa[(i + 1) as usize] - sa[i as usize] + 1 + SAINT_MIN;
6912        i += 1;
6913    }
6914
6915    let tail = (sa[n_usize - 1] as SaUint >> 1) as usize;
6916    sa[m_usize + tail] = 1 + SAINT_MIN;
6917
6918    clamp_lms_suffixes_length_32s_omp(sa, n, m, threads);
6919
6920    let mut name = 1;
6921    if m_usize > 0 {
6922        let mut i = 1usize;
6923        let mut j = m_usize.saturating_sub(64 + 1);
6924        let mut p = sa[0] as usize;
6925        let mut plen = sa[m_usize + (p >> 1)];
6926        let mut pdiff = SAINT_MIN;
6927
6928        while i < j {
6929            let q = sa[i] as usize;
6930            let qlen = sa[m_usize + (q >> 1)];
6931            let mut qdiff = SAINT_MIN;
6932            if plen == qlen {
6933                let mut l = 0;
6934                while l < qlen as usize {
6935                    if t[p + l] != t[q + l] {
6936                        break;
6937                    }
6938                    l += 1;
6939                }
6940                qdiff = ((l as SaSint) - qlen) & SAINT_MIN;
6941            }
6942            sa[m_usize + (p >> 1)] = name | (pdiff & qdiff);
6943            name += SaSint::from(qdiff < 0);
6944
6945            p = sa[i + 1] as usize;
6946            plen = sa[m_usize + (p >> 1)];
6947            pdiff = SAINT_MIN;
6948            if qlen == plen {
6949                let mut l = 0;
6950                while l < plen as usize {
6951                    if t[q + l] != t[p + l] {
6952                        break;
6953                    }
6954                    l += 1;
6955                }
6956                pdiff = ((l as SaSint) - plen) & SAINT_MIN;
6957            }
6958            sa[m_usize + (q >> 1)] = name | (qdiff & pdiff);
6959            name += SaSint::from(pdiff < 0);
6960            i += 2;
6961        }
6962
6963        j = m_usize;
6964        while i < j {
6965            let q = sa[i] as usize;
6966            let qlen = sa[m_usize + (q >> 1)];
6967            let mut qdiff = SAINT_MIN;
6968            if plen == qlen {
6969                let mut l = 0;
6970                while l < plen as usize {
6971                    if t[p + l] != t[q + l] {
6972                        break;
6973                    }
6974                    l += 1;
6975                }
6976                qdiff = ((l as SaSint) - plen) & SAINT_MIN;
6977            }
6978            sa[m_usize + (p >> 1)] = name | (pdiff & qdiff);
6979            name += SaSint::from(qdiff < 0);
6980            p = q;
6981            plen = qlen;
6982            pdiff = qdiff;
6983            i += 1;
6984        }
6985
6986        sa[m_usize + (p >> 1)] = name | pdiff;
6987        name += 1;
6988    }
6989
6990    if name <= m {
6991        mark_distinct_lms_suffixes_32s_omp(sa, n, m, threads);
6992    }
6993
6994    name - 1
6995}
6996
6997fn renumber_unique_and_nonunique_lms_suffixes_32s(
6998    t: &mut [SaSint],
6999    sa: &mut [SaSint],
7000    m: SaSint,
7001    mut f: SaSint,
7002    omp_block_start: isize,
7003    omp_block_size: isize,
7004) -> SaSint {
7005    if omp_block_size <= 0 {
7006        return f;
7007    }
7008
7009    let m_usize = m as usize;
7010    let (sa_head, sam) = sa.split_at_mut(m_usize);
7011    let mut i = omp_block_start;
7012    let mut j = omp_block_start + omp_block_size - 128 - 3;
7013    while i < j {
7014        for offset in 0..4 {
7015            let idx = (i + offset) as usize;
7016            let p = sa_head[idx] as SaUint;
7017            let mut s = sam[(p >> 1) as usize];
7018            if s < 0 {
7019                t[p as usize] |= SAINT_MIN;
7020                f += 1;
7021                s = i as SaSint + offset as SaSint + SAINT_MIN + f;
7022            }
7023            sam[(p >> 1) as usize] = s - f;
7024        }
7025        i += 4;
7026    }
7027
7028    j += 128 + 3;
7029    while i < j {
7030        let p = sa_head[i as usize] as SaUint;
7031        let mut s = sam[(p >> 1) as usize];
7032        if s < 0 {
7033            t[p as usize] |= SAINT_MIN;
7034            f += 1;
7035            s = i as SaSint + SAINT_MIN + f;
7036        }
7037        sam[(p >> 1) as usize] = s - f;
7038        i += 1;
7039    }
7040
7041    f
7042}
7043
7044fn compact_unique_and_nonunique_lms_suffixes_32s(
7045    sa: &mut [SaSint],
7046    m: SaSint,
7047    pl: &mut isize,
7048    pr: &mut isize,
7049    omp_block_start: isize,
7050    omp_block_size: isize,
7051) {
7052    if omp_block_size <= 0 {
7053        return;
7054    }
7055
7056    let m_usize = m as usize;
7057    let source: Vec<SaSint> = sa
7058        [m_usize + omp_block_start as usize..m_usize + (omp_block_start + omp_block_size) as usize]
7059        .to_vec();
7060    let mut l = *pl - 1;
7061    let mut r = *pr - 1;
7062
7063    for &p in source.iter().rev() {
7064        sa[l as usize] = p & SAINT_MAX;
7065        l -= isize::from(p < 0);
7066
7067        sa[r as usize] = p.wrapping_sub(1);
7068        r -= isize::from(p > 0);
7069    }
7070
7071    *pl = l + 1;
7072    *pr = r + 1;
7073}
7074
7075fn count_unique_suffixes(
7076    sa: &[SaSint],
7077    m: SaSint,
7078    omp_block_start: isize,
7079    omp_block_size: isize,
7080) -> SaSint {
7081    let base = m as usize;
7082    let start = omp_block_start as usize;
7083    let end = start + omp_block_size as usize;
7084    let mut count = 0;
7085    for i in start..end {
7086        count += SaSint::from(sa[base + ((sa[i] as SaUint) >> 1) as usize] < 0);
7087    }
7088    count
7089}
7090
7091fn renumber_unique_and_nonunique_lms_suffixes_32s_omp(
7092    t: &mut [SaSint],
7093    sa: &mut [SaSint],
7094    m: SaSint,
7095    threads: SaSint,
7096) -> SaSint {
7097    if threads == 1 || m < 65_536 {
7098        return renumber_unique_and_nonunique_lms_suffixes_32s(t, sa, m, 0, 0, m as isize);
7099    }
7100
7101    let thread_count = threads as usize;
7102    let block_stride = (m / threads) & !15;
7103    let mut counts = vec![0; thread_count];
7104
7105    for thread in 0..thread_count {
7106        let block_start = thread as SaSint * block_stride;
7107        let block_size = if thread + 1 < thread_count {
7108            block_stride
7109        } else {
7110            m - block_start
7111        };
7112        counts[thread] = count_unique_suffixes(sa, m, block_start as isize, block_size as isize);
7113    }
7114
7115    let mut f = 0;
7116    for thread in 0..thread_count {
7117        let block_start = thread as SaSint * block_stride;
7118        let block_size = if thread + 1 < thread_count {
7119            block_stride
7120        } else {
7121            m - block_start
7122        };
7123        renumber_unique_and_nonunique_lms_suffixes_32s(
7124            t,
7125            sa,
7126            m,
7127            f,
7128            block_start as isize,
7129            block_size as isize,
7130        );
7131        f += counts[thread];
7132    }
7133
7134    f
7135}
7136
7137fn compact_unique_and_nonunique_lms_suffixes_32s_omp(
7138    sa: &mut [SaSint],
7139    n: SaSint,
7140    m: SaSint,
7141    fs: SaSint,
7142    f: SaSint,
7143    threads: SaSint,
7144) {
7145    let half_n = n >> 1;
7146    if threads == 1 || n < 131_072 || m >= fs {
7147        let mut l = m as isize;
7148        let mut r = (n + fs) as isize;
7149        compact_unique_and_nonunique_lms_suffixes_32s(sa, m, &mut l, &mut r, 0, half_n as isize);
7150    } else {
7151        let thread_count = threads as usize;
7152        let block_stride = (half_n / threads) & !15;
7153        let mut positions = vec![0isize; thread_count];
7154        let mut counts = vec![0isize; thread_count];
7155
7156        for thread in 0..thread_count {
7157            let block_start = thread as SaSint * block_stride;
7158            let block_size = if thread + 1 < thread_count {
7159                block_stride
7160            } else {
7161                half_n - block_start
7162            };
7163            let mut position = (m + half_n + block_start + block_size) as isize;
7164            let mut count = (m + block_start + block_size) as isize;
7165            compact_unique_and_nonunique_lms_suffixes_32s(
7166                sa,
7167                m,
7168                &mut position,
7169                &mut count,
7170                block_start as isize,
7171                block_size as isize,
7172            );
7173            positions[thread] = position;
7174            counts[thread] = count;
7175        }
7176
7177        let mut position = m as isize;
7178        for thread in (0..thread_count).rev() {
7179            let block_end = if thread + 1 < thread_count {
7180                block_stride * (thread as SaSint + 1)
7181            } else {
7182                half_n
7183            };
7184            let count = (m + half_n + block_end) as isize - positions[thread];
7185            if count > 0 {
7186                position -= count;
7187                let src = positions[thread] as usize;
7188                let dst = position as usize;
7189                sa.copy_within(src..src + count as usize, dst);
7190            }
7191        }
7192
7193        let mut position = (n + fs) as isize;
7194        for thread in (0..thread_count).rev() {
7195            let block_end = if thread + 1 < thread_count {
7196                block_stride * (thread as SaSint + 1)
7197            } else {
7198                half_n
7199            };
7200            let count = (m + block_end) as isize - counts[thread];
7201            if count > 0 {
7202                position -= count;
7203                let src = counts[thread] as usize;
7204                let dst = position as usize;
7205                sa.copy_within(src..src + count as usize, dst);
7206            }
7207        }
7208    }
7209
7210    let dst = (n + fs - m) as usize;
7211    let src = (m - f) as usize;
7212    sa.copy_within(src..src + f as usize, dst);
7213}
7214
7215fn compact_lms_suffixes_32s_omp(
7216    t: &mut [SaSint],
7217    sa: &mut [SaSint],
7218    n: SaSint,
7219    m: SaSint,
7220    fs: SaSint,
7221    threads: SaSint,
7222) -> SaSint {
7223    let f = renumber_unique_and_nonunique_lms_suffixes_32s_omp(t, sa, m, threads);
7224    compact_unique_and_nonunique_lms_suffixes_32s_omp(sa, n, m, fs, f, threads);
7225    f
7226}
7227
7228fn merge_unique_lms_suffixes_32s(
7229    t: &mut [SaSint],
7230    sa: &mut [SaSint],
7231    n: SaSint,
7232    m: SaSint,
7233    l: isize,
7234    omp_block_start: isize,
7235    omp_block_size: isize,
7236) {
7237    let mut src_index = (n as isize - m as isize - 1 + l) as usize;
7238    let mut tmp = sa[src_index] as isize;
7239    src_index += 1;
7240
7241    let mut i = omp_block_start;
7242    let mut j = omp_block_start + omp_block_size - 6;
7243    while i < j {
7244        let iu = i as usize;
7245
7246        let c0 = t[iu];
7247        if c0 < 0 {
7248            t[iu] = c0 & SAINT_MAX;
7249            sa[tmp as usize] = i as SaSint;
7250            i += 1;
7251            tmp = sa[src_index] as isize;
7252            src_index += 1;
7253        }
7254
7255        let c1 = t[(i + 1) as usize];
7256        if c1 < 0 {
7257            t[(i + 1) as usize] = c1 & SAINT_MAX;
7258            sa[tmp as usize] = i as SaSint + 1;
7259            i += 1;
7260            tmp = sa[src_index] as isize;
7261            src_index += 1;
7262        }
7263
7264        let c2 = t[(i + 2) as usize];
7265        if c2 < 0 {
7266            t[(i + 2) as usize] = c2 & SAINT_MAX;
7267            sa[tmp as usize] = i as SaSint + 2;
7268            i += 1;
7269            tmp = sa[src_index] as isize;
7270            src_index += 1;
7271        }
7272
7273        let c3 = t[(i + 3) as usize];
7274        if c3 < 0 {
7275            t[(i + 3) as usize] = c3 & SAINT_MAX;
7276            sa[tmp as usize] = i as SaSint + 3;
7277            i += 1;
7278            tmp = sa[src_index] as isize;
7279            src_index += 1;
7280        }
7281
7282        i += 4;
7283    }
7284
7285    j += 6;
7286    while i < j {
7287        let c = t[i as usize];
7288        if c < 0 {
7289            t[i as usize] = c & SAINT_MAX;
7290            sa[tmp as usize] = i as SaSint;
7291            i += 1;
7292            tmp = sa[src_index] as isize;
7293            src_index += 1;
7294        }
7295        i += 1;
7296    }
7297}
7298
7299fn merge_nonunique_lms_suffixes_32s(
7300    sa: &mut [SaSint],
7301    n: SaSint,
7302    m: SaSint,
7303    l: isize,
7304    omp_block_start: isize,
7305    omp_block_size: isize,
7306) {
7307    let mut src_index = (n as isize - m as isize - 1 + l) as usize;
7308    let mut tmp = sa[src_index];
7309    src_index += 1;
7310
7311    let mut i = omp_block_start;
7312    let mut j = omp_block_start + omp_block_size - 3;
7313    while i < j {
7314        if sa[i as usize] == 0 {
7315            sa[i as usize] = tmp;
7316            tmp = sa[src_index];
7317            src_index += 1;
7318        }
7319        if sa[(i + 1) as usize] == 0 {
7320            sa[(i + 1) as usize] = tmp;
7321            tmp = sa[src_index];
7322            src_index += 1;
7323        }
7324        if sa[(i + 2) as usize] == 0 {
7325            sa[(i + 2) as usize] = tmp;
7326            tmp = sa[src_index];
7327            src_index += 1;
7328        }
7329        if sa[(i + 3) as usize] == 0 {
7330            sa[(i + 3) as usize] = tmp;
7331            tmp = sa[src_index];
7332            src_index += 1;
7333        }
7334        i += 4;
7335    }
7336
7337    j += 3;
7338    while i < j {
7339        if sa[i as usize] == 0 {
7340            sa[i as usize] = tmp;
7341            tmp = sa[src_index];
7342            src_index += 1;
7343        }
7344        i += 1;
7345    }
7346}
7347
7348fn merge_unique_lms_suffixes_32s_omp(
7349    t: &mut [SaSint],
7350    sa: &mut [SaSint],
7351    n: SaSint,
7352    m: SaSint,
7353    threads: SaSint,
7354) {
7355    if threads == 1 || n < 65_536 {
7356        merge_unique_lms_suffixes_32s(t, sa, n, m, 0, 0, n as isize);
7357        return;
7358    }
7359
7360    let thread_count = threads as usize;
7361    let block_stride = (n / threads) & !15;
7362    let mut counts = vec![0; thread_count];
7363
7364    for thread in 0..thread_count {
7365        let block_start = thread as SaSint * block_stride;
7366        let block_size = if thread + 1 < thread_count {
7367            block_stride
7368        } else {
7369            n - block_start
7370        };
7371        counts[thread] = count_negative_marked_suffixes(t, block_start, block_size);
7372    }
7373
7374    let mut count = 0;
7375    for thread in 0..thread_count {
7376        let block_start = thread as SaSint * block_stride;
7377        let block_size = if thread + 1 < thread_count {
7378            block_stride
7379        } else {
7380            n - block_start
7381        };
7382        merge_unique_lms_suffixes_32s(
7383            t,
7384            sa,
7385            n,
7386            m,
7387            count as isize,
7388            block_start as isize,
7389            block_size as isize,
7390        );
7391        count += counts[thread];
7392    }
7393}
7394
7395fn merge_nonunique_lms_suffixes_32s_omp(
7396    sa: &mut [SaSint],
7397    n: SaSint,
7398    m: SaSint,
7399    f: SaSint,
7400    threads: SaSint,
7401) {
7402    if threads == 1 || m < 65_536 {
7403        merge_nonunique_lms_suffixes_32s(sa, n, m, f as isize, 0, m as isize);
7404        return;
7405    }
7406
7407    let thread_count = threads as usize;
7408    let block_stride = (m / threads) & !15;
7409    let mut counts = vec![0; thread_count];
7410
7411    for thread in 0..thread_count {
7412        let block_start = thread as SaSint * block_stride;
7413        let block_size = if thread + 1 < thread_count {
7414            block_stride
7415        } else {
7416            m - block_start
7417        };
7418        counts[thread] = count_zero_marked_suffixes(sa, block_start, block_size);
7419    }
7420
7421    let mut count = f;
7422    for thread in 0..thread_count {
7423        let block_start = thread as SaSint * block_stride;
7424        let block_size = if thread + 1 < thread_count {
7425            block_stride
7426        } else {
7427            m - block_start
7428        };
7429        merge_nonunique_lms_suffixes_32s(
7430            sa,
7431            n,
7432            m,
7433            count as isize,
7434            block_start as isize,
7435            block_size as isize,
7436        );
7437        count += counts[thread];
7438    }
7439}
7440
7441fn merge_compacted_lms_suffixes_32s_omp(
7442    t: &mut [SaSint],
7443    sa: &mut [SaSint],
7444    n: SaSint,
7445    m: SaSint,
7446    f: SaSint,
7447    threads: SaSint,
7448) {
7449    merge_unique_lms_suffixes_32s_omp(t, sa, n, m, threads);
7450    merge_nonunique_lms_suffixes_32s_omp(sa, n, m, f, threads);
7451}
7452
7453fn reconstruct_compacted_lms_suffixes_32s_2k_omp(
7454    t: &mut [SaSint],
7455    sa: &mut [SaSint],
7456    n: SaSint,
7457    k: SaSint,
7458    m: SaSint,
7459    fs: SaSint,
7460    f: SaSint,
7461    buckets: &mut [SaSint],
7462    local_buckets: SaSint,
7463    threads: SaSint,
7464    thread_state: &mut [ThreadState],
7465) {
7466    if f > 0 {
7467        let dst = (n - m - 1) as usize;
7468        let src = (n + fs - m) as usize;
7469        sa.copy_within(src..src + f as usize, dst);
7470
7471        count_and_gather_compacted_lms_suffixes_32s_2k_omp(
7472            t,
7473            sa,
7474            n,
7475            k,
7476            buckets,
7477            local_buckets,
7478            threads,
7479            thread_state,
7480        );
7481        reconstruct_lms_suffixes_omp(sa, n, m - f, threads);
7482
7483        let dst = (n - m - 1 + f) as usize;
7484        sa.copy_within(0..(m - f) as usize, dst);
7485        sa[..m as usize].fill(0);
7486
7487        merge_compacted_lms_suffixes_32s_omp(t, sa, n, m, f, threads);
7488    } else {
7489        count_and_gather_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as isize);
7490        reconstruct_lms_suffixes_omp(sa, n, m, threads);
7491    }
7492}
7493
7494fn reconstruct_compacted_lms_suffixes_32s_1k_omp(
7495    t: &mut [SaSint],
7496    sa: &mut [SaSint],
7497    n: SaSint,
7498    m: SaSint,
7499    fs: SaSint,
7500    f: SaSint,
7501    threads: SaSint,
7502) {
7503    if f > 0 {
7504        let dst = (n - m - 1) as usize;
7505        let src = (n + fs - m) as usize;
7506        sa.copy_within(src..src + f as usize, dst);
7507
7508        gather_compacted_lms_suffixes_32s(t, sa, n);
7509        reconstruct_lms_suffixes_omp(sa, n, m - f, threads);
7510
7511        let dst = (n - m - 1 + f) as usize;
7512        sa.copy_within(0..(m - f) as usize, dst);
7513        sa[..m as usize].fill(0);
7514
7515        merge_compacted_lms_suffixes_32s_omp(t, sa, n, m, f, threads);
7516    } else {
7517        gather_lms_suffixes_32s(t, sa, n);
7518        reconstruct_lms_suffixes_omp(sa, n, m, threads);
7519    }
7520}
7521
7522fn place_lms_suffixes_interval_16u(
7523    sa: &mut [SaSint],
7524    n: SaSint,
7525    mut m: SaSint,
7526    flags: SaSint,
7527    buckets: &mut [SaSint],
7528) {
7529    if (flags & LIBSAIS_FLAGS_GSA) != 0 {
7530        buckets[7 * ALPHABET_SIZE] -= 1;
7531    }
7532
7533    let mut j = n as isize;
7534    let mut c = ALPHABET_SIZE as isize - 2;
7535    while c >= 0 {
7536        let ci = c as usize;
7537        let l =
7538            buckets[buckets_index2(ci, 1) + buckets_index2(1, 0)] - buckets[buckets_index2(ci, 1)];
7539        if l > 0 {
7540            let i = buckets[7 * ALPHABET_SIZE + ci] as isize;
7541            if j - i > 0 {
7542                sa[i as usize..j as usize].fill(0);
7543            }
7544
7545            m -= l;
7546            j = i - l as isize;
7547            let src = m as usize;
7548            let dst = j as usize;
7549            sa.copy_within(src..src + l as usize, dst);
7550        }
7551        c -= 1;
7552    }
7553
7554    sa[..j as usize].fill(0);
7555
7556    if (flags & LIBSAIS_FLAGS_GSA) != 0 {
7557        buckets[7 * ALPHABET_SIZE] += 1;
7558    }
7559}
7560
7561fn place_lms_suffixes_interval_32s_4k(
7562    sa: &mut [SaSint],
7563    n: SaSint,
7564    k: SaSint,
7565    mut m: SaSint,
7566    buckets: &[SaSint],
7567) {
7568    let bucket_end = &buckets[3 * k as usize..4 * k as usize];
7569    let mut j = n as usize;
7570    let mut c = k - 2;
7571    while c >= 0 {
7572        let cu = c as usize;
7573        let l =
7574            buckets[buckets_index2(cu, 1) + buckets_index2(1, 0)] - buckets[buckets_index2(cu, 1)];
7575        if l > 0 {
7576            let i = bucket_end[cu] as usize;
7577            if j > i {
7578                sa[i..j].fill(0);
7579            }
7580
7581            m -= l;
7582            let dst = i - l as usize;
7583            sa.copy_within(m as usize..m as usize + l as usize, dst);
7584            j = dst;
7585        }
7586        c -= 1;
7587    }
7588
7589    sa[..j].fill(0);
7590}
7591
7592fn place_lms_suffixes_interval_32s_2k(
7593    sa: &mut [SaSint],
7594    n: SaSint,
7595    k: SaSint,
7596    mut m: SaSint,
7597    buckets: &[SaSint],
7598) {
7599    let mut j = n as usize;
7600    if k > 1 {
7601        let mut c = buckets_index2(k as usize - 2, 0) as isize;
7602        while c >= buckets_index2(0, 0) as isize {
7603            let cu = c as usize;
7604            let l = buckets[cu + buckets_index2(1, 1)] - buckets[cu + buckets_index2(0, 1)];
7605            if l > 0 {
7606                let i = buckets[cu] as usize;
7607                if j > i {
7608                    sa[i..j].fill(0);
7609                }
7610
7611                m -= l;
7612                let dst = i - l as usize;
7613                sa.copy_within(m as usize..m as usize + l as usize, dst);
7614                j = dst;
7615            }
7616            c -= buckets_index2(1, 0) as isize;
7617        }
7618    }
7619
7620    sa[..j].fill(0);
7621}
7622
7623fn place_lms_suffixes_interval_32s_1k(
7624    t: &[SaSint],
7625    sa: &mut [SaSint],
7626    k: SaSint,
7627    m: SaSint,
7628    buckets: &[SaSint],
7629) {
7630    let mut c = k - 1;
7631    let mut l = buckets[c as usize] as usize;
7632
7633    let mut i = m - 1;
7634    while i >= 0 {
7635        let p = sa[i as usize] as usize;
7636        if t[p] != c {
7637            c = t[p];
7638            let bucket_pos = buckets[c as usize] as usize;
7639            if l > bucket_pos {
7640                sa[bucket_pos..l].fill(0);
7641            }
7642            l = bucket_pos;
7643        }
7644        l -= 1;
7645        sa[l] = p as SaSint;
7646        i -= 1;
7647    }
7648
7649    sa[..l].fill(0);
7650}
7651
7652fn place_lms_suffixes_histogram_32s_6k(
7653    sa: &mut [SaSint],
7654    n: SaSint,
7655    k: SaSint,
7656    mut m: SaSint,
7657    buckets: &[SaSint],
7658) {
7659    let bucket_end = &buckets[5 * k as usize..6 * k as usize];
7660    let mut j = n as usize;
7661    let mut c = k - 2;
7662    while c >= 0 {
7663        let l = buckets[buckets_index4(c as usize, 1)] as usize;
7664        if l > 0 {
7665            let i = bucket_end[c as usize] as usize;
7666            if j > i {
7667                sa[i..j].fill(0);
7668            }
7669            let dst = i - l;
7670            m -= l as SaSint;
7671            sa.copy_within(m as usize..m as usize + l, dst);
7672            j = dst;
7673        }
7674        c -= 1;
7675    }
7676    sa[..j].fill(0);
7677}
7678
7679fn place_lms_suffixes_histogram_32s_4k(
7680    sa: &mut [SaSint],
7681    n: SaSint,
7682    k: SaSint,
7683    mut m: SaSint,
7684    buckets: &[SaSint],
7685) {
7686    let bucket_end = &buckets[3 * k as usize..4 * k as usize];
7687    let mut j = n as usize;
7688    let mut c = k - 2;
7689    while c >= 0 {
7690        let l = buckets[buckets_index2(c as usize, 1)] as usize;
7691        if l > 0 {
7692            let i = bucket_end[c as usize] as usize;
7693            if j > i {
7694                sa[i..j].fill(0);
7695            }
7696            let dst = i - l;
7697            m -= l as SaSint;
7698            sa.copy_within(m as usize..m as usize + l, dst);
7699            j = dst;
7700        }
7701        c -= 1;
7702    }
7703    sa[..j].fill(0);
7704}
7705
7706fn place_lms_suffixes_histogram_32s_2k(
7707    sa: &mut [SaSint],
7708    n: SaSint,
7709    k: SaSint,
7710    mut m: SaSint,
7711    buckets: &[SaSint],
7712) {
7713    let mut j = n as usize;
7714    if k > 1 {
7715        let mut c = buckets_index2(k as usize - 2, 0) as isize;
7716        while c >= buckets_index2(0, 0) as isize {
7717            let cu = c as usize;
7718            let l = buckets[cu + buckets_index2(0, 1)] as usize;
7719            if l > 0 {
7720                let i = buckets[cu] as usize;
7721                if j > i {
7722                    sa[i..j].fill(0);
7723                }
7724                let dst = i - l;
7725                m -= l as SaSint;
7726                sa.copy_within(m as usize..m as usize + l, dst);
7727                j = dst;
7728            }
7729            c -= buckets_index2(1, 0) as isize;
7730        }
7731    }
7732    sa[..j].fill(0);
7733}
7734
7735fn final_bwt_scan_left_to_right_16u_block_prepare(
7736    t: &[u16],
7737    sa: &mut [SaSint],
7738    k: SaSint,
7739    buckets: &mut [SaSint],
7740    cache: &mut [ThreadCache],
7741    omp_block_start: SaSint,
7742    omp_block_size: SaSint,
7743) -> SaSint {
7744    buckets[..k as usize].fill(0);
7745    let mut count = 0usize;
7746    for i in omp_block_start as usize..(omp_block_start + omp_block_size) as usize {
7747        let mut p = sa[i];
7748        sa[i] = p & SAINT_MAX;
7749        if p > 0 {
7750            p -= 1;
7751            let c = t[p as usize] as usize;
7752            sa[i] = c as SaSint | SAINT_MIN;
7753            buckets[c] += 1;
7754            cache[count].symbol = c as SaSint;
7755            cache[count].index = p
7756                | ((usize::from(t[(p - SaSint::from(p > 0)) as usize] < t[p as usize]) as SaSint)
7757                    << (SAINT_BIT - 1));
7758            count += 1;
7759        }
7760    }
7761    count as SaSint
7762}
7763
7764fn final_sorting_scan_left_to_right_16u_block_prepare(
7765    t: &[u16],
7766    sa: &mut [SaSint],
7767    k: SaSint,
7768    buckets: &mut [SaSint],
7769    cache: &mut [ThreadCache],
7770    omp_block_start: SaSint,
7771    omp_block_size: SaSint,
7772) -> SaSint {
7773    buckets[..k as usize].fill(0);
7774    let mut count = 0usize;
7775    for i in omp_block_start as usize..(omp_block_start + omp_block_size) as usize {
7776        let mut p = sa[i];
7777        sa[i] = p ^ SAINT_MIN;
7778        if p > 0 {
7779            p -= 1;
7780            let c = t[p as usize] as usize;
7781            buckets[c] += 1;
7782            cache[count].symbol = c as SaSint;
7783            cache[count].index = p
7784                | ((usize::from(t[(p - SaSint::from(p > 0)) as usize] < t[p as usize]) as SaSint)
7785                    << (SAINT_BIT - 1));
7786            count += 1;
7787        }
7788    }
7789    count as SaSint
7790}
7791
7792fn final_order_scan_left_to_right_16u_block_place(
7793    sa: &mut [SaSint],
7794    buckets: &mut [SaSint],
7795    cache: &[ThreadCache],
7796    count: SaSint,
7797) {
7798    for entry in cache.iter().take(count as usize) {
7799        let c = entry.symbol as usize;
7800        let dst = buckets[c] as usize;
7801        sa[dst] = entry.index;
7802        buckets[c] += 1;
7803    }
7804}
7805
7806fn final_bwt_aux_scan_left_to_right_16u_block_place(
7807    sa: &mut [SaSint],
7808    rm: SaSint,
7809    i_sample: &mut [SaSint],
7810    buckets: &mut [SaSint],
7811    cache: &[ThreadCache],
7812    count: SaSint,
7813) {
7814    for entry in cache.iter().take(count as usize) {
7815        let c = entry.symbol as usize;
7816        let dst = buckets[c] as usize;
7817        sa[dst] = entry.index;
7818        buckets[c] += 1;
7819        let p = entry.index & SAINT_MAX;
7820        if (p & rm) == 0 {
7821            i_sample[(p / (rm + 1)) as usize] = buckets[c];
7822        }
7823    }
7824}
7825
7826fn final_bwt_scan_left_to_right_16u_block_omp(
7827    t: &[u16],
7828    sa: &mut [SaSint],
7829    k: SaSint,
7830    induction_bucket: &mut [SaSint],
7831    block_start: SaSint,
7832    block_size: SaSint,
7833    threads: SaSint,
7834    thread_state: &mut [ThreadState],
7835) {
7836    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
7837        usize::try_from(threads)
7838            .expect("threads must be non-negative")
7839            .min(thread_state.len())
7840    } else {
7841        1
7842    };
7843    if thread_count <= 1 {
7844        final_bwt_scan_left_to_right_16u(t, sa, induction_bucket, block_start, block_size);
7845        return;
7846    }
7847
7848    let k_usize = usize::try_from(k).expect("k must be non-negative");
7849    let block_stride = (block_size / thread_count as SaSint) & !15;
7850
7851    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
7852        let local_start = thread as SaSint * block_stride;
7853        let local_size = if thread + 1 < thread_count {
7854            block_stride
7855        } else {
7856            block_size - local_start
7857        };
7858        state.count = final_bwt_scan_left_to_right_16u_block_prepare(
7859            t,
7860            sa,
7861            k,
7862            &mut state.buckets[..k_usize],
7863            &mut state.cache,
7864            block_start + local_start,
7865            local_size,
7866        );
7867    }
7868
7869    for state in thread_state.iter_mut().take(thread_count) {
7870        for c in 0..k_usize {
7871            let a = induction_bucket[c];
7872            let b = state.buckets[c];
7873            induction_bucket[c] = a + b;
7874            state.buckets[c] = a;
7875        }
7876    }
7877
7878    for state in thread_state.iter_mut().take(thread_count) {
7879        final_order_scan_left_to_right_16u_block_place(
7880            sa,
7881            &mut state.buckets[..k_usize],
7882            &state.cache,
7883            state.count,
7884        );
7885    }
7886}
7887
7888fn final_bwt_aux_scan_left_to_right_16u_block_omp(
7889    t: &[u16],
7890    sa: &mut [SaSint],
7891    k: SaSint,
7892    rm: SaSint,
7893    i_sample: &mut [SaSint],
7894    induction_bucket: &mut [SaSint],
7895    block_start: SaSint,
7896    block_size: SaSint,
7897    threads: SaSint,
7898    thread_state: &mut [ThreadState],
7899) {
7900    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
7901        usize::try_from(threads)
7902            .expect("threads must be non-negative")
7903            .min(thread_state.len())
7904    } else {
7905        1
7906    };
7907    if thread_count <= 1 {
7908        final_bwt_aux_scan_left_to_right_16u(
7909            t,
7910            sa,
7911            rm,
7912            i_sample,
7913            induction_bucket,
7914            block_start,
7915            block_size,
7916        );
7917        return;
7918    }
7919
7920    let k_usize = usize::try_from(k).expect("k must be non-negative");
7921    let block_stride = (block_size / thread_count as SaSint) & !15;
7922
7923    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
7924        let local_start = thread as SaSint * block_stride;
7925        let local_size = if thread + 1 < thread_count {
7926            block_stride
7927        } else {
7928            block_size - local_start
7929        };
7930        state.count = final_bwt_scan_left_to_right_16u_block_prepare(
7931            t,
7932            sa,
7933            k,
7934            &mut state.buckets[..k_usize],
7935            &mut state.cache,
7936            block_start + local_start,
7937            local_size,
7938        );
7939    }
7940
7941    for state in thread_state.iter_mut().take(thread_count) {
7942        for c in 0..k_usize {
7943            let a = induction_bucket[c];
7944            let b = state.buckets[c];
7945            induction_bucket[c] = a + b;
7946            state.buckets[c] = a;
7947        }
7948    }
7949
7950    for state in thread_state.iter_mut().take(thread_count) {
7951        final_bwt_aux_scan_left_to_right_16u_block_place(
7952            sa,
7953            rm,
7954            i_sample,
7955            &mut state.buckets[..k_usize],
7956            &state.cache,
7957            state.count,
7958        );
7959    }
7960}
7961
7962fn final_sorting_scan_left_to_right_16u_block_omp(
7963    t: &[u16],
7964    sa: &mut [SaSint],
7965    k: SaSint,
7966    induction_bucket: &mut [SaSint],
7967    block_start: SaSint,
7968    block_size: SaSint,
7969    threads: SaSint,
7970    thread_state: &mut [ThreadState],
7971) {
7972    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
7973        usize::try_from(threads)
7974            .expect("threads must be non-negative")
7975            .min(thread_state.len())
7976    } else {
7977        1
7978    };
7979    if thread_count <= 1 {
7980        final_sorting_scan_left_to_right_16u(t, sa, induction_bucket, block_start, block_size);
7981        return;
7982    }
7983
7984    let k_usize = usize::try_from(k).expect("k must be non-negative");
7985    let block_stride = (block_size / thread_count as SaSint) & !15;
7986
7987    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
7988        let local_start = thread as SaSint * block_stride;
7989        let local_size = if thread + 1 < thread_count {
7990            block_stride
7991        } else {
7992            block_size - local_start
7993        };
7994        state.count = final_sorting_scan_left_to_right_16u_block_prepare(
7995            t,
7996            sa,
7997            k,
7998            &mut state.buckets[..k_usize],
7999            &mut state.cache,
8000            block_start + local_start,
8001            local_size,
8002        );
8003    }
8004
8005    for state in thread_state.iter_mut().take(thread_count) {
8006        for c in 0..k_usize {
8007            let a = induction_bucket[c];
8008            let b = state.buckets[c];
8009            induction_bucket[c] = a + b;
8010            state.buckets[c] = a;
8011        }
8012    }
8013
8014    for state in thread_state.iter_mut().take(thread_count) {
8015        final_order_scan_left_to_right_16u_block_place(
8016            sa,
8017            &mut state.buckets[..k_usize],
8018            &state.cache,
8019            state.count,
8020        );
8021    }
8022}
8023
8024fn final_bwt_scan_left_to_right_16u_omp(
8025    t: &[u16],
8026    sa: &mut [SaSint],
8027    n: SaSint,
8028    k: SaSint,
8029    induction_bucket: &mut [SaSint],
8030    threads: SaSint,
8031) {
8032    let c = t[(n - 1) as usize] as usize;
8033    let dst = induction_bucket[c] as usize;
8034    induction_bucket[c] += 1;
8035    let mark = if t[(n - 2) as usize] < t[(n - 1) as usize] {
8036        SAINT_MIN
8037    } else {
8038        0
8039    };
8040    sa[dst] = (n - 1) | mark;
8041
8042    if threads == 1 || n < 65536 {
8043        final_bwt_scan_left_to_right_16u(t, sa, induction_bucket, 0, n);
8044    } else {
8045        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
8046        let mut block_start = 0;
8047        while block_start < n {
8048            if sa[block_start as usize] == 0 {
8049                block_start += 1;
8050            } else {
8051                let mut block_end =
8052                    block_start + threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
8053                if block_end > n {
8054                    block_end = n;
8055                }
8056                let mut block_scan_end = block_start + 1;
8057                while block_scan_end < block_end && sa[block_scan_end as usize] != 0 {
8058                    block_scan_end += 1;
8059                }
8060                let block_size = block_scan_end - block_start;
8061                if block_size < 32 {
8062                    while block_start < block_scan_end {
8063                        let mut p = sa[block_start as usize];
8064                        sa[block_start as usize] = p & SAINT_MAX;
8065                        if p > 0 {
8066                            p -= 1;
8067                            let c = t[p as usize] as usize;
8068                            sa[block_start as usize] = c as SaSint | SAINT_MIN;
8069                            let dst = induction_bucket[c] as usize;
8070                            induction_bucket[c] += 1;
8071                            let mark = if t[(p - SaSint::from(p > 0)) as usize] < t[p as usize] {
8072                                SAINT_MIN
8073                            } else {
8074                                0
8075                            };
8076                            sa[dst] = p | mark;
8077                        }
8078                        block_start += 1;
8079                    }
8080                } else {
8081                    final_bwt_scan_left_to_right_16u_block_omp(
8082                        t,
8083                        sa,
8084                        k,
8085                        induction_bucket,
8086                        block_start,
8087                        block_size,
8088                        threads,
8089                        &mut thread_state,
8090                    );
8091                    block_start = block_scan_end;
8092                }
8093            }
8094        }
8095    }
8096}
8097
8098fn final_bwt_aux_scan_left_to_right_16u_omp(
8099    t: &[u16],
8100    sa: &mut [SaSint],
8101    n: SaSint,
8102    k: SaSint,
8103    rm: SaSint,
8104    i_sample: &mut [SaSint],
8105    induction_bucket: &mut [SaSint],
8106    threads: SaSint,
8107) {
8108    let c = t[(n - 1) as usize] as usize;
8109    let dst = induction_bucket[c] as usize;
8110    induction_bucket[c] += 1;
8111    let mark = if t[(n - 2) as usize] < t[(n - 1) as usize] {
8112        SAINT_MIN
8113    } else {
8114        0
8115    };
8116    sa[dst] = (n - 1) | mark;
8117
8118    if ((n - 1) & rm) == 0 {
8119        i_sample[((n - 1) / (rm + 1)) as usize] = induction_bucket[c];
8120    }
8121
8122    if threads == 1 || n < 65536 {
8123        final_bwt_aux_scan_left_to_right_16u(t, sa, rm, i_sample, induction_bucket, 0, n);
8124    } else {
8125        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
8126        let mut block_start = 0;
8127        while block_start < n {
8128            if sa[block_start as usize] == 0 {
8129                block_start += 1;
8130            } else {
8131                let mut block_end =
8132                    block_start + threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
8133                if block_end > n {
8134                    block_end = n;
8135                }
8136                let mut block_scan_end = block_start + 1;
8137                while block_scan_end < block_end && sa[block_scan_end as usize] != 0 {
8138                    block_scan_end += 1;
8139                }
8140                let block_size = block_scan_end - block_start;
8141                if block_size < 32 {
8142                    while block_start < block_scan_end {
8143                        let mut p = sa[block_start as usize];
8144                        sa[block_start as usize] = p & SAINT_MAX;
8145                        if p > 0 {
8146                            p -= 1;
8147                            let c = t[p as usize] as usize;
8148                            sa[block_start as usize] = c as SaSint | SAINT_MIN;
8149                            let dst = induction_bucket[c] as usize;
8150                            induction_bucket[c] += 1;
8151                            let mark = if t[(p - SaSint::from(p > 0)) as usize] < t[p as usize] {
8152                                SAINT_MIN
8153                            } else {
8154                                0
8155                            };
8156                            sa[dst] = p | mark;
8157                            if (p & rm) == 0 {
8158                                i_sample[(p / (rm + 1)) as usize] = induction_bucket[c];
8159                            }
8160                        }
8161                        block_start += 1;
8162                    }
8163                } else {
8164                    final_bwt_aux_scan_left_to_right_16u_block_omp(
8165                        t,
8166                        sa,
8167                        k,
8168                        rm,
8169                        i_sample,
8170                        induction_bucket,
8171                        block_start,
8172                        block_size,
8173                        threads,
8174                        &mut thread_state,
8175                    );
8176                    block_start = block_scan_end;
8177                }
8178            }
8179        }
8180    }
8181}
8182
8183fn final_sorting_scan_left_to_right_16u_omp(
8184    t: &[u16],
8185    sa: &mut [SaSint],
8186    n: SaSint,
8187    k: SaSint,
8188    induction_bucket: &mut [SaSint],
8189    threads: SaSint,
8190) {
8191    let c = t[(n - 1) as usize] as usize;
8192    let dst = induction_bucket[c] as usize;
8193    induction_bucket[c] += 1;
8194    let mark = if t[(n - 2) as usize] < t[(n - 1) as usize] {
8195        SAINT_MIN
8196    } else {
8197        0
8198    };
8199    sa[dst] = (n - 1) | mark;
8200
8201    if threads == 1 || n < 65536 {
8202        final_sorting_scan_left_to_right_16u(t, sa, induction_bucket, 0, n);
8203    } else {
8204        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
8205        let mut block_start = 0;
8206        while block_start < n {
8207            if sa[block_start as usize] == 0 {
8208                block_start += 1;
8209            } else {
8210                let mut block_end =
8211                    block_start + threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
8212                if block_end > n {
8213                    block_end = n;
8214                }
8215                let mut block_scan_end = block_start + 1;
8216                while block_scan_end < block_end && sa[block_scan_end as usize] != 0 {
8217                    block_scan_end += 1;
8218                }
8219                let block_size = block_scan_end - block_start;
8220                if block_size < 32 {
8221                    while block_start < block_scan_end {
8222                        let mut p = sa[block_start as usize];
8223                        sa[block_start as usize] = p ^ SAINT_MIN;
8224                        if p > 0 {
8225                            p -= 1;
8226                            let c = t[p as usize] as usize;
8227                            let dst = induction_bucket[c] as usize;
8228                            induction_bucket[c] += 1;
8229                            let mark = if t[(p - SaSint::from(p > 0)) as usize] < t[p as usize] {
8230                                SAINT_MIN
8231                            } else {
8232                                0
8233                            };
8234                            sa[dst] = p | mark;
8235                        }
8236                        block_start += 1;
8237                    }
8238                } else {
8239                    final_sorting_scan_left_to_right_16u_block_omp(
8240                        t,
8241                        sa,
8242                        k,
8243                        induction_bucket,
8244                        block_start,
8245                        block_size,
8246                        threads,
8247                        &mut thread_state,
8248                    );
8249                    block_start = block_scan_end;
8250                }
8251            }
8252        }
8253    }
8254}
8255
8256fn final_bwt_scan_right_to_left_16u_block_prepare(
8257    t: &[u16],
8258    sa: &mut [SaSint],
8259    k: SaSint,
8260    buckets: &mut [SaSint],
8261    cache: &mut [ThreadCache],
8262    omp_block_start: SaSint,
8263    omp_block_size: SaSint,
8264) -> SaSint {
8265    buckets[..k as usize].fill(0);
8266    let mut count = 0usize;
8267    for i in (omp_block_start as usize..(omp_block_start + omp_block_size) as usize).rev() {
8268        let mut p = sa[i];
8269        sa[i] = p & SAINT_MAX;
8270        if p > 0 {
8271            p -= 1;
8272            let c0 = t[(p - SaSint::from(p > 0)) as usize];
8273            let c1 = t[p as usize];
8274            sa[i] = c1 as SaSint;
8275            buckets[c1 as usize] += 1;
8276            cache[count].symbol = c1 as SaSint;
8277            cache[count].index = if c0 <= c1 {
8278                p
8279            } else {
8280                c0 as SaSint | SAINT_MIN
8281            };
8282            count += 1;
8283        }
8284    }
8285    count as SaSint
8286}
8287
8288fn final_bwt_aux_scan_right_to_left_16u_block_prepare(
8289    t: &[u16],
8290    sa: &mut [SaSint],
8291    k: SaSint,
8292    buckets: &mut [SaSint],
8293    cache: &mut [ThreadCache],
8294    omp_block_start: SaSint,
8295    omp_block_size: SaSint,
8296) -> SaSint {
8297    buckets[..k as usize].fill(0);
8298    let mut count = 0usize;
8299    for i in (omp_block_start as usize..(omp_block_start + omp_block_size) as usize).rev() {
8300        let mut p = sa[i];
8301        sa[i] = p & SAINT_MAX;
8302        if p > 0 {
8303            p -= 1;
8304            let c0 = t[(p - SaSint::from(p > 0)) as usize];
8305            let c1 = t[p as usize];
8306            sa[i] = c1 as SaSint;
8307            buckets[c1 as usize] += 1;
8308            cache[count].symbol = c1 as SaSint;
8309            cache[count].index = if c0 <= c1 {
8310                p
8311            } else {
8312                c0 as SaSint | SAINT_MIN
8313            };
8314            cache[count + 1].index = p;
8315            count += 2;
8316        }
8317    }
8318    count as SaSint
8319}
8320
8321fn final_sorting_scan_right_to_left_16u_block_prepare(
8322    t: &[u16],
8323    sa: &mut [SaSint],
8324    k: SaSint,
8325    buckets: &mut [SaSint],
8326    cache: &mut [ThreadCache],
8327    omp_block_start: SaSint,
8328    omp_block_size: SaSint,
8329) -> SaSint {
8330    buckets[..k as usize].fill(0);
8331    let mut count = 0usize;
8332    for i in (omp_block_start as usize..(omp_block_start + omp_block_size) as usize).rev() {
8333        let mut p = sa[i];
8334        sa[i] = p & SAINT_MAX;
8335        if p > 0 {
8336            p -= 1;
8337            let c = t[p as usize] as usize;
8338            buckets[c] += 1;
8339            cache[count].symbol = c as SaSint;
8340            cache[count].index = p
8341                | ((usize::from(t[(p - SaSint::from(p > 0)) as usize] > t[p as usize]) as SaSint)
8342                    << (SAINT_BIT - 1));
8343            count += 1;
8344        }
8345    }
8346    count as SaSint
8347}
8348
8349fn final_order_scan_right_to_left_16u_block_place(
8350    sa: &mut [SaSint],
8351    buckets: &mut [SaSint],
8352    cache: &[ThreadCache],
8353    count: SaSint,
8354) {
8355    for entry in cache.iter().take(count as usize) {
8356        let c = entry.symbol as usize;
8357        buckets[c] -= 1;
8358        sa[buckets[c] as usize] = entry.index;
8359    }
8360}
8361
8362fn final_gsa_scan_right_to_left_16u_block_place(
8363    sa: &mut [SaSint],
8364    buckets: &mut [SaSint],
8365    cache: &[ThreadCache],
8366    count: SaSint,
8367) {
8368    for entry in cache.iter().take(count as usize) {
8369        let c = entry.symbol as usize;
8370        if c > 0 {
8371            buckets[c] -= 1;
8372            sa[buckets[c] as usize] = entry.index;
8373        }
8374    }
8375}
8376
8377fn final_bwt_aux_scan_right_to_left_16u_block_place(
8378    sa: &mut [SaSint],
8379    rm: SaSint,
8380    i_sample: &mut [SaSint],
8381    buckets: &mut [SaSint],
8382    cache: &[ThreadCache],
8383    count: SaSint,
8384) {
8385    let mut i = 0usize;
8386    while i < count as usize {
8387        let c = cache[i].symbol as usize;
8388        buckets[c] -= 1;
8389        sa[buckets[c] as usize] = cache[i].index;
8390        let p = cache[i + 1].index;
8391        if (p & rm) == 0 {
8392            i_sample[(p / (rm + 1)) as usize] = buckets[c] + 1;
8393        }
8394        i += 2;
8395    }
8396}
8397
8398fn final_bwt_scan_right_to_left_16u_block_omp(
8399    t: &[u16],
8400    sa: &mut [SaSint],
8401    k: SaSint,
8402    induction_bucket: &mut [SaSint],
8403    block_start: SaSint,
8404    block_size: SaSint,
8405    threads: SaSint,
8406    thread_state: &mut [ThreadState],
8407) -> SaSint {
8408    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
8409        usize::try_from(threads)
8410            .expect("threads must be non-negative")
8411            .min(thread_state.len())
8412    } else {
8413        1
8414    };
8415    if thread_count <= 1 {
8416        return final_bwt_scan_right_to_left_16u(t, sa, induction_bucket, block_start, block_size);
8417    }
8418
8419    let k_usize = usize::try_from(k).expect("k must be non-negative");
8420    let block_stride = (block_size / thread_count as SaSint) & !15;
8421
8422    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
8423        let local_start = thread as SaSint * block_stride;
8424        let local_size = if thread + 1 < thread_count {
8425            block_stride
8426        } else {
8427            block_size - local_start
8428        };
8429        state.count = final_bwt_scan_right_to_left_16u_block_prepare(
8430            t,
8431            sa,
8432            k,
8433            &mut state.buckets[..k_usize],
8434            &mut state.cache,
8435            block_start + local_start,
8436            local_size,
8437        );
8438    }
8439
8440    for state in thread_state.iter_mut().take(thread_count).rev() {
8441        for c in 0..k_usize {
8442            let a = induction_bucket[c];
8443            let b = state.buckets[c];
8444            induction_bucket[c] = a - b;
8445            state.buckets[c] = a;
8446        }
8447    }
8448
8449    for state in thread_state.iter_mut().take(thread_count) {
8450        final_order_scan_right_to_left_16u_block_place(
8451            sa,
8452            &mut state.buckets[..k_usize],
8453            &state.cache,
8454            state.count,
8455        );
8456    }
8457
8458    -1
8459}
8460
8461fn final_bwt_aux_scan_right_to_left_16u_block_omp(
8462    t: &[u16],
8463    sa: &mut [SaSint],
8464    k: SaSint,
8465    rm: SaSint,
8466    i_sample: &mut [SaSint],
8467    induction_bucket: &mut [SaSint],
8468    block_start: SaSint,
8469    block_size: SaSint,
8470    threads: SaSint,
8471    thread_state: &mut [ThreadState],
8472) {
8473    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
8474        usize::try_from(threads)
8475            .expect("threads must be non-negative")
8476            .min(thread_state.len())
8477    } else {
8478        1
8479    };
8480    if thread_count <= 1 {
8481        final_bwt_aux_scan_right_to_left_16u(
8482            t,
8483            sa,
8484            rm,
8485            i_sample,
8486            induction_bucket,
8487            block_start,
8488            block_size,
8489        );
8490        return;
8491    }
8492
8493    let k_usize = usize::try_from(k).expect("k must be non-negative");
8494    let block_stride = (block_size / thread_count as SaSint) & !15;
8495
8496    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
8497        let local_start = thread as SaSint * block_stride;
8498        let local_size = if thread + 1 < thread_count {
8499            block_stride
8500        } else {
8501            block_size - local_start
8502        };
8503        state.count = final_bwt_aux_scan_right_to_left_16u_block_prepare(
8504            t,
8505            sa,
8506            k,
8507            &mut state.buckets[..k_usize],
8508            &mut state.cache,
8509            block_start + local_start,
8510            local_size,
8511        );
8512    }
8513
8514    for state in thread_state.iter_mut().take(thread_count).rev() {
8515        for c in 0..k_usize {
8516            let a = induction_bucket[c];
8517            let b = state.buckets[c];
8518            induction_bucket[c] = a - b;
8519            state.buckets[c] = a;
8520        }
8521    }
8522
8523    for state in thread_state.iter_mut().take(thread_count) {
8524        final_bwt_aux_scan_right_to_left_16u_block_place(
8525            sa,
8526            rm,
8527            i_sample,
8528            &mut state.buckets[..k_usize],
8529            &state.cache,
8530            state.count,
8531        );
8532    }
8533}
8534
8535fn final_sorting_scan_right_to_left_16u_block_omp(
8536    t: &[u16],
8537    sa: &mut [SaSint],
8538    k: SaSint,
8539    induction_bucket: &mut [SaSint],
8540    block_start: SaSint,
8541    block_size: SaSint,
8542    threads: SaSint,
8543    thread_state: &mut [ThreadState],
8544) {
8545    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
8546        usize::try_from(threads)
8547            .expect("threads must be non-negative")
8548            .min(thread_state.len())
8549    } else {
8550        1
8551    };
8552    if thread_count <= 1 {
8553        final_sorting_scan_right_to_left_16u(t, sa, induction_bucket, block_start, block_size);
8554        return;
8555    }
8556
8557    let k_usize = usize::try_from(k).expect("k must be non-negative");
8558    let block_stride = (block_size / thread_count as SaSint) & !15;
8559
8560    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
8561        let local_start = thread as SaSint * block_stride;
8562        let local_size = if thread + 1 < thread_count {
8563            block_stride
8564        } else {
8565            block_size - local_start
8566        };
8567        state.count = final_sorting_scan_right_to_left_16u_block_prepare(
8568            t,
8569            sa,
8570            k,
8571            &mut state.buckets[..k_usize],
8572            &mut state.cache,
8573            block_start + local_start,
8574            local_size,
8575        );
8576    }
8577
8578    for state in thread_state.iter_mut().take(thread_count).rev() {
8579        for c in 0..k_usize {
8580            let a = induction_bucket[c];
8581            let b = state.buckets[c];
8582            induction_bucket[c] = a - b;
8583            state.buckets[c] = a;
8584        }
8585    }
8586
8587    for state in thread_state.iter_mut().take(thread_count) {
8588        final_order_scan_right_to_left_16u_block_place(
8589            sa,
8590            &mut state.buckets[..k_usize],
8591            &state.cache,
8592            state.count,
8593        );
8594    }
8595}
8596
8597fn final_gsa_scan_right_to_left_16u_block_omp(
8598    t: &[u16],
8599    sa: &mut [SaSint],
8600    k: SaSint,
8601    induction_bucket: &mut [SaSint],
8602    block_start: SaSint,
8603    block_size: SaSint,
8604    threads: SaSint,
8605    thread_state: &mut [ThreadState],
8606) {
8607    let thread_count = if threads > 1 && block_size >= 64 * k.max(256) {
8608        usize::try_from(threads)
8609            .expect("threads must be non-negative")
8610            .min(thread_state.len())
8611    } else {
8612        1
8613    };
8614    if thread_count <= 1 {
8615        final_gsa_scan_right_to_left_16u(t, sa, induction_bucket, block_start, block_size);
8616        return;
8617    }
8618
8619    let k_usize = usize::try_from(k).expect("k must be non-negative");
8620    let block_stride = (block_size / thread_count as SaSint) & !15;
8621
8622    for (thread, state) in thread_state.iter_mut().take(thread_count).enumerate() {
8623        let local_start = thread as SaSint * block_stride;
8624        let local_size = if thread + 1 < thread_count {
8625            block_stride
8626        } else {
8627            block_size - local_start
8628        };
8629        state.count = final_sorting_scan_right_to_left_16u_block_prepare(
8630            t,
8631            sa,
8632            k,
8633            &mut state.buckets[..k_usize],
8634            &mut state.cache,
8635            block_start + local_start,
8636            local_size,
8637        );
8638    }
8639
8640    for state in thread_state.iter_mut().take(thread_count).rev() {
8641        for c in 0..k_usize {
8642            let a = induction_bucket[c];
8643            let b = state.buckets[c];
8644            induction_bucket[c] = a - b;
8645            state.buckets[c] = a;
8646        }
8647    }
8648
8649    for state in thread_state.iter_mut().take(thread_count) {
8650        final_gsa_scan_right_to_left_16u_block_place(
8651            sa,
8652            &mut state.buckets[..k_usize],
8653            &state.cache,
8654            state.count,
8655        );
8656    }
8657}
8658
8659fn final_bwt_scan_right_to_left_16u_omp(
8660    t: &[u16],
8661    sa: &mut [SaSint],
8662    n: SaSint,
8663    k: SaSint,
8664    induction_bucket: &mut [SaSint],
8665    threads: SaSint,
8666) -> SaSint {
8667    let mut index = -1;
8668
8669    if threads == 1 || n < 65536 {
8670        index = final_bwt_scan_right_to_left_16u(t, sa, induction_bucket, 0, n);
8671    } else {
8672        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
8673        let mut block_start = n - 1;
8674        while block_start >= 0 {
8675            if sa[block_start as usize] == 0 {
8676                index = block_start;
8677                block_start -= 1;
8678            } else {
8679                let mut block_max_end =
8680                    block_start - threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
8681                if block_max_end < 0 {
8682                    block_max_end = -1;
8683                }
8684                let mut block_end = block_start - 1;
8685                while block_end > block_max_end && sa[block_end as usize] != 0 {
8686                    block_end -= 1;
8687                }
8688                let block_size = block_start - block_end;
8689                if block_size < 32 {
8690                    while block_start > block_end {
8691                        let mut p = sa[block_start as usize];
8692                        sa[block_start as usize] = p & SAINT_MAX;
8693                        if p > 0 {
8694                            p -= 1;
8695                            let c0 = t[(p - SaSint::from(p > 0)) as usize];
8696                            let c1 = t[p as usize] as usize;
8697                            sa[block_start as usize] = c1 as SaSint;
8698                            induction_bucket[c1] -= 1;
8699                            sa[induction_bucket[c1] as usize] = if c0 <= c1 as u16 {
8700                                p
8701                            } else {
8702                                c0 as SaSint | SAINT_MIN
8703                            };
8704                        }
8705                        block_start -= 1;
8706                    }
8707                } else {
8708                    final_bwt_scan_right_to_left_16u_block_omp(
8709                        t,
8710                        sa,
8711                        k,
8712                        induction_bucket,
8713                        block_end + 1,
8714                        block_size,
8715                        threads,
8716                        &mut thread_state,
8717                    );
8718                    block_start = block_end;
8719                }
8720            }
8721        }
8722    }
8723    index
8724}
8725
8726fn final_bwt_aux_scan_right_to_left_16u_omp(
8727    t: &[u16],
8728    sa: &mut [SaSint],
8729    n: SaSint,
8730    k: SaSint,
8731    rm: SaSint,
8732    i_sample: &mut [SaSint],
8733    induction_bucket: &mut [SaSint],
8734    threads: SaSint,
8735) {
8736    if threads == 1 || n < 65536 {
8737        final_bwt_aux_scan_right_to_left_16u(t, sa, rm, i_sample, induction_bucket, 0, n);
8738    } else {
8739        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
8740        let mut block_start = n - 1;
8741        while block_start >= 0 {
8742            if sa[block_start as usize] == 0 {
8743                block_start -= 1;
8744            } else {
8745                let mut block_max_end =
8746                    block_start - threads * ((PER_THREAD_CACHE_SIZE as SaSint - 16 * threads) / 2);
8747                if block_max_end < 0 {
8748                    block_max_end = -1;
8749                }
8750                let mut block_end = block_start - 1;
8751                while block_end > block_max_end && sa[block_end as usize] != 0 {
8752                    block_end -= 1;
8753                }
8754                let block_size = block_start - block_end;
8755                if block_size < 32 {
8756                    while block_start > block_end {
8757                        let mut p = sa[block_start as usize];
8758                        sa[block_start as usize] = p & SAINT_MAX;
8759                        if p > 0 {
8760                            p -= 1;
8761                            let c0 = t[(p - SaSint::from(p > 0)) as usize];
8762                            let c1 = t[p as usize] as usize;
8763                            sa[block_start as usize] = c1 as SaSint;
8764                            induction_bucket[c1] -= 1;
8765                            sa[induction_bucket[c1] as usize] = if c0 <= c1 as u16 {
8766                                p
8767                            } else {
8768                                c0 as SaSint | SAINT_MIN
8769                            };
8770                            if (p & rm) == 0 {
8771                                i_sample[(p / (rm + 1)) as usize] = induction_bucket[c1] + 1;
8772                            }
8773                        }
8774                        block_start -= 1;
8775                    }
8776                } else {
8777                    final_bwt_aux_scan_right_to_left_16u_block_omp(
8778                        t,
8779                        sa,
8780                        k,
8781                        rm,
8782                        i_sample,
8783                        induction_bucket,
8784                        block_end + 1,
8785                        block_size,
8786                        threads,
8787                        &mut thread_state,
8788                    );
8789                    block_start = block_end;
8790                }
8791            }
8792        }
8793    }
8794}
8795
8796fn final_sorting_scan_right_to_left_16u_omp(
8797    t: &[u16],
8798    sa: &mut [SaSint],
8799    omp_block_start: SaSint,
8800    omp_block_size: SaSint,
8801    k: SaSint,
8802    induction_bucket: &mut [SaSint],
8803    threads: SaSint,
8804) {
8805    if threads == 1 || omp_block_size < 65536 {
8806        final_sorting_scan_right_to_left_16u(
8807            t,
8808            sa,
8809            induction_bucket,
8810            omp_block_start,
8811            omp_block_size,
8812        );
8813    } else {
8814        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
8815        let mut block_start = omp_block_start + omp_block_size - 1;
8816        while block_start >= omp_block_start {
8817            if sa[block_start as usize] == 0 {
8818                block_start -= 1;
8819            } else {
8820                let mut block_max_end =
8821                    block_start - threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
8822                if block_max_end < omp_block_start {
8823                    block_max_end = omp_block_start - 1;
8824                }
8825                let mut block_end = block_start - 1;
8826                while block_end > block_max_end && sa[block_end as usize] != 0 {
8827                    block_end -= 1;
8828                }
8829                let block_size = block_start - block_end;
8830                if block_size < 32 {
8831                    while block_start > block_end {
8832                        let mut p = sa[block_start as usize];
8833                        sa[block_start as usize] = p & SAINT_MAX;
8834                        if p > 0 {
8835                            p -= 1;
8836                            let c = t[p as usize] as usize;
8837                            induction_bucket[c] -= 1;
8838                            let mark = if t[(p - SaSint::from(p > 0)) as usize] > t[p as usize] {
8839                                SAINT_MIN
8840                            } else {
8841                                0
8842                            };
8843                            sa[induction_bucket[c] as usize] = p | mark;
8844                        }
8845                        block_start -= 1;
8846                    }
8847                } else {
8848                    final_sorting_scan_right_to_left_16u_block_omp(
8849                        t,
8850                        sa,
8851                        k,
8852                        induction_bucket,
8853                        block_end + 1,
8854                        block_size,
8855                        threads,
8856                        &mut thread_state,
8857                    );
8858                    block_start = block_end;
8859                }
8860            }
8861        }
8862    }
8863}
8864
8865fn final_gsa_scan_right_to_left_16u_omp(
8866    t: &[u16],
8867    sa: &mut [SaSint],
8868    omp_block_start: SaSint,
8869    omp_block_size: SaSint,
8870    k: SaSint,
8871    induction_bucket: &mut [SaSint],
8872    threads: SaSint,
8873) {
8874    if threads == 1 || omp_block_size < 65536 {
8875        final_gsa_scan_right_to_left_16u(t, sa, induction_bucket, omp_block_start, omp_block_size);
8876    } else {
8877        let mut thread_state = alloc_thread_state(threads).unwrap_or_default();
8878        let mut block_start = omp_block_start + omp_block_size - 1;
8879        while block_start >= omp_block_start {
8880            if sa[block_start as usize] == 0 {
8881                block_start -= 1;
8882            } else {
8883                let mut block_max_end =
8884                    block_start - threads * (PER_THREAD_CACHE_SIZE as SaSint - 16 * threads);
8885                if block_max_end < omp_block_start {
8886                    block_max_end = omp_block_start - 1;
8887                }
8888                let mut block_end = block_start - 1;
8889                while block_end > block_max_end && sa[block_end as usize] != 0 {
8890                    block_end -= 1;
8891                }
8892                let block_size = block_start - block_end;
8893                if block_size < 32 {
8894                    while block_start > block_end {
8895                        let mut p = sa[block_start as usize];
8896                        sa[block_start as usize] = p & SAINT_MAX;
8897                        if p > 0 && t[(p - 1) as usize] > 0 {
8898                            p -= 1;
8899                            let c = t[p as usize] as usize;
8900                            induction_bucket[c] -= 1;
8901                            let mark = if t[(p - SaSint::from(p > 0)) as usize] > t[p as usize] {
8902                                SAINT_MIN
8903                            } else {
8904                                0
8905                            };
8906                            sa[induction_bucket[c] as usize] = p | mark;
8907                        }
8908                        block_start -= 1;
8909                    }
8910                } else {
8911                    final_gsa_scan_right_to_left_16u_block_omp(
8912                        t,
8913                        sa,
8914                        k,
8915                        induction_bucket,
8916                        block_end + 1,
8917                        block_size,
8918                        threads,
8919                        &mut thread_state,
8920                    );
8921                    block_start = block_end;
8922                }
8923            }
8924        }
8925    }
8926}
8927
8928fn induce_final_order_16u_omp(
8929    t: &[u16],
8930    sa: &mut [SaSint],
8931    n: SaSint,
8932    k: SaSint,
8933    flags: SaSint,
8934    r: SaSint,
8935    i_out: Option<&mut [SaSint]>,
8936    buckets: &mut [SaSint],
8937    threads: SaSint,
8938    _thread_state: &mut [ThreadState],
8939) -> SaSint {
8940    if (flags & LIBSAIS_FLAGS_BWT) == 0 {
8941        if (flags & LIBSAIS_FLAGS_GSA) != 0 {
8942            buckets[6 * ALPHABET_SIZE] = buckets[7 * ALPHABET_SIZE] - 1;
8943        }
8944
8945        let (left_buckets, right_tail) = buckets.split_at_mut(7 * ALPHABET_SIZE);
8946        let bucket_start = &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE];
8947        let bucket_end = &mut right_tail[..ALPHABET_SIZE];
8948
8949        final_sorting_scan_left_to_right_16u_omp(t, sa, n, k, bucket_start, threads);
8950        if threads > 1 && n >= 65_536 {
8951            clear_lms_suffixes_omp(
8952                sa,
8953                n,
8954                ALPHABET_SIZE as SaSint,
8955                bucket_start,
8956                bucket_end,
8957                threads,
8958            );
8959        }
8960
8961        if (flags & LIBSAIS_FLAGS_GSA) != 0 {
8962            flip_suffix_markers_omp(sa, bucket_end[0], threads);
8963            final_gsa_scan_right_to_left_16u_omp(
8964                t,
8965                sa,
8966                bucket_end[0],
8967                n - bucket_end[0],
8968                k,
8969                bucket_end,
8970                threads,
8971            );
8972        } else {
8973            final_sorting_scan_right_to_left_16u_omp(t, sa, 0, n, k, bucket_end, threads);
8974        }
8975
8976        0
8977    } else if let Some(i_out) = i_out {
8978        let (left_buckets, right_tail) = buckets.split_at_mut(7 * ALPHABET_SIZE);
8979        let bucket_start = &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE];
8980        let bucket_end = &mut right_tail[..ALPHABET_SIZE];
8981
8982        final_bwt_aux_scan_left_to_right_16u_omp(t, sa, n, k, r - 1, i_out, bucket_start, threads);
8983        if threads > 1 && n >= 65_536 {
8984            clear_lms_suffixes_omp(
8985                sa,
8986                n,
8987                ALPHABET_SIZE as SaSint,
8988                bucket_start,
8989                bucket_end,
8990                threads,
8991            );
8992        }
8993        final_bwt_aux_scan_right_to_left_16u_omp(t, sa, n, k, r - 1, i_out, bucket_end, threads);
8994        0
8995    } else {
8996        let (left_buckets, right_tail) = buckets.split_at_mut(7 * ALPHABET_SIZE);
8997        let bucket_start = &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE];
8998        let bucket_end = &mut right_tail[..ALPHABET_SIZE];
8999
9000        final_bwt_scan_left_to_right_16u_omp(t, sa, n, k, bucket_start, threads);
9001        if threads > 1 && n >= 65_536 {
9002            clear_lms_suffixes_omp(
9003                sa,
9004                n,
9005                ALPHABET_SIZE as SaSint,
9006                bucket_start,
9007                bucket_end,
9008                threads,
9009            );
9010        }
9011        final_bwt_scan_right_to_left_16u_omp(t, sa, n, k, bucket_end, threads)
9012    }
9013}
9014
9015fn bwt_copy_16u(u: &mut [u16], a: &[SaSint], n: SaSint) {
9016    let mut i = 0isize;
9017    let mut j = n as isize - 7;
9018    while i < j {
9019        u[i as usize] = a[i as usize] as u16;
9020        u[(i + 1) as usize] = a[(i + 1) as usize] as u16;
9021        u[(i + 2) as usize] = a[(i + 2) as usize] as u16;
9022        u[(i + 3) as usize] = a[(i + 3) as usize] as u16;
9023        u[(i + 4) as usize] = a[(i + 4) as usize] as u16;
9024        u[(i + 5) as usize] = a[(i + 5) as usize] as u16;
9025        u[(i + 6) as usize] = a[(i + 6) as usize] as u16;
9026        u[(i + 7) as usize] = a[(i + 7) as usize] as u16;
9027        i += 8;
9028    }
9029
9030    j += 7;
9031    while i < j {
9032        u[i as usize] = a[i as usize] as u16;
9033        i += 1;
9034    }
9035}
9036
9037#[allow(dead_code)]
9038fn bwt_copy_16u_omp(u: &mut [u16], a: &[SaSint], n: SaSint, threads: SaSint) {
9039    if threads == 1 || n < 65_536 {
9040        bwt_copy_16u(u, a, n);
9041        return;
9042    }
9043
9044    let block_stride = (n / threads) & !15;
9045    for thread in 0..threads {
9046        let block_start = thread * block_stride;
9047        let block_size = if thread < threads - 1 {
9048            block_stride
9049        } else {
9050            n - block_start
9051        };
9052        let start = block_start as usize;
9053        bwt_copy_16u(&mut u[start..], &a[start..], block_size);
9054    }
9055}
9056
9057#[allow(dead_code)]
9058fn convert_32u_to_64u(s: &[u32], d: &mut [u64], block_start: usize, block_size: usize) {
9059    for i in block_start..block_start + block_size {
9060        d[i] = u64::from(s[i]);
9061    }
9062}
9063
9064#[allow(dead_code)]
9065fn convert_inplace_32u_to_64u(v: &mut [u32], block_start: usize, block_size: usize) {
9066    for i in (block_start..block_start + block_size).rev() {
9067        v[i + i] = v[i];
9068        v[i + i + 1] = 0;
9069    }
9070}
9071
9072#[allow(dead_code)]
9073fn convert_inplace_64u_to_32u(v: &mut [u32], block_start: usize, block_size: usize) {
9074    for i in block_start..block_start + block_size {
9075        v[i] = v[i + i];
9076    }
9077}
9078
9079#[allow(dead_code)]
9080fn convert_inplace_32u_to_64u_omp(v: &mut [u32], n: SaSint, threads: SaSint) {
9081    let mut n = usize::try_from(n).expect("n must be non-negative");
9082    let threads = usize::try_from(threads.max(1)).expect("threads must be non-negative");
9083
9084    while n >= 65_536 {
9085        let block_size = n >> 1;
9086        n -= block_size;
9087
9088        let omp_block_stride = (block_size / threads) & !15usize;
9089        for thread in 0..threads {
9090            let block_start = thread * omp_block_stride;
9091            let size = if thread + 1 < threads {
9092                omp_block_stride
9093            } else {
9094                block_size - block_start
9095            };
9096            convert_inplace_32u_to_64u(v, n + block_start, size);
9097        }
9098    }
9099
9100    convert_inplace_32u_to_64u(v, 0, n);
9101}
9102
9103fn final_bwt_ltr_step(t: &[u16], sa: &mut [SaSint], induction_bucket: &mut [SaSint], index: usize) {
9104    let mut p = sa[index];
9105    sa[index] = p & SAINT_MAX;
9106    if p > 0 {
9107        p -= 1;
9108        let c = t[p as usize] as usize;
9109        sa[index] = t[p as usize] as SaSint | SAINT_MIN;
9110        let mark = if t[(p - SaSint::from(p > 0)) as usize] < t[p as usize] {
9111            SAINT_MIN
9112        } else {
9113            0
9114        };
9115        let dst = induction_bucket[c] as usize;
9116        sa[dst] = p | mark;
9117        induction_bucket[c] += 1;
9118    }
9119}
9120
9121fn final_bwt_rtl_step(
9122    t: &[u16],
9123    sa: &mut [SaSint],
9124    induction_bucket: &mut [SaSint],
9125    index: usize,
9126    primary_index: &mut SaSint,
9127) {
9128    let mut p = sa[index];
9129    if p == 0 {
9130        *primary_index = index as SaSint;
9131    }
9132    sa[index] = p & SAINT_MAX;
9133    if p > 0 {
9134        p -= 1;
9135        let c0 = t[(p - SaSint::from(p > 0)) as usize];
9136        let c1 = t[p as usize];
9137        sa[index] = c1 as SaSint;
9138        let induced = if c0 <= c1 {
9139            p
9140        } else {
9141            c0 as SaSint | SAINT_MIN
9142        };
9143        induction_bucket[c1 as usize] -= 1;
9144        sa[induction_bucket[c1 as usize] as usize] = induced;
9145    }
9146}
9147
9148fn final_bwt_aux_ltr_step(
9149    t: &[u16],
9150    sa: &mut [SaSint],
9151    rm: SaSint,
9152    i_sample: &mut [SaSint],
9153    induction_bucket: &mut [SaSint],
9154    index: usize,
9155) {
9156    let mut p = sa[index];
9157    sa[index] = p & SAINT_MAX;
9158    if p > 0 {
9159        p -= 1;
9160        let c = t[p as usize] as usize;
9161        sa[index] = t[p as usize] as SaSint | SAINT_MIN;
9162        let mark = if t[(p - SaSint::from(p > 0)) as usize] < t[p as usize] {
9163            SAINT_MIN
9164        } else {
9165            0
9166        };
9167        let dst = induction_bucket[c] as usize;
9168        sa[dst] = p | mark;
9169        induction_bucket[c] += 1;
9170        if (p & rm) == 0 {
9171            i_sample[(p / (rm + 1)) as usize] = induction_bucket[c];
9172        }
9173    }
9174}
9175
9176fn final_bwt_aux_rtl_step(
9177    t: &[u16],
9178    sa: &mut [SaSint],
9179    rm: SaSint,
9180    i_sample: &mut [SaSint],
9181    induction_bucket: &mut [SaSint],
9182    index: usize,
9183) {
9184    let mut p = sa[index];
9185    sa[index] = p & SAINT_MAX;
9186    if p > 0 {
9187        p -= 1;
9188        let c0 = t[(p - SaSint::from(p > 0)) as usize];
9189        let c1 = t[p as usize];
9190        sa[index] = c1 as SaSint;
9191        let induced = if c0 <= c1 {
9192            p
9193        } else {
9194            c0 as SaSint | SAINT_MIN
9195        };
9196        induction_bucket[c1 as usize] -= 1;
9197        sa[induction_bucket[c1 as usize] as usize] = induced;
9198        if (p & rm) == 0 {
9199            i_sample[(p / (rm + 1)) as usize] = induction_bucket[c1 as usize] + 1;
9200        }
9201    }
9202}
9203
9204fn main_32s_recursion(
9205    t_ptr: *mut SaSint,
9206    sa_ptr: *mut SaSint,
9207    sa_capacity: usize,
9208    n: SaSint,
9209    k: SaSint,
9210    fs: SaSint,
9211    threads: SaSint,
9212    thread_state: &mut [ThreadState],
9213    local_buffer: &mut [SaSint],
9214) -> SaSint {
9215    let fs = fs.min(SAINT_MAX - n);
9216    let local_buffer_size = SaSint::try_from(LIBSAIS_LOCAL_BUFFER_SIZE).expect("fits");
9217    let n_usize = usize::try_from(n).expect("n must be non-negative");
9218    let fs_usize = usize::try_from(fs).expect("fs must be non-negative");
9219    let total_len = n_usize + fs_usize;
9220    assert!(total_len <= sa_capacity);
9221
9222    if n <= i32::MAX as SaSint && k > 0 {
9223        let doubled_space = i128::from(fs) + i128::from(fs) + i128::from(n) + i128::from(n);
9224        let new_fs = if doubled_space <= i128::from(i32::MAX) {
9225            fs + fs + n
9226        } else {
9227            i32::MAX as SaSint - n
9228        };
9229
9230        if (new_fs / k >= 6)
9231            || (new_fs / k >= 4 && n <= (i32::MAX as SaSint) / 2)
9232            || (new_fs / k < 4 && new_fs >= fs)
9233        {
9234            let t = unsafe { std::slice::from_raw_parts_mut(t_ptr, n_usize) };
9235            let mut t32 = Vec::with_capacity(n_usize);
9236            for &value in t.iter() {
9237                let Ok(value) = i32::try_from(value) else {
9238                    break;
9239                };
9240                t32.push(value);
9241            }
9242
9243            if t32.len() == n_usize {
9244                let mut sa32 = vec![0_i32; n_usize + usize::try_from(new_fs).expect("fits")];
9245                let index = crate::libsais16::libsais16_int_omp(
9246                    &mut t32,
9247                    &mut sa32,
9248                    k as i32,
9249                    new_fs as i32,
9250                    threads as i32,
9251                ) as SaSint;
9252
9253                if index >= 0 {
9254                    let sa = unsafe { std::slice::from_raw_parts_mut(sa_ptr, n_usize) };
9255                    for (dst, src) in sa.iter_mut().zip(sa32.iter()) {
9256                        *dst = SaSint::from(*src);
9257                    }
9258                }
9259
9260                return index;
9261            }
9262        }
9263    }
9264
9265    if k > 0 && ((fs / k) >= 6 || (local_buffer_size / k) >= 6) {
9266        let k_usize = usize::try_from(k).expect("k must be non-negative");
9267        let alignment = if fs >= 1024 && ((fs - 1024) / k) >= 6 {
9268            1024usize
9269        } else {
9270            16usize
9271        };
9272        let need = 6 * k_usize;
9273        let use_local_buffer = local_buffer_size > fs;
9274        let buckets_ptr = if use_local_buffer {
9275            local_buffer.as_mut_ptr()
9276        } else {
9277            unsafe {
9278                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9279                let start =
9280                    if fs_usize >= need + alignment && ((fs_usize - alignment) / k_usize) >= 6 {
9281                        let byte_ptr = sa[total_len - need - alignment..].as_mut_ptr() as usize;
9282                        let aligned = align_up(byte_ptr, alignment * mem::size_of::<SaSint>());
9283                        (aligned - sa_ptr as usize) / mem::size_of::<SaSint>()
9284                    } else {
9285                        total_len - need
9286                    };
9287                sa[start..].as_mut_ptr()
9288            }
9289        };
9290
9291        let m = unsafe {
9292            let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9293            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9294            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9295            count_and_gather_lms_suffixes_32s_4k_omp(
9296                t,
9297                sa,
9298                n,
9299                k,
9300                buckets,
9301                SaSint::from(use_local_buffer),
9302                threads,
9303                thread_state,
9304            )
9305        };
9306        if m > 1 {
9307            let m_usize = usize::try_from(m).expect("m must be non-negative");
9308            unsafe {
9309                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9310                sa[..n_usize - m_usize].fill(0);
9311            }
9312
9313            let first_lms_suffix = unsafe {
9314                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9315                sa[n_usize - m_usize]
9316            };
9317            let left_suffixes_count = unsafe {
9318                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9319                initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
9320                    std::slice::from_raw_parts(t_ptr, n_usize),
9321                    k,
9322                    buckets,
9323                    first_lms_suffix,
9324                )
9325            };
9326
9327            unsafe {
9328                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9329                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9330                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9331                let (_, induction_bucket) = buckets.split_at_mut(4 * k_usize);
9332                radix_sort_lms_suffixes_32s_6k_omp(t, sa, n, m, induction_bucket, threads);
9333                if (n / 8192) < k {
9334                    radix_sort_set_markers_32s_6k_omp(sa, k, induction_bucket, threads);
9335                }
9336                if threads > 1 && n >= 65_536 {
9337                    sa[n_usize - m_usize..n_usize].fill(0);
9338                }
9339                initialize_buckets_for_partial_sorting_32s_6k(
9340                    t,
9341                    k,
9342                    buckets,
9343                    first_lms_suffix,
9344                    left_suffixes_count,
9345                );
9346                induce_partial_order_32s_6k_omp(
9347                    t,
9348                    sa,
9349                    n,
9350                    k,
9351                    buckets,
9352                    first_lms_suffix,
9353                    left_suffixes_count,
9354                    threads,
9355                    thread_state,
9356                );
9357            }
9358
9359            let names = unsafe {
9360                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9361                if (n / 8192) < k {
9362                    renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
9363                        sa,
9364                        n,
9365                        m,
9366                        threads,
9367                        thread_state,
9368                    )
9369                } else {
9370                    renumber_and_gather_lms_suffixes_omp(sa, n, m, fs, threads, thread_state)
9371                }
9372            };
9373
9374            if names < m {
9375                let f = if (n / 8192) < k {
9376                    unsafe {
9377                        let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9378                        let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9379                        compact_lms_suffixes_32s_omp(t, sa, n, m, fs, threads)
9380                    }
9381                } else {
9382                    0
9383                };
9384
9385                let new_t_start =
9386                    total_len - usize::try_from(m - f).expect("m - f must be non-negative");
9387                if main_32s_recursion(
9388                    unsafe {
9389                        std::slice::from_raw_parts_mut(sa_ptr, total_len)[new_t_start..]
9390                            .as_mut_ptr()
9391                    },
9392                    sa_ptr,
9393                    sa_capacity,
9394                    m - f,
9395                    names - f,
9396                    fs + n - 2 * m + f,
9397                    threads,
9398                    thread_state,
9399                    local_buffer,
9400                ) != 0
9401                {
9402                    return -2;
9403                }
9404
9405                unsafe {
9406                    let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9407                    let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9408                    let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9409                    reconstruct_compacted_lms_suffixes_32s_2k_omp(
9410                        t,
9411                        sa,
9412                        n,
9413                        k,
9414                        m,
9415                        fs,
9416                        f,
9417                        buckets,
9418                        SaSint::from(use_local_buffer),
9419                        threads,
9420                        thread_state,
9421                    );
9422                }
9423            } else {
9424                unsafe {
9425                    let t = std::slice::from_raw_parts(t_ptr, n_usize);
9426                    let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9427                    count_lms_suffixes_32s_2k(t, n, k, buckets);
9428                }
9429            }
9430
9431            unsafe {
9432                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9433                initialize_buckets_start_and_end_32s_4k(k, buckets);
9434                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9435                place_lms_suffixes_histogram_32s_4k(sa, n, k, m, buckets);
9436                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9437                induce_final_order_32s_4k(t, sa, n, k, buckets, threads, thread_state);
9438            }
9439        } else {
9440            unsafe {
9441                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9442                sa[0] = sa[n_usize - 1];
9443            }
9444
9445            unsafe {
9446                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9447                initialize_buckets_start_and_end_32s_6k(k, buckets);
9448                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9449                place_lms_suffixes_histogram_32s_6k(sa, n, k, m, buckets);
9450                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9451                induce_final_order_32s_6k(t, sa, n, k, buckets, threads, thread_state);
9452            }
9453        }
9454
9455        return 0;
9456    } else if k > 0 && n <= SAINT_MAX / 2 && ((fs / k) >= 4 || (local_buffer_size / k) >= 4) {
9457        let k_usize = usize::try_from(k).expect("k must be non-negative");
9458        let alignment = if fs >= 1024 && ((fs - 1024) / k) >= 4 {
9459            1024usize
9460        } else {
9461            16usize
9462        };
9463        let need = 4 * k_usize;
9464        let use_local_buffer = local_buffer_size > fs;
9465        let buckets_ptr = if use_local_buffer {
9466            local_buffer.as_mut_ptr()
9467        } else {
9468            unsafe {
9469                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9470                let start =
9471                    if fs_usize >= need + alignment && ((fs_usize - alignment) / k_usize) >= 4 {
9472                        let byte_ptr = sa[total_len - need - alignment..].as_mut_ptr() as usize;
9473                        let aligned = align_up(byte_ptr, alignment * mem::size_of::<SaSint>());
9474                        (aligned - sa_ptr as usize) / mem::size_of::<SaSint>()
9475                    } else {
9476                        total_len - need
9477                    };
9478                sa[start..].as_mut_ptr()
9479            }
9480        };
9481
9482        let m = unsafe {
9483            let t = std::slice::from_raw_parts(t_ptr, n_usize);
9484            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9485            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9486            count_and_gather_lms_suffixes_32s_2k_omp(
9487                t,
9488                sa,
9489                n,
9490                k,
9491                buckets,
9492                SaSint::from(use_local_buffer),
9493                threads,
9494                thread_state,
9495            )
9496        };
9497        if m > 1 {
9498            let m_usize = usize::try_from(m).expect("m must be non-negative");
9499            unsafe {
9500                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9501                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9502                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9503                initialize_buckets_for_radix_and_partial_sorting_32s_4k(
9504                    t,
9505                    k,
9506                    buckets,
9507                    sa[n_usize - m_usize],
9508                );
9509                let (_, induction_bucket) = buckets.split_at_mut(1);
9510                radix_sort_lms_suffixes_32s_2k_omp(t, sa, n, m, induction_bucket, threads);
9511                radix_sort_set_markers_32s_4k_omp(sa, k, induction_bucket, threads);
9512                place_lms_suffixes_interval_32s_4k(sa, n, k, m - 1, buckets);
9513                induce_partial_order_32s_4k_omp(t, sa, n, k, buckets, threads, thread_state);
9514            }
9515
9516            let names = unsafe {
9517                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9518                renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(sa, n, m, threads, thread_state)
9519            };
9520            if names < m {
9521                let f = unsafe {
9522                    let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9523                    let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9524                    compact_lms_suffixes_32s_omp(t, sa, n, m, fs, threads)
9525                };
9526
9527                let new_t_start =
9528                    total_len - usize::try_from(m - f).expect("m - f must be non-negative");
9529                if main_32s_recursion(
9530                    unsafe {
9531                        std::slice::from_raw_parts_mut(sa_ptr, total_len)[new_t_start..]
9532                            .as_mut_ptr()
9533                    },
9534                    sa_ptr,
9535                    sa_capacity,
9536                    m - f,
9537                    names - f,
9538                    fs + n - 2 * m + f,
9539                    threads,
9540                    thread_state,
9541                    local_buffer,
9542                ) != 0
9543                {
9544                    return -2;
9545                }
9546
9547                unsafe {
9548                    let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9549                    let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9550                    let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9551                    reconstruct_compacted_lms_suffixes_32s_2k_omp(
9552                        t,
9553                        sa,
9554                        n,
9555                        k,
9556                        m,
9557                        fs,
9558                        f,
9559                        buckets,
9560                        SaSint::from(use_local_buffer),
9561                        threads,
9562                        thread_state,
9563                    );
9564                }
9565            } else {
9566                unsafe {
9567                    let t = std::slice::from_raw_parts(t_ptr, n_usize);
9568                    let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9569                    count_lms_suffixes_32s_2k(t, n, k, buckets);
9570                }
9571            }
9572        } else {
9573            unsafe {
9574                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9575                sa[0] = sa[n_usize - 1];
9576            }
9577        }
9578
9579        unsafe {
9580            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9581            initialize_buckets_start_and_end_32s_4k(k, buckets);
9582            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9583            place_lms_suffixes_histogram_32s_4k(sa, n, k, m, buckets);
9584            let t = std::slice::from_raw_parts(t_ptr, n_usize);
9585            induce_final_order_32s_4k(t, sa, n, k, buckets, threads, thread_state);
9586        }
9587
9588        return 0;
9589    } else if k > 0 && ((fs / k) >= 2 || (local_buffer_size / k) >= 2) {
9590        let k_usize = usize::try_from(k).expect("k must be non-negative");
9591        let alignment = if fs >= 1024 && ((fs - 1024) / k) >= 2 {
9592            1024usize
9593        } else {
9594            16usize
9595        };
9596        let need = 2 * k_usize;
9597        let use_local_buffer = local_buffer_size > fs;
9598        let buckets_ptr = if use_local_buffer {
9599            local_buffer.as_mut_ptr()
9600        } else {
9601            unsafe {
9602                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9603                let start =
9604                    if fs_usize >= need + alignment && ((fs_usize - alignment) / k_usize) >= 2 {
9605                        let byte_ptr = sa[total_len - need - alignment..].as_mut_ptr() as usize;
9606                        let aligned = align_up(byte_ptr, alignment * mem::size_of::<SaSint>());
9607                        (aligned - sa_ptr as usize) / mem::size_of::<SaSint>()
9608                    } else {
9609                        total_len - need
9610                    };
9611                sa[start..].as_mut_ptr()
9612            }
9613        };
9614
9615        let m = unsafe {
9616            let t = std::slice::from_raw_parts(t_ptr, n_usize);
9617            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9618            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9619            count_and_gather_lms_suffixes_32s_2k_omp(
9620                t,
9621                sa,
9622                n,
9623                k,
9624                buckets,
9625                SaSint::from(use_local_buffer),
9626                threads,
9627                thread_state,
9628            )
9629        };
9630        if m > 1 {
9631            let m_usize = usize::try_from(m).expect("m must be non-negative");
9632            unsafe {
9633                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9634                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9635                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9636                initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
9637                    t,
9638                    k,
9639                    buckets,
9640                    sa[n_usize - m_usize],
9641                );
9642                let (_, induction_bucket) = buckets.split_at_mut(1);
9643                radix_sort_lms_suffixes_32s_2k_omp(t, sa, n, m, induction_bucket, threads);
9644                place_lms_suffixes_interval_32s_2k(sa, n, k, m - 1, buckets);
9645            }
9646
9647            unsafe {
9648                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9649                initialize_buckets_start_and_end_32s_2k(k, buckets);
9650                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9651                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9652                induce_partial_order_32s_2k_omp(t, sa, n, k, buckets, threads, thread_state);
9653            }
9654
9655            let names = unsafe {
9656                let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9657                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9658                renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(t, sa, n, m, threads)
9659            };
9660            if names < m {
9661                let f = unsafe {
9662                    let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9663                    let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9664                    compact_lms_suffixes_32s_omp(t, sa, n, m, fs, threads)
9665                };
9666
9667                let new_t_start =
9668                    total_len - usize::try_from(m - f).expect("m - f must be non-negative");
9669                if main_32s_recursion(
9670                    unsafe {
9671                        std::slice::from_raw_parts_mut(sa_ptr, total_len)[new_t_start..]
9672                            .as_mut_ptr()
9673                    },
9674                    sa_ptr,
9675                    sa_capacity,
9676                    m - f,
9677                    names - f,
9678                    fs + n - 2 * m + f,
9679                    threads,
9680                    thread_state,
9681                    local_buffer,
9682                ) != 0
9683                {
9684                    return -2;
9685                }
9686
9687                unsafe {
9688                    let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9689                    let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9690                    let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9691                    reconstruct_compacted_lms_suffixes_32s_2k_omp(
9692                        t,
9693                        sa,
9694                        n,
9695                        k,
9696                        m,
9697                        fs,
9698                        f,
9699                        buckets,
9700                        SaSint::from(use_local_buffer),
9701                        threads,
9702                        thread_state,
9703                    );
9704                }
9705            } else {
9706                unsafe {
9707                    let t = std::slice::from_raw_parts(t_ptr, n_usize);
9708                    let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9709                    count_lms_suffixes_32s_2k(t, n, k, buckets);
9710                }
9711            }
9712        } else {
9713            unsafe {
9714                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9715                sa[0] = sa[n_usize - 1];
9716            }
9717        }
9718
9719        unsafe {
9720            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9721            initialize_buckets_end_32s_2k(k, buckets);
9722            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9723            place_lms_suffixes_histogram_32s_2k(sa, n, k, m, buckets);
9724        }
9725
9726        unsafe {
9727            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
9728            initialize_buckets_start_and_end_32s_2k(k, buckets);
9729            let t = std::slice::from_raw_parts(t_ptr, n_usize);
9730            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9731            induce_final_order_32s_2k(t, sa, n, k, buckets, threads, thread_state);
9732        }
9733
9734        0
9735    } else {
9736        let k_usize = usize::try_from(k).expect("k must be non-negative");
9737        let mut heap_buckets = if fs < k { Some(vec![0; k_usize]) } else { None };
9738        let alignment = if fs >= 1024 && (fs - 1024) >= k {
9739            1024usize
9740        } else {
9741            16usize
9742        };
9743        let mut buckets_ptr = if let Some(ref mut heap) = heap_buckets {
9744            heap.as_mut_ptr()
9745        } else {
9746            unsafe {
9747                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9748                let start = if fs_usize >= k_usize + alignment {
9749                    let byte_ptr = sa[total_len - k_usize - alignment..].as_mut_ptr() as usize;
9750                    let aligned = align_up(byte_ptr, alignment * mem::size_of::<SaSint>());
9751                    (aligned - sa_ptr as usize) / mem::size_of::<SaSint>()
9752                } else {
9753                    total_len - k_usize
9754                };
9755                sa[start..].as_mut_ptr()
9756            }
9757        };
9758
9759        if buckets_ptr.is_null() {
9760            return -2;
9761        }
9762
9763        unsafe {
9764            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9765            sa[..n_usize].fill(0);
9766        }
9767
9768        unsafe {
9769            let t = std::slice::from_raw_parts(t_ptr, n_usize);
9770            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
9771            count_suffixes_32s(t, n, k, buckets);
9772            initialize_buckets_end_32s_1k(k, buckets);
9773        }
9774
9775        let m = unsafe {
9776            let t = std::slice::from_raw_parts(t_ptr, n_usize);
9777            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9778            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
9779            radix_sort_lms_suffixes_32s_1k(t, sa, n, buckets)
9780        };
9781        if m > 1 {
9782            unsafe {
9783                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9784                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9785                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
9786                induce_partial_order_32s_1k_omp(t, sa, n, k, buckets, threads, thread_state);
9787            }
9788
9789            let names = unsafe {
9790                let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9791                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9792                renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(t, sa, n, m, threads)
9793            };
9794            if names < m {
9795                if heap_buckets.is_some() {
9796                    let _ = heap_buckets.take();
9797                    buckets_ptr = std::ptr::null_mut();
9798                }
9799
9800                let f = unsafe {
9801                    let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9802                    let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9803                    compact_lms_suffixes_32s_omp(t, sa, n, m, fs, threads)
9804                };
9805
9806                let new_t_start =
9807                    total_len - usize::try_from(m - f).expect("m - f must be non-negative");
9808                if main_32s_recursion(
9809                    unsafe {
9810                        std::slice::from_raw_parts_mut(sa_ptr, total_len)[new_t_start..]
9811                            .as_mut_ptr()
9812                    },
9813                    sa_ptr,
9814                    sa_capacity,
9815                    m - f,
9816                    names - f,
9817                    fs + n - 2 * m + f,
9818                    threads,
9819                    thread_state,
9820                    local_buffer,
9821                ) != 0
9822                {
9823                    return -2;
9824                }
9825
9826                unsafe {
9827                    let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
9828                    let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9829                    reconstruct_compacted_lms_suffixes_32s_1k_omp(t, sa, n, m, fs, f, threads);
9830                }
9831
9832                if buckets_ptr.is_null() {
9833                    heap_buckets = Some(vec![0; k_usize]);
9834                    buckets_ptr = heap_buckets.as_mut().unwrap().as_mut_ptr();
9835                    if buckets_ptr.is_null() {
9836                        return -2;
9837                    }
9838                }
9839            }
9840
9841            unsafe {
9842                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9843                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
9844                count_suffixes_32s(t, n, k, buckets);
9845                initialize_buckets_end_32s_1k(k, buckets);
9846            }
9847            unsafe {
9848                let t = std::slice::from_raw_parts(t_ptr, n_usize);
9849                let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9850                let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
9851                place_lms_suffixes_interval_32s_1k(t, sa, k, m, buckets);
9852            }
9853        }
9854
9855        unsafe {
9856            let t = std::slice::from_raw_parts(t_ptr, n_usize);
9857            let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
9858            let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
9859            induce_final_order_32s_1k(t, sa, n, k, buckets, threads, thread_state);
9860        }
9861
9862        0
9863    }
9864}
9865
9866fn main_32s_entry(
9867    t_ptr: *mut SaSint,
9868    sa: &mut [SaSint],
9869    n: SaSint,
9870    k: SaSint,
9871    fs: SaSint,
9872    threads: SaSint,
9873    thread_state: &mut [ThreadState],
9874) -> SaSint {
9875    let mut local_buffer = [0; 2 * LIBSAIS_LOCAL_BUFFER_SIZE];
9876    main_32s_recursion(
9877        t_ptr,
9878        sa.as_mut_ptr(),
9879        sa.len(),
9880        n,
9881        k,
9882        fs,
9883        threads,
9884        thread_state,
9885        &mut local_buffer[LIBSAIS_LOCAL_BUFFER_SIZE..],
9886    )
9887}
9888
9889fn main_16u(
9890    t: &[u16],
9891    sa: &mut [SaSint],
9892    n: SaSint,
9893    buckets: &mut [SaSint],
9894    flags: SaSint,
9895    r: SaSint,
9896    i_out: Option<&mut [SaSint]>,
9897    fs: SaSint,
9898    freq: Option<&mut [SaSint]>,
9899    threads: SaSint,
9900    thread_state: &mut [ThreadState],
9901) -> SaSint {
9902    let fs = fs.min(SAINT_MAX - n);
9903
9904    let m = count_and_gather_lms_suffixes_16u_omp(t, sa, n, buckets, threads, thread_state);
9905    let k = initialize_buckets_start_and_end_16u(buckets, freq);
9906
9907    if (flags & LIBSAIS_FLAGS_GSA) != 0 && (buckets[0] != 0 || buckets[2] != 0 || buckets[3] != 1) {
9908        return -1;
9909    }
9910
9911    if m > 0 {
9912        let first_lms_suffix = sa[(n - m) as usize];
9913        let left_suffixes_count =
9914            initialize_buckets_for_lms_suffixes_radix_sort_16u(t, buckets, first_lms_suffix);
9915
9916        if threads > 1 && n >= 65_536 {
9917            sa[..(n - m) as usize].fill(0);
9918        }
9919        radix_sort_lms_suffixes_16u_omp(t, sa, n, m, flags, buckets, threads, thread_state);
9920        if threads > 1 && n >= 65_536 {
9921            sa[(n - m) as usize..n as usize].fill(0);
9922        }
9923
9924        initialize_buckets_for_partial_sorting_16u(
9925            t,
9926            buckets,
9927            first_lms_suffix,
9928            left_suffixes_count,
9929        );
9930        induce_partial_order_16u_omp(
9931            t,
9932            sa,
9933            n,
9934            k,
9935            flags,
9936            buckets,
9937            first_lms_suffix,
9938            left_suffixes_count,
9939            threads,
9940        );
9941
9942        let names = renumber_and_gather_lms_suffixes_omp(sa, n, m, fs, threads, thread_state);
9943        if names < m {
9944            let recursive_t_start = (n + fs - m) as usize;
9945            let recursive_t_ptr = sa[recursive_t_start..].as_mut_ptr();
9946            if main_32s_entry(
9947                recursive_t_ptr,
9948                sa,
9949                m,
9950                names,
9951                fs + n - 2 * m,
9952                threads,
9953                thread_state,
9954            ) != 0
9955            {
9956                return -2;
9957            }
9958
9959            gather_lms_suffixes_16u_omp(t, sa, n, threads, thread_state);
9960            reconstruct_lms_suffixes_omp(sa, n, m, threads);
9961        }
9962
9963        place_lms_suffixes_interval_16u(sa, n, m, flags, buckets);
9964    } else {
9965        sa[..n as usize].fill(0);
9966    }
9967
9968    induce_final_order_16u_omp(t, sa, n, k, flags, r, i_out, buckets, threads, thread_state)
9969}
9970
9971fn main_16u_alloc(
9972    t: &[u16],
9973    sa: &mut [SaSint],
9974    flags: SaSint,
9975    r: SaSint,
9976    i_out: Option<&mut [SaSint]>,
9977    fs: SaSint,
9978    freq: Option<&mut [SaSint]>,
9979    threads: SaSint,
9980) -> SaSint {
9981    if fs < 0
9982        || threads < 0
9983        || sa.len()
9984            < t.len()
9985                .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
9986        || freq.as_ref().is_some_and(|freq| freq.len() < ALPHABET_SIZE)
9987    {
9988        return -1;
9989    }
9990
9991    fill_freq(t, freq);
9992    if t.len() <= 1 {
9993        if t.len() == 1 {
9994            sa[0] = 0;
9995        }
9996        return if (flags & LIBSAIS_FLAGS_BWT) != 0 {
9997            t.len() as SaSint
9998        } else {
9999            0
10000        };
10001    }
10002
10003    let mut buckets = vec![0; 8 * ALPHABET_SIZE];
10004    let threads = normalize_threads(threads);
10005    let mut thread_state = if threads > 1 {
10006        match alloc_thread_state(threads) {
10007            Some(thread_state) => thread_state,
10008            None => return -2,
10009        }
10010    } else {
10011        Vec::new()
10012    };
10013
10014    main_16u(
10015        t,
10016        sa,
10017        t.len() as SaSint,
10018        &mut buckets,
10019        flags,
10020        r,
10021        i_out,
10022        fs,
10023        None,
10024        threads,
10025        &mut thread_state,
10026    )
10027}
10028
10029fn main_16u_ctx(
10030    ctx: &mut Context,
10031    t: &[u16],
10032    sa: &mut [SaSint],
10033    flags: SaSint,
10034    r: SaSint,
10035    i_out: Option<&mut [SaSint]>,
10036    fs: SaSint,
10037    freq: Option<&mut [SaSint]>,
10038) -> SaSint {
10039    if fs < 0
10040        || sa.len()
10041            < t.len()
10042                .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
10043        || freq.as_ref().is_some_and(|freq| freq.len() < ALPHABET_SIZE)
10044    {
10045        return -1;
10046    }
10047
10048    if ctx.threads <= 0 || ctx.buckets.len() < 8 * ALPHABET_SIZE {
10049        return -2;
10050    }
10051
10052    fill_freq(t, freq);
10053    if t.len() <= 1 {
10054        if t.len() == 1 {
10055            sa[0] = 0;
10056        }
10057        return if (flags & LIBSAIS_FLAGS_BWT) != 0 {
10058            t.len() as SaSint
10059        } else {
10060            0
10061        };
10062    }
10063
10064    let mut empty_thread_state = [];
10065    let thread_state = if ctx.threads > 1 {
10066        match ctx.thread_state.as_deref_mut() {
10067            Some(thread_state) if thread_state.len() >= ctx.threads as usize => thread_state,
10068            None => return -2,
10069            Some(_) => return -2,
10070        }
10071    } else {
10072        &mut empty_thread_state
10073    };
10074
10075    main_16u(
10076        t,
10077        sa,
10078        t.len() as SaSint,
10079        &mut ctx.buckets,
10080        flags,
10081        r,
10082        i_out,
10083        fs,
10084        None,
10085        ctx.threads,
10086        thread_state,
10087    )
10088}
10089
10090fn main_long(
10091    t: &mut [SaSint],
10092    sa: &mut [SaSint],
10093    k: SaSint,
10094    fs: SaSint,
10095    threads: SaSint,
10096) -> SaSint {
10097    let threads = normalize_threads(threads);
10098    let mut thread_state = if threads > 1 {
10099        match alloc_thread_state(threads) {
10100            Some(thread_state) => thread_state,
10101            None => return -2,
10102        }
10103    } else {
10104        Vec::new()
10105    };
10106
10107    main_32s_entry(
10108        t.as_mut_ptr(),
10109        sa,
10110        t.len() as SaSint,
10111        k,
10112        fs,
10113        threads,
10114        &mut thread_state,
10115    )
10116}
10117
10118/// Constructs the suffix array of a given 16-bit string.
10119///
10120/// # Arguments
10121/// - `T` `[0..n-1]`: The input 16-bit string.
10122/// - `SA` `[0..n-1+fs]`: The output array of suffixes.
10123/// - `n`: The length of the given 16-bit string.
10124/// - `fs`: The extra space available at the end of SA array (0 should be enough for most cases).
10125/// - `freq` `[0..65535]`: The output 16-bit symbol frequency table (can be NULL).
10126///
10127/// # Returns
10128/// 0 if no error occurred, -1 or -2 otherwise.
10129pub fn libsais16x64(
10130    t: &[u16],
10131    sa: &mut [SaSint],
10132    fs: SaSint,
10133    freq: Option<&mut [SaSint]>,
10134) -> SaSint {
10135    main_16u_alloc(t, sa, 0, 0, None, fs, freq, 1)
10136}
10137
10138/// Constructs the generalized suffix array (GSA) of a given 16-bit string set.
10139///
10140/// # Arguments
10141/// - `T` `[0..n-1]`: The input 16-bit string set using 0 as separators (T[n-1] must be 0).
10142/// - `SA` `[0..n-1+fs]`: The output array of suffixes.
10143/// - `n`: The length of the given 16-bit string set.
10144/// - `fs`: The extra space available at the end of SA array (0 should be enough for most cases).
10145/// - `freq` `[0..65535]`: The output 16-bit symbol frequency table (can be NULL).
10146///
10147/// # Returns
10148/// 0 if no error occurred, -1 or -2 otherwise.
10149pub fn libsais16x64_gsa(
10150    t: &[u16],
10151    sa: &mut [SaSint],
10152    fs: SaSint,
10153    freq: Option<&mut [SaSint]>,
10154) -> SaSint {
10155    main_16u_alloc(t, sa, LIBSAIS_FLAGS_GSA, 0, None, fs, freq, 1)
10156}
10157
10158/// Alias for `libsais16x64_long`. See its documentation.
10159pub fn libsais16x64_int(t: &mut [SaSint], sa: &mut [SaSint], k: SaSint, fs: SaSint) -> SaSint {
10160    if fs < 0
10161        || sa.len()
10162            < t.len()
10163                .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
10164    {
10165        return -1;
10166    }
10167
10168    if t.len() <= 1 {
10169        if t.len() == 1 {
10170            sa[0] = 0;
10171        }
10172        return 0;
10173    }
10174
10175    main_long(t, sa, k, fs, 1)
10176}
10177
10178/// Constructs the suffix array of a given integer array.
10179///
10180/// Note, during construction input array will be modified, but restored at the end if no errors occurred.
10181///
10182/// # Arguments
10183/// - `T` `[0..n-1]`: The input integer array.
10184/// - `SA` `[0..n-1+fs]`: The output array of suffixes.
10185/// - `n`: The length of the integer array.
10186/// - `k`: The alphabet size of the input integer array.
10187/// - `fs`: Extra space available at the end of SA array (can be 0, but 4k or better 6k is recommended for optimal performance).
10188///
10189/// # Returns
10190/// 0 if no error occurred, -1 or -2 otherwise.
10191pub fn libsais16x64_long(t: &mut [SaSint], sa: &mut [SaSint], k: SaSint, fs: SaSint) -> SaSint {
10192    libsais16x64_int(t, sa, k, fs)
10193}
10194
10195/// Constructs the suffix array of a given 16-bit string using a libsais16x64 context.
10196///
10197/// - `ctx`: the libsais16x64 context.
10198/// - `t` (`[0..n-1]`): the input 16-bit string.
10199/// - `sa` (`[0..n-1+fs]`): the output array of suffixes.
10200/// - `fs`: extra space available at the end of `sa` (0 should be enough for most cases).
10201/// - `freq` (`[0..65535]`): optional output symbol frequency table.
10202///
10203/// Returns 0 on success, -1 or -2 on error.
10204pub fn libsais16x64_ctx(
10205    ctx: &mut Context,
10206    t: &[u16],
10207    sa: &mut [SaSint],
10208    fs: SaSint,
10209    freq: Option<&mut [SaSint]>,
10210) -> SaSint {
10211    main_16u_ctx(ctx, t, sa, 0, 0, None, fs, freq)
10212}
10213
10214/// Constructs the generalized suffix array (GSA) of a given 16-bit string set using a libsais16x64 context.
10215///
10216/// - `ctx`: the libsais16x64 context.
10217/// - `t` (`[0..n-1]`): the input 16-bit string set using 0 as separators (`t[n-1]` must be 0).
10218/// - `sa` (`[0..n-1+fs]`): the output array of suffixes.
10219/// - `fs`: extra space available at the end of `sa` (0 should be enough for most cases).
10220/// - `freq` (`[0..65535]`): optional output symbol frequency table.
10221///
10222/// Returns 0 on success, -1 or -2 on error.
10223pub fn libsais16x64_gsa_ctx(
10224    ctx: &mut Context,
10225    t: &[u16],
10226    sa: &mut [SaSint],
10227    fs: SaSint,
10228    freq: Option<&mut [SaSint]>,
10229) -> SaSint {
10230    main_16u_ctx(ctx, t, sa, LIBSAIS_FLAGS_GSA, 0, None, fs, freq)
10231}
10232
10233/// Constructs the suffix array of a given 16-bit string in parallel using OpenMP.
10234///
10235/// # Arguments
10236/// - `T` `[0..n-1]`: The input 16-bit string.
10237/// - `SA` `[0..n-1+fs]`: The output array of suffixes.
10238/// - `n`: The length of the given 16-bit string.
10239/// - `fs`: The extra space available at the end of SA array (0 should be enough for most cases).
10240/// - `freq` `[0..65535]`: The output 16-bit symbol frequency table (can be NULL).
10241/// - `threads`: The number of OpenMP threads to use (can be 0 for OpenMP default).
10242///
10243/// # Returns
10244/// 0 if no error occurred, -1 or -2 otherwise.
10245pub fn libsais16x64_omp(
10246    t: &[u16],
10247    sa: &mut [SaSint],
10248    fs: SaSint,
10249    freq: Option<&mut [SaSint]>,
10250    threads: SaSint,
10251) -> SaSint {
10252    if threads < 0 {
10253        -1
10254    } else {
10255        main_16u_alloc(t, sa, 0, 0, None, fs, freq, threads)
10256    }
10257}
10258
10259/// Constructs the generalized suffix array (GSA) of a given 16-bit string set in parallel using OpenMP.
10260///
10261/// # Arguments
10262/// - `T` `[0..n-1]`: The input 16-bit string set using 0 as separators (T[n-1] must be 0).
10263/// - `SA` `[0..n-1+fs]`: The output array of suffixes.
10264/// - `n`: The length of the given 16-bit string set.
10265/// - `fs`: The extra space available at the end of SA array (0 should be enough for most cases).
10266/// - `freq` `[0..65535]`: The output 16-bit symbol frequency table (can be NULL).
10267/// - `threads`: The number of OpenMP threads to use (can be 0 for OpenMP default).
10268///
10269/// # Returns
10270/// 0 if no error occurred, -1 or -2 otherwise.
10271pub fn libsais16x64_gsa_omp(
10272    t: &[u16],
10273    sa: &mut [SaSint],
10274    fs: SaSint,
10275    freq: Option<&mut [SaSint]>,
10276    threads: SaSint,
10277) -> SaSint {
10278    if threads < 0 {
10279        -1
10280    } else {
10281        main_16u_alloc(t, sa, LIBSAIS_FLAGS_GSA, 0, None, fs, freq, threads)
10282    }
10283}
10284
10285/// Alias for `libsais16x64_long_omp`. See its documentation.
10286pub fn libsais16x64_int_omp(
10287    t: &mut [SaSint],
10288    sa: &mut [SaSint],
10289    k: SaSint,
10290    fs: SaSint,
10291    threads: SaSint,
10292) -> SaSint {
10293    if threads < 0
10294        || fs < 0
10295        || sa.len()
10296            < t.len()
10297                .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
10298    {
10299        return -1;
10300    }
10301
10302    if t.len() <= 1 {
10303        if t.len() == 1 {
10304            sa[0] = 0;
10305        }
10306        return 0;
10307    }
10308
10309    main_long(t, sa, k, fs, threads)
10310}
10311
10312/// Constructs the suffix array of a given integer array in parallel using OpenMP.
10313///
10314/// Note, during construction input array will be modified, but restored at the end if no errors occurred.
10315///
10316/// # Arguments
10317/// - `T` `[0..n-1]`: The input integer array.
10318/// - `SA` `[0..n-1+fs]`: The output array of suffixes.
10319/// - `n`: The length of the integer array.
10320/// - `k`: The alphabet size of the input integer array.
10321/// - `fs`: Extra space available at the end of SA array (can be 0, but 4k or better 6k is recommended for optimal performance).
10322/// - `threads`: The number of OpenMP threads to use (can be 0 for OpenMP default).
10323///
10324/// # Returns
10325/// 0 if no error occurred, -1 or -2 otherwise.
10326pub fn libsais16x64_long_omp(
10327    t: &mut [SaSint],
10328    sa: &mut [SaSint],
10329    k: SaSint,
10330    fs: SaSint,
10331    threads: SaSint,
10332) -> SaSint {
10333    libsais16x64_int_omp(t, sa, k, fs, threads)
10334}
10335
10336fn build_bwt(
10337    t: &[u16],
10338    u: &mut [u16],
10339    a: &mut [SaSint],
10340    fs: SaSint,
10341    freq: Option<&mut [SaSint]>,
10342    threads: SaSint,
10343) -> SaSint {
10344    if fs < 0
10345        || threads < 0
10346        || u.len() < t.len()
10347        || a.len()
10348            < t.len()
10349                .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
10350        || freq.as_ref().is_some_and(|freq| freq.len() < ALPHABET_SIZE)
10351    {
10352        return -1;
10353    }
10354    if t.len() <= 1 {
10355        fill_freq(t, freq);
10356        if t.len() == 1 {
10357            u[0] = t[0];
10358        }
10359        return t.len() as SaSint;
10360    }
10361
10362    let n = t.len();
10363    let mut index = main_16u_alloc(t, a, LIBSAIS_FLAGS_BWT, 0, None, fs, freq, threads);
10364    if index >= 0 {
10365        index += 1;
10366        u[0] = t[n - 1];
10367        bwt_copy_16u(&mut u[1..], a, index - 1);
10368        bwt_copy_16u(
10369            &mut u[index as usize..],
10370            &a[index as usize..],
10371            n as SaSint - index,
10372        );
10373    }
10374    index
10375}
10376
10377/// Constructs the burrows-wheeler transformed 16-bit string (BWT) of a given 16-bit string.
10378///
10379/// # Arguments
10380/// - `T` `[0..n-1]`: The input 16-bit string.
10381/// - `U` `[0..n-1]`: The output 16-bit string (can be T).
10382/// - `A` `[0..n-1+fs]`: The temporary array.
10383/// - `n`: The length of the given 16-bit string.
10384/// - `fs`: The extra space available at the end of A array (0 should be enough for most cases).
10385/// - `freq` `[0..65535]`: The output 16-bit symbol frequency table (can be NULL).
10386///
10387/// # Returns
10388/// The primary index if no error occurred, -1 or -2 otherwise.
10389pub fn libsais16x64_bwt(
10390    t: &[u16],
10391    u: &mut [u16],
10392    a: &mut [SaSint],
10393    fs: SaSint,
10394    freq: Option<&mut [SaSint]>,
10395) -> SaSint {
10396    build_bwt(t, u, a, fs, freq, 1)
10397}
10398
10399fn build_bwt_aux(
10400    t: &[u16],
10401    u: &mut [u16],
10402    a: &mut [SaSint],
10403    fs: SaSint,
10404    freq: Option<&mut [SaSint]>,
10405    r: SaSint,
10406    i: &mut [SaSint],
10407    threads: SaSint,
10408) -> SaSint {
10409    if threads < 0 || r < 2 || (r & (r - 1)) != 0 {
10410        return -1;
10411    }
10412    let samples = if t.is_empty() {
10413        1
10414    } else {
10415        (t.len() - 1) / r as usize + 1
10416    };
10417    if i.len() < samples {
10418        return -1;
10419    }
10420    let n = t.len();
10421    if n <= 1 {
10422        fill_freq(t, freq);
10423        if n == 1 {
10424            u[0] = t[0];
10425        }
10426        i[0] = n as SaSint;
10427        return 0;
10428    }
10429
10430    let index = main_16u_alloc(t, a, LIBSAIS_FLAGS_BWT, r, Some(i), fs, freq, threads);
10431    if index == 0 {
10432        u[0] = t[n - 1];
10433        bwt_copy_16u(&mut u[1..], a, i[0] - 1);
10434        bwt_copy_16u(
10435            &mut u[i[0] as usize..],
10436            &a[i[0] as usize..],
10437            n as SaSint - i[0],
10438        );
10439    }
10440    index
10441}
10442
10443/// Constructs the burrows-wheeler transformed 16-bit string (BWT) of a given 16-bit string with auxiliary indexes.
10444///
10445/// # Arguments
10446/// - `T` `[0..n-1]`: The input 16-bit string.
10447/// - `U` `[0..n-1]`: The output 16-bit string (can be T).
10448/// - `A` `[0..n-1+fs]`: The temporary array.
10449/// - `n`: The length of the given 16-bit string.
10450/// - `fs`: The extra space available at the end of A array (0 should be enough for most cases).
10451/// - `freq` `[0..65535]`: The output 16-bit symbol frequency table (can be NULL).
10452/// - `r`: The sampling rate for auxiliary indexes (must be power of 2).
10453/// - `I` `[0..(n-1)/r]`: The output auxiliary indexes.
10454///
10455/// # Returns
10456/// 0 if no error occurred, -1 or -2 otherwise.
10457pub fn libsais16x64_bwt_aux(
10458    t: &[u16],
10459    u: &mut [u16],
10460    a: &mut [SaSint],
10461    fs: SaSint,
10462    freq: Option<&mut [SaSint]>,
10463    r: SaSint,
10464    i: &mut [SaSint],
10465) -> SaSint {
10466    build_bwt_aux(t, u, a, fs, freq, r, i, 1)
10467}
10468
10469/// Constructs the Burrows-Wheeler transformed 16-bit string (BWT) of a given 16-bit string using a libsais16x64 context.
10470///
10471/// - `ctx`: the libsais16x64 context.
10472/// - `t` (`[0..n-1]`): the input 16-bit string.
10473/// - `u` (`[0..n-1]`): the output 16-bit string (can alias `t`).
10474/// - `a` (`[0..n-1+fs]`): the temporary array.
10475/// - `fs`: extra space available at the end of `a` (0 should be enough for most cases).
10476/// - `freq` (`[0..65535]`): optional output symbol frequency table.
10477///
10478/// Returns the primary index on success, -1 or -2 on error.
10479pub fn libsais16x64_bwt_ctx(
10480    ctx: &mut Context,
10481    t: &[u16],
10482    u: &mut [u16],
10483    a: &mut [SaSint],
10484    fs: SaSint,
10485    freq: Option<&mut [SaSint]>,
10486) -> SaSint {
10487    if fs < 0
10488        || u.len() < t.len()
10489        || a.len()
10490            < t.len()
10491                .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
10492        || freq.as_ref().is_some_and(|freq| freq.len() < ALPHABET_SIZE)
10493    {
10494        return -1;
10495    }
10496    if t.len() <= 1 {
10497        fill_freq(t, freq);
10498        if t.len() == 1 {
10499            u[0] = t[0];
10500        }
10501        return t.len() as SaSint;
10502    }
10503
10504    let n = t.len();
10505    let mut index = main_16u_ctx(ctx, t, a, LIBSAIS_FLAGS_BWT, 0, None, fs, freq);
10506    if index >= 0 {
10507        index += 1;
10508        u[0] = t[n - 1];
10509        bwt_copy_16u(&mut u[1..], a, index - 1);
10510        bwt_copy_16u(
10511            &mut u[index as usize..],
10512            &a[index as usize..],
10513            n as SaSint - index,
10514        );
10515    }
10516    index
10517}
10518
10519/// Constructs the BWT of a given 16-bit string with auxiliary indexes using a libsais16x64 context.
10520///
10521/// - `ctx`: the libsais16x64 context.
10522/// - `t` (`[0..n-1]`): the input 16-bit string.
10523/// - `u` (`[0..n-1]`): the output 16-bit string (can alias `t`).
10524/// - `a` (`[0..n-1+fs]`): the temporary array.
10525/// - `fs`: extra space available at the end of `a` (0 should be enough for most cases).
10526/// - `freq` (`[0..65535]`): optional output symbol frequency table.
10527/// - `r`: sampling rate for the auxiliary indexes (must be a power of two).
10528/// - `i` (`[0..(n-1)/r]`): output auxiliary indexes.
10529///
10530/// Returns 0 on success, -1 or -2 on error.
10531pub fn libsais16x64_bwt_aux_ctx(
10532    ctx: &mut Context,
10533    t: &[u16],
10534    u: &mut [u16],
10535    a: &mut [SaSint],
10536    fs: SaSint,
10537    freq: Option<&mut [SaSint]>,
10538    r: SaSint,
10539    i: &mut [SaSint],
10540) -> SaSint {
10541    if fs < 0 || r < 2 || (r & (r - 1)) != 0 {
10542        return -1;
10543    }
10544    let samples = if t.is_empty() {
10545        1
10546    } else {
10547        (t.len() - 1) / r as usize + 1
10548    };
10549    if u.len() < t.len()
10550        || a.len()
10551            < t.len()
10552                .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
10553        || i.len() < samples
10554        || freq.as_ref().is_some_and(|freq| freq.len() < ALPHABET_SIZE)
10555    {
10556        return -1;
10557    }
10558    if t.len() <= 1 {
10559        fill_freq(t, freq);
10560        if t.len() == 1 {
10561            u[0] = t[0];
10562        }
10563        i[0] = t.len() as SaSint;
10564        return 0;
10565    }
10566
10567    let n = t.len();
10568    let index = main_16u_ctx(ctx, t, a, LIBSAIS_FLAGS_BWT, r, Some(i), fs, freq);
10569    if index == 0 {
10570        u[0] = t[n - 1];
10571        bwt_copy_16u(&mut u[1..], a, i[0] - 1);
10572        bwt_copy_16u(
10573            &mut u[i[0] as usize..],
10574            &a[i[0] as usize..],
10575            n as SaSint - i[0],
10576        );
10577    }
10578    index
10579}
10580
10581/// Constructs the burrows-wheeler transformed 16-bit string (BWT) of a given 16-bit string in parallel using OpenMP.
10582///
10583/// # Arguments
10584/// - `T` `[0..n-1]`: The input 16-bit string.
10585/// - `U` `[0..n-1]`: The output 16-bit string (can be T).
10586/// - `A` `[0..n-1+fs]`: The temporary array.
10587/// - `n`: The length of the given 16-bit string.
10588/// - `fs`: The extra space available at the end of A array (0 should be enough for most cases).
10589/// - `freq` `[0..65535]`: The output 16-bit symbol frequency table (can be NULL).
10590/// - `threads`: The number of OpenMP threads to use (can be 0 for OpenMP default).
10591///
10592/// # Returns
10593/// The primary index if no error occurred, -1 or -2 otherwise.
10594pub fn libsais16x64_bwt_omp(
10595    t: &[u16],
10596    u: &mut [u16],
10597    a: &mut [SaSint],
10598    fs: SaSint,
10599    freq: Option<&mut [SaSint]>,
10600    threads: SaSint,
10601) -> SaSint {
10602    if threads < 0 {
10603        -1
10604    } else {
10605        build_bwt(t, u, a, fs, freq, threads)
10606    }
10607}
10608
10609/// Constructs the burrows-wheeler transformed 16-bit string (BWT) of a given 16-bit string with auxiliary indexes in parallel using OpenMP.
10610///
10611/// # Arguments
10612/// - `T` `[0..n-1]`: The input 16-bit string.
10613/// - `U` `[0..n-1]`: The output 16-bit string (can be T).
10614/// - `A` `[0..n-1+fs]`: The temporary array.
10615/// - `n`: The length of the given 16-bit string.
10616/// - `fs`: The extra space available at the end of A array (0 should be enough for most cases).
10617/// - `freq` `[0..65535]`: The output 16-bit symbol frequency table (can be NULL).
10618/// - `r`: The sampling rate for auxiliary indexes (must be power of 2).
10619/// - `I` `[0..(n-1)/r]`: The output auxiliary indexes.
10620/// - `threads`: The number of OpenMP threads to use (can be 0 for OpenMP default).
10621///
10622/// # Returns
10623/// 0 if no error occurred, -1 or -2 otherwise.
10624pub fn libsais16x64_bwt_aux_omp(
10625    t: &[u16],
10626    u: &mut [u16],
10627    a: &mut [SaSint],
10628    fs: SaSint,
10629    freq: Option<&mut [SaSint]>,
10630    r: SaSint,
10631    i: &mut [SaSint],
10632    threads: SaSint,
10633) -> SaSint {
10634    if threads < 0 {
10635        -1
10636    } else {
10637        build_bwt_aux(t, u, a, fs, freq, r, i, threads)
10638    }
10639}
10640
10641fn validate_unbwt_aux(
10642    t: &[u16],
10643    u: &[u16],
10644    a: &[SaSint],
10645    freq: Option<&[SaSint]>,
10646    r: SaSint,
10647    i: &[SaSint],
10648) -> SaSint {
10649    let n = t.len();
10650    if u.len() < n
10651        || a.len() < n
10652        || freq.is_some_and(|freq| freq.len() < ALPHABET_SIZE)
10653        || ((r != n as SaSint) && (r < 2 || (r & (r - 1)) != 0))
10654        || i.is_empty()
10655    {
10656        return -1;
10657    }
10658    if n <= 1 {
10659        return if i[0] == n as SaSint { 0 } else { -1 };
10660    }
10661
10662    let samples = (n - 1) / r as usize + 1;
10663    if i.len() < samples {
10664        return -1;
10665    }
10666
10667    for &index in &i[..samples] {
10668        if index <= 0 || index as usize > n {
10669            return -1;
10670        }
10671    }
10672    0
10673}
10674
10675fn unbwt_compute_histogram(t: &[u16], count: &mut [usize]) {
10676    for &symbol in t {
10677        count[symbol as usize] += 1;
10678    }
10679}
10680
10681fn unbwt_shift(n: usize) -> usize {
10682    let mut shift = 0usize;
10683    while (n >> shift) > (1usize << UNBWT_FASTBITS) {
10684        shift += 1;
10685    }
10686    shift
10687}
10688
10689fn unbwt_calculate_fastbits(bucket2: &mut [usize], fastbits: &mut [u16], shift: usize) {
10690    let mut v = 0usize;
10691    let mut sum = 1usize;
10692    for (w, bucket) in bucket2.iter_mut().enumerate().take(ALPHABET_SIZE) {
10693        let prev = sum;
10694        sum += *bucket;
10695        *bucket = prev;
10696        if prev != sum {
10697            while v <= ((sum - 1) >> shift) {
10698                fastbits[v] = w as u16;
10699                v += 1;
10700            }
10701        }
10702    }
10703}
10704
10705fn unbwt_calculate_p(t: &[u16], p: &mut [usize], bucket2: &mut [usize], index: usize) {
10706    for row in 0..index {
10707        let symbol = t[row] as usize;
10708        p[bucket2[symbol]] = row;
10709        bucket2[symbol] += 1;
10710    }
10711
10712    for row in index + 1..=t.len() {
10713        let symbol = t[row - 1] as usize;
10714        p[bucket2[symbol]] = row;
10715        bucket2[symbol] += 1;
10716    }
10717}
10718
10719#[allow(dead_code, non_snake_case)]
10720fn unbwt_calculate_P(
10721    t: &[u16],
10722    p: &mut [usize],
10723    bucket2: &mut [usize],
10724    index: usize,
10725    block_start: usize,
10726    block_end: usize,
10727) {
10728    let first_end = index.min(block_end);
10729    for row in block_start..first_end {
10730        let symbol = t[row] as usize;
10731        p[bucket2[symbol]] = row;
10732        bucket2[symbol] += 1;
10733    }
10734
10735    let second_start = block_start.max(index) + 1;
10736    for row in second_start..=block_end {
10737        let symbol = t[row - 1] as usize;
10738        p[bucket2[symbol]] = row;
10739        bucket2[symbol] += 1;
10740    }
10741}
10742
10743fn unbwt_init_single(
10744    t: &[u16],
10745    p: &mut [usize],
10746    freq: Option<&[SaSint]>,
10747    i: &[SaSint],
10748    bucket2: &mut [usize],
10749    fastbits: &mut [u16],
10750) {
10751    let shift = unbwt_shift(t.len());
10752    if let Some(freq) = freq {
10753        for c in 0..ALPHABET_SIZE {
10754            bucket2[c] = freq[c] as usize;
10755        }
10756    } else {
10757        bucket2.fill(0);
10758        unbwt_compute_histogram(t, bucket2);
10759    }
10760
10761    unbwt_calculate_fastbits(bucket2, fastbits, shift);
10762    unbwt_calculate_p(t, p, bucket2, i[0] as usize);
10763}
10764
10765#[allow(dead_code)]
10766fn unbwt_init_parallel(
10767    t: &[u16],
10768    p: &mut [usize],
10769    freq: Option<&[SaSint]>,
10770    i: &[SaSint],
10771    bucket2: &mut [usize],
10772    fastbits: &mut [u16],
10773    buckets: &mut [usize],
10774    threads: SaSint,
10775) {
10776    let n = t.len();
10777    let available_threads = buckets.len() / ALPHABET_SIZE;
10778    let num_threads = if threads > 1 && n >= 65_536 && available_threads > 1 {
10779        usize::try_from(threads)
10780            .expect("threads must be non-negative")
10781            .min(available_threads)
10782            .max(1)
10783    } else {
10784        1
10785    };
10786
10787    if num_threads == 1 {
10788        unbwt_init_single(t, p, freq, i, bucket2, fastbits);
10789        return;
10790    }
10791
10792    let index = usize::try_from(i[0]).expect("primary index must be non-negative");
10793    let shift = unbwt_shift(n);
10794    let block_stride = (n / num_threads) & !15usize;
10795
10796    for thread in 0..num_threads {
10797        let block_start = thread * block_stride;
10798        let block_size = if thread + 1 < num_threads {
10799            block_stride
10800        } else {
10801            n - block_start
10802        };
10803        let local = &mut buckets[thread * ALPHABET_SIZE..(thread + 1) * ALPHABET_SIZE];
10804        local.fill(0);
10805        unbwt_compute_histogram(&t[block_start..block_start + block_size], local);
10806    }
10807
10808    bucket2.fill(0);
10809    for thread in 0..num_threads {
10810        let local = &mut buckets[thread * ALPHABET_SIZE..(thread + 1) * ALPHABET_SIZE];
10811        for c in 0..ALPHABET_SIZE {
10812            let a = bucket2[c];
10813            let b = local[c];
10814            bucket2[c] = a + b;
10815            local[c] = a;
10816        }
10817    }
10818
10819    unbwt_calculate_fastbits(bucket2, fastbits, shift);
10820
10821    for thread in 0..num_threads {
10822        let block_start = thread * block_stride;
10823        let block_size = if thread + 1 < num_threads {
10824            block_stride
10825        } else {
10826            n - block_start
10827        };
10828        let local = &mut buckets[thread * ALPHABET_SIZE..(thread + 1) * ALPHABET_SIZE];
10829        for c in 0..ALPHABET_SIZE {
10830            local[c] += bucket2[c];
10831        }
10832        unbwt_calculate_P(t, p, local, index, block_start, block_start + block_size);
10833    }
10834
10835    let last_local = &buckets[(num_threads - 1) * ALPHABET_SIZE..num_threads * ALPHABET_SIZE];
10836    bucket2.copy_from_slice(last_local);
10837}
10838
10839fn unbwt_decode_symbol(
10840    p0: usize,
10841    p: &[usize],
10842    bucket2: &[usize],
10843    fastbits: &[u16],
10844    shift: usize,
10845) -> (u16, usize) {
10846    let mut c0 = fastbits[p0 >> shift] as usize;
10847    if bucket2[c0] <= p0 {
10848        while bucket2[c0] <= p0 {
10849            c0 += 1;
10850        }
10851    }
10852    (c0 as u16, p[p0])
10853}
10854
10855#[allow(dead_code)]
10856fn unbwt_decode_1(
10857    u: &mut [u16],
10858    p: &[usize],
10859    bucket2: &[usize],
10860    fastbits: &[u16],
10861    shift: usize,
10862    i0: &mut usize,
10863    k: usize,
10864) {
10865    let mut cursors = [*i0];
10866    unbwt_decode_lanes::<1>(u, p, bucket2, fastbits, shift, k, &mut cursors, k);
10867    *i0 = cursors[0];
10868}
10869
10870#[allow(dead_code)]
10871fn unbwt_decode_2(
10872    u: &mut [u16],
10873    p: &[usize],
10874    bucket2: &[usize],
10875    fastbits: &[u16],
10876    shift: usize,
10877    r: usize,
10878    i0: &mut usize,
10879    i1: &mut usize,
10880    k: usize,
10881) {
10882    let mut cursors = [*i0, *i1];
10883    unbwt_decode_lanes::<2>(u, p, bucket2, fastbits, shift, r, &mut cursors, k);
10884    *i0 = cursors[0];
10885    *i1 = cursors[1];
10886}
10887
10888#[allow(dead_code)]
10889fn unbwt_decode_3(
10890    u: &mut [u16],
10891    p: &[usize],
10892    bucket2: &[usize],
10893    fastbits: &[u16],
10894    shift: usize,
10895    r: usize,
10896    i0: &mut usize,
10897    i1: &mut usize,
10898    i2: &mut usize,
10899    k: usize,
10900) {
10901    let mut cursors = [*i0, *i1, *i2];
10902    unbwt_decode_lanes::<3>(u, p, bucket2, fastbits, shift, r, &mut cursors, k);
10903    *i0 = cursors[0];
10904    *i1 = cursors[1];
10905    *i2 = cursors[2];
10906}
10907
10908#[allow(dead_code)]
10909fn unbwt_decode_4(
10910    u: &mut [u16],
10911    p: &[usize],
10912    bucket2: &[usize],
10913    fastbits: &[u16],
10914    shift: usize,
10915    r: usize,
10916    i0: &mut usize,
10917    i1: &mut usize,
10918    i2: &mut usize,
10919    i3: &mut usize,
10920    k: usize,
10921) {
10922    let mut cursors = [*i0, *i1, *i2, *i3];
10923    unbwt_decode_lanes::<4>(u, p, bucket2, fastbits, shift, r, &mut cursors, k);
10924    *i0 = cursors[0];
10925    *i1 = cursors[1];
10926    *i2 = cursors[2];
10927    *i3 = cursors[3];
10928}
10929
10930#[allow(dead_code)]
10931fn unbwt_decode_5(
10932    u: &mut [u16],
10933    p: &[usize],
10934    bucket2: &[usize],
10935    fastbits: &[u16],
10936    shift: usize,
10937    r: usize,
10938    cursors: &mut [usize; 5],
10939    k: usize,
10940) {
10941    unbwt_decode_lanes::<5>(u, p, bucket2, fastbits, shift, r, cursors, k);
10942}
10943
10944#[allow(dead_code)]
10945fn unbwt_decode_6(
10946    u: &mut [u16],
10947    p: &[usize],
10948    bucket2: &[usize],
10949    fastbits: &[u16],
10950    shift: usize,
10951    r: usize,
10952    cursors: &mut [usize; 6],
10953    k: usize,
10954) {
10955    unbwt_decode_lanes::<6>(u, p, bucket2, fastbits, shift, r, cursors, k);
10956}
10957
10958#[allow(dead_code)]
10959fn unbwt_decode_7(
10960    u: &mut [u16],
10961    p: &[usize],
10962    bucket2: &[usize],
10963    fastbits: &[u16],
10964    shift: usize,
10965    r: usize,
10966    cursors: &mut [usize; 7],
10967    k: usize,
10968) {
10969    unbwt_decode_lanes::<7>(u, p, bucket2, fastbits, shift, r, cursors, k);
10970}
10971
10972#[allow(dead_code)]
10973fn unbwt_decode_8(
10974    u: &mut [u16],
10975    p: &[usize],
10976    bucket2: &[usize],
10977    fastbits: &[u16],
10978    shift: usize,
10979    r: usize,
10980    cursors: &mut [usize; 8],
10981    k: usize,
10982) {
10983    unbwt_decode_lanes::<8>(u, p, bucket2, fastbits, shift, r, cursors, k);
10984}
10985
10986fn unbwt_decode(
10987    u: &mut [u16],
10988    p: &[usize],
10989    n: usize,
10990    r: usize,
10991    i: &[SaSint],
10992    bucket2: &[usize],
10993    fastbits: &[u16],
10994) {
10995    let shift = unbwt_shift(n);
10996    let blocks = 1 + (n - 1) / r;
10997    let remainder = n - r * (blocks - 1);
10998    unbwt_decode_blocks(u, p, r, i, bucket2, fastbits, shift, blocks, remainder);
10999}
11000
11001fn unbwt_decode_blocks(
11002    u: &mut [u16],
11003    p: &[usize],
11004    r: usize,
11005    i: &[SaSint],
11006    bucket2: &[usize],
11007    fastbits: &[u16],
11008    shift: usize,
11009    blocks: usize,
11010    remainder: usize,
11011) {
11012    let mut blocks_left = blocks;
11013    let mut i_offset = 0usize;
11014    let mut u_offset = 0usize;
11015
11016    while blocks_left > 8 {
11017        let mut cursors = [
11018            i[i_offset] as usize,
11019            i[i_offset + 1] as usize,
11020            i[i_offset + 2] as usize,
11021            i[i_offset + 3] as usize,
11022            i[i_offset + 4] as usize,
11023            i[i_offset + 5] as usize,
11024            i[i_offset + 6] as usize,
11025            i[i_offset + 7] as usize,
11026        ];
11027        unbwt_decode_lanes::<8>(
11028            &mut u[u_offset..],
11029            p,
11030            bucket2,
11031            fastbits,
11032            shift,
11033            r,
11034            &mut cursors,
11035            r,
11036        );
11037        i_offset += 8;
11038        blocks_left -= 8;
11039        u_offset += 8 * r;
11040    }
11041
11042    match blocks_left {
11043        1 => {
11044            let mut cursors = [i[i_offset] as usize];
11045            unbwt_decode_lanes::<1>(
11046                &mut u[u_offset..],
11047                p,
11048                bucket2,
11049                fastbits,
11050                shift,
11051                r,
11052                &mut cursors,
11053                remainder,
11054            );
11055        }
11056        2 => {
11057            let mut cursors = [i[i_offset] as usize, i[i_offset + 1] as usize];
11058            unbwt_decode_lanes::<2>(
11059                &mut u[u_offset..],
11060                p,
11061                bucket2,
11062                fastbits,
11063                shift,
11064                r,
11065                &mut cursors,
11066                remainder,
11067            );
11068            let mut first = [cursors[0]];
11069            unbwt_decode_lanes::<1>(
11070                &mut u[u_offset + remainder..],
11071                p,
11072                bucket2,
11073                fastbits,
11074                shift,
11075                r,
11076                &mut first,
11077                r - remainder,
11078            );
11079        }
11080        3 => {
11081            let mut cursors = [
11082                i[i_offset] as usize,
11083                i[i_offset + 1] as usize,
11084                i[i_offset + 2] as usize,
11085            ];
11086            unbwt_decode_lanes::<3>(
11087                &mut u[u_offset..],
11088                p,
11089                bucket2,
11090                fastbits,
11091                shift,
11092                r,
11093                &mut cursors,
11094                remainder,
11095            );
11096            let mut first = [cursors[0], cursors[1]];
11097            unbwt_decode_lanes::<2>(
11098                &mut u[u_offset + remainder..],
11099                p,
11100                bucket2,
11101                fastbits,
11102                shift,
11103                r,
11104                &mut first,
11105                r - remainder,
11106            );
11107        }
11108        4 => {
11109            let mut cursors = [
11110                i[i_offset] as usize,
11111                i[i_offset + 1] as usize,
11112                i[i_offset + 2] as usize,
11113                i[i_offset + 3] as usize,
11114            ];
11115            unbwt_decode_lanes::<4>(
11116                &mut u[u_offset..],
11117                p,
11118                bucket2,
11119                fastbits,
11120                shift,
11121                r,
11122                &mut cursors,
11123                remainder,
11124            );
11125            let mut first = [cursors[0], cursors[1], cursors[2]];
11126            unbwt_decode_lanes::<3>(
11127                &mut u[u_offset + remainder..],
11128                p,
11129                bucket2,
11130                fastbits,
11131                shift,
11132                r,
11133                &mut first,
11134                r - remainder,
11135            );
11136        }
11137        5 => {
11138            let mut cursors = [
11139                i[i_offset] as usize,
11140                i[i_offset + 1] as usize,
11141                i[i_offset + 2] as usize,
11142                i[i_offset + 3] as usize,
11143                i[i_offset + 4] as usize,
11144            ];
11145            unbwt_decode_lanes::<5>(
11146                &mut u[u_offset..],
11147                p,
11148                bucket2,
11149                fastbits,
11150                shift,
11151                r,
11152                &mut cursors,
11153                remainder,
11154            );
11155            let mut first = [cursors[0], cursors[1], cursors[2], cursors[3]];
11156            unbwt_decode_lanes::<4>(
11157                &mut u[u_offset + remainder..],
11158                p,
11159                bucket2,
11160                fastbits,
11161                shift,
11162                r,
11163                &mut first,
11164                r - remainder,
11165            );
11166        }
11167        6 => {
11168            let mut cursors = [
11169                i[i_offset] as usize,
11170                i[i_offset + 1] as usize,
11171                i[i_offset + 2] as usize,
11172                i[i_offset + 3] as usize,
11173                i[i_offset + 4] as usize,
11174                i[i_offset + 5] as usize,
11175            ];
11176            unbwt_decode_lanes::<6>(
11177                &mut u[u_offset..],
11178                p,
11179                bucket2,
11180                fastbits,
11181                shift,
11182                r,
11183                &mut cursors,
11184                remainder,
11185            );
11186            let mut first = [cursors[0], cursors[1], cursors[2], cursors[3], cursors[4]];
11187            unbwt_decode_lanes::<5>(
11188                &mut u[u_offset + remainder..],
11189                p,
11190                bucket2,
11191                fastbits,
11192                shift,
11193                r,
11194                &mut first,
11195                r - remainder,
11196            );
11197        }
11198        7 => {
11199            let mut cursors = [
11200                i[i_offset] as usize,
11201                i[i_offset + 1] as usize,
11202                i[i_offset + 2] as usize,
11203                i[i_offset + 3] as usize,
11204                i[i_offset + 4] as usize,
11205                i[i_offset + 5] as usize,
11206                i[i_offset + 6] as usize,
11207            ];
11208            unbwt_decode_lanes::<7>(
11209                &mut u[u_offset..],
11210                p,
11211                bucket2,
11212                fastbits,
11213                shift,
11214                r,
11215                &mut cursors,
11216                remainder,
11217            );
11218            let mut first = [
11219                cursors[0], cursors[1], cursors[2], cursors[3], cursors[4], cursors[5],
11220            ];
11221            unbwt_decode_lanes::<6>(
11222                &mut u[u_offset + remainder..],
11223                p,
11224                bucket2,
11225                fastbits,
11226                shift,
11227                r,
11228                &mut first,
11229                r - remainder,
11230            );
11231        }
11232        _ => {
11233            let mut cursors = [
11234                i[i_offset] as usize,
11235                i[i_offset + 1] as usize,
11236                i[i_offset + 2] as usize,
11237                i[i_offset + 3] as usize,
11238                i[i_offset + 4] as usize,
11239                i[i_offset + 5] as usize,
11240                i[i_offset + 6] as usize,
11241                i[i_offset + 7] as usize,
11242            ];
11243            unbwt_decode_lanes::<8>(
11244                &mut u[u_offset..],
11245                p,
11246                bucket2,
11247                fastbits,
11248                shift,
11249                r,
11250                &mut cursors,
11251                remainder,
11252            );
11253            let mut first = [
11254                cursors[0], cursors[1], cursors[2], cursors[3], cursors[4], cursors[5], cursors[6],
11255            ];
11256            unbwt_decode_lanes::<7>(
11257                &mut u[u_offset + remainder..],
11258                p,
11259                bucket2,
11260                fastbits,
11261                shift,
11262                r,
11263                &mut first,
11264                r - remainder,
11265            );
11266        }
11267    }
11268}
11269
11270fn unbwt_decode_omp(
11271    u: &mut [u16],
11272    p: &[usize],
11273    n: usize,
11274    r: usize,
11275    i: &[SaSint],
11276    bucket2: &[usize],
11277    fastbits: &[u16],
11278    threads: SaSint,
11279) {
11280    let blocks = 1 + (n - 1) / r;
11281    let remainder = n - r * (blocks - 1);
11282    let num_threads = if threads > 1 && n >= 65_536 {
11283        usize::try_from(threads)
11284            .expect("threads must be non-negative")
11285            .min(blocks)
11286            .max(1)
11287    } else {
11288        1
11289    };
11290
11291    if num_threads == 1 {
11292        unbwt_decode(u, p, n, r, i, bucket2, fastbits);
11293        return;
11294    }
11295
11296    let shift = unbwt_shift(n);
11297    let block_stride = blocks / num_threads;
11298    let block_remainder = blocks % num_threads;
11299    let u_ptr = SyncMutPtr::new(u);
11300    run_rayon_with_threads(num_threads, || {
11301        (0..num_threads).into_par_iter().for_each(|thread| {
11302            let block_count = block_stride + usize::from(thread < block_remainder);
11303            let block_start = block_stride * thread + thread.min(block_remainder);
11304            let tail = if thread + 1 < num_threads {
11305                r
11306            } else {
11307                remainder
11308            };
11309            let u = unsafe { u_ptr.as_slice() };
11310            unbwt_decode_blocks(
11311                &mut u[r * block_start..],
11312                p,
11313                r,
11314                &i[block_start..],
11315                bucket2,
11316                fastbits,
11317                shift,
11318                block_count,
11319                tail,
11320            );
11321        });
11322    });
11323}
11324
11325fn unbwt_decode_lanes<const LANES: usize>(
11326    u: &mut [u16],
11327    p: &[usize],
11328    bucket2: &[usize],
11329    fastbits: &[u16],
11330    shift: usize,
11331    r: usize,
11332    cursors: &mut [usize; LANES],
11333    k: usize,
11334) {
11335    for pos in 0..k {
11336        for lane in 0..LANES {
11337            let (symbol, next) = unbwt_decode_symbol(cursors[lane], p, bucket2, fastbits, shift);
11338            cursors[lane] = next;
11339            u[lane * r + pos] = symbol;
11340        }
11341    }
11342}
11343
11344fn unbwt_core(
11345    t: &[u16],
11346    u: &mut [u16],
11347    a: &mut [SaSint],
11348    freq: Option<&[SaSint]>,
11349    r: SaSint,
11350    i: &[SaSint],
11351) -> SaSint {
11352    let n = t.len();
11353    let shift = unbwt_shift(n);
11354    let mut bucket2 = vec![0usize; ALPHABET_SIZE];
11355    let mut fastbits = vec![0u16; 1 + (n >> shift)];
11356
11357    unbwt_core_with_buffers(t, u, a, freq, r, i, &mut bucket2, &mut fastbits, 1)
11358}
11359
11360fn unbwt_core_with_buffers(
11361    t: &[u16],
11362    u: &mut [u16],
11363    a: &mut [SaSint],
11364    freq: Option<&[SaSint]>,
11365    r: SaSint,
11366    i: &[SaSint],
11367    bucket2: &mut [usize],
11368    fastbits: &mut [u16],
11369    threads: SaSint,
11370) -> SaSint {
11371    let n = t.len();
11372    let shift = unbwt_shift(n);
11373    if bucket2.len() < ALPHABET_SIZE || fastbits.len() < 1 + (n >> shift) {
11374        return -2;
11375    }
11376
11377    let mut p = vec![0usize; n + 1];
11378    unbwt_init_single(
11379        t,
11380        &mut p,
11381        freq,
11382        i,
11383        &mut bucket2[..ALPHABET_SIZE],
11384        &mut fastbits[..1 + (n >> shift)],
11385    );
11386    unbwt_decode_omp(
11387        u,
11388        &p,
11389        n,
11390        r as usize,
11391        i,
11392        &bucket2[..ALPHABET_SIZE],
11393        &fastbits[..1 + (n >> shift)],
11394        threads,
11395    );
11396
11397    for (dst, &src) in a.iter_mut().zip(p.iter().skip(1)) {
11398        *dst = src as SaSint;
11399    }
11400    0
11401}
11402
11403fn inverse_bwt(
11404    t: &[u16],
11405    u: &mut [u16],
11406    a: &mut [SaSint],
11407    freq: Option<&[SaSint]>,
11408    primary: SaSint,
11409) -> SaSint {
11410    let n = t.len();
11411    let i = [primary];
11412    let rc = validate_unbwt_aux(t, u, a, freq, n as SaSint, &i);
11413    if rc != 0 {
11414        return rc;
11415    }
11416    if n <= 1 {
11417        if n == 1 {
11418            u[0] = t[0];
11419        }
11420        return 0;
11421    }
11422    unbwt_core(t, u, a, freq, n as SaSint, &i)
11423}
11424
11425/// Constructs the original 16-bit string from a given burrows-wheeler transformed 16-bit string (BWT) with primary index.
11426///
11427/// # Arguments
11428/// - `T` `[0..n-1]`: The input 16-bit string.
11429/// - `U` `[0..n-1]`: The output 16-bit string (can be T).
11430/// - `A` `[0..n]`: The temporary array (NOTE, temporary array must be n + 1 size).
11431/// - `n`: The length of the given 16-bit string.
11432/// - `freq` `[0..65535]`: The input 16-bit symbol frequency table (can be NULL).
11433/// - `i`: The primary index.
11434///
11435/// # Returns
11436/// 0 if no error occurred, -1 or -2 otherwise.
11437pub fn libsais16x64_unbwt(
11438    t: &[u16],
11439    u: &mut [u16],
11440    a: &mut [SaSint],
11441    freq: Option<&[SaSint]>,
11442    i: SaSint,
11443) -> SaSint {
11444    inverse_bwt(t, u, a, freq, i)
11445}
11446
11447/// Reconstructs the original 16-bit string from a given BWT and primary index using a libsais16x64 reverse-BWT context.
11448///
11449/// - `ctx`: the libsais16x64 reverse-BWT context.
11450/// - `t` (`[0..n-1]`): the input 16-bit string.
11451/// - `u` (`[0..n-1]`): the output 16-bit string (can alias `t`).
11452/// - `a` (`[0..n]`): the temporary array (must have length `n + 1`).
11453/// - `freq` (`[0..65535]`): optional input symbol frequency table.
11454/// - `i`: the primary index.
11455///
11456/// Returns 0 on success, -1 or -2 on error.
11457pub fn libsais16x64_unbwt_ctx(
11458    ctx: &mut UnbwtContext,
11459    t: &[u16],
11460    u: &mut [u16],
11461    a: &mut [SaSint],
11462    freq: Option<&[SaSint]>,
11463    i: SaSint,
11464) -> SaSint {
11465    libsais16x64_unbwt_aux_ctx(ctx, t, u, a, freq, t.len() as SaSint, &[i])
11466}
11467
11468/// Constructs the original 16-bit string from a given burrows-wheeler transformed 16-bit string (BWT) with auxiliary indexes.
11469///
11470/// # Arguments
11471/// - `T` `[0..n-1]`: The input 16-bit string.
11472/// - `U` `[0..n-1]`: The output 16-bit string (can be T).
11473/// - `A` `[0..n]`: The temporary array (NOTE, temporary array must be n + 1 size).
11474/// - `n`: The length of the given 16-bit string.
11475/// - `freq` `[0..65535]`: The input 16-bit symbol frequency table (can be NULL).
11476/// - `r`: The sampling rate for auxiliary indexes (must be power of 2).
11477/// - `I` `[0..(n-1)/r]`: The input auxiliary indexes.
11478///
11479/// # Returns
11480/// 0 if no error occurred, -1 or -2 otherwise.
11481pub fn libsais16x64_unbwt_aux(
11482    t: &[u16],
11483    u: &mut [u16],
11484    a: &mut [SaSint],
11485    freq: Option<&[SaSint]>,
11486    r: SaSint,
11487    i: &[SaSint],
11488) -> SaSint {
11489    let rc = validate_unbwt_aux(t, u, a, freq, r, i);
11490    if rc != 0 {
11491        return rc;
11492    }
11493    if t.len() <= 1 {
11494        if t.len() == 1 {
11495            u[0] = t[0];
11496        }
11497        return 0;
11498    }
11499    unbwt_core(t, u, a, freq, r, i)
11500}
11501
11502/// Reconstructs the original 16-bit string from a given BWT with auxiliary indexes using a libsais16x64 reverse-BWT context.
11503///
11504/// - `ctx`: the libsais16x64 reverse-BWT context.
11505/// - `t` (`[0..n-1]`): the input 16-bit string.
11506/// - `u` (`[0..n-1]`): the output 16-bit string (can alias `t`).
11507/// - `a` (`[0..n]`): the temporary array (must have length `n + 1`).
11508/// - `freq` (`[0..65535]`): optional input symbol frequency table.
11509/// - `r`: sampling rate for the auxiliary indexes (must be a power of two).
11510/// - `i` (`[0..(n-1)/r]`): input auxiliary indexes.
11511///
11512/// Returns 0 on success, -1 or -2 on error.
11513pub fn libsais16x64_unbwt_aux_ctx(
11514    ctx: &mut UnbwtContext,
11515    t: &[u16],
11516    u: &mut [u16],
11517    a: &mut [SaSint],
11518    freq: Option<&[SaSint]>,
11519    r: SaSint,
11520    i: &[SaSint],
11521) -> SaSint {
11522    let rc = validate_unbwt_aux(t, u, a, freq, r, i);
11523    if rc != 0 {
11524        return rc;
11525    }
11526    if t.len() <= 1 {
11527        if t.len() == 1 {
11528            u[0] = t[0];
11529        }
11530        return 0;
11531    }
11532    unbwt_core_with_buffers(
11533        t,
11534        u,
11535        a,
11536        freq,
11537        r,
11538        i,
11539        &mut ctx.bucket2,
11540        &mut ctx.fastbits,
11541        ctx.threads,
11542    )
11543}
11544
11545/// Constructs the original 16-bit string from a given burrows-wheeler transformed 16-bit string (BWT) with primary index in parallel using OpenMP.
11546///
11547/// # Arguments
11548/// - `T` `[0..n-1]`: The input 16-bit string.
11549/// - `U` `[0..n-1]`: The output 16-bit string (can be T).
11550/// - `A` `[0..n]`: The temporary array (NOTE, temporary array must be n + 1 size).
11551/// - `n`: The length of the given 16-bit string.
11552/// - `freq` `[0..65535]`: The input 16-bit symbol frequency table (can be NULL).
11553/// - `i`: The primary index.
11554/// - `threads`: The number of OpenMP threads to use (can be 0 for OpenMP default).
11555///
11556/// # Returns
11557/// 0 if no error occurred, -1 or -2 otherwise.
11558pub fn libsais16x64_unbwt_omp(
11559    t: &[u16],
11560    u: &mut [u16],
11561    a: &mut [SaSint],
11562    freq: Option<&[SaSint]>,
11563    i: SaSint,
11564    threads: SaSint,
11565) -> SaSint {
11566    if threads < 0 {
11567        -1
11568    } else {
11569        let primary = [i];
11570        libsais16x64_unbwt_aux_omp(t, u, a, freq, t.len() as SaSint, &primary, threads)
11571    }
11572}
11573
11574/// Constructs the original 16-bit string from a given burrows-wheeler transformed 16-bit string (BWT) with auxiliary indexes in parallel using OpenMP.
11575///
11576/// # Arguments
11577/// - `T` `[0..n-1]`: The input 16-bit string.
11578/// - `U` `[0..n-1]`: The output 16-bit string (can be T).
11579/// - `A` `[0..n]`: The temporary array (NOTE, temporary array must be n + 1 size).
11580/// - `n`: The length of the given 16-bit string.
11581/// - `freq` `[0..65535]`: The input 16-bit symbol frequency table (can be NULL).
11582/// - `r`: The sampling rate for auxiliary indexes (must be power of 2).
11583/// - `I` `[0..(n-1)/r]`: The input auxiliary indexes.
11584/// - `threads`: The number of OpenMP threads to use (can be 0 for OpenMP default).
11585///
11586/// # Returns
11587/// 0 if no error occurred, -1 or -2 otherwise.
11588pub fn libsais16x64_unbwt_aux_omp(
11589    t: &[u16],
11590    u: &mut [u16],
11591    a: &mut [SaSint],
11592    freq: Option<&[SaSint]>,
11593    r: SaSint,
11594    i: &[SaSint],
11595    threads: SaSint,
11596) -> SaSint {
11597    if threads < 0 {
11598        -1
11599    } else {
11600        let rc = validate_unbwt_aux(t, u, a, freq, r, i);
11601        if rc != 0 {
11602            return rc;
11603        }
11604        if t.len() <= 1 {
11605            if t.len() == 1 {
11606                u[0] = t[0];
11607            }
11608            return 0;
11609        }
11610        let n = t.len();
11611        let shift = unbwt_shift(n);
11612        let mut bucket2 = vec![0usize; ALPHABET_SIZE];
11613        let mut fastbits = vec![0u16; 1 + (n >> shift)];
11614        unbwt_core_with_buffers(
11615            t,
11616            u,
11617            a,
11618            freq,
11619            r,
11620            i,
11621            &mut bucket2,
11622            &mut fastbits,
11623            normalize_threads(threads),
11624        )
11625    }
11626}
11627
11628/// Constructs the permuted longest common prefix array (PLCP) of a given 16-bit string and a suffix array.
11629///
11630/// # Arguments
11631/// - `T` `[0..n-1]`: The input 16-bit string.
11632/// - `SA` `[0..n-1]`: The input suffix array.
11633/// - `PLCP` `[0..n-1]`: The output permuted longest common prefix array.
11634/// - `n`: The length of the 16-bit string and the suffix array.
11635///
11636/// # Returns
11637/// 0 if no error occurred, -1 otherwise.
11638pub fn libsais16x64_plcp(t: &[u16], sa: &[SaSint], plcp: &mut [SaSint]) -> SaSint {
11639    compute_plcp(t, sa, plcp, false)
11640}
11641
11642/// Constructs the permuted longest common prefix array (PLCP) of a given 16-bit string set and a generalized suffix array (GSA).
11643///
11644/// # Arguments
11645/// - `T` `[0..n-1]`: The input 16-bit string set using 0 as separators (T[n-1] must be 0).
11646/// - `SA` `[0..n-1]`: The input generalized suffix array.
11647/// - `PLCP` `[0..n-1]`: The output permuted longest common prefix array.
11648/// - `n`: The length of the string set and the generalized suffix array.
11649///
11650/// # Returns
11651/// 0 if no error occurred, -1 otherwise.
11652pub fn libsais16x64_plcp_gsa(t: &[u16], sa: &[SaSint], plcp: &mut [SaSint]) -> SaSint {
11653    if t.last().copied().unwrap_or(0) != 0 {
11654        -1
11655    } else {
11656        compute_plcp(t, sa, plcp, true)
11657    }
11658}
11659
11660fn compute_plcp(t: &[u16], sa: &[SaSint], plcp: &mut [SaSint], gsa: bool) -> SaSint {
11661    if sa.len() != t.len() || plcp.len() != t.len() {
11662        return -1;
11663    }
11664    if t.len() <= 1 {
11665        if t.len() == 1 {
11666            plcp[0] = 0;
11667        }
11668        return 0;
11669    }
11670
11671    if compute_phi(sa, plcp) != 0 {
11672        return -1;
11673    }
11674
11675    compute_plcp_from_phi(t, plcp, gsa)
11676}
11677
11678fn compute_phi(sa: &[SaSint], plcp: &mut [SaSint]) -> SaSint {
11679    let n = sa.len();
11680    let mut previous = n as SaSint;
11681    for &suffix_value in sa {
11682        let Some(suffix) = suffix_index(suffix_value, n) else {
11683            return -1;
11684        };
11685        plcp[suffix] = previous;
11686        previous = suffix_value;
11687    }
11688    0
11689}
11690
11691fn compute_plcp_from_phi(t: &[u16], plcp: &mut [SaSint], gsa: bool) -> SaSint {
11692    let n = t.len();
11693    let mut l = 0usize;
11694    for i in 0..t.len() {
11695        let previous = plcp[i];
11696        if previous == n as SaSint {
11697            plcp[i] = 0;
11698            l = 0;
11699            continue;
11700        }
11701
11702        let Some(prev) = suffix_index(previous, n) else {
11703            return -1;
11704        };
11705
11706        while i + l < t.len()
11707            && prev + l < t.len()
11708            && t[i + l] == t[prev + l]
11709            && (!gsa || t[i + l] != 0)
11710        {
11711            l += 1;
11712        }
11713        plcp[i] = l as SaSint;
11714        l = l.saturating_sub(1);
11715    }
11716    0
11717}
11718
11719fn compute_phi_omp(sa: &[SaSint], plcp: &mut [SaSint], n: SaSint, threads: SaSint) -> SaSint {
11720    let n_usize = n as usize;
11721    if threads == 1 || n < 65_536 {
11722        return compute_phi(&sa[..n_usize], &mut plcp[..n_usize]);
11723    }
11724
11725    let block_stride = (n / threads) & !15;
11726    for thread in 0..threads {
11727        let block_start = thread * block_stride;
11728        let block_size = if thread < threads - 1 {
11729            block_stride
11730        } else {
11731            n - block_start
11732        };
11733        let start = block_start as usize;
11734        let end = (block_start + block_size) as usize;
11735        let mut previous = if start > 0 { sa[start - 1] } else { n };
11736        for &suffix_value in &sa[start..end] {
11737            let Some(suffix) = suffix_index(suffix_value, n_usize) else {
11738                return -1;
11739            };
11740            plcp[suffix] = previous;
11741            previous = suffix_value;
11742        }
11743    }
11744    0
11745}
11746
11747fn compute_plcp_omp(t: &[u16], plcp: &mut [SaSint], n: SaSint, threads: SaSint) -> SaSint {
11748    if threads == 1 || n < 65_536 {
11749        let n = n as usize;
11750        return compute_plcp_from_phi(&t[..n], &mut plcp[..n], false);
11751    }
11752
11753    let block_stride = (n / threads) & !15;
11754    for thread in 0..threads {
11755        let block_start = thread * block_stride;
11756        let block_size = if thread < threads - 1 {
11757            block_stride
11758        } else {
11759            n - block_start
11760        };
11761        let rc = compute_plcp_range(
11762            t,
11763            plcp,
11764            n as usize,
11765            block_start as isize,
11766            block_size as isize,
11767            false,
11768        );
11769        if rc != 0 {
11770            return rc;
11771        }
11772    }
11773    0
11774}
11775
11776fn compute_plcp_range(
11777    t: &[u16],
11778    plcp: &mut [SaSint],
11779    n: usize,
11780    omp_block_start: isize,
11781    omp_block_size: isize,
11782    gsa: bool,
11783) -> SaSint {
11784    let mut l = 0usize;
11785    let end = (omp_block_start + omp_block_size) as usize;
11786    for i in omp_block_start as usize..end {
11787        let previous = plcp[i];
11788        if previous == n as SaSint {
11789            plcp[i] = 0;
11790            l = 0;
11791            continue;
11792        }
11793
11794        let Some(prev) = suffix_index(previous, n) else {
11795            return -1;
11796        };
11797
11798        while i + l < t.len()
11799            && prev + l < t.len()
11800            && t[i + l] == t[prev + l]
11801            && (!gsa || t[i + l] != 0)
11802        {
11803            l += 1;
11804        }
11805        plcp[i] = l as SaSint;
11806        l = l.saturating_sub(1);
11807    }
11808    0
11809}
11810
11811fn compute_plcp_gsa(
11812    t: &[u16],
11813    plcp: &mut [SaSint],
11814    omp_block_start: isize,
11815    omp_block_size: isize,
11816) -> SaSint {
11817    let n = t.len();
11818    let mut l = 0usize;
11819    let end = (omp_block_start + omp_block_size) as usize;
11820    for i in omp_block_start as usize..end {
11821        let previous = plcp[i];
11822        if previous == n as SaSint {
11823            plcp[i] = 0;
11824            l = 0;
11825            continue;
11826        }
11827
11828        let Some(prev) = suffix_index(previous, n) else {
11829            return -1;
11830        };
11831
11832        while i + l < t.len() && prev + l < t.len() && t[i + l] == t[prev + l] && t[i + l] != 0 {
11833            l += 1;
11834        }
11835        plcp[i] = l as SaSint;
11836        l = l.saturating_sub(1);
11837    }
11838    0
11839}
11840
11841fn compute_plcp_gsa_omp(t: &[u16], plcp: &mut [SaSint], n: SaSint, threads: SaSint) -> SaSint {
11842    if threads == 1 || n < 65_536 {
11843        return compute_plcp_gsa(t, plcp, 0, n as isize);
11844    }
11845
11846    let block_stride = (n / threads) & !15;
11847    for thread in 0..threads {
11848        let block_start = thread * block_stride;
11849        let block_size = if thread < threads - 1 {
11850            block_stride
11851        } else {
11852            n - block_start
11853        };
11854        let rc = compute_plcp_gsa(t, plcp, block_start as isize, block_size as isize);
11855        if rc != 0 {
11856            return rc;
11857        }
11858    }
11859    0
11860}
11861
11862fn compute_lcp(
11863    plcp: &[SaSint],
11864    sa: &[SaSint],
11865    lcp: &mut [SaSint],
11866    omp_block_start: isize,
11867    omp_block_size: isize,
11868) -> SaSint {
11869    let end = (omp_block_start + omp_block_size) as usize;
11870    for row in omp_block_start as usize..end {
11871        let Some(suffix) = suffix_index(sa[row], plcp.len()) else {
11872            return -1;
11873        };
11874        lcp[row] = plcp[suffix];
11875    }
11876    0
11877}
11878
11879fn compute_lcp_omp(
11880    plcp: &[SaSint],
11881    sa: &[SaSint],
11882    lcp: &mut [SaSint],
11883    n: SaSint,
11884    threads: SaSint,
11885) -> SaSint {
11886    if threads == 1 || n < 65_536 {
11887        return compute_lcp(plcp, sa, lcp, 0, n as isize);
11888    }
11889
11890    let block_stride = (n / threads) & !15;
11891    for thread in 0..threads {
11892        let block_start = thread * block_stride;
11893        let block_size = if thread < threads - 1 {
11894            block_stride
11895        } else {
11896            n - block_start
11897        };
11898        let rc = compute_lcp(plcp, sa, lcp, block_start as isize, block_size as isize);
11899        if rc != 0 {
11900            return rc;
11901        }
11902    }
11903    0
11904}
11905
11906/// Constructs the longest common prefix array (LCP) of a given permuted longest common prefix array (PLCP) and a suffix array.
11907///
11908/// # Arguments
11909/// - `PLCP` `[0..n-1]`: The input permuted longest common prefix array.
11910/// - `SA` `[0..n-1]`: The input suffix array or generalized suffix array (GSA).
11911/// - `LCP` `[0..n-1]`: The output longest common prefix array (can be SA).
11912/// - `n`: The length of the permuted longest common prefix array and the suffix array.
11913///
11914/// # Returns
11915/// 0 if no error occurred, -1 otherwise.
11916pub fn libsais16x64_lcp(plcp: &[SaSint], sa: &[SaSint], lcp: &mut [SaSint]) -> SaSint {
11917    if plcp.len() != sa.len() || lcp.len() != sa.len() {
11918        return -1;
11919    }
11920    for (row, &suffix) in sa.iter().enumerate() {
11921        let Some(suffix) = suffix_index(suffix, plcp.len()) else {
11922            return -1;
11923        };
11924        lcp[row] = plcp[suffix];
11925    }
11926    0
11927}
11928
11929fn suffix_index(value: SaSint, len: usize) -> Option<usize> {
11930    usize::try_from(value).ok().filter(|&index| index < len)
11931}
11932
11933/// Constructs the permuted longest common prefix array (PLCP) of a given 16-bit string and a suffix array in parallel using OpenMP.
11934///
11935/// # Arguments
11936/// - `T` `[0..n-1]`: The input 16-bit string.
11937/// - `SA` `[0..n-1]`: The input suffix array.
11938/// - `PLCP` `[0..n-1]`: The output permuted longest common prefix array.
11939/// - `n`: The length of the 16-bit string and the suffix array.
11940/// - `threads`: The number of OpenMP threads to use (can be 0 for OpenMP default).
11941///
11942/// # Returns
11943/// 0 if no error occurred, -1 otherwise.
11944pub fn libsais16x64_plcp_omp(
11945    t: &[u16],
11946    sa: &[SaSint],
11947    plcp: &mut [SaSint],
11948    threads: SaSint,
11949) -> SaSint {
11950    if threads < 0 {
11951        return -1;
11952    }
11953    if sa.len() != t.len() || plcp.len() != t.len() {
11954        return -1;
11955    }
11956    if t.len() <= 1 {
11957        if t.len() == 1 {
11958            plcp[0] = 0;
11959        }
11960        return 0;
11961    }
11962
11963    let n = t.len() as SaSint;
11964    let threads = normalize_threads(threads);
11965    if compute_phi_omp(sa, plcp, n, threads) != 0 {
11966        return -1;
11967    }
11968    compute_plcp_omp(t, plcp, n, threads)
11969}
11970
11971/// Constructs the permuted longest common prefix array (PLCP) of a given 16-bit string set and a generalized suffix array (GSA) in parallel using OpenMP.
11972///
11973/// # Arguments
11974/// - `T` `[0..n-1]`: The input 16-bit string set using 0 as separators (T[n-1] must be 0).
11975/// - `SA` `[0..n-1]`: The input generalized suffix array.
11976/// - `PLCP` `[0..n-1]`: The output permuted longest common prefix array.
11977/// - `n`: The length of the string set and the generalized suffix array.
11978/// - `threads`: The number of OpenMP threads to use (can be 0 for OpenMP default).
11979///
11980/// # Returns
11981/// 0 if no error occurred, -1 otherwise.
11982pub fn libsais16x64_plcp_gsa_omp(
11983    t: &[u16],
11984    sa: &[SaSint],
11985    plcp: &mut [SaSint],
11986    threads: SaSint,
11987) -> SaSint {
11988    if threads < 0 {
11989        return -1;
11990    }
11991    if t.last().copied().unwrap_or(0) != 0 {
11992        return -1;
11993    }
11994    if sa.len() != t.len() || plcp.len() != t.len() {
11995        return -1;
11996    }
11997    if t.len() <= 1 {
11998        if t.len() == 1 {
11999            plcp[0] = 0;
12000        }
12001        return 0;
12002    }
12003
12004    let n = t.len() as SaSint;
12005    let threads = normalize_threads(threads);
12006    if compute_phi_omp(sa, plcp, n, threads) != 0 {
12007        return -1;
12008    }
12009    compute_plcp_gsa_omp(t, plcp, n, threads)
12010}
12011
12012/// Constructs the longest common prefix array (LCP) of a given permuted longest common prefix array (PLCP) and a suffix array in parallel using OpenMP.
12013///
12014/// # Arguments
12015/// - `PLCP` `[0..n-1]`: The input permuted longest common prefix array.
12016/// - `SA` `[0..n-1]`: The input suffix array or generalized suffix array (GSA).
12017/// - `LCP` `[0..n-1]`: The output longest common prefix array (can be SA).
12018/// - `n`: The length of the permuted longest common prefix array and the suffix array.
12019/// - `threads`: The number of OpenMP threads to use (can be 0 for OpenMP default).
12020///
12021/// # Returns
12022/// 0 if no error occurred, -1 otherwise.
12023pub fn libsais16x64_lcp_omp(
12024    plcp: &[SaSint],
12025    sa: &[SaSint],
12026    lcp: &mut [SaSint],
12027    threads: SaSint,
12028) -> SaSint {
12029    if threads < 0 {
12030        return -1;
12031    }
12032    if plcp.len() != sa.len() || lcp.len() != sa.len() {
12033        return -1;
12034    }
12035
12036    compute_lcp_omp(
12037        plcp,
12038        sa,
12039        lcp,
12040        sa.len() as SaSint,
12041        normalize_threads(threads),
12042    )
12043}
12044
12045#[cfg(all(test, feature = "upstream-c"))]
12046mod tests {
12047    use super::*;
12048
12049    unsafe extern "C" {
12050        fn probe_public_libsais16x64(
12051            t: *const u16,
12052            sa: *mut SaSint,
12053            n: SaSint,
12054            fs: SaSint,
12055        ) -> SaSint;
12056        fn probe_public_libsais16x64_freq(
12057            t: *const u16,
12058            sa: *mut SaSint,
12059            n: SaSint,
12060            fs: SaSint,
12061            freq: *mut SaSint,
12062        ) -> SaSint;
12063        fn probe_public_libsais16x64_gsa(
12064            t: *const u16,
12065            sa: *mut SaSint,
12066            n: SaSint,
12067            fs: SaSint,
12068        ) -> SaSint;
12069        fn probe_public_libsais16x64_gsa_freq(
12070            t: *const u16,
12071            sa: *mut SaSint,
12072            n: SaSint,
12073            fs: SaSint,
12074            freq: *mut SaSint,
12075        ) -> SaSint;
12076        fn probe_public_libsais16x64_long(
12077            t: *mut SaSint,
12078            sa: *mut SaSint,
12079            n: SaSint,
12080            k: SaSint,
12081            fs: SaSint,
12082        ) -> SaSint;
12083        fn probe_libsais16x64_main_32s_entry(
12084            t: *mut SaSint,
12085            sa: *mut SaSint,
12086            n: SaSint,
12087            k: SaSint,
12088            fs: SaSint,
12089            threads: SaSint,
12090        ) -> SaSint;
12091        fn probe_libsais16x64_final_sorting_scan_left_to_right_32s(
12092            t: *const SaSint,
12093            sa: *mut SaSint,
12094            induction_bucket: *mut SaSint,
12095            omp_block_start: SaSint,
12096            omp_block_size: SaSint,
12097        );
12098        fn probe_libsais16x64_final_sorting_scan_right_to_left_32s(
12099            t: *const SaSint,
12100            sa: *mut SaSint,
12101            induction_bucket: *mut SaSint,
12102            omp_block_start: SaSint,
12103            omp_block_size: SaSint,
12104        );
12105        fn probe_libsais16x64_clear_lms_suffixes_omp(
12106            sa: *mut SaSint,
12107            n: SaSint,
12108            k: SaSint,
12109            bucket_start: *mut SaSint,
12110            bucket_end: *mut SaSint,
12111            threads: SaSint,
12112        );
12113        fn probe_libsais16x64_flip_suffix_markers_omp(sa: *mut SaSint, l: SaSint, threads: SaSint);
12114        fn probe_libsais16x64_induce_final_order_32s_6k(
12115            t: *const SaSint,
12116            sa: *mut SaSint,
12117            n: SaSint,
12118            k: SaSint,
12119            buckets: *mut SaSint,
12120            threads: SaSint,
12121        );
12122        fn probe_libsais16x64_induce_final_order_32s_4k(
12123            t: *const SaSint,
12124            sa: *mut SaSint,
12125            n: SaSint,
12126            k: SaSint,
12127            buckets: *mut SaSint,
12128            threads: SaSint,
12129        );
12130        fn probe_libsais16x64_induce_final_order_32s_2k(
12131            t: *const SaSint,
12132            sa: *mut SaSint,
12133            n: SaSint,
12134            k: SaSint,
12135            buckets: *mut SaSint,
12136            threads: SaSint,
12137        );
12138        fn probe_libsais16x64_induce_final_order_32s_1k(
12139            t: *const SaSint,
12140            sa: *mut SaSint,
12141            n: SaSint,
12142            k: SaSint,
12143            buckets: *mut SaSint,
12144            threads: SaSint,
12145        );
12146        fn probe_libsais16x64_induce_partial_order_32s_6k_omp(
12147            t: *const SaSint,
12148            sa: *mut SaSint,
12149            n: SaSint,
12150            k: SaSint,
12151            buckets: *mut SaSint,
12152            first_lms_suffix: SaSint,
12153            left_suffixes_count: SaSint,
12154            threads: SaSint,
12155        );
12156        fn probe_libsais16x64_induce_partial_order_32s_4k_omp(
12157            t: *const SaSint,
12158            sa: *mut SaSint,
12159            n: SaSint,
12160            k: SaSint,
12161            buckets: *mut SaSint,
12162            threads: SaSint,
12163        );
12164        fn probe_libsais16x64_induce_partial_order_32s_2k_omp(
12165            t: *const SaSint,
12166            sa: *mut SaSint,
12167            n: SaSint,
12168            k: SaSint,
12169            buckets: *mut SaSint,
12170            threads: SaSint,
12171        );
12172        fn probe_libsais16x64_induce_partial_order_32s_1k_omp(
12173            t: *const SaSint,
12174            sa: *mut SaSint,
12175            n: SaSint,
12176            k: SaSint,
12177            buckets: *mut SaSint,
12178            threads: SaSint,
12179        );
12180        fn probe_libsais16x64_induce_partial_order_16u_omp(
12181            t: *const u16,
12182            sa: *mut SaSint,
12183            n: SaSint,
12184            k: SaSint,
12185            flags: SaSint,
12186            buckets: *mut SaSint,
12187            first_lms_suffix: SaSint,
12188            left_suffixes_count: SaSint,
12189            threads: SaSint,
12190        );
12191        fn probe_libsais16x64_induce_final_order_16u_omp(
12192            t: *const u16,
12193            sa: *mut SaSint,
12194            n: SaSint,
12195            k: SaSint,
12196            flags: SaSint,
12197            r: SaSint,
12198            i: *mut SaSint,
12199            buckets: *mut SaSint,
12200            threads: SaSint,
12201        ) -> SaSint;
12202        fn probe_public_libsais16x64_bwt(
12203            t: *const u16,
12204            u: *mut u16,
12205            a: *mut SaSint,
12206            n: SaSint,
12207            fs: SaSint,
12208        ) -> SaSint;
12209        fn probe_public_libsais16x64_bwt_freq(
12210            t: *const u16,
12211            u: *mut u16,
12212            a: *mut SaSint,
12213            n: SaSint,
12214            fs: SaSint,
12215            freq: *mut SaSint,
12216        ) -> SaSint;
12217        fn probe_public_libsais16x64_bwt_aux(
12218            t: *const u16,
12219            u: *mut u16,
12220            a: *mut SaSint,
12221            n: SaSint,
12222            fs: SaSint,
12223            r: SaSint,
12224            i: *mut SaSint,
12225        ) -> SaSint;
12226        fn probe_public_libsais16x64_bwt_aux_freq(
12227            t: *const u16,
12228            u: *mut u16,
12229            a: *mut SaSint,
12230            n: SaSint,
12231            fs: SaSint,
12232            freq: *mut SaSint,
12233            r: SaSint,
12234            i: *mut SaSint,
12235        ) -> SaSint;
12236        fn probe_public_libsais16x64_unbwt(
12237            t: *const u16,
12238            u: *mut u16,
12239            a: *mut SaSint,
12240            n: SaSint,
12241            i: SaSint,
12242        ) -> SaSint;
12243        fn probe_public_libsais16x64_unbwt_freq(
12244            t: *const u16,
12245            u: *mut u16,
12246            a: *mut SaSint,
12247            n: SaSint,
12248            freq: *const SaSint,
12249            i: SaSint,
12250        ) -> SaSint;
12251        fn probe_public_libsais16x64_unbwt_aux(
12252            t: *const u16,
12253            u: *mut u16,
12254            a: *mut SaSint,
12255            n: SaSint,
12256            r: SaSint,
12257            i: *const SaSint,
12258        ) -> SaSint;
12259        fn probe_public_libsais16x64_unbwt_aux_freq(
12260            t: *const u16,
12261            u: *mut u16,
12262            a: *mut SaSint,
12263            n: SaSint,
12264            freq: *const SaSint,
12265            r: SaSint,
12266            i: *const SaSint,
12267        ) -> SaSint;
12268        fn probe_public_libsais16x64_plcp(
12269            t: *const u16,
12270            sa: *const SaSint,
12271            plcp: *mut SaSint,
12272            n: SaSint,
12273        ) -> SaSint;
12274        fn probe_public_libsais16x64_plcp_gsa(
12275            t: *const u16,
12276            sa: *const SaSint,
12277            plcp: *mut SaSint,
12278            n: SaSint,
12279        ) -> SaSint;
12280        fn probe_public_libsais16x64_lcp(
12281            plcp: *const SaSint,
12282            sa: *const SaSint,
12283            lcp: *mut SaSint,
12284            n: SaSint,
12285        ) -> SaSint;
12286        fn probe_libsais16x64_gather_lms_suffixes_16u(
12287            t: *const u16,
12288            sa: *mut SaSint,
12289            n: SaSint,
12290            m: SaSint,
12291            omp_block_start: SaSint,
12292            omp_block_size: SaSint,
12293        );
12294        fn probe_libsais16x64_count_and_gather_lms_suffixes_16u(
12295            t: *const u16,
12296            sa: *mut SaSint,
12297            n: SaSint,
12298            buckets: *mut SaSint,
12299            omp_block_start: SaSint,
12300            omp_block_size: SaSint,
12301        ) -> SaSint;
12302        fn probe_libsais16x64_initialize_buckets_start_and_end_16u(
12303            buckets: *mut SaSint,
12304            freq: *mut SaSint,
12305        ) -> SaSint;
12306        fn probe_libsais16x64_initialize_buckets_for_lms_suffixes_radix_sort_16u(
12307            t: *const u16,
12308            buckets: *mut SaSint,
12309            first_lms_suffix: SaSint,
12310        ) -> SaSint;
12311        fn probe_libsais16x64_radix_sort_lms_suffixes_16u(
12312            t: *const u16,
12313            sa: *mut SaSint,
12314            induction_bucket: *mut SaSint,
12315            omp_block_start: SaSint,
12316            omp_block_size: SaSint,
12317        );
12318        fn probe_libsais16x64_initialize_buckets_for_partial_sorting_16u(
12319            t: *const u16,
12320            buckets: *mut SaSint,
12321            first_lms_suffix: SaSint,
12322            left_suffixes_count: SaSint,
12323        );
12324        fn probe_libsais16x64_partial_sorting_scan_left_to_right_16u(
12325            t: *const u16,
12326            sa: *mut SaSint,
12327            buckets: *mut SaSint,
12328            d: SaSint,
12329            omp_block_start: SaSint,
12330            omp_block_size: SaSint,
12331        ) -> SaSint;
12332        fn probe_libsais16x64_partial_sorting_scan_right_to_left_16u(
12333            t: *const u16,
12334            sa: *mut SaSint,
12335            buckets: *mut SaSint,
12336            d: SaSint,
12337            omp_block_start: SaSint,
12338            omp_block_size: SaSint,
12339        ) -> SaSint;
12340        fn probe_libsais16x64_partial_gsa_scan_right_to_left_16u(
12341            t: *const u16,
12342            sa: *mut SaSint,
12343            buckets: *mut SaSint,
12344            d: SaSint,
12345            omp_block_start: SaSint,
12346            omp_block_size: SaSint,
12347        ) -> SaSint;
12348        fn probe_libsais16x64_partial_sorting_shift_markers_16u_omp(
12349            sa: *mut SaSint,
12350            n: SaSint,
12351            buckets: *const SaSint,
12352            threads: SaSint,
12353        );
12354        fn probe_libsais16x64_final_sorting_scan_left_to_right_16u(
12355            t: *const u16,
12356            sa: *mut SaSint,
12357            induction_bucket: *mut SaSint,
12358            omp_block_start: SaSint,
12359            omp_block_size: SaSint,
12360        );
12361        fn probe_libsais16x64_final_sorting_scan_right_to_left_16u(
12362            t: *const u16,
12363            sa: *mut SaSint,
12364            induction_bucket: *mut SaSint,
12365            omp_block_start: SaSint,
12366            omp_block_size: SaSint,
12367        );
12368        fn probe_libsais16x64_final_gsa_scan_right_to_left_16u(
12369            t: *const u16,
12370            sa: *mut SaSint,
12371            induction_bucket: *mut SaSint,
12372            omp_block_start: SaSint,
12373            omp_block_size: SaSint,
12374        );
12375        fn probe_libsais16x64_final_bwt_scan_left_to_right_16u(
12376            t: *const u16,
12377            sa: *mut SaSint,
12378            induction_bucket: *mut SaSint,
12379            omp_block_start: SaSint,
12380            omp_block_size: SaSint,
12381        );
12382        fn probe_libsais16x64_final_bwt_scan_right_to_left_16u(
12383            t: *const u16,
12384            sa: *mut SaSint,
12385            induction_bucket: *mut SaSint,
12386            omp_block_start: SaSint,
12387            omp_block_size: SaSint,
12388        ) -> SaSint;
12389        fn probe_libsais16x64_final_bwt_aux_scan_left_to_right_16u(
12390            t: *const u16,
12391            sa: *mut SaSint,
12392            rm: SaSint,
12393            i_sample: *mut SaSint,
12394            induction_bucket: *mut SaSint,
12395            omp_block_start: SaSint,
12396            omp_block_size: SaSint,
12397        );
12398        fn probe_libsais16x64_final_bwt_aux_scan_right_to_left_16u(
12399            t: *const u16,
12400            sa: *mut SaSint,
12401            rm: SaSint,
12402            i_sample: *mut SaSint,
12403            induction_bucket: *mut SaSint,
12404            omp_block_start: SaSint,
12405            omp_block_size: SaSint,
12406        );
12407        fn probe_libsais16x64_renumber_lms_suffixes_16u(
12408            sa: *mut SaSint,
12409            m: SaSint,
12410            name: SaSint,
12411            omp_block_start: SaSint,
12412            omp_block_size: SaSint,
12413        ) -> SaSint;
12414        fn probe_libsais16x64_place_lms_suffixes_interval_16u(
12415            sa: *mut SaSint,
12416            n: SaSint,
12417            m: SaSint,
12418            flags: SaSint,
12419            buckets: *mut SaSint,
12420        );
12421        fn probe_libsais16x64_bwt_copy_16u(u: *mut u16, a: *mut SaSint, n: SaSint);
12422        fn probe_libsais16x64_gather_lms_suffixes_16u_omp(
12423            t: *const u16,
12424            sa: *mut SaSint,
12425            n: SaSint,
12426            threads: SaSint,
12427        );
12428        fn probe_libsais16x64_count_and_gather_lms_suffixes_16u_omp(
12429            t: *const u16,
12430            sa: *mut SaSint,
12431            n: SaSint,
12432            buckets: *mut SaSint,
12433            threads: SaSint,
12434        ) -> SaSint;
12435        fn probe_libsais16x64_radix_sort_lms_suffixes_16u_omp(
12436            t: *const u16,
12437            sa: *mut SaSint,
12438            n: SaSint,
12439            m: SaSint,
12440            flags: SaSint,
12441            buckets: *mut SaSint,
12442            threads: SaSint,
12443        );
12444        fn probe_libsais16x64_partial_sorting_scan_left_to_right_16u_omp(
12445            t: *const u16,
12446            sa: *mut SaSint,
12447            n: SaSint,
12448            k: SaSint,
12449            buckets: *mut SaSint,
12450            left_suffixes_count: SaSint,
12451            d: SaSint,
12452            threads: SaSint,
12453        ) -> SaSint;
12454        fn probe_libsais16x64_partial_sorting_scan_right_to_left_16u_omp(
12455            t: *const u16,
12456            sa: *mut SaSint,
12457            n: SaSint,
12458            k: SaSint,
12459            buckets: *mut SaSint,
12460            first_lms_suffix: SaSint,
12461            left_suffixes_count: SaSint,
12462            d: SaSint,
12463            threads: SaSint,
12464        );
12465        fn probe_libsais16x64_partial_gsa_scan_right_to_left_16u_omp(
12466            t: *const u16,
12467            sa: *mut SaSint,
12468            n: SaSint,
12469            k: SaSint,
12470            buckets: *mut SaSint,
12471            first_lms_suffix: SaSint,
12472            left_suffixes_count: SaSint,
12473            d: SaSint,
12474            threads: SaSint,
12475        );
12476        fn probe_libsais16x64_renumber_lms_suffixes_16u_omp(
12477            sa: *mut SaSint,
12478            m: SaSint,
12479            threads: SaSint,
12480        ) -> SaSint;
12481        fn probe_libsais16x64_final_bwt_scan_left_to_right_16u_omp(
12482            t: *const u16,
12483            sa: *mut SaSint,
12484            n: SaSint,
12485            k: SaSint,
12486            induction_bucket: *mut SaSint,
12487            threads: SaSint,
12488        );
12489        fn probe_libsais16x64_final_bwt_aux_scan_left_to_right_16u_omp(
12490            t: *const u16,
12491            sa: *mut SaSint,
12492            n: SaSint,
12493            k: SaSint,
12494            rm: SaSint,
12495            i_sample: *mut SaSint,
12496            induction_bucket: *mut SaSint,
12497            threads: SaSint,
12498        );
12499        fn probe_libsais16x64_final_sorting_scan_left_to_right_16u_omp(
12500            t: *const u16,
12501            sa: *mut SaSint,
12502            n: SaSint,
12503            k: SaSint,
12504            induction_bucket: *mut SaSint,
12505            threads: SaSint,
12506        );
12507        fn probe_libsais16x64_final_bwt_scan_right_to_left_16u_omp(
12508            t: *const u16,
12509            sa: *mut SaSint,
12510            n: SaSint,
12511            k: SaSint,
12512            induction_bucket: *mut SaSint,
12513            threads: SaSint,
12514        ) -> SaSint;
12515        fn probe_libsais16x64_final_bwt_aux_scan_right_to_left_16u_omp(
12516            t: *const u16,
12517            sa: *mut SaSint,
12518            n: SaSint,
12519            k: SaSint,
12520            rm: SaSint,
12521            i_sample: *mut SaSint,
12522            induction_bucket: *mut SaSint,
12523            threads: SaSint,
12524        );
12525        fn probe_libsais16x64_final_sorting_scan_right_to_left_16u_omp(
12526            t: *const u16,
12527            sa: *mut SaSint,
12528            omp_block_start: SaSint,
12529            omp_block_size: SaSint,
12530            k: SaSint,
12531            induction_bucket: *mut SaSint,
12532            threads: SaSint,
12533        );
12534        fn probe_libsais16x64_final_gsa_scan_right_to_left_16u_omp(
12535            t: *const u16,
12536            sa: *mut SaSint,
12537            omp_block_start: SaSint,
12538            omp_block_size: SaSint,
12539            k: SaSint,
12540            induction_bucket: *mut SaSint,
12541            threads: SaSint,
12542        );
12543        fn probe_libsais16x64_bwt_copy_16u_omp(
12544            u: *mut u16,
12545            a: *mut SaSint,
12546            n: SaSint,
12547            threads: SaSint,
12548        );
12549        fn probe_libsais16x64_gather_marked_lms_suffixes(
12550            sa: *mut SaSint,
12551            m: SaSint,
12552            l: SaSint,
12553            omp_block_start: SaSint,
12554            omp_block_size: SaSint,
12555        ) -> SaSint;
12556        fn probe_libsais16x64_gather_marked_lms_suffixes_omp(
12557            sa: *mut SaSint,
12558            n: SaSint,
12559            m: SaSint,
12560            fs: SaSint,
12561            threads: SaSint,
12562        );
12563        fn probe_libsais16x64_renumber_and_gather_lms_suffixes_omp(
12564            sa: *mut SaSint,
12565            n: SaSint,
12566            m: SaSint,
12567            fs: SaSint,
12568            threads: SaSint,
12569        ) -> SaSint;
12570        fn probe_libsais16x64_reconstruct_lms_suffixes(
12571            sa: *mut SaSint,
12572            n: SaSint,
12573            m: SaSint,
12574            omp_block_start: SaSint,
12575            omp_block_size: SaSint,
12576        );
12577        fn probe_libsais16x64_reconstruct_lms_suffixes_omp(
12578            sa: *mut SaSint,
12579            n: SaSint,
12580            m: SaSint,
12581            threads: SaSint,
12582        );
12583        fn probe_libsais16x64_renumber_distinct_lms_suffixes_32s_4k(
12584            sa: *mut SaSint,
12585            m: SaSint,
12586            name: SaSint,
12587            omp_block_start: SaSint,
12588            omp_block_size: SaSint,
12589        ) -> SaSint;
12590        fn probe_libsais16x64_mark_distinct_lms_suffixes_32s(
12591            sa: *mut SaSint,
12592            m: SaSint,
12593            omp_block_start: SaSint,
12594            omp_block_size: SaSint,
12595        );
12596        fn probe_libsais16x64_clamp_lms_suffixes_length_32s(
12597            sa: *mut SaSint,
12598            m: SaSint,
12599            omp_block_start: SaSint,
12600            omp_block_size: SaSint,
12601        );
12602        fn probe_libsais16x64_renumber_distinct_lms_suffixes_32s_4k_omp(
12603            sa: *mut SaSint,
12604            m: SaSint,
12605            threads: SaSint,
12606        ) -> SaSint;
12607        fn probe_libsais16x64_mark_distinct_lms_suffixes_32s_omp(
12608            sa: *mut SaSint,
12609            n: SaSint,
12610            m: SaSint,
12611            threads: SaSint,
12612        );
12613        fn probe_libsais16x64_clamp_lms_suffixes_length_32s_omp(
12614            sa: *mut SaSint,
12615            n: SaSint,
12616            m: SaSint,
12617            threads: SaSint,
12618        );
12619        fn probe_libsais16x64_renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
12620            sa: *mut SaSint,
12621            n: SaSint,
12622            m: SaSint,
12623            threads: SaSint,
12624        ) -> SaSint;
12625        fn probe_libsais16x64_renumber_unique_and_nonunique_lms_suffixes_32s(
12626            t: *mut SaSint,
12627            sa: *mut SaSint,
12628            m: SaSint,
12629            f: SaSint,
12630            omp_block_start: SaSint,
12631            omp_block_size: SaSint,
12632        ) -> SaSint;
12633        fn probe_libsais16x64_compact_unique_and_nonunique_lms_suffixes_32s(
12634            sa: *mut SaSint,
12635            m: SaSint,
12636            pl: *mut SaSint,
12637            pr: *mut SaSint,
12638            omp_block_start: SaSint,
12639            omp_block_size: SaSint,
12640        );
12641        fn probe_libsais16x64_renumber_unique_and_nonunique_lms_suffixes_32s_omp(
12642            t: *mut SaSint,
12643            sa: *mut SaSint,
12644            m: SaSint,
12645            threads: SaSint,
12646        ) -> SaSint;
12647        fn probe_libsais16x64_compact_unique_and_nonunique_lms_suffixes_32s_omp(
12648            sa: *mut SaSint,
12649            n: SaSint,
12650            m: SaSint,
12651            fs: SaSint,
12652            f: SaSint,
12653            threads: SaSint,
12654        );
12655        fn probe_libsais16x64_compact_lms_suffixes_32s_omp(
12656            t: *mut SaSint,
12657            sa: *mut SaSint,
12658            n: SaSint,
12659            m: SaSint,
12660            fs: SaSint,
12661            threads: SaSint,
12662        ) -> SaSint;
12663        fn probe_libsais16x64_merge_unique_lms_suffixes_32s(
12664            t: *mut SaSint,
12665            sa: *mut SaSint,
12666            n: SaSint,
12667            m: SaSint,
12668            l: SaSint,
12669            omp_block_start: SaSint,
12670            omp_block_size: SaSint,
12671        );
12672        fn probe_libsais16x64_merge_nonunique_lms_suffixes_32s(
12673            sa: *mut SaSint,
12674            n: SaSint,
12675            m: SaSint,
12676            l: SaSint,
12677            omp_block_start: SaSint,
12678            omp_block_size: SaSint,
12679        );
12680        fn probe_libsais16x64_merge_unique_lms_suffixes_32s_omp(
12681            t: *mut SaSint,
12682            sa: *mut SaSint,
12683            n: SaSint,
12684            m: SaSint,
12685            threads: SaSint,
12686        );
12687        fn probe_libsais16x64_merge_nonunique_lms_suffixes_32s_omp(
12688            sa: *mut SaSint,
12689            n: SaSint,
12690            m: SaSint,
12691            f: SaSint,
12692            threads: SaSint,
12693        );
12694        fn probe_libsais16x64_merge_compacted_lms_suffixes_32s_omp(
12695            t: *mut SaSint,
12696            sa: *mut SaSint,
12697            n: SaSint,
12698            m: SaSint,
12699            f: SaSint,
12700            threads: SaSint,
12701        );
12702        fn probe_libsais16x64_radix_sort_lms_suffixes_32s_6k(
12703            t: *const SaSint,
12704            sa: *mut SaSint,
12705            induction_bucket: *mut SaSint,
12706            omp_block_start: SaSint,
12707            omp_block_size: SaSint,
12708        );
12709        fn probe_libsais16x64_radix_sort_lms_suffixes_32s_2k(
12710            t: *const SaSint,
12711            sa: *mut SaSint,
12712            induction_bucket: *mut SaSint,
12713            omp_block_start: SaSint,
12714            omp_block_size: SaSint,
12715        );
12716        fn probe_libsais16x64_radix_sort_lms_suffixes_32s_6k_omp(
12717            t: *const SaSint,
12718            sa: *mut SaSint,
12719            n: SaSint,
12720            m: SaSint,
12721            induction_bucket: *mut SaSint,
12722            threads: SaSint,
12723        );
12724        fn probe_libsais16x64_radix_sort_lms_suffixes_32s_2k_omp(
12725            t: *const SaSint,
12726            sa: *mut SaSint,
12727            n: SaSint,
12728            m: SaSint,
12729            induction_bucket: *mut SaSint,
12730            threads: SaSint,
12731        );
12732        fn probe_libsais16x64_radix_sort_lms_suffixes_32s_1k(
12733            t: *const SaSint,
12734            sa: *mut SaSint,
12735            n: SaSint,
12736            buckets: *mut SaSint,
12737        ) -> SaSint;
12738        fn probe_libsais16x64_radix_sort_set_markers_32s_6k(
12739            sa: *mut SaSint,
12740            induction_bucket: *mut SaSint,
12741            omp_block_start: SaSint,
12742            omp_block_size: SaSint,
12743        );
12744        fn probe_libsais16x64_radix_sort_set_markers_32s_4k(
12745            sa: *mut SaSint,
12746            induction_bucket: *mut SaSint,
12747            omp_block_start: SaSint,
12748            omp_block_size: SaSint,
12749        );
12750        fn probe_libsais16x64_radix_sort_set_markers_32s_6k_omp(
12751            sa: *mut SaSint,
12752            k: SaSint,
12753            induction_bucket: *mut SaSint,
12754            threads: SaSint,
12755        );
12756        fn probe_libsais16x64_radix_sort_set_markers_32s_4k_omp(
12757            sa: *mut SaSint,
12758            k: SaSint,
12759            induction_bucket: *mut SaSint,
12760            threads: SaSint,
12761        );
12762        fn probe_libsais16x64_place_lms_suffixes_histogram_32s_6k(
12763            sa: *mut SaSint,
12764            n: SaSint,
12765            k: SaSint,
12766            m: SaSint,
12767            buckets: *const SaSint,
12768        );
12769        fn probe_libsais16x64_place_lms_suffixes_histogram_32s_4k(
12770            sa: *mut SaSint,
12771            n: SaSint,
12772            k: SaSint,
12773            m: SaSint,
12774            buckets: *const SaSint,
12775        );
12776        fn probe_libsais16x64_place_lms_suffixes_histogram_32s_2k(
12777            sa: *mut SaSint,
12778            n: SaSint,
12779            k: SaSint,
12780            m: SaSint,
12781            buckets: *const SaSint,
12782        );
12783        fn probe_libsais16x64_gather_lms_suffixes_32s(
12784            t: *const SaSint,
12785            sa: *mut SaSint,
12786            n: SaSint,
12787        ) -> SaSint;
12788        fn probe_libsais16x64_gather_compacted_lms_suffixes_32s(
12789            t: *const SaSint,
12790            sa: *mut SaSint,
12791            n: SaSint,
12792        ) -> SaSint;
12793        fn probe_libsais16x64_count_lms_suffixes_32s_2k(
12794            t: *const SaSint,
12795            n: SaSint,
12796            k: SaSint,
12797            buckets: *mut SaSint,
12798        );
12799        fn probe_libsais16x64_count_and_gather_lms_suffixes_32s_4k(
12800            t: *const SaSint,
12801            sa: *mut SaSint,
12802            n: SaSint,
12803            k: SaSint,
12804            buckets: *mut SaSint,
12805            omp_block_start: SaSint,
12806            omp_block_size: SaSint,
12807        ) -> SaSint;
12808        fn probe_libsais16x64_count_and_gather_lms_suffixes_32s_4k_omp(
12809            t: *const SaSint,
12810            sa: *mut SaSint,
12811            n: SaSint,
12812            k: SaSint,
12813            buckets: *mut SaSint,
12814            local_buckets: SaSint,
12815            threads: SaSint,
12816        ) -> SaSint;
12817        fn probe_libsais16x64_count_suffixes_32s(
12818            t: *const SaSint,
12819            n: SaSint,
12820            k: SaSint,
12821            buckets: *mut SaSint,
12822        );
12823        fn probe_libsais16x64_initialize_buckets_start_and_end_32s_6k(
12824            k: SaSint,
12825            buckets: *mut SaSint,
12826        );
12827        fn probe_libsais16x64_initialize_buckets_start_and_end_32s_4k(
12828            k: SaSint,
12829            buckets: *mut SaSint,
12830        );
12831        fn probe_libsais16x64_initialize_buckets_end_32s_2k(k: SaSint, buckets: *mut SaSint);
12832        fn probe_libsais16x64_initialize_buckets_start_and_end_32s_2k(
12833            k: SaSint,
12834            buckets: *mut SaSint,
12835        );
12836        fn probe_libsais16x64_initialize_buckets_start_32s_1k(k: SaSint, buckets: *mut SaSint);
12837        fn probe_libsais16x64_initialize_buckets_end_32s_1k(k: SaSint, buckets: *mut SaSint);
12838        fn probe_libsais16x64_initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
12839            t: *const SaSint,
12840            k: SaSint,
12841            buckets: *mut SaSint,
12842            first_lms_suffix: SaSint,
12843        );
12844        fn probe_libsais16x64_initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
12845            t: *const SaSint,
12846            k: SaSint,
12847            buckets: *mut SaSint,
12848            first_lms_suffix: SaSint,
12849        ) -> SaSint;
12850        fn probe_libsais16x64_initialize_buckets_for_radix_and_partial_sorting_32s_4k(
12851            t: *const SaSint,
12852            k: SaSint,
12853            buckets: *mut SaSint,
12854            first_lms_suffix: SaSint,
12855        );
12856        fn probe_libsais16x64_place_lms_suffixes_interval_32s_4k(
12857            sa: *mut SaSint,
12858            n: SaSint,
12859            k: SaSint,
12860            m: SaSint,
12861            buckets: *const SaSint,
12862        );
12863        fn probe_libsais16x64_place_lms_suffixes_interval_32s_2k(
12864            sa: *mut SaSint,
12865            n: SaSint,
12866            k: SaSint,
12867            m: SaSint,
12868            buckets: *const SaSint,
12869        );
12870        fn probe_libsais16x64_place_lms_suffixes_interval_32s_1k(
12871            t: *const SaSint,
12872            sa: *mut SaSint,
12873            k: SaSint,
12874            m: SaSint,
12875            buckets: *mut SaSint,
12876        );
12877        fn probe_libsais16x64_renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(
12878            t: *mut SaSint,
12879            sa: *mut SaSint,
12880            n: SaSint,
12881            m: SaSint,
12882            threads: SaSint,
12883        ) -> SaSint;
12884        fn probe_libsais16x64_partial_sorting_shift_markers_32s_6k_omp(
12885            sa: *mut SaSint,
12886            k: SaSint,
12887            buckets: *const SaSint,
12888            threads: SaSint,
12889        );
12890        fn probe_libsais16x64_partial_sorting_shift_markers_32s_4k(sa: *mut SaSint, n: SaSint);
12891        fn probe_libsais16x64_partial_sorting_shift_buckets_32s_6k(k: SaSint, buckets: *mut SaSint);
12892        fn probe_libsais16x64_partial_sorting_scan_left_to_right_32s_6k(
12893            t: *const SaSint,
12894            sa: *mut SaSint,
12895            buckets: *mut SaSint,
12896            d: SaSint,
12897            omp_block_start: SaSint,
12898            omp_block_size: SaSint,
12899        ) -> SaSint;
12900        fn probe_libsais16x64_partial_sorting_scan_left_to_right_32s_4k(
12901            t: *const SaSint,
12902            sa: *mut SaSint,
12903            k: SaSint,
12904            buckets: *mut SaSint,
12905            d: SaSint,
12906            omp_block_start: SaSint,
12907            omp_block_size: SaSint,
12908        ) -> SaSint;
12909        fn probe_libsais16x64_partial_sorting_scan_left_to_right_32s_1k(
12910            t: *const SaSint,
12911            sa: *mut SaSint,
12912            buckets: *mut SaSint,
12913            omp_block_start: SaSint,
12914            omp_block_size: SaSint,
12915        );
12916        fn probe_libsais16x64_partial_sorting_scan_left_to_right_32s_6k_omp(
12917            t: *const SaSint,
12918            sa: *mut SaSint,
12919            n: SaSint,
12920            buckets: *mut SaSint,
12921            left_suffixes_count: SaSint,
12922            d: SaSint,
12923            threads: SaSint,
12924        ) -> SaSint;
12925        fn probe_libsais16x64_partial_sorting_scan_left_to_right_32s_4k_omp(
12926            t: *const SaSint,
12927            sa: *mut SaSint,
12928            n: SaSint,
12929            k: SaSint,
12930            buckets: *mut SaSint,
12931            d: SaSint,
12932            threads: SaSint,
12933        ) -> SaSint;
12934        fn probe_libsais16x64_partial_sorting_scan_left_to_right_32s_1k_omp(
12935            t: *const SaSint,
12936            sa: *mut SaSint,
12937            n: SaSint,
12938            buckets: *mut SaSint,
12939            threads: SaSint,
12940        );
12941        fn probe_libsais16x64_partial_sorting_scan_right_to_left_32s_6k(
12942            t: *const SaSint,
12943            sa: *mut SaSint,
12944            buckets: *mut SaSint,
12945            d: SaSint,
12946            omp_block_start: SaSint,
12947            omp_block_size: SaSint,
12948        ) -> SaSint;
12949        fn probe_libsais16x64_partial_sorting_scan_right_to_left_32s_4k(
12950            t: *const SaSint,
12951            sa: *mut SaSint,
12952            k: SaSint,
12953            buckets: *mut SaSint,
12954            d: SaSint,
12955            omp_block_start: SaSint,
12956            omp_block_size: SaSint,
12957        ) -> SaSint;
12958        fn probe_libsais16x64_partial_sorting_scan_right_to_left_32s_1k(
12959            t: *const SaSint,
12960            sa: *mut SaSint,
12961            buckets: *mut SaSint,
12962            omp_block_start: SaSint,
12963            omp_block_size: SaSint,
12964        );
12965        fn probe_libsais16x64_partial_sorting_scan_right_to_left_32s_6k_omp(
12966            t: *const SaSint,
12967            sa: *mut SaSint,
12968            n: SaSint,
12969            buckets: *mut SaSint,
12970            first_lms_suffix: SaSint,
12971            left_suffixes_count: SaSint,
12972            d: SaSint,
12973            threads: SaSint,
12974        ) -> SaSint;
12975        fn probe_libsais16x64_partial_sorting_scan_right_to_left_32s_4k_omp(
12976            t: *const SaSint,
12977            sa: *mut SaSint,
12978            n: SaSint,
12979            k: SaSint,
12980            buckets: *mut SaSint,
12981            d: SaSint,
12982            threads: SaSint,
12983        ) -> SaSint;
12984        fn probe_libsais16x64_partial_sorting_scan_right_to_left_32s_1k_omp(
12985            t: *const SaSint,
12986            sa: *mut SaSint,
12987            n: SaSint,
12988            buckets: *mut SaSint,
12989            threads: SaSint,
12990        );
12991        fn probe_libsais16x64_partial_sorting_gather_lms_suffixes_32s_4k(
12992            sa: *mut SaSint,
12993            omp_block_start: SaSint,
12994            omp_block_size: SaSint,
12995        ) -> SaSint;
12996        fn probe_libsais16x64_partial_sorting_gather_lms_suffixes_32s_1k(
12997            sa: *mut SaSint,
12998            omp_block_start: SaSint,
12999            omp_block_size: SaSint,
13000        ) -> SaSint;
13001        fn probe_libsais16x64_partial_sorting_gather_lms_suffixes_32s_4k_omp(
13002            sa: *mut SaSint,
13003            n: SaSint,
13004            threads: SaSint,
13005        );
13006        fn probe_libsais16x64_partial_sorting_gather_lms_suffixes_32s_1k_omp(
13007            sa: *mut SaSint,
13008            n: SaSint,
13009            threads: SaSint,
13010        );
13011        fn probe_libsais16x64_count_and_gather_lms_suffixes_32s_2k(
13012            t: *const SaSint,
13013            sa: *mut SaSint,
13014            n: SaSint,
13015            k: SaSint,
13016            buckets: *mut SaSint,
13017            omp_block_start: SaSint,
13018            omp_block_size: SaSint,
13019        ) -> SaSint;
13020        fn probe_libsais16x64_count_and_gather_compacted_lms_suffixes_32s_2k(
13021            t: *const SaSint,
13022            sa: *mut SaSint,
13023            n: SaSint,
13024            k: SaSint,
13025            buckets: *mut SaSint,
13026            omp_block_start: SaSint,
13027            omp_block_size: SaSint,
13028        ) -> SaSint;
13029        fn probe_libsais16x64_count_and_gather_lms_suffixes_32s_2k_omp(
13030            t: *const SaSint,
13031            sa: *mut SaSint,
13032            n: SaSint,
13033            k: SaSint,
13034            buckets: *mut SaSint,
13035            local_buckets: SaSint,
13036            threads: SaSint,
13037        ) -> SaSint;
13038        fn probe_libsais16x64_count_and_gather_compacted_lms_suffixes_32s_2k_omp(
13039            t: *const SaSint,
13040            sa: *mut SaSint,
13041            n: SaSint,
13042            k: SaSint,
13043            buckets: *mut SaSint,
13044            local_buckets: SaSint,
13045            threads: SaSint,
13046        );
13047        fn probe_libsais16x64_reconstruct_compacted_lms_suffixes_32s_2k_omp(
13048            t: *mut SaSint,
13049            sa: *mut SaSint,
13050            n: SaSint,
13051            k: SaSint,
13052            m: SaSint,
13053            fs: SaSint,
13054            f: SaSint,
13055            buckets: *mut SaSint,
13056            local_buckets: SaSint,
13057            threads: SaSint,
13058        );
13059        fn probe_libsais16x64_reconstruct_compacted_lms_suffixes_32s_1k_omp(
13060            t: *mut SaSint,
13061            sa: *mut SaSint,
13062            n: SaSint,
13063            m: SaSint,
13064            fs: SaSint,
13065            f: SaSint,
13066            threads: SaSint,
13067        );
13068    }
13069
13070    fn brute_sa(t: &[u16]) -> Vec<SaSint> {
13071        let mut sa: Vec<_> = (0..t.len() as SaSint).collect();
13072        sa.sort_by(|&a, &b| t[a as usize..].cmp(&t[b as usize..]));
13073        sa
13074    }
13075
13076    #[test]
13077    fn libsais16x64_gather_lms_suffixes_16u_matches_c() {
13078        let cases: &[&[u16]] = &[
13079            &[2, 1, 3, 1, 2, 0],
13080            &[7, 7, 7, 7, 0],
13081            &[3, 1, 2, 1, 0, 4, 1, 0],
13082            &[9, 1, 9, 1, 9, 0, 2, 2, 0],
13083        ];
13084
13085        for &text in cases {
13086            let n = text.len() as SaSint;
13087            let mut rust_sa = vec![-99; text.len()];
13088            let mut c_sa = rust_sa.clone();
13089
13090            gather_lms_suffixes_16u(text, &mut rust_sa, n, n - 1, 0, n);
13091            unsafe {
13092                probe_libsais16x64_gather_lms_suffixes_16u(
13093                    text.as_ptr(),
13094                    c_sa.as_mut_ptr(),
13095                    n,
13096                    n - 1,
13097                    0,
13098                    n,
13099                );
13100            }
13101
13102            assert_eq!(rust_sa, c_sa);
13103        }
13104    }
13105
13106    #[test]
13107    fn libsais16x64_count_and_gather_lms_suffixes_16u_matches_c() {
13108        let cases: &[&[u16]] = &[
13109            &[2, 1, 3, 1, 2, 0],
13110            &[7, 7, 7, 7, 0],
13111            &[3, 1, 2, 1, 0, 4, 1, 0],
13112            &[9, 1, 9, 1, 9, 0, 2, 2, 0],
13113        ];
13114
13115        for &text in cases {
13116            let n = text.len() as SaSint;
13117            let mut rust_sa = vec![-99; text.len()];
13118            let mut c_sa = rust_sa.clone();
13119            let mut rust_buckets = vec![-1; 4 * ALPHABET_SIZE];
13120            let mut c_buckets = rust_buckets.clone();
13121
13122            let rust_m =
13123                count_and_gather_lms_suffixes_16u(text, &mut rust_sa, n, &mut rust_buckets, 0, n);
13124            let c_m = unsafe {
13125                probe_libsais16x64_count_and_gather_lms_suffixes_16u(
13126                    text.as_ptr(),
13127                    c_sa.as_mut_ptr(),
13128                    n,
13129                    c_buckets.as_mut_ptr(),
13130                    0,
13131                    n,
13132                )
13133            };
13134
13135            assert_eq!(rust_m, c_m);
13136            assert_eq!(rust_sa, c_sa);
13137            assert_eq!(rust_buckets, c_buckets);
13138        }
13139    }
13140
13141    #[test]
13142    fn libsais16x64_initialize_buckets_start_and_end_16u_matches_c() {
13143        let mut rust_buckets = vec![0; 8 * ALPHABET_SIZE];
13144        for (symbol, counts) in [
13145            (0usize, [1, 0, 0, 2]),
13146            (1, [0, 3, 1, 0]),
13147            (7, [2, 1, 0, 1]),
13148            (1024, [0, 0, 5, 0]),
13149        ] {
13150            for state in 0..4 {
13151                rust_buckets[buckets_index4(symbol, state)] = counts[state];
13152            }
13153        }
13154        let mut c_buckets = rust_buckets.clone();
13155        let mut rust_freq = vec![-1; ALPHABET_SIZE];
13156        let mut c_freq = rust_freq.clone();
13157
13158        let rust_k = initialize_buckets_start_and_end_16u(&mut rust_buckets, Some(&mut rust_freq));
13159        let c_k = unsafe {
13160            probe_libsais16x64_initialize_buckets_start_and_end_16u(
13161                c_buckets.as_mut_ptr(),
13162                c_freq.as_mut_ptr(),
13163            )
13164        };
13165
13166        assert_eq!(rust_k, c_k);
13167        assert_eq!(rust_buckets, c_buckets);
13168        assert_eq!(rust_freq, c_freq);
13169
13170        let mut rust_buckets_no_freq = vec![0; 8 * ALPHABET_SIZE];
13171        rust_buckets_no_freq[..4 * ALPHABET_SIZE]
13172            .copy_from_slice(&rust_buckets[..4 * ALPHABET_SIZE]);
13173        let mut c_buckets_no_freq = rust_buckets_no_freq.clone();
13174
13175        let rust_k = initialize_buckets_start_and_end_16u(&mut rust_buckets_no_freq, None);
13176        let c_k = unsafe {
13177            probe_libsais16x64_initialize_buckets_start_and_end_16u(
13178                c_buckets_no_freq.as_mut_ptr(),
13179                std::ptr::null_mut(),
13180            )
13181        };
13182
13183        assert_eq!(rust_k, c_k);
13184        assert_eq!(rust_buckets_no_freq, c_buckets_no_freq);
13185    }
13186
13187    #[test]
13188    fn libsais16x64_lms_radix_bucket_initialization_matches_c() {
13189        let text = [3, 1, 2, 1, 0, 4, 1, 0];
13190        let n = text.len() as SaSint;
13191        let mut rust_sa = vec![-99; text.len()];
13192        let mut rust_buckets = vec![0; 8 * ALPHABET_SIZE];
13193        let m = count_and_gather_lms_suffixes_16u(
13194            &text,
13195            &mut rust_sa,
13196            n,
13197            &mut rust_buckets[..4 * ALPHABET_SIZE],
13198            0,
13199            n,
13200        );
13201        initialize_buckets_start_and_end_16u(&mut rust_buckets, None);
13202        let first_lms_suffix = rust_sa[(n - m) as usize];
13203
13204        let mut c_buckets = rust_buckets.clone();
13205        let rust_count = initialize_buckets_for_lms_suffixes_radix_sort_16u(
13206            &text,
13207            &mut rust_buckets,
13208            first_lms_suffix,
13209        );
13210        let c_count = unsafe {
13211            probe_libsais16x64_initialize_buckets_for_lms_suffixes_radix_sort_16u(
13212                text.as_ptr(),
13213                c_buckets.as_mut_ptr(),
13214                first_lms_suffix,
13215            )
13216        };
13217
13218        assert_eq!(rust_count, c_count);
13219        assert_eq!(rust_buckets, c_buckets);
13220    }
13221
13222    #[test]
13223    fn libsais16x64_radix_sort_lms_suffixes_16u_matches_c() {
13224        let text = [3, 1, 2, 1, 0, 4, 1, 0];
13225        let n = text.len() as SaSint;
13226        let mut rust_sa = vec![-99; text.len()];
13227        let mut rust_buckets = vec![0; 8 * ALPHABET_SIZE];
13228        let m = count_and_gather_lms_suffixes_16u(
13229            &text,
13230            &mut rust_sa,
13231            n,
13232            &mut rust_buckets[..4 * ALPHABET_SIZE],
13233            0,
13234            n,
13235        );
13236        initialize_buckets_start_and_end_16u(&mut rust_buckets, None);
13237        let first_lms_suffix = rust_sa[(n - m) as usize];
13238        initialize_buckets_for_lms_suffixes_radix_sort_16u(
13239            &text,
13240            &mut rust_buckets,
13241            first_lms_suffix,
13242        );
13243
13244        let mut c_sa = rust_sa.clone();
13245        let mut c_buckets = rust_buckets.clone();
13246        {
13247            let induction_bucket = &mut rust_buckets[4 * ALPHABET_SIZE..];
13248            radix_sort_lms_suffixes_16u(&text, &mut rust_sa, induction_bucket, n - m + 1, m - 1);
13249        }
13250        unsafe {
13251            probe_libsais16x64_radix_sort_lms_suffixes_16u(
13252                text.as_ptr(),
13253                c_sa.as_mut_ptr(),
13254                c_buckets[4 * ALPHABET_SIZE..].as_mut_ptr(),
13255                n - m + 1,
13256                m - 1,
13257            );
13258        }
13259
13260        assert_eq!(rust_sa, c_sa);
13261        assert_eq!(rust_buckets, c_buckets);
13262    }
13263
13264    #[test]
13265    fn libsais16x64_initialize_buckets_for_partial_sorting_16u_matches_c() {
13266        let text = [3, 1, 2, 1, 0, 4, 1, 0];
13267        let n = text.len() as SaSint;
13268        let mut rust_sa = vec![-99; text.len()];
13269        let mut rust_buckets = vec![0; 8 * ALPHABET_SIZE];
13270        let m = count_and_gather_lms_suffixes_16u(
13271            &text,
13272            &mut rust_sa,
13273            n,
13274            &mut rust_buckets[..4 * ALPHABET_SIZE],
13275            0,
13276            n,
13277        );
13278        initialize_buckets_start_and_end_16u(&mut rust_buckets, None);
13279        let first_lms_suffix = rust_sa[(n - m) as usize];
13280        let left_suffixes_count = initialize_buckets_for_lms_suffixes_radix_sort_16u(
13281            &text,
13282            &mut rust_buckets,
13283            first_lms_suffix,
13284        );
13285        let mut c_buckets = rust_buckets.clone();
13286
13287        initialize_buckets_for_partial_sorting_16u(
13288            &text,
13289            &mut rust_buckets,
13290            first_lms_suffix,
13291            left_suffixes_count,
13292        );
13293        unsafe {
13294            probe_libsais16x64_initialize_buckets_for_partial_sorting_16u(
13295                text.as_ptr(),
13296                c_buckets.as_mut_ptr(),
13297                first_lms_suffix,
13298                left_suffixes_count,
13299            );
13300        }
13301
13302        assert_eq!(rust_buckets, c_buckets);
13303    }
13304
13305    fn partial_scan_fixture() -> ([u16; 10], Vec<SaSint>, Vec<SaSint>) {
13306        let text = [1, 0, 2, 1, 3, 0, 2, 4, 1, 0];
13307        let mut sa = vec![0; 128];
13308        sa[..5].copy_from_slice(&[3, 5 | SAINT_MIN, 7, 2, 9 | SAINT_MIN]);
13309
13310        let mut buckets = vec![0; 6 * ALPHABET_SIZE];
13311        for v in 0..32 {
13312            buckets[v] = 80 + (v as SaSint) * 4;
13313            buckets[2 * ALPHABET_SIZE + v] = if v % 3 == 0 { 2 } else { 0 };
13314            buckets[4 * ALPHABET_SIZE + v] = 20 + (v as SaSint) * 4;
13315        }
13316
13317        (text, sa, buckets)
13318    }
13319
13320    #[test]
13321    fn libsais16x64_partial_sorting_scan_left_to_right_16u_matches_c() {
13322        let (text, mut rust_sa, mut rust_buckets) = partial_scan_fixture();
13323        let mut c_sa = rust_sa.clone();
13324        let mut c_buckets = rust_buckets.clone();
13325
13326        let rust_d =
13327            partial_sorting_scan_left_to_right_16u(&text, &mut rust_sa, &mut rust_buckets, 3, 0, 5);
13328        let c_d = unsafe {
13329            probe_libsais16x64_partial_sorting_scan_left_to_right_16u(
13330                text.as_ptr(),
13331                c_sa.as_mut_ptr(),
13332                c_buckets.as_mut_ptr(),
13333                3,
13334                0,
13335                5,
13336            )
13337        };
13338
13339        assert_eq!(rust_d, c_d);
13340        assert_eq!(rust_sa, c_sa);
13341        assert_eq!(rust_buckets, c_buckets);
13342    }
13343
13344    #[test]
13345    fn libsais16x64_partial_sorting_scan_right_to_left_16u_matches_c() {
13346        let (text, mut rust_sa, mut rust_buckets) = partial_scan_fixture();
13347        let mut c_sa = rust_sa.clone();
13348        let mut c_buckets = rust_buckets.clone();
13349
13350        let rust_d =
13351            partial_sorting_scan_right_to_left_16u(&text, &mut rust_sa, &mut rust_buckets, 3, 0, 5);
13352        let c_d = unsafe {
13353            probe_libsais16x64_partial_sorting_scan_right_to_left_16u(
13354                text.as_ptr(),
13355                c_sa.as_mut_ptr(),
13356                c_buckets.as_mut_ptr(),
13357                3,
13358                0,
13359                5,
13360            )
13361        };
13362
13363        assert_eq!(rust_d, c_d);
13364        assert_eq!(rust_sa, c_sa);
13365        assert_eq!(rust_buckets, c_buckets);
13366    }
13367
13368    #[test]
13369    fn libsais16x64_partial_gsa_scan_right_to_left_16u_matches_c() {
13370        let (text, mut rust_sa, mut rust_buckets) = partial_scan_fixture();
13371        let mut c_sa = rust_sa.clone();
13372        let mut c_buckets = rust_buckets.clone();
13373
13374        let rust_d =
13375            partial_gsa_scan_right_to_left_16u(&text, &mut rust_sa, &mut rust_buckets, 3, 0, 5);
13376        let c_d = unsafe {
13377            probe_libsais16x64_partial_gsa_scan_right_to_left_16u(
13378                text.as_ptr(),
13379                c_sa.as_mut_ptr(),
13380                c_buckets.as_mut_ptr(),
13381                3,
13382                0,
13383                5,
13384            )
13385        };
13386
13387        assert_eq!(rust_d, c_d);
13388        assert_eq!(rust_sa, c_sa);
13389        assert_eq!(rust_buckets, c_buckets);
13390    }
13391
13392    #[test]
13393    fn libsais16x64_partial_sorting_shift_markers_16u_matches_c() {
13394        let mut rust_sa = vec![0; 16];
13395        rust_sa[2..6].copy_from_slice(&[1, 2 | SAINT_MIN, 3 | SAINT_MIN, 4]);
13396        rust_sa[8..12].copy_from_slice(&[5 | SAINT_MIN, 6, 7 | SAINT_MIN, 8]);
13397        let mut c_sa = rust_sa.clone();
13398
13399        let mut buckets = vec![0; 6 * ALPHABET_SIZE];
13400        buckets[0] = 2;
13401        buckets[2] = 8;
13402        buckets[4 * ALPHABET_SIZE + 2] = 6;
13403        buckets[4 * ALPHABET_SIZE + 4] = 12;
13404
13405        let n = rust_sa.len() as SaSint;
13406        partial_sorting_shift_markers_16u_omp(&mut rust_sa, n, &buckets, 1);
13407        unsafe {
13408            probe_libsais16x64_partial_sorting_shift_markers_16u_omp(
13409                c_sa.as_mut_ptr(),
13410                c_sa.len() as SaSint,
13411                buckets.as_ptr(),
13412                1,
13413            );
13414        }
13415
13416        assert_eq!(rust_sa, c_sa);
13417    }
13418
13419    #[test]
13420    fn libsais16x64_partial_left_to_right_16u_block_omp_uses_cache_pipeline() {
13421        let block_size = 65_536usize;
13422        let k = 512usize;
13423        let text: Vec<u16> = (0..block_size + 2)
13424            .map(|i| 1 + ((i * 17 + i / 7) % (k - 1)) as u16)
13425            .collect();
13426        let sa_len = block_size + 2 * k * 100;
13427        let mut base_sa = vec![0; sa_len];
13428        for (i, slot) in base_sa.iter_mut().take(block_size).enumerate() {
13429            *slot = (i + 2) as SaSint;
13430        }
13431        let mut base_buckets = vec![0; 8 * ALPHABET_SIZE];
13432        for v in 0..2 * k {
13433            base_buckets[4 * ALPHABET_SIZE + v] = (block_size + v * 100) as SaSint;
13434        }
13435
13436        let mut scalar_sa = base_sa.clone();
13437        let mut threaded_sa = base_sa;
13438        let mut scalar_buckets = base_buckets.clone();
13439        let mut threaded_buckets = base_buckets;
13440        let mut thread_state = alloc_thread_state(4).unwrap();
13441        let scalar_d = partial_sorting_scan_left_to_right_16u(
13442            &text,
13443            &mut scalar_sa,
13444            &mut scalar_buckets,
13445            0,
13446            0,
13447            block_size as SaSint,
13448        );
13449        let threaded_d = partial_sorting_scan_left_to_right_16u_block_omp(
13450            &text,
13451            &mut threaded_sa,
13452            k as SaSint,
13453            &mut threaded_buckets,
13454            0,
13455            0,
13456            block_size as SaSint,
13457            4,
13458            &mut thread_state,
13459        );
13460
13461        assert_eq!(threaded_d, scalar_d);
13462        assert_eq!(threaded_sa, scalar_sa);
13463        assert_eq!(threaded_buckets, scalar_buckets);
13464    }
13465
13466    #[test]
13467    fn libsais16x64_partial_left_to_right_16u_omp_uses_block_pipeline() {
13468        let block_size = 65_536usize;
13469        let k = 512usize;
13470        let text: Vec<u16> = (0..block_size + 2)
13471            .map(|i| 1 + ((i * 17 + i / 7) % (k - 1)) as u16)
13472            .collect();
13473        let sa_len = block_size + 2 * k * 100;
13474        let mut base_sa = vec![0; sa_len];
13475        for (i, slot) in base_sa.iter_mut().take(block_size).enumerate() {
13476            let value = (i + 2) as SaSint;
13477            *slot = if i % 17 == 0 {
13478                value | SAINT_MIN
13479            } else {
13480                value
13481            };
13482        }
13483        let mut base_buckets = vec![0; 8 * ALPHABET_SIZE];
13484        for v in 0..2 * k {
13485            base_buckets[4 * ALPHABET_SIZE + v] = (block_size + v * 100) as SaSint;
13486            base_buckets[2 * ALPHABET_SIZE + v] = if v % 5 == 0 { 3 } else { 0 };
13487        }
13488
13489        let mut scalar_sa = base_sa.clone();
13490        let mut threaded_sa = base_sa;
13491        let mut scalar_buckets = base_buckets.clone();
13492        let mut threaded_buckets = base_buckets;
13493        let scalar_d = partial_sorting_scan_left_to_right_16u_omp(
13494            &text,
13495            &mut scalar_sa,
13496            text.len() as SaSint,
13497            k as SaSint,
13498            &mut scalar_buckets,
13499            block_size as SaSint,
13500            7,
13501            1,
13502        );
13503        let threaded_d = partial_sorting_scan_left_to_right_16u_omp(
13504            &text,
13505            &mut threaded_sa,
13506            text.len() as SaSint,
13507            k as SaSint,
13508            &mut threaded_buckets,
13509            block_size as SaSint,
13510            7,
13511            4,
13512        );
13513
13514        assert_eq!(threaded_d, scalar_d);
13515        assert_eq!(threaded_sa, scalar_sa);
13516        assert_eq!(threaded_buckets, scalar_buckets);
13517    }
13518
13519    #[test]
13520    fn libsais16x64_partial_right_to_left_16u_block_omp_uses_cache_pipeline() {
13521        let block_size = 65_536usize;
13522        let k = 512usize;
13523        let width = 2 * k;
13524        let block_start = width * 200 + 1024;
13525        let text: Vec<u16> = (0..block_size + 2)
13526            .map(|i| 1 + ((i * 17 + i / 7) % (k - 1)) as u16)
13527            .collect();
13528        let sa_len = block_start + block_size + 1;
13529        let mut base_sa = vec![0; sa_len];
13530        for i in 0..block_size {
13531            let value = (i + 2) as SaSint;
13532            base_sa[block_start + i] = if i % 17 == 0 {
13533                value | SAINT_MIN
13534            } else {
13535                value
13536            };
13537        }
13538        let mut base_buckets = vec![0; 8 * ALPHABET_SIZE];
13539        for v in 0..width {
13540            base_buckets[v] = ((v + 1) * 200) as SaSint;
13541            base_buckets[2 * ALPHABET_SIZE + v] = if v % 5 == 0 { 3 } else { 0 };
13542        }
13543
13544        let mut scalar_sa = base_sa.clone();
13545        let mut threaded_sa = base_sa.clone();
13546        let mut scalar_buckets = base_buckets.clone();
13547        let mut threaded_buckets = base_buckets.clone();
13548        let mut thread_state = alloc_thread_state(4).unwrap();
13549        let scalar_d = partial_sorting_scan_right_to_left_16u(
13550            &text,
13551            &mut scalar_sa,
13552            &mut scalar_buckets,
13553            7,
13554            block_start as SaSint,
13555            block_size as SaSint,
13556        );
13557        let threaded_d = partial_sorting_scan_right_to_left_16u_block_omp(
13558            &text,
13559            &mut threaded_sa,
13560            k as SaSint,
13561            &mut threaded_buckets,
13562            7,
13563            block_start as SaSint,
13564            block_size as SaSint,
13565            4,
13566            &mut thread_state,
13567        );
13568        assert_eq!(threaded_d, scalar_d);
13569        assert_eq!(threaded_sa, scalar_sa);
13570        assert_eq!(threaded_buckets, scalar_buckets);
13571
13572        let mut scalar_sa = base_sa;
13573        let mut threaded_sa = scalar_sa.clone();
13574        let mut scalar_buckets = base_buckets.clone();
13575        let mut threaded_buckets = base_buckets;
13576        let scalar_d = partial_gsa_scan_right_to_left_16u(
13577            &text,
13578            &mut scalar_sa,
13579            &mut scalar_buckets,
13580            7,
13581            block_start as SaSint,
13582            block_size as SaSint,
13583        );
13584        let threaded_d = partial_gsa_scan_right_to_left_16u_block_omp(
13585            &text,
13586            &mut threaded_sa,
13587            k as SaSint,
13588            &mut threaded_buckets,
13589            7,
13590            block_start as SaSint,
13591            block_size as SaSint,
13592            4,
13593            &mut thread_state,
13594        );
13595        assert_eq!(threaded_d, scalar_d);
13596        assert_eq!(threaded_sa, scalar_sa);
13597        assert_eq!(threaded_buckets, scalar_buckets);
13598    }
13599
13600    #[test]
13601    fn libsais16x64_partial_right_to_left_16u_omp_uses_block_pipeline() {
13602        let block_size = 65_536usize;
13603        let k = 512usize;
13604        let width = 2 * k;
13605        let block_start = width * 200 + 1024;
13606        let text: Vec<u16> = (0..block_size + 2)
13607            .map(|i| 1 + ((i * 17 + i / 7) % (k - 1)) as u16)
13608            .collect();
13609        let sa_len = block_start + block_size + 1;
13610        let n = sa_len as SaSint;
13611        let first_lms_suffix = n - (block_start + block_size) as SaSint;
13612        let left_suffixes_count = block_start as SaSint - 1;
13613        let mut base_sa = vec![0; sa_len];
13614        for i in 0..block_size {
13615            let value = (i + 2) as SaSint;
13616            base_sa[block_start + i] = if i % 17 == 0 {
13617                value | SAINT_MIN
13618            } else {
13619                value
13620            };
13621        }
13622        let mut base_buckets = vec![0; 8 * ALPHABET_SIZE];
13623        for v in 0..width {
13624            base_buckets[v] = ((v + 1) * 200) as SaSint;
13625            base_buckets[2 * ALPHABET_SIZE + v] = if v % 5 == 0 { 3 } else { 0 };
13626        }
13627
13628        let mut scalar_sa = base_sa.clone();
13629        let mut threaded_sa = base_sa.clone();
13630        let mut scalar_buckets = base_buckets.clone();
13631        let mut threaded_buckets = base_buckets.clone();
13632        partial_sorting_scan_right_to_left_16u_omp(
13633            &text,
13634            &mut scalar_sa,
13635            n,
13636            k as SaSint,
13637            &mut scalar_buckets,
13638            first_lms_suffix,
13639            left_suffixes_count,
13640            7,
13641            1,
13642        );
13643        partial_sorting_scan_right_to_left_16u_omp(
13644            &text,
13645            &mut threaded_sa,
13646            n,
13647            k as SaSint,
13648            &mut threaded_buckets,
13649            first_lms_suffix,
13650            left_suffixes_count,
13651            7,
13652            4,
13653        );
13654        assert_eq!(threaded_sa, scalar_sa);
13655        assert_eq!(threaded_buckets, scalar_buckets);
13656
13657        let mut scalar_sa = base_sa;
13658        let mut threaded_sa = scalar_sa.clone();
13659        let mut scalar_buckets = base_buckets.clone();
13660        let mut threaded_buckets = base_buckets;
13661        partial_gsa_scan_right_to_left_16u_omp(
13662            &text,
13663            &mut scalar_sa,
13664            n,
13665            k as SaSint,
13666            &mut scalar_buckets,
13667            first_lms_suffix,
13668            left_suffixes_count,
13669            7,
13670            1,
13671        );
13672        partial_gsa_scan_right_to_left_16u_omp(
13673            &text,
13674            &mut threaded_sa,
13675            n,
13676            k as SaSint,
13677            &mut threaded_buckets,
13678            first_lms_suffix,
13679            left_suffixes_count,
13680            7,
13681            4,
13682        );
13683        assert_eq!(threaded_sa, scalar_sa);
13684        assert_eq!(threaded_buckets, scalar_buckets);
13685    }
13686
13687    fn final_scan_fixture() -> ([u16; 10], Vec<SaSint>, Vec<SaSint>) {
13688        let text = [1, 0, 2, 1, 3, 0, 2, 4, 1, 0];
13689        let mut sa = vec![0; 96];
13690        sa[..6].copy_from_slice(&[3, 0, 5 | SAINT_MIN, 7, 2, 9 | SAINT_MIN]);
13691
13692        let mut induction_bucket = vec![0; ALPHABET_SIZE];
13693        for c in 0..8 {
13694            induction_bucket[c] = 24 + (c as SaSint) * 6;
13695        }
13696
13697        (text, sa, induction_bucket)
13698    }
13699
13700    fn final_order_buckets(induction_bucket: &[SaSint]) -> Vec<SaSint> {
13701        let mut buckets = vec![0; 8 * ALPHABET_SIZE];
13702        buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE].copy_from_slice(induction_bucket);
13703        buckets[7 * ALPHABET_SIZE..8 * ALPHABET_SIZE].copy_from_slice(induction_bucket);
13704        buckets
13705    }
13706
13707    #[test]
13708    fn libsais16x64_final_sorting_scan_left_to_right_16u_matches_c() {
13709        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
13710        let mut c_sa = rust_sa.clone();
13711        let mut c_bucket = rust_bucket.clone();
13712
13713        final_sorting_scan_left_to_right_16u(&text, &mut rust_sa, &mut rust_bucket, 0, 6);
13714        unsafe {
13715            probe_libsais16x64_final_sorting_scan_left_to_right_16u(
13716                text.as_ptr(),
13717                c_sa.as_mut_ptr(),
13718                c_bucket.as_mut_ptr(),
13719                0,
13720                6,
13721            );
13722        }
13723
13724        assert_eq!(rust_sa, c_sa);
13725        assert_eq!(rust_bucket, c_bucket);
13726    }
13727
13728    #[test]
13729    fn libsais16x64_final_sorting_scan_right_to_left_16u_matches_c() {
13730        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
13731        let mut c_sa = rust_sa.clone();
13732        let mut c_bucket = rust_bucket.clone();
13733
13734        final_sorting_scan_right_to_left_16u(&text, &mut rust_sa, &mut rust_bucket, 0, 6);
13735        unsafe {
13736            probe_libsais16x64_final_sorting_scan_right_to_left_16u(
13737                text.as_ptr(),
13738                c_sa.as_mut_ptr(),
13739                c_bucket.as_mut_ptr(),
13740                0,
13741                6,
13742            );
13743        }
13744
13745        assert_eq!(rust_sa, c_sa);
13746        assert_eq!(rust_bucket, c_bucket);
13747    }
13748
13749    #[test]
13750    fn libsais16x64_final_gsa_scan_right_to_left_16u_matches_c() {
13751        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
13752        let mut c_sa = rust_sa.clone();
13753        let mut c_bucket = rust_bucket.clone();
13754
13755        final_gsa_scan_right_to_left_16u(&text, &mut rust_sa, &mut rust_bucket, 0, 6);
13756        unsafe {
13757            probe_libsais16x64_final_gsa_scan_right_to_left_16u(
13758                text.as_ptr(),
13759                c_sa.as_mut_ptr(),
13760                c_bucket.as_mut_ptr(),
13761                0,
13762                6,
13763            );
13764        }
13765
13766        assert_eq!(rust_sa, c_sa);
13767        assert_eq!(rust_bucket, c_bucket);
13768    }
13769
13770    #[test]
13771    fn libsais16x64_final_sorting_32s_helpers_behave_like_upstream_shapes() {
13772        let t = vec![0, 1, 2, 1, 0, 1, 2, 1, 0];
13773
13774        let mut rust_sa = vec![1, 0, 0];
13775        let mut rust_bucket = vec![0, 1, 3];
13776        let mut c_sa = rust_sa.clone();
13777        let mut c_bucket = rust_bucket.clone();
13778        final_sorting_scan_left_to_right_32s(&t, &mut rust_sa, &mut rust_bucket, 0, 1);
13779        unsafe {
13780            probe_libsais16x64_final_sorting_scan_left_to_right_32s(
13781                t.as_ptr(),
13782                c_sa.as_mut_ptr(),
13783                c_bucket.as_mut_ptr(),
13784                0,
13785                1,
13786            );
13787        }
13788        assert_eq!(rust_sa, c_sa);
13789        assert_eq!(rust_bucket, c_bucket);
13790
13791        let mut rust_sa = vec![0, 2, 0];
13792        let mut rust_bucket = vec![1, 2, 3];
13793        let mut c_sa = rust_sa.clone();
13794        let mut c_bucket = rust_bucket.clone();
13795        final_sorting_scan_right_to_left_32s(&t, &mut rust_sa, &mut rust_bucket, 0, 2);
13796        unsafe {
13797            probe_libsais16x64_final_sorting_scan_right_to_left_32s(
13798                t.as_ptr(),
13799                c_sa.as_mut_ptr(),
13800                c_bucket.as_mut_ptr(),
13801                0,
13802                2,
13803            );
13804        }
13805        assert_eq!(rust_sa, c_sa);
13806        assert_eq!(rust_bucket, c_bucket);
13807
13808        let mut sa = vec![1, 2, 0, 0];
13809        let mut induction_bucket = vec![0, 1, 3];
13810        let mut cache = vec![ThreadCache::default(); PER_THREAD_CACHE_SIZE];
13811        final_sorting_scan_left_to_right_32s_block_omp(
13812            &t,
13813            &mut sa,
13814            &mut induction_bucket,
13815            &mut cache,
13816            0,
13817            2,
13818            2,
13819        );
13820        assert_eq!(sa[0] & SAINT_MAX, 0);
13821        assert_eq!(sa[1] & SAINT_MAX, 1);
13822        assert_eq!(induction_bucket[0], 1);
13823        assert_eq!(induction_bucket[1], 2);
13824
13825        let mut sa = vec![0, 2, 0, 0];
13826        let mut induction_bucket = vec![1, 2, 3];
13827        let mut cache = vec![ThreadCache::default(); PER_THREAD_CACHE_SIZE];
13828        final_sorting_scan_right_to_left_32s_block_omp(
13829            &t,
13830            &mut sa,
13831            &mut induction_bucket,
13832            &mut cache,
13833            0,
13834            2,
13835            2,
13836        );
13837        assert_eq!(sa[1] & SAINT_MAX, 1);
13838        assert_eq!(induction_bucket[1], 1);
13839    }
13840
13841    #[test]
13842    fn libsais16x64_final_left_to_right_16u_block_omp_uses_cache_pipeline() {
13843        let block_size = 65_536usize;
13844        let k = 512usize;
13845        let text: Vec<u16> = (0..=block_size).map(|i| 1 + (i % (k - 1)) as u16).collect();
13846        let sa_len = block_size + k * 200;
13847        let mut base_sa = vec![0; sa_len];
13848        for (i, slot) in base_sa.iter_mut().take(block_size).enumerate() {
13849            *slot = (i + 1) as SaSint;
13850        }
13851        let mut base_bucket = vec![0; k];
13852        for c in 0..k {
13853            base_bucket[c] = (block_size + c * 200) as SaSint;
13854        }
13855
13856        let mut scalar_sa = base_sa.clone();
13857        let mut threaded_sa = base_sa.clone();
13858        let mut scalar_bucket = base_bucket.clone();
13859        let mut threaded_bucket = base_bucket.clone();
13860        let mut thread_state = alloc_thread_state(4).unwrap();
13861        final_bwt_scan_left_to_right_16u(
13862            &text,
13863            &mut scalar_sa,
13864            &mut scalar_bucket,
13865            0,
13866            block_size as SaSint,
13867        );
13868        final_bwt_scan_left_to_right_16u_block_omp(
13869            &text,
13870            &mut threaded_sa,
13871            k as SaSint,
13872            &mut threaded_bucket,
13873            0,
13874            block_size as SaSint,
13875            4,
13876            &mut thread_state,
13877        );
13878        assert_eq!(threaded_sa, scalar_sa);
13879        assert_eq!(threaded_bucket, scalar_bucket);
13880
13881        let rm = 3;
13882        let mut scalar_sa = base_sa.clone();
13883        let mut threaded_sa = base_sa.clone();
13884        let mut scalar_bucket = base_bucket.clone();
13885        let mut threaded_bucket = base_bucket.clone();
13886        let mut scalar_i = vec![-1; (block_size / (rm as usize + 1)) + 2];
13887        let mut threaded_i = scalar_i.clone();
13888        final_bwt_aux_scan_left_to_right_16u(
13889            &text,
13890            &mut scalar_sa,
13891            rm,
13892            &mut scalar_i,
13893            &mut scalar_bucket,
13894            0,
13895            block_size as SaSint,
13896        );
13897        final_bwt_aux_scan_left_to_right_16u_block_omp(
13898            &text,
13899            &mut threaded_sa,
13900            k as SaSint,
13901            rm,
13902            &mut threaded_i,
13903            &mut threaded_bucket,
13904            0,
13905            block_size as SaSint,
13906            4,
13907            &mut thread_state,
13908        );
13909        assert_eq!(threaded_sa, scalar_sa);
13910        assert_eq!(threaded_i, scalar_i);
13911        assert_eq!(threaded_bucket, scalar_bucket);
13912
13913        let mut scalar_sa = base_sa;
13914        let mut threaded_sa = scalar_sa.clone();
13915        let mut scalar_bucket = base_bucket.clone();
13916        let mut threaded_bucket = base_bucket;
13917        final_sorting_scan_left_to_right_16u(
13918            &text,
13919            &mut scalar_sa,
13920            &mut scalar_bucket,
13921            0,
13922            block_size as SaSint,
13923        );
13924        final_sorting_scan_left_to_right_16u_block_omp(
13925            &text,
13926            &mut threaded_sa,
13927            k as SaSint,
13928            &mut threaded_bucket,
13929            0,
13930            block_size as SaSint,
13931            4,
13932            &mut thread_state,
13933        );
13934        assert_eq!(threaded_sa, scalar_sa);
13935        assert_eq!(threaded_bucket, scalar_bucket);
13936    }
13937
13938    #[test]
13939    fn libsais16x64_final_right_to_left_16u_block_omp_uses_cache_pipeline() {
13940        let block_size = 65_536usize;
13941        let k = 512usize;
13942        let block_start = k * 200 + 1024;
13943        let text: Vec<u16> = (0..=block_size + 1)
13944            .map(|i| 1 + (i % (k - 1)) as u16)
13945            .collect();
13946        let sa_len = block_start + block_size + 1;
13947        let mut base_sa = vec![0; sa_len];
13948        for i in 0..block_size {
13949            base_sa[block_start + i] = (i + 1) as SaSint;
13950        }
13951        let mut base_bucket = vec![0; k];
13952        for c in 0..k {
13953            base_bucket[c] = ((c + 1) * 200) as SaSint;
13954        }
13955
13956        let mut scalar_sa = base_sa.clone();
13957        let mut threaded_sa = base_sa.clone();
13958        let mut scalar_bucket = base_bucket.clone();
13959        let mut threaded_bucket = base_bucket.clone();
13960        let mut thread_state = alloc_thread_state(4).unwrap();
13961        final_bwt_scan_right_to_left_16u(
13962            &text,
13963            &mut scalar_sa,
13964            &mut scalar_bucket,
13965            block_start as SaSint,
13966            block_size as SaSint,
13967        );
13968        final_bwt_scan_right_to_left_16u_block_omp(
13969            &text,
13970            &mut threaded_sa,
13971            k as SaSint,
13972            &mut threaded_bucket,
13973            block_start as SaSint,
13974            block_size as SaSint,
13975            4,
13976            &mut thread_state,
13977        );
13978        assert_eq!(threaded_sa, scalar_sa);
13979        assert_eq!(threaded_bucket, scalar_bucket);
13980
13981        let rm = 3;
13982        let mut scalar_sa = base_sa.clone();
13983        let mut threaded_sa = base_sa.clone();
13984        let mut scalar_bucket = base_bucket.clone();
13985        let mut threaded_bucket = base_bucket.clone();
13986        let mut scalar_i = vec![-1; (block_size / (rm as usize + 1)) + 2];
13987        let mut threaded_i = scalar_i.clone();
13988        final_bwt_aux_scan_right_to_left_16u(
13989            &text,
13990            &mut scalar_sa,
13991            rm,
13992            &mut scalar_i,
13993            &mut scalar_bucket,
13994            block_start as SaSint,
13995            block_size as SaSint,
13996        );
13997        final_bwt_aux_scan_right_to_left_16u_block_omp(
13998            &text,
13999            &mut threaded_sa,
14000            k as SaSint,
14001            rm,
14002            &mut threaded_i,
14003            &mut threaded_bucket,
14004            block_start as SaSint,
14005            block_size as SaSint,
14006            4,
14007            &mut thread_state,
14008        );
14009        assert_eq!(threaded_sa, scalar_sa);
14010        assert_eq!(threaded_i, scalar_i);
14011        assert_eq!(threaded_bucket, scalar_bucket);
14012
14013        let mut scalar_sa = base_sa.clone();
14014        let mut threaded_sa = base_sa.clone();
14015        let mut scalar_bucket = base_bucket.clone();
14016        let mut threaded_bucket = base_bucket.clone();
14017        final_sorting_scan_right_to_left_16u(
14018            &text,
14019            &mut scalar_sa,
14020            &mut scalar_bucket,
14021            block_start as SaSint,
14022            block_size as SaSint,
14023        );
14024        final_sorting_scan_right_to_left_16u_block_omp(
14025            &text,
14026            &mut threaded_sa,
14027            k as SaSint,
14028            &mut threaded_bucket,
14029            block_start as SaSint,
14030            block_size as SaSint,
14031            4,
14032            &mut thread_state,
14033        );
14034        assert_eq!(threaded_sa, scalar_sa);
14035        assert_eq!(threaded_bucket, scalar_bucket);
14036
14037        let mut scalar_sa = base_sa;
14038        let mut threaded_sa = scalar_sa.clone();
14039        let mut scalar_bucket = base_bucket.clone();
14040        let mut threaded_bucket = base_bucket;
14041        final_gsa_scan_right_to_left_16u(
14042            &text,
14043            &mut scalar_sa,
14044            &mut scalar_bucket,
14045            block_start as SaSint,
14046            block_size as SaSint,
14047        );
14048        final_gsa_scan_right_to_left_16u_block_omp(
14049            &text,
14050            &mut threaded_sa,
14051            k as SaSint,
14052            &mut threaded_bucket,
14053            block_start as SaSint,
14054            block_size as SaSint,
14055            4,
14056            &mut thread_state,
14057        );
14058        assert_eq!(threaded_sa, scalar_sa);
14059        assert_eq!(threaded_bucket, scalar_bucket);
14060    }
14061
14062    #[test]
14063    fn libsais16x64_clear_lms_suffixes_omp_zeroes_requested_bucket_ranges() {
14064        let mut rust_sa = vec![5, 4, 3, 2, 1, 9];
14065        let mut c_sa = rust_sa.clone();
14066        let n = rust_sa.len() as SaSint;
14067        let mut bucket_start = vec![1, 4, 5];
14068        let mut bucket_end = vec![3, 5, 5];
14069
14070        clear_lms_suffixes_omp(&mut rust_sa, n, 3, &bucket_start, &bucket_end, 2);
14071        unsafe {
14072            probe_libsais16x64_clear_lms_suffixes_omp(
14073                c_sa.as_mut_ptr(),
14074                n,
14075                3,
14076                bucket_start.as_mut_ptr(),
14077                bucket_end.as_mut_ptr(),
14078                2,
14079            );
14080        }
14081
14082        assert_eq!(rust_sa, c_sa);
14083    }
14084
14085    #[test]
14086    fn libsais16x64_partial_order_wrapper_helpers_match_manual_sequence() {
14087        let mut rust_sa = vec![1, 2, 3, 4];
14088        let mut c_sa = rust_sa.clone();
14089        flip_suffix_markers_omp(&mut rust_sa, 3, 2);
14090        unsafe {
14091            probe_libsais16x64_flip_suffix_markers_omp(c_sa.as_mut_ptr(), 3, 2);
14092        }
14093        assert_eq!(rust_sa, c_sa);
14094
14095        let t = vec![0, 1, 2, 1, 0, 1, 2, 1, 0];
14096        let n = t.len() as SaSint;
14097        let k = 3;
14098        let mut wrapped_sa = vec![0; t.len()];
14099        let mut wrapped_buckets = vec![0; k as usize];
14100        let mut wrapped_state = alloc_thread_state(1).unwrap();
14101        induce_partial_order_32s_1k_omp(
14102            &t,
14103            &mut wrapped_sa,
14104            n,
14105            k,
14106            &mut wrapped_buckets,
14107            1,
14108            &mut wrapped_state,
14109        );
14110
14111        let mut manual_sa = vec![0; t.len()];
14112        let mut manual_buckets = vec![0; k as usize];
14113        let mut manual_state = alloc_thread_state(1).unwrap();
14114        count_suffixes_32s(&t, n, k, &mut manual_buckets);
14115        initialize_buckets_start_32s_1k(k, &mut manual_buckets);
14116        partial_sorting_scan_left_to_right_32s_1k_omp(
14117            &t,
14118            &mut manual_sa,
14119            n,
14120            &mut manual_buckets,
14121            1,
14122            &mut manual_state,
14123        );
14124        count_suffixes_32s(&t, n, k, &mut manual_buckets);
14125        initialize_buckets_end_32s_1k(k, &mut manual_buckets);
14126        partial_sorting_scan_right_to_left_32s_1k_omp(
14127            &t,
14128            &mut manual_sa,
14129            n,
14130            &mut manual_buckets,
14131            1,
14132            &mut manual_state,
14133        );
14134        partial_sorting_gather_lms_suffixes_32s_1k_omp(&mut manual_sa, n, 1, &mut manual_state);
14135
14136        assert_eq!(wrapped_sa, manual_sa);
14137        assert_eq!(wrapped_buckets, manual_buckets);
14138    }
14139
14140    #[test]
14141    fn libsais16x64_induce_partial_order_32s_wrappers_match_c() {
14142        let t = make_main_32s_stress_text(128, 24);
14143        let n = t.len() as SaSint;
14144        let k = 24;
14145        let threads = 1;
14146
14147        let mut rust_sa = vec![0; t.len()];
14148        let mut rust_buckets = vec![0; 6 * k as usize];
14149        let mut rust_state = alloc_thread_state(threads).unwrap();
14150        let m = count_and_gather_lms_suffixes_32s_4k_omp(
14151            &t,
14152            &mut rust_sa,
14153            n,
14154            k,
14155            &mut rust_buckets,
14156            1,
14157            threads,
14158            &mut rust_state,
14159        );
14160        assert!(m > 1);
14161        rust_sa[..(n - m) as usize].fill(0);
14162        let first_lms_suffix = rust_sa[(n - m) as usize];
14163        let left_suffixes_count = initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
14164            &t,
14165            k,
14166            &mut rust_buckets,
14167            first_lms_suffix,
14168        );
14169        let (_, induction_bucket) = rust_buckets.split_at_mut(4 * k as usize);
14170        radix_sort_lms_suffixes_32s_6k_omp(&t, &mut rust_sa, n, m, induction_bucket, threads);
14171        radix_sort_set_markers_32s_6k_omp(&mut rust_sa, k, induction_bucket, threads);
14172        initialize_buckets_for_partial_sorting_32s_6k(
14173            &t,
14174            k,
14175            &mut rust_buckets,
14176            first_lms_suffix,
14177            left_suffixes_count,
14178        );
14179        let mut c_sa = rust_sa.clone();
14180        let mut c_buckets = rust_buckets.clone();
14181        induce_partial_order_32s_6k_omp(
14182            &t,
14183            &mut rust_sa,
14184            n,
14185            k,
14186            &mut rust_buckets,
14187            first_lms_suffix,
14188            left_suffixes_count,
14189            threads,
14190            &mut rust_state,
14191        );
14192        unsafe {
14193            probe_libsais16x64_induce_partial_order_32s_6k_omp(
14194                t.as_ptr(),
14195                c_sa.as_mut_ptr(),
14196                n,
14197                k,
14198                c_buckets.as_mut_ptr(),
14199                first_lms_suffix,
14200                left_suffixes_count,
14201                threads,
14202            );
14203        }
14204        assert_eq!(rust_sa, c_sa);
14205        assert_eq!(rust_buckets, c_buckets);
14206
14207        let mut rust_sa = vec![0; t.len()];
14208        let mut rust_buckets = vec![0; 4 * k as usize];
14209        let mut rust_state = alloc_thread_state(threads).unwrap();
14210        let m = count_and_gather_lms_suffixes_32s_2k_omp(
14211            &t,
14212            &mut rust_sa,
14213            n,
14214            k,
14215            &mut rust_buckets,
14216            1,
14217            threads,
14218            &mut rust_state,
14219        );
14220        assert!(m > 1);
14221        let first_lms_suffix = rust_sa[(n - m) as usize];
14222        initialize_buckets_for_radix_and_partial_sorting_32s_4k(
14223            &t,
14224            k,
14225            &mut rust_buckets,
14226            first_lms_suffix,
14227        );
14228        let (_, induction_bucket) = rust_buckets.split_at_mut(1);
14229        radix_sort_lms_suffixes_32s_2k_omp(&t, &mut rust_sa, n, m, induction_bucket, threads);
14230        radix_sort_set_markers_32s_4k_omp(&mut rust_sa, k, induction_bucket, threads);
14231        place_lms_suffixes_interval_32s_4k(&mut rust_sa, n, k, m - 1, &rust_buckets);
14232        let mut c_sa = rust_sa.clone();
14233        let mut c_buckets = rust_buckets.clone();
14234        induce_partial_order_32s_4k_omp(
14235            &t,
14236            &mut rust_sa,
14237            n,
14238            k,
14239            &mut rust_buckets,
14240            threads,
14241            &mut rust_state,
14242        );
14243        unsafe {
14244            probe_libsais16x64_induce_partial_order_32s_4k_omp(
14245                t.as_ptr(),
14246                c_sa.as_mut_ptr(),
14247                n,
14248                k,
14249                c_buckets.as_mut_ptr(),
14250                threads,
14251            );
14252        }
14253        assert_eq!(rust_sa, c_sa);
14254        assert_eq!(rust_buckets, c_buckets);
14255
14256        let mut rust_sa = vec![0; t.len()];
14257        let mut rust_buckets = vec![0; 2 * k as usize];
14258        let mut rust_state = alloc_thread_state(threads).unwrap();
14259        let m = count_and_gather_lms_suffixes_32s_2k_omp(
14260            &t,
14261            &mut rust_sa,
14262            n,
14263            k,
14264            &mut rust_buckets,
14265            1,
14266            threads,
14267            &mut rust_state,
14268        );
14269        assert!(m > 1);
14270        let first_lms_suffix = rust_sa[(n - m) as usize];
14271        initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
14272            &t,
14273            k,
14274            &mut rust_buckets,
14275            first_lms_suffix,
14276        );
14277        let (_, induction_bucket) = rust_buckets.split_at_mut(1);
14278        radix_sort_lms_suffixes_32s_2k_omp(&t, &mut rust_sa, n, m, induction_bucket, threads);
14279        place_lms_suffixes_interval_32s_2k(&mut rust_sa, n, k, m - 1, &rust_buckets);
14280        initialize_buckets_start_and_end_32s_2k(k, &mut rust_buckets);
14281        let mut c_sa = rust_sa.clone();
14282        let mut c_buckets = rust_buckets.clone();
14283        induce_partial_order_32s_2k_omp(
14284            &t,
14285            &mut rust_sa,
14286            n,
14287            k,
14288            &mut rust_buckets,
14289            threads,
14290            &mut rust_state,
14291        );
14292        unsafe {
14293            probe_libsais16x64_induce_partial_order_32s_2k_omp(
14294                t.as_ptr(),
14295                c_sa.as_mut_ptr(),
14296                n,
14297                k,
14298                c_buckets.as_mut_ptr(),
14299                threads,
14300            );
14301        }
14302        assert_eq!(rust_sa, c_sa);
14303        assert_eq!(rust_buckets, c_buckets);
14304
14305        let mut rust_sa = vec![0; t.len()];
14306        let mut rust_buckets = vec![0; k as usize];
14307        let mut rust_state = alloc_thread_state(threads).unwrap();
14308        count_suffixes_32s(&t, n, k, &mut rust_buckets);
14309        initialize_buckets_end_32s_1k(k, &mut rust_buckets);
14310        let m = radix_sort_lms_suffixes_32s_1k(&t, &mut rust_sa, n, &mut rust_buckets);
14311        assert!(m > 1);
14312        let mut c_sa = rust_sa.clone();
14313        let mut c_buckets = rust_buckets.clone();
14314        induce_partial_order_32s_1k_omp(
14315            &t,
14316            &mut rust_sa,
14317            n,
14318            k,
14319            &mut rust_buckets,
14320            threads,
14321            &mut rust_state,
14322        );
14323        unsafe {
14324            probe_libsais16x64_induce_partial_order_32s_1k_omp(
14325                t.as_ptr(),
14326                c_sa.as_mut_ptr(),
14327                n,
14328                k,
14329                c_buckets.as_mut_ptr(),
14330                threads,
14331            );
14332        }
14333        assert_eq!(rust_sa, c_sa);
14334        assert_eq!(rust_buckets, c_buckets);
14335    }
14336
14337    #[test]
14338    fn libsais16x64_induce_partial_order_16u_omp_matches_c() {
14339        let text = [3, 1, 2, 1, 0, 4, 1, 0];
14340        let n = text.len() as SaSint;
14341        let flags = 0;
14342        let threads = 1;
14343        let mut rust_sa = vec![0; text.len()];
14344        let mut rust_buckets = vec![0; 8 * ALPHABET_SIZE];
14345
14346        let m = count_and_gather_lms_suffixes_16u_omp(
14347            &text,
14348            &mut rust_sa,
14349            n,
14350            &mut rust_buckets[..4 * ALPHABET_SIZE],
14351            threads,
14352            &mut [],
14353        );
14354        let k = initialize_buckets_start_and_end_16u(&mut rust_buckets, None);
14355        assert!(m > 0);
14356        let first_lms_suffix = rust_sa[(n - m) as usize];
14357        let left_suffixes_count = initialize_buckets_for_lms_suffixes_radix_sort_16u(
14358            &text,
14359            &mut rust_buckets,
14360            first_lms_suffix,
14361        );
14362        radix_sort_lms_suffixes_16u_omp(
14363            &text,
14364            &mut rust_sa,
14365            n,
14366            m,
14367            flags,
14368            &mut rust_buckets,
14369            threads,
14370            &mut [],
14371        );
14372        initialize_buckets_for_partial_sorting_16u(
14373            &text,
14374            &mut rust_buckets,
14375            first_lms_suffix,
14376            left_suffixes_count,
14377        );
14378
14379        let mut c_sa = rust_sa.clone();
14380        let mut c_buckets = rust_buckets.clone();
14381        induce_partial_order_16u_omp(
14382            &text,
14383            &mut rust_sa,
14384            n,
14385            k,
14386            flags,
14387            &mut rust_buckets,
14388            first_lms_suffix,
14389            left_suffixes_count,
14390            threads,
14391        );
14392        unsafe {
14393            probe_libsais16x64_induce_partial_order_16u_omp(
14394                text.as_ptr(),
14395                c_sa.as_mut_ptr(),
14396                n,
14397                k,
14398                flags,
14399                c_buckets.as_mut_ptr(),
14400                first_lms_suffix,
14401                left_suffixes_count,
14402                threads,
14403            );
14404        }
14405
14406        assert_eq!(rust_sa, c_sa);
14407        assert_eq!(rust_buckets, c_buckets);
14408    }
14409
14410    fn final_order_32s_fixture() -> (Vec<SaSint>, Vec<SaSint>) {
14411        (
14412            vec![0, 1, 2, 1, 0, 1, 2, 1, 0],
14413            vec![1, 0, 2, 0, 0, 0, 0, 0, 0],
14414        )
14415    }
14416
14417    fn seed_final_order_bucket_sections(buckets: &mut [SaSint], k: usize, branch_k: usize) {
14418        let left = [0, 1, 3];
14419        let right = [1, 2, 3];
14420        let left_section = match branch_k {
14421            6 => 4 * k,
14422            4 => 2 * k,
14423            2 => k,
14424            _ => 0,
14425        };
14426        let right_section = match branch_k {
14427            6 => 5 * k,
14428            4 => 3 * k,
14429            2 => 0,
14430            _ => 0,
14431        };
14432        buckets[left_section..left_section + k].copy_from_slice(&left);
14433        buckets[right_section..right_section + k].copy_from_slice(&right);
14434    }
14435
14436    #[test]
14437    fn libsais16x64_induce_final_order_32s_wrappers_match_c() {
14438        let (t, sa) = final_order_32s_fixture();
14439        let n = t.len() as SaSint;
14440        let k = 3;
14441        let threads = 1;
14442
14443        let mut rust_sa = sa.clone();
14444        let mut rust_buckets = vec![0; 6 * k as usize];
14445        seed_final_order_bucket_sections(&mut rust_buckets, k as usize, 6);
14446        let mut c_sa = rust_sa.clone();
14447        let mut c_buckets = rust_buckets.clone();
14448        let mut rust_state = alloc_thread_state(threads).unwrap();
14449        induce_final_order_32s_6k(
14450            &t,
14451            &mut rust_sa,
14452            n,
14453            k,
14454            &mut rust_buckets,
14455            threads,
14456            &mut rust_state,
14457        );
14458        unsafe {
14459            probe_libsais16x64_induce_final_order_32s_6k(
14460                t.as_ptr(),
14461                c_sa.as_mut_ptr(),
14462                n,
14463                k,
14464                c_buckets.as_mut_ptr(),
14465                threads,
14466            );
14467        }
14468        assert_eq!(rust_sa, c_sa);
14469        assert_eq!(rust_buckets, c_buckets);
14470
14471        let mut rust_sa = sa.clone();
14472        let mut rust_buckets = vec![0; 4 * k as usize];
14473        seed_final_order_bucket_sections(&mut rust_buckets, k as usize, 4);
14474        let mut c_sa = rust_sa.clone();
14475        let mut c_buckets = rust_buckets.clone();
14476        let mut rust_state = alloc_thread_state(threads).unwrap();
14477        induce_final_order_32s_4k(
14478            &t,
14479            &mut rust_sa,
14480            n,
14481            k,
14482            &mut rust_buckets,
14483            threads,
14484            &mut rust_state,
14485        );
14486        unsafe {
14487            probe_libsais16x64_induce_final_order_32s_4k(
14488                t.as_ptr(),
14489                c_sa.as_mut_ptr(),
14490                n,
14491                k,
14492                c_buckets.as_mut_ptr(),
14493                threads,
14494            );
14495        }
14496        assert_eq!(rust_sa, c_sa);
14497        assert_eq!(rust_buckets, c_buckets);
14498
14499        let mut rust_sa = sa.clone();
14500        let mut rust_buckets = vec![0; 2 * k as usize];
14501        seed_final_order_bucket_sections(&mut rust_buckets, k as usize, 2);
14502        let mut c_sa = rust_sa.clone();
14503        let mut c_buckets = rust_buckets.clone();
14504        let mut rust_state = alloc_thread_state(threads).unwrap();
14505        induce_final_order_32s_2k(
14506            &t,
14507            &mut rust_sa,
14508            n,
14509            k,
14510            &mut rust_buckets,
14511            threads,
14512            &mut rust_state,
14513        );
14514        unsafe {
14515            probe_libsais16x64_induce_final_order_32s_2k(
14516                t.as_ptr(),
14517                c_sa.as_mut_ptr(),
14518                n,
14519                k,
14520                c_buckets.as_mut_ptr(),
14521                threads,
14522            );
14523        }
14524        assert_eq!(rust_sa, c_sa);
14525        assert_eq!(rust_buckets, c_buckets);
14526
14527        let mut rust_sa = sa;
14528        let mut rust_buckets = vec![0; k as usize];
14529        let mut c_sa = rust_sa.clone();
14530        let mut c_buckets = rust_buckets.clone();
14531        let mut rust_state = alloc_thread_state(threads).unwrap();
14532        induce_final_order_32s_1k(
14533            &t,
14534            &mut rust_sa,
14535            n,
14536            k,
14537            &mut rust_buckets,
14538            threads,
14539            &mut rust_state,
14540        );
14541        unsafe {
14542            probe_libsais16x64_induce_final_order_32s_1k(
14543                t.as_ptr(),
14544                c_sa.as_mut_ptr(),
14545                n,
14546                k,
14547                c_buckets.as_mut_ptr(),
14548                threads,
14549            );
14550        }
14551        assert_eq!(rust_sa, c_sa);
14552        assert_eq!(rust_buckets, c_buckets);
14553    }
14554
14555    #[test]
14556    fn libsais16x64_induce_final_order_16u_omp_matches_manual_sequence() {
14557        let (text, mut wrapped_sa, induction_bucket) = final_scan_fixture();
14558        let mut wrapped_buckets = final_order_buckets(&induction_bucket);
14559        let mut c_sa = wrapped_sa.clone();
14560        let mut c_buckets = wrapped_buckets.clone();
14561        let mut wrapped_state = alloc_thread_state(1).unwrap();
14562        let wrapped_index = induce_final_order_16u_omp(
14563            &text,
14564            &mut wrapped_sa,
14565            text.len() as SaSint,
14566            8,
14567            0,
14568            0,
14569            None,
14570            &mut wrapped_buckets,
14571            1,
14572            &mut wrapped_state,
14573        );
14574        let c_index = unsafe {
14575            probe_libsais16x64_induce_final_order_16u_omp(
14576                text.as_ptr(),
14577                c_sa.as_mut_ptr(),
14578                text.len() as SaSint,
14579                8,
14580                0,
14581                0,
14582                std::ptr::null_mut(),
14583                c_buckets.as_mut_ptr(),
14584                1,
14585            )
14586        };
14587
14588        let (text, mut manual_sa, induction_bucket) = final_scan_fixture();
14589        let mut manual_buckets = final_order_buckets(&induction_bucket);
14590        {
14591            let (left_buckets, right_tail) = manual_buckets.split_at_mut(7 * ALPHABET_SIZE);
14592            final_sorting_scan_left_to_right_16u_omp(
14593                &text,
14594                &mut manual_sa,
14595                text.len() as SaSint,
14596                8,
14597                &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE],
14598                1,
14599            );
14600            final_sorting_scan_right_to_left_16u_omp(
14601                &text,
14602                &mut manual_sa,
14603                0,
14604                text.len() as SaSint,
14605                8,
14606                &mut right_tail[..ALPHABET_SIZE],
14607                1,
14608            );
14609        }
14610
14611        assert_eq!(wrapped_index, 0);
14612        assert_eq!(wrapped_index, c_index);
14613        assert_eq!(wrapped_sa, manual_sa);
14614        assert_eq!(wrapped_sa, c_sa);
14615        assert_eq!(wrapped_buckets, manual_buckets);
14616        assert_eq!(wrapped_buckets, c_buckets);
14617
14618        let (text, mut wrapped_sa, induction_bucket) = final_scan_fixture();
14619        let mut wrapped_buckets = final_order_buckets(&induction_bucket);
14620        let mut c_sa = wrapped_sa.clone();
14621        let mut c_buckets = wrapped_buckets.clone();
14622        let mut wrapped_state = alloc_thread_state(1).unwrap();
14623        let wrapped_index = induce_final_order_16u_omp(
14624            &text,
14625            &mut wrapped_sa,
14626            text.len() as SaSint,
14627            8,
14628            LIBSAIS_FLAGS_BWT,
14629            0,
14630            None,
14631            &mut wrapped_buckets,
14632            1,
14633            &mut wrapped_state,
14634        );
14635        let c_index = unsafe {
14636            probe_libsais16x64_induce_final_order_16u_omp(
14637                text.as_ptr(),
14638                c_sa.as_mut_ptr(),
14639                text.len() as SaSint,
14640                8,
14641                LIBSAIS_FLAGS_BWT,
14642                0,
14643                std::ptr::null_mut(),
14644                c_buckets.as_mut_ptr(),
14645                1,
14646            )
14647        };
14648
14649        let (text, mut manual_sa, induction_bucket) = final_scan_fixture();
14650        let mut manual_buckets = final_order_buckets(&induction_bucket);
14651        let manual_index = {
14652            let (left_buckets, right_tail) = manual_buckets.split_at_mut(7 * ALPHABET_SIZE);
14653            final_bwt_scan_left_to_right_16u_omp(
14654                &text,
14655                &mut manual_sa,
14656                text.len() as SaSint,
14657                8,
14658                &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE],
14659                1,
14660            );
14661            final_bwt_scan_right_to_left_16u_omp(
14662                &text,
14663                &mut manual_sa,
14664                text.len() as SaSint,
14665                8,
14666                &mut right_tail[..ALPHABET_SIZE],
14667                1,
14668            )
14669        };
14670
14671        assert_eq!(wrapped_index, manual_index);
14672        assert_eq!(wrapped_index, c_index);
14673        assert_eq!(wrapped_sa, manual_sa);
14674        assert_eq!(wrapped_sa, c_sa);
14675        assert_eq!(wrapped_buckets, manual_buckets);
14676        assert_eq!(wrapped_buckets, c_buckets);
14677
14678        let (text, mut wrapped_sa, induction_bucket) = final_scan_fixture();
14679        let mut wrapped_buckets = final_order_buckets(&induction_bucket);
14680        let mut c_sa = wrapped_sa.clone();
14681        let mut c_buckets = wrapped_buckets.clone();
14682        let mut wrapped_state = alloc_thread_state(1).unwrap();
14683        let mut wrapped_i = vec![-1; 8];
14684        let mut c_i = wrapped_i.clone();
14685        let wrapped_index = induce_final_order_16u_omp(
14686            &text,
14687            &mut wrapped_sa,
14688            text.len() as SaSint,
14689            8,
14690            LIBSAIS_FLAGS_BWT,
14691            2,
14692            Some(&mut wrapped_i),
14693            &mut wrapped_buckets,
14694            1,
14695            &mut wrapped_state,
14696        );
14697        let c_index = unsafe {
14698            probe_libsais16x64_induce_final_order_16u_omp(
14699                text.as_ptr(),
14700                c_sa.as_mut_ptr(),
14701                text.len() as SaSint,
14702                8,
14703                LIBSAIS_FLAGS_BWT,
14704                2,
14705                c_i.as_mut_ptr(),
14706                c_buckets.as_mut_ptr(),
14707                1,
14708            )
14709        };
14710
14711        let (text, mut manual_sa, induction_bucket) = final_scan_fixture();
14712        let mut manual_buckets = final_order_buckets(&induction_bucket);
14713        let mut manual_i = vec![-1; 8];
14714        {
14715            let (left_buckets, right_tail) = manual_buckets.split_at_mut(7 * ALPHABET_SIZE);
14716            final_bwt_aux_scan_left_to_right_16u_omp(
14717                &text,
14718                &mut manual_sa,
14719                text.len() as SaSint,
14720                8,
14721                1,
14722                &mut manual_i,
14723                &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE],
14724                1,
14725            );
14726            final_bwt_aux_scan_right_to_left_16u_omp(
14727                &text,
14728                &mut manual_sa,
14729                text.len() as SaSint,
14730                8,
14731                1,
14732                &mut manual_i,
14733                &mut right_tail[..ALPHABET_SIZE],
14734                1,
14735            );
14736        }
14737
14738        assert_eq!(wrapped_index, 0);
14739        assert_eq!(wrapped_index, c_index);
14740        assert_eq!(wrapped_sa, manual_sa);
14741        assert_eq!(wrapped_sa, c_sa);
14742        assert_eq!(wrapped_buckets, manual_buckets);
14743        assert_eq!(wrapped_buckets, c_buckets);
14744        assert_eq!(wrapped_i, manual_i);
14745        assert_eq!(wrapped_i, c_i);
14746    }
14747
14748    #[test]
14749    fn libsais16x64_main_16u_matches_public_c_suffix_array_paths() {
14750        let text = [3, 1, 4, 1, 5, 9, 0, 2];
14751        let n = text.len() as SaSint;
14752        let fs = 32;
14753        let mut rust_sa = vec![0; text.len() + fs as usize];
14754        let mut rust_buckets = vec![0; 8 * ALPHABET_SIZE];
14755        let mut rust_freq = vec![0; ALPHABET_SIZE];
14756        let mut rust_state = alloc_thread_state(1).unwrap();
14757        let rust_index = main_16u(
14758            &text,
14759            &mut rust_sa,
14760            n,
14761            &mut rust_buckets,
14762            0,
14763            0,
14764            None,
14765            fs,
14766            Some(&mut rust_freq),
14767            1,
14768            &mut rust_state,
14769        );
14770
14771        let mut c_sa = vec![0; text.len() + fs as usize];
14772        let mut c_freq = vec![0; ALPHABET_SIZE];
14773        let c_index = unsafe {
14774            probe_public_libsais16x64_freq(
14775                text.as_ptr(),
14776                c_sa.as_mut_ptr(),
14777                n,
14778                fs,
14779                c_freq.as_mut_ptr(),
14780            )
14781        };
14782
14783        assert_eq!(rust_index, c_index);
14784        assert_eq!(&rust_sa[..text.len()], &c_sa[..text.len()]);
14785        assert_eq!(rust_freq, c_freq);
14786
14787        let text = [2, 1, 0, 2, 0];
14788        let n = text.len() as SaSint;
14789        let fs = 24;
14790        let mut rust_sa = vec![0; text.len() + fs as usize];
14791        let mut rust_buckets = vec![0; 8 * ALPHABET_SIZE];
14792        let mut rust_freq = vec![0; ALPHABET_SIZE];
14793        let mut rust_state = alloc_thread_state(1).unwrap();
14794        let rust_index = main_16u(
14795            &text,
14796            &mut rust_sa,
14797            n,
14798            &mut rust_buckets,
14799            LIBSAIS_FLAGS_GSA,
14800            0,
14801            None,
14802            fs,
14803            Some(&mut rust_freq),
14804            1,
14805            &mut rust_state,
14806        );
14807
14808        let mut c_sa = vec![0; text.len() + fs as usize];
14809        let mut c_freq = vec![0; ALPHABET_SIZE];
14810        let c_index = unsafe {
14811            probe_public_libsais16x64_gsa_freq(
14812                text.as_ptr(),
14813                c_sa.as_mut_ptr(),
14814                n,
14815                fs,
14816                c_freq.as_mut_ptr(),
14817            )
14818        };
14819
14820        assert_eq!(rust_index, c_index);
14821        assert_eq!(&rust_sa[..text.len()], &c_sa[..text.len()]);
14822        assert_eq!(rust_freq, c_freq);
14823    }
14824
14825    #[test]
14826    fn libsais16x64_final_bwt_scan_left_to_right_16u_matches_c() {
14827        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
14828        let mut c_sa = rust_sa.clone();
14829        let mut c_bucket = rust_bucket.clone();
14830
14831        final_bwt_scan_left_to_right_16u(&text, &mut rust_sa, &mut rust_bucket, 0, 6);
14832        unsafe {
14833            probe_libsais16x64_final_bwt_scan_left_to_right_16u(
14834                text.as_ptr(),
14835                c_sa.as_mut_ptr(),
14836                c_bucket.as_mut_ptr(),
14837                0,
14838                6,
14839            );
14840        }
14841
14842        assert_eq!(rust_sa, c_sa);
14843        assert_eq!(rust_bucket, c_bucket);
14844    }
14845
14846    #[test]
14847    fn libsais16x64_final_bwt_scan_right_to_left_16u_matches_c() {
14848        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
14849        let mut c_sa = rust_sa.clone();
14850        let mut c_bucket = rust_bucket.clone();
14851
14852        let rust_index =
14853            final_bwt_scan_right_to_left_16u(&text, &mut rust_sa, &mut rust_bucket, 0, 6);
14854        let c_index = unsafe {
14855            probe_libsais16x64_final_bwt_scan_right_to_left_16u(
14856                text.as_ptr(),
14857                c_sa.as_mut_ptr(),
14858                c_bucket.as_mut_ptr(),
14859                0,
14860                6,
14861            )
14862        };
14863
14864        assert_eq!(rust_index, c_index);
14865        assert_eq!(rust_sa, c_sa);
14866        assert_eq!(rust_bucket, c_bucket);
14867    }
14868
14869    #[test]
14870    fn libsais16x64_final_bwt_aux_scan_left_to_right_16u_matches_c() {
14871        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
14872        let mut c_sa = rust_sa.clone();
14873        let mut c_bucket = rust_bucket.clone();
14874        let mut rust_i = vec![-1; 8];
14875        let mut c_i = rust_i.clone();
14876
14877        final_bwt_aux_scan_left_to_right_16u(
14878            &text,
14879            &mut rust_sa,
14880            1,
14881            &mut rust_i,
14882            &mut rust_bucket,
14883            0,
14884            6,
14885        );
14886        unsafe {
14887            probe_libsais16x64_final_bwt_aux_scan_left_to_right_16u(
14888                text.as_ptr(),
14889                c_sa.as_mut_ptr(),
14890                1,
14891                c_i.as_mut_ptr(),
14892                c_bucket.as_mut_ptr(),
14893                0,
14894                6,
14895            );
14896        }
14897
14898        assert_eq!(rust_sa, c_sa);
14899        assert_eq!(rust_bucket, c_bucket);
14900        assert_eq!(rust_i, c_i);
14901    }
14902
14903    #[test]
14904    fn libsais16x64_final_bwt_aux_scan_right_to_left_16u_matches_c() {
14905        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
14906        let mut c_sa = rust_sa.clone();
14907        let mut c_bucket = rust_bucket.clone();
14908        let mut rust_i = vec![-1; 8];
14909        let mut c_i = rust_i.clone();
14910
14911        final_bwt_aux_scan_right_to_left_16u(
14912            &text,
14913            &mut rust_sa,
14914            1,
14915            &mut rust_i,
14916            &mut rust_bucket,
14917            0,
14918            6,
14919        );
14920        unsafe {
14921            probe_libsais16x64_final_bwt_aux_scan_right_to_left_16u(
14922                text.as_ptr(),
14923                c_sa.as_mut_ptr(),
14924                1,
14925                c_i.as_mut_ptr(),
14926                c_bucket.as_mut_ptr(),
14927                0,
14928                6,
14929            );
14930        }
14931
14932        assert_eq!(rust_sa, c_sa);
14933        assert_eq!(rust_bucket, c_bucket);
14934        assert_eq!(rust_i, c_i);
14935    }
14936
14937    #[test]
14938    fn libsais16x64_renumber_lms_suffixes_16u_matches_c() {
14939        let m = 6;
14940        let mut rust_sa = vec![0; 20];
14941        rust_sa[..m].copy_from_slice(&[2, 4 | SAINT_MIN, 6, 8 | SAINT_MIN, 10, 12 | SAINT_MIN]);
14942        let mut c_sa = rust_sa.clone();
14943
14944        let rust_name = renumber_lms_suffixes_16u(&mut rust_sa, m as SaSint, 5, 0, m as SaSint);
14945        let c_name = unsafe {
14946            probe_libsais16x64_renumber_lms_suffixes_16u(
14947                c_sa.as_mut_ptr(),
14948                m as SaSint,
14949                5,
14950                0,
14951                m as SaSint,
14952            )
14953        };
14954
14955        assert_eq!(rust_name, c_name);
14956        assert_eq!(rust_sa, c_sa);
14957    }
14958
14959    fn lms_interval_fixture() -> (Vec<SaSint>, Vec<SaSint>) {
14960        let mut sa = vec![-7; 16];
14961        sa[4..8].copy_from_slice(&[41, 42, 61, 62]);
14962
14963        let mut buckets = vec![0; 8 * ALPHABET_SIZE];
14964        buckets[buckets_index2(2, 1)] = 0;
14965        buckets[buckets_index2(3, 1)] = 2;
14966        buckets[buckets_index2(4, 1)] = 2;
14967        buckets[buckets_index2(5, 1)] = 2;
14968        buckets[buckets_index2(6, 1)] = 4;
14969        buckets[buckets_index2(7, 1)] = 4;
14970        buckets[7 * ALPHABET_SIZE + 2] = 6;
14971        buckets[7 * ALPHABET_SIZE + 5] = 12;
14972
14973        (sa, buckets)
14974    }
14975
14976    #[test]
14977    fn libsais16x64_place_lms_suffixes_interval_16u_matches_c() {
14978        for flags in [0, LIBSAIS_FLAGS_GSA] {
14979            let (mut rust_sa, mut rust_buckets) = lms_interval_fixture();
14980            let mut c_sa = rust_sa.clone();
14981            let mut c_buckets = rust_buckets.clone();
14982
14983            place_lms_suffixes_interval_16u(&mut rust_sa, 16, 8, flags, &mut rust_buckets);
14984            unsafe {
14985                probe_libsais16x64_place_lms_suffixes_interval_16u(
14986                    c_sa.as_mut_ptr(),
14987                    16,
14988                    8,
14989                    flags,
14990                    c_buckets.as_mut_ptr(),
14991                );
14992            }
14993
14994            assert_eq!(rust_sa, c_sa);
14995            assert_eq!(rust_buckets, c_buckets);
14996        }
14997    }
14998
14999    #[test]
15000    fn libsais16x64_bwt_copy_16u_matches_c() {
15001        let mut a = vec![0, 1, 65535, 65536, -1, -2, 70000, 17, 131071, -65536];
15002        let mut rust_u = vec![999; a.len()];
15003        let mut c_u = rust_u.clone();
15004
15005        bwt_copy_16u(&mut rust_u, &a, a.len() as SaSint);
15006        unsafe {
15007            probe_libsais16x64_bwt_copy_16u(c_u.as_mut_ptr(), a.as_mut_ptr(), a.len() as SaSint);
15008        }
15009
15010        assert_eq!(rust_u, c_u);
15011    }
15012
15013    #[test]
15014    fn libsais16x64_early_omp_wrappers_match_c() {
15015        let text = [3, 1, 2, 1, 0, 4, 1, 0];
15016        let n = text.len() as SaSint;
15017
15018        let mut rust_sa = vec![-99; text.len()];
15019        let mut c_sa = rust_sa.clone();
15020        gather_lms_suffixes_16u_omp(&text, &mut rust_sa, n, 1, &mut []);
15021        unsafe {
15022            probe_libsais16x64_gather_lms_suffixes_16u_omp(text.as_ptr(), c_sa.as_mut_ptr(), n, 1);
15023        }
15024        assert_eq!(rust_sa, c_sa);
15025
15026        let mut rust_sa = vec![-99; text.len()];
15027        let mut c_sa = rust_sa.clone();
15028        let mut rust_buckets = vec![-1; 4 * ALPHABET_SIZE];
15029        let mut c_buckets = rust_buckets.clone();
15030        let rust_m = count_and_gather_lms_suffixes_16u_omp(
15031            &text,
15032            &mut rust_sa,
15033            n,
15034            &mut rust_buckets,
15035            1,
15036            &mut [],
15037        );
15038        let c_m = unsafe {
15039            probe_libsais16x64_count_and_gather_lms_suffixes_16u_omp(
15040                text.as_ptr(),
15041                c_sa.as_mut_ptr(),
15042                n,
15043                c_buckets.as_mut_ptr(),
15044                1,
15045            )
15046        };
15047        assert_eq!(rust_m, c_m);
15048        assert_eq!(rust_sa, c_sa);
15049        assert_eq!(rust_buckets, c_buckets);
15050
15051        let mut rust_buckets = vec![0; 8 * ALPHABET_SIZE];
15052        let m = count_and_gather_lms_suffixes_16u(
15053            &text,
15054            &mut rust_sa,
15055            n,
15056            &mut rust_buckets[..4 * ALPHABET_SIZE],
15057            0,
15058            n,
15059        );
15060        initialize_buckets_start_and_end_16u(&mut rust_buckets, None);
15061        let first_lms_suffix = rust_sa[(n - m) as usize];
15062        initialize_buckets_for_lms_suffixes_radix_sort_16u(
15063            &text,
15064            &mut rust_buckets,
15065            first_lms_suffix,
15066        );
15067        let mut c_sa = rust_sa.clone();
15068        let mut c_buckets = rust_buckets.clone();
15069        radix_sort_lms_suffixes_16u_omp(
15070            &text,
15071            &mut rust_sa,
15072            n,
15073            m,
15074            0,
15075            &mut rust_buckets,
15076            1,
15077            &mut [],
15078        );
15079        unsafe {
15080            probe_libsais16x64_radix_sort_lms_suffixes_16u_omp(
15081                text.as_ptr(),
15082                c_sa.as_mut_ptr(),
15083                n,
15084                m,
15085                0,
15086                c_buckets.as_mut_ptr(),
15087                1,
15088            );
15089        }
15090        assert_eq!(rust_sa, c_sa);
15091        assert_eq!(rust_buckets, c_buckets);
15092    }
15093
15094    #[test]
15095    fn libsais16x64_early_omp_wrappers_use_block_partition_for_large_inputs() {
15096        let n = 65_600usize;
15097        let text: Vec<u16> = (0..n)
15098            .map(|i| 1 + ((i * 37 + i / 17) % 509) as u16)
15099            .collect();
15100
15101        let mut gathered_threaded = vec![-99; n];
15102        let mut gathered_scalar = vec![-99; n];
15103        let mut thread_state = alloc_thread_state(4).unwrap();
15104        let mut count_sa = vec![-99; n];
15105        let mut count_buckets = vec![0; 4 * ALPHABET_SIZE];
15106        count_and_gather_lms_suffixes_16u_omp(
15107            &text,
15108            &mut count_sa,
15109            n as SaSint,
15110            &mut count_buckets,
15111            4,
15112            &mut thread_state,
15113        );
15114        gather_lms_suffixes_16u_omp(
15115            &text,
15116            &mut gathered_threaded,
15117            n as SaSint,
15118            4,
15119            &mut thread_state,
15120        );
15121        gather_lms_suffixes_16u(
15122            &text,
15123            &mut gathered_scalar,
15124            n as SaSint,
15125            n as SaSint - 1,
15126            0,
15127            n as SaSint,
15128        );
15129        assert_eq!(gathered_threaded, gathered_scalar);
15130
15131        let mut sa_threaded = vec![-99; n];
15132        let mut sa_scalar = vec![-99; n];
15133        let mut buckets_threaded = vec![0; 4 * ALPHABET_SIZE];
15134        let mut buckets_scalar = vec![0; 4 * ALPHABET_SIZE];
15135        let m_threaded = count_and_gather_lms_suffixes_16u_omp(
15136            &text,
15137            &mut sa_threaded,
15138            n as SaSint,
15139            &mut buckets_threaded,
15140            4,
15141            &mut thread_state,
15142        );
15143        let m_scalar = count_and_gather_lms_suffixes_16u(
15144            &text,
15145            &mut sa_scalar,
15146            n as SaSint,
15147            &mut buckets_scalar,
15148            0,
15149            n as SaSint,
15150        );
15151        assert_eq!(m_threaded, m_scalar);
15152        assert_eq!(
15153            &sa_threaded[n - m_threaded as usize..],
15154            &sa_scalar[n - m_scalar as usize..]
15155        );
15156        assert_eq!(buckets_threaded, buckets_scalar);
15157    }
15158
15159    #[test]
15160    fn libsais16x64_late_omp_wrappers_match_c() {
15161        let m = 6;
15162        let mut rust_sa = vec![0; 20];
15163        rust_sa[..m].copy_from_slice(&[2, 4 | SAINT_MIN, 6, 8 | SAINT_MIN, 10, 12 | SAINT_MIN]);
15164        let mut c_sa = rust_sa.clone();
15165        let mut rust_thread_state = alloc_thread_state(1).unwrap();
15166        let rust_name =
15167            renumber_lms_suffixes_16u_omp(&mut rust_sa, m as SaSint, 1, &mut rust_thread_state);
15168        let c_name = unsafe {
15169            probe_libsais16x64_renumber_lms_suffixes_16u_omp(c_sa.as_mut_ptr(), m as SaSint, 1)
15170        };
15171        assert_eq!(rust_name, c_name);
15172        assert_eq!(rust_sa, c_sa);
15173
15174        let mut a = vec![0, 1, 65535, 65536, -1, -2, 70000, 17, 131071, -65536];
15175        let mut rust_u = vec![999; a.len()];
15176        let mut c_u = rust_u.clone();
15177        bwt_copy_16u_omp(&mut rust_u, &a, a.len() as SaSint, 1);
15178        unsafe {
15179            probe_libsais16x64_bwt_copy_16u_omp(
15180                c_u.as_mut_ptr(),
15181                a.as_mut_ptr(),
15182                a.len() as SaSint,
15183                1,
15184            );
15185        }
15186        assert_eq!(rust_u, c_u);
15187    }
15188
15189    #[test]
15190    fn libsais16x64_gather_marked_lms_suffixes_matches_c() {
15191        let mut rust_sa = vec![0, 0, 3 | SAINT_MIN, 4, 5 | SAINT_MIN, 6, -7, 8];
15192        let mut c_sa = rust_sa.clone();
15193
15194        let rust_l = gather_marked_lms_suffixes(&mut rust_sa, 2, 8, 0, 4) as SaSint;
15195        let c_l =
15196            unsafe { probe_libsais16x64_gather_marked_lms_suffixes(c_sa.as_mut_ptr(), 2, 8, 0, 4) };
15197
15198        assert_eq!(rust_l, c_l);
15199        assert_eq!(rust_sa, c_sa);
15200    }
15201
15202    #[test]
15203    fn libsais16x64_gather_marked_lms_suffixes_omp_matches_c() {
15204        let mut rust_sa = vec![0; 10];
15205        rust_sa[4..8].copy_from_slice(&[2 | SAINT_MIN, 4, 6 | SAINT_MIN, 8]);
15206        let mut c_sa = rust_sa.clone();
15207
15208        let mut rust_thread_state = alloc_thread_state(1).unwrap();
15209        gather_marked_lms_suffixes_omp(&mut rust_sa, 8, 4, 2, 1, &mut rust_thread_state);
15210        unsafe {
15211            probe_libsais16x64_gather_marked_lms_suffixes_omp(c_sa.as_mut_ptr(), 8, 4, 2, 1);
15212        }
15213
15214        assert_eq!(rust_sa, c_sa);
15215    }
15216
15217    #[test]
15218    fn libsais16x64_renumber_and_gather_lms_suffixes_omp_matches_c() {
15219        let mut rust_sa = vec![0; 10];
15220        rust_sa[..4].copy_from_slice(&[2, 4 | SAINT_MIN, 6, 8 | SAINT_MIN]);
15221        let mut c_sa = rust_sa.clone();
15222
15223        let mut rust_thread_state = alloc_thread_state(1).unwrap();
15224        let rust_name =
15225            renumber_and_gather_lms_suffixes_omp(&mut rust_sa, 8, 4, 2, 1, &mut rust_thread_state);
15226        let c_name = unsafe {
15227            probe_libsais16x64_renumber_and_gather_lms_suffixes_omp(c_sa.as_mut_ptr(), 8, 4, 2, 1)
15228        };
15229
15230        assert_eq!(rust_name, c_name);
15231        assert_eq!(rust_sa, c_sa);
15232    }
15233
15234    #[test]
15235    fn libsais16x64_reconstruct_lms_suffixes_matches_c() {
15236        let mut rust_sa = vec![2, 0, 1, 77, 88, 10, 11, 12];
15237        let mut c_sa = rust_sa.clone();
15238
15239        reconstruct_lms_suffixes(&mut rust_sa, 8, 3, 0, 3);
15240        unsafe {
15241            probe_libsais16x64_reconstruct_lms_suffixes(c_sa.as_mut_ptr(), 8, 3, 0, 3);
15242        }
15243
15244        assert_eq!(rust_sa, c_sa);
15245
15246        let mut rust_sa = vec![2, 0, 1, 77, 88, 10, 11, 12];
15247        let mut c_sa = rust_sa.clone();
15248        reconstruct_lms_suffixes_omp(&mut rust_sa, 8, 3, 1);
15249        unsafe {
15250            probe_libsais16x64_reconstruct_lms_suffixes_omp(c_sa.as_mut_ptr(), 8, 3, 1);
15251        }
15252
15253        assert_eq!(rust_sa, c_sa);
15254    }
15255
15256    #[test]
15257    fn libsais16x64_lms_late_omp_wrappers_use_block_partition() {
15258        let m = 65_536usize;
15259        let mut scalar = vec![0; 2 * m + 8];
15260        for i in 0..m {
15261            let value = (2 * i) as SaSint;
15262            scalar[i] = if i % 7 == 0 { value | SAINT_MIN } else { value };
15263        }
15264        let mut threaded = scalar.clone();
15265
15266        let mut scalar_state = alloc_thread_state(1).unwrap();
15267        let mut threaded_state = alloc_thread_state(4).unwrap();
15268        let scalar_name =
15269            renumber_lms_suffixes_16u_omp(&mut scalar, m as SaSint, 1, &mut scalar_state);
15270        let threaded_name =
15271            renumber_lms_suffixes_16u_omp(&mut threaded, m as SaSint, 4, &mut threaded_state);
15272        assert_eq!(threaded_name, scalar_name);
15273        assert_eq!(threaded, scalar);
15274
15275        let n = 131_072usize;
15276        let m = 65_536usize;
15277        let fs = 128usize;
15278        let mut scalar = vec![0; n + fs];
15279        for i in 0..(n >> 1) {
15280            let value = (i as SaSint + 1) & SAINT_MAX;
15281            scalar[m + i] = if i % 7 == 0 { value | SAINT_MIN } else { value };
15282        }
15283        let marked_count = (0..(n >> 1)).filter(|i| i % 7 == 0).count();
15284        let mut threaded = scalar.clone();
15285
15286        let mut scalar_state = alloc_thread_state(1).unwrap();
15287        let mut threaded_state = alloc_thread_state(4).unwrap();
15288        gather_marked_lms_suffixes_omp(
15289            &mut scalar,
15290            n as SaSint,
15291            m as SaSint,
15292            fs as SaSint,
15293            1,
15294            &mut scalar_state,
15295        );
15296        gather_marked_lms_suffixes_omp(
15297            &mut threaded,
15298            n as SaSint,
15299            m as SaSint,
15300            fs as SaSint,
15301            4,
15302            &mut threaded_state,
15303        );
15304        assert_eq!(
15305            &threaded[n + fs - marked_count..n + fs],
15306            &scalar[n + fs - marked_count..n + fs]
15307        );
15308
15309        let m = 65_536usize;
15310        let n = 2 * m;
15311        let mut scalar = vec![0; n];
15312        for i in 0..m {
15313            scalar[i] = i as SaSint;
15314            scalar[n - m + i] = 1_000_000 + i as SaSint;
15315        }
15316        let mut threaded = scalar.clone();
15317
15318        reconstruct_lms_suffixes_omp(&mut scalar, n as SaSint, m as SaSint, 1);
15319        reconstruct_lms_suffixes_omp(&mut threaded, n as SaSint, m as SaSint, 4);
15320        assert_eq!(threaded, scalar);
15321    }
15322
15323    #[test]
15324    fn libsais16x64_distinct_lms_helpers_match_c() {
15325        let m = 6;
15326        let mut rust_sa = vec![0; 18];
15327        rust_sa[..m].copy_from_slice(&[
15328            2 | SAINT_MIN,
15329            4 | SAINT_MIN,
15330            6,
15331            8 | SAINT_MIN,
15332            10,
15333            12 | SAINT_MIN,
15334        ]);
15335        let mut c_sa = rust_sa.clone();
15336        let rust_name =
15337            renumber_distinct_lms_suffixes_32s_4k(&mut rust_sa, m as SaSint, 1, 0, m as isize);
15338        let c_name = unsafe {
15339            probe_libsais16x64_renumber_distinct_lms_suffixes_32s_4k(
15340                c_sa.as_mut_ptr(),
15341                m as SaSint,
15342                1,
15343                0,
15344                m as SaSint,
15345            )
15346        };
15347        assert_eq!(rust_name, c_name);
15348        assert_eq!(rust_sa, c_sa);
15349
15350        let mut rust_sa = vec![0; 12];
15351        rust_sa[m..m + 6].copy_from_slice(&[SAINT_MIN | 1, 0, SAINT_MIN | 2, 0, 3, 0]);
15352        let mut c_sa = rust_sa.clone();
15353        mark_distinct_lms_suffixes_32s(&mut rust_sa, m as SaSint, 0, 6);
15354        unsafe {
15355            probe_libsais16x64_mark_distinct_lms_suffixes_32s(c_sa.as_mut_ptr(), m as SaSint, 0, 6);
15356        }
15357        assert_eq!(rust_sa, c_sa);
15358
15359        let mut rust_sa = vec![0; 12];
15360        rust_sa[m..m + 6].copy_from_slice(&[SAINT_MIN | 1, 7, SAINT_MIN | 2, 0, -5, 9]);
15361        let mut c_sa = rust_sa.clone();
15362        clamp_lms_suffixes_length_32s(&mut rust_sa, m as SaSint, 0, 6);
15363        unsafe {
15364            probe_libsais16x64_clamp_lms_suffixes_length_32s(c_sa.as_mut_ptr(), m as SaSint, 0, 6);
15365        }
15366        assert_eq!(rust_sa, c_sa);
15367    }
15368
15369    #[test]
15370    fn libsais16x64_distinct_lms_omp_wrappers_match_c() {
15371        let n = 12;
15372        let m = 6;
15373        let mut rust_sa = vec![0; 18];
15374        rust_sa[..m].copy_from_slice(&[
15375            2 | SAINT_MIN,
15376            4 | SAINT_MIN,
15377            6,
15378            8 | SAINT_MIN,
15379            10,
15380            12 | SAINT_MIN,
15381        ]);
15382        let mut c_sa = rust_sa.clone();
15383        let mut rust_thread_state = alloc_thread_state(1).unwrap();
15384        let rust_name = renumber_distinct_lms_suffixes_32s_4k_omp(
15385            &mut rust_sa,
15386            m as SaSint,
15387            1,
15388            &mut rust_thread_state,
15389        );
15390        let c_name = unsafe {
15391            probe_libsais16x64_renumber_distinct_lms_suffixes_32s_4k_omp(
15392                c_sa.as_mut_ptr(),
15393                m as SaSint,
15394                1,
15395            )
15396        };
15397        assert_eq!(rust_name, c_name);
15398        assert_eq!(rust_sa, c_sa);
15399
15400        let mut rust_sa = vec![0; 18];
15401        rust_sa[m..m + 6].copy_from_slice(&[SAINT_MIN | 1, 0, SAINT_MIN | 2, 0, 3, 0]);
15402        let mut c_sa = rust_sa.clone();
15403        mark_distinct_lms_suffixes_32s_omp(&mut rust_sa, n, m as SaSint, 1);
15404        unsafe {
15405            probe_libsais16x64_mark_distinct_lms_suffixes_32s_omp(
15406                c_sa.as_mut_ptr(),
15407                n,
15408                m as SaSint,
15409                1,
15410            );
15411        }
15412        assert_eq!(rust_sa, c_sa);
15413
15414        let mut rust_sa = vec![0; 18];
15415        rust_sa[m..m + 6].copy_from_slice(&[SAINT_MIN | 1, 7, SAINT_MIN | 2, 0, -5, 9]);
15416        let mut c_sa = rust_sa.clone();
15417        clamp_lms_suffixes_length_32s_omp(&mut rust_sa, n, m as SaSint, 1);
15418        unsafe {
15419            probe_libsais16x64_clamp_lms_suffixes_length_32s_omp(
15420                c_sa.as_mut_ptr(),
15421                n,
15422                m as SaSint,
15423                1,
15424            );
15425        }
15426        assert_eq!(rust_sa, c_sa);
15427
15428        let mut rust_sa = vec![0; 18];
15429        rust_sa[..m].copy_from_slice(&[
15430            2 | SAINT_MIN,
15431            4 | SAINT_MIN,
15432            6,
15433            8 | SAINT_MIN,
15434            10,
15435            12 | SAINT_MIN,
15436        ]);
15437        let mut c_sa = rust_sa.clone();
15438        let mut rust_thread_state = alloc_thread_state(1).unwrap();
15439        let rust_name = renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
15440            &mut rust_sa,
15441            n,
15442            m as SaSint,
15443            1,
15444            &mut rust_thread_state,
15445        );
15446        let c_name = unsafe {
15447            probe_libsais16x64_renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
15448                c_sa.as_mut_ptr(),
15449                n,
15450                m as SaSint,
15451                1,
15452            )
15453        };
15454        assert_eq!(rust_name, c_name);
15455        assert_eq!(rust_sa, c_sa);
15456    }
15457
15458    #[test]
15459    fn libsais16x64_distinct_lms_omp_wrappers_use_block_partition() {
15460        let m = 65_536usize;
15461        let mut scalar = vec![0; 2 * m];
15462        for i in 0..m {
15463            let value = (2 * i) as SaSint;
15464            scalar[i] = if i % 7 == 0 { value | SAINT_MIN } else { value };
15465        }
15466        let mut threaded = scalar.clone();
15467
15468        let mut scalar_state = alloc_thread_state(1).unwrap();
15469        let mut threaded_state = alloc_thread_state(4).unwrap();
15470        let scalar_name = renumber_distinct_lms_suffixes_32s_4k_omp(
15471            &mut scalar,
15472            m as SaSint,
15473            1,
15474            &mut scalar_state,
15475        );
15476        let threaded_name = renumber_distinct_lms_suffixes_32s_4k_omp(
15477            &mut threaded,
15478            m as SaSint,
15479            4,
15480            &mut threaded_state,
15481        );
15482        assert_eq!(threaded_name, scalar_name);
15483        assert_eq!(threaded, scalar);
15484
15485        let n = 131_072usize;
15486        let m = 65_536usize;
15487        let mut scalar = vec![0; n];
15488        for i in 0..(n >> 1) {
15489            scalar[m + i] = if i % 5 == 0 {
15490                SAINT_MIN | (i as SaSint + 1)
15491            } else if i % 11 == 0 {
15492                0
15493            } else {
15494                i as SaSint + 1
15495            };
15496        }
15497        let mut threaded = scalar.clone();
15498        mark_distinct_lms_suffixes_32s_omp(&mut scalar, n as SaSint, m as SaSint, 1);
15499        mark_distinct_lms_suffixes_32s_omp(&mut threaded, n as SaSint, m as SaSint, 4);
15500        assert_eq!(&threaded[m..n], &scalar[m..n]);
15501
15502        let mut scalar = vec![0; n];
15503        for i in 0..(n >> 1) {
15504            scalar[m + i] = if i % 5 == 0 {
15505                SAINT_MIN | (i as SaSint + 1)
15506            } else {
15507                i as SaSint + 1
15508            };
15509        }
15510        let mut threaded = scalar.clone();
15511        clamp_lms_suffixes_length_32s_omp(&mut scalar, n as SaSint, m as SaSint, 1);
15512        clamp_lms_suffixes_length_32s_omp(&mut threaded, n as SaSint, m as SaSint, 4);
15513        assert_eq!(&threaded[m..n], &scalar[m..n]);
15514    }
15515
15516    #[test]
15517    fn libsais16x64_unique_nonunique_lms_helpers_match_c() {
15518        let m = 4;
15519        let mut rust_t = vec![0; 12];
15520        let mut rust_sa = vec![0; 12];
15521        rust_sa[..m].copy_from_slice(&[2, 4, 6, 8]);
15522        rust_sa[m + 1] = SAINT_MIN | 11;
15523        rust_sa[m + 2] = 22;
15524        rust_sa[m + 3] = SAINT_MIN | 33;
15525        rust_sa[m + 4] = 44;
15526        let mut c_t = rust_t.clone();
15527        let mut c_sa = rust_sa.clone();
15528
15529        let rust_f = renumber_unique_and_nonunique_lms_suffixes_32s(
15530            &mut rust_t,
15531            &mut rust_sa,
15532            m as SaSint,
15533            0,
15534            0,
15535            m as isize,
15536        );
15537        let c_f = unsafe {
15538            probe_libsais16x64_renumber_unique_and_nonunique_lms_suffixes_32s(
15539                c_t.as_mut_ptr(),
15540                c_sa.as_mut_ptr(),
15541                m as SaSint,
15542                0,
15543                0,
15544                m as SaSint,
15545            )
15546        };
15547        assert_eq!(rust_f, c_f);
15548        assert_eq!(rust_t, c_t);
15549        assert_eq!(rust_sa, c_sa);
15550
15551        let mut rust_sa = vec![0; 10];
15552        rust_sa[m..m + 4].copy_from_slice(&[SAINT_MIN | 3, 4, SAINT_MIN | 5, 6]);
15553        let mut c_sa = rust_sa.clone();
15554        let mut rust_l = m as isize;
15555        let mut rust_r = 10isize;
15556        let mut c_l = rust_l as SaSint;
15557        let mut c_r = rust_r as SaSint;
15558        compact_unique_and_nonunique_lms_suffixes_32s(
15559            &mut rust_sa,
15560            m as SaSint,
15561            &mut rust_l,
15562            &mut rust_r,
15563            0,
15564            4,
15565        );
15566        unsafe {
15567            probe_libsais16x64_compact_unique_and_nonunique_lms_suffixes_32s(
15568                c_sa.as_mut_ptr(),
15569                m as SaSint,
15570                &mut c_l,
15571                &mut c_r,
15572                0,
15573                4,
15574            );
15575        }
15576        assert_eq!(rust_l as SaSint, c_l);
15577        assert_eq!(rust_r as SaSint, c_r);
15578        assert_eq!(rust_sa, c_sa);
15579    }
15580
15581    #[test]
15582    fn libsais16x64_unique_nonunique_lms_omp_wrappers_match_c() {
15583        let n = 8;
15584        let m = 4;
15585        let fs = 4;
15586        let mut rust_t = vec![0; 12];
15587        let mut rust_sa = vec![0; 12];
15588        rust_sa[..m].copy_from_slice(&[2, 4, 6, 8]);
15589        rust_sa[m + 1] = SAINT_MIN | 11;
15590        rust_sa[m + 2] = 22;
15591        rust_sa[m + 3] = SAINT_MIN | 33;
15592        rust_sa[m + 4] = 44;
15593        let mut c_t = rust_t.clone();
15594        let mut c_sa = rust_sa.clone();
15595
15596        let rust_f = renumber_unique_and_nonunique_lms_suffixes_32s_omp(
15597            &mut rust_t,
15598            &mut rust_sa,
15599            m as SaSint,
15600            1,
15601        );
15602        let c_f = unsafe {
15603            probe_libsais16x64_renumber_unique_and_nonunique_lms_suffixes_32s_omp(
15604                c_t.as_mut_ptr(),
15605                c_sa.as_mut_ptr(),
15606                m as SaSint,
15607                1,
15608            )
15609        };
15610        assert_eq!(rust_f, c_f);
15611        assert_eq!(rust_t, c_t);
15612        assert_eq!(rust_sa, c_sa);
15613
15614        let mut rust_sa = vec![0; 12];
15615        rust_sa[m..m + 4].copy_from_slice(&[SAINT_MIN | 3, 4, SAINT_MIN | 5, 6]);
15616        rust_sa[m - 2..m].copy_from_slice(&[101, 102]);
15617        let mut c_sa = rust_sa.clone();
15618        compact_unique_and_nonunique_lms_suffixes_32s_omp(&mut rust_sa, n, m as SaSint, fs, 2, 1);
15619        unsafe {
15620            probe_libsais16x64_compact_unique_and_nonunique_lms_suffixes_32s_omp(
15621                c_sa.as_mut_ptr(),
15622                n,
15623                m as SaSint,
15624                fs,
15625                2,
15626                1,
15627            );
15628        }
15629        assert_eq!(rust_sa, c_sa);
15630
15631        let mut rust_t = vec![0; 12];
15632        let mut rust_sa = vec![0; 12];
15633        rust_sa[..m].copy_from_slice(&[2, 4, 6, 8]);
15634        rust_sa[m + 1] = SAINT_MIN | 11;
15635        rust_sa[m + 2] = 22;
15636        rust_sa[m + 3] = SAINT_MIN | 33;
15637        rust_sa[m + 4] = 44;
15638        let mut c_t = rust_t.clone();
15639        let mut c_sa = rust_sa.clone();
15640        let rust_f = compact_lms_suffixes_32s_omp(&mut rust_t, &mut rust_sa, n, m as SaSint, fs, 1);
15641        let c_f = unsafe {
15642            probe_libsais16x64_compact_lms_suffixes_32s_omp(
15643                c_t.as_mut_ptr(),
15644                c_sa.as_mut_ptr(),
15645                n,
15646                m as SaSint,
15647                fs,
15648                1,
15649            )
15650        };
15651        assert_eq!(rust_f, c_f);
15652        assert_eq!(rust_t, c_t);
15653        assert_eq!(rust_sa, c_sa);
15654    }
15655
15656    #[test]
15657    fn libsais16x64_unique_nonunique_lms_omp_wrappers_use_block_partition() {
15658        let m = 65_536usize;
15659        let mut scalar_t = vec![0; 2 * m];
15660        let mut scalar_sa = vec![0; 2 * m];
15661        for i in 0..m {
15662            scalar_sa[i] = (2 * i) as SaSint;
15663            scalar_sa[m + i] = if i % 5 == 0 {
15664                SAINT_MIN | (i as SaSint + 3)
15665            } else {
15666                i as SaSint + 3
15667            };
15668        }
15669        let mut threaded_t = scalar_t.clone();
15670        let mut threaded_sa = scalar_sa.clone();
15671
15672        let scalar_f = renumber_unique_and_nonunique_lms_suffixes_32s_omp(
15673            &mut scalar_t,
15674            &mut scalar_sa,
15675            m as SaSint,
15676            1,
15677        );
15678        let threaded_f = renumber_unique_and_nonunique_lms_suffixes_32s_omp(
15679            &mut threaded_t,
15680            &mut threaded_sa,
15681            m as SaSint,
15682            4,
15683        );
15684        assert_eq!(threaded_f, scalar_f);
15685        assert_eq!(threaded_t, scalar_t);
15686        assert_eq!(threaded_sa, scalar_sa);
15687
15688        let n = 131_072usize;
15689        let m = 4_096usize;
15690        let fs = 8_192usize;
15691        let mut scalar_sa = vec![0; n + fs];
15692        for i in 0..(n >> 1) {
15693            scalar_sa[m + i] = if i % 32 == 0 {
15694                SAINT_MIN | (i as SaSint + 1)
15695            } else {
15696                i as SaSint + 1
15697            };
15698        }
15699        let f = 1_024usize;
15700        for i in 0..f {
15701            scalar_sa[m - f + i] = 1_000_000 + i as SaSint;
15702        }
15703        let mut threaded_sa = scalar_sa.clone();
15704
15705        compact_unique_and_nonunique_lms_suffixes_32s_omp(
15706            &mut scalar_sa,
15707            n as SaSint,
15708            m as SaSint,
15709            fs as SaSint,
15710            f as SaSint,
15711            1,
15712        );
15713        compact_unique_and_nonunique_lms_suffixes_32s_omp(
15714            &mut threaded_sa,
15715            n as SaSint,
15716            m as SaSint,
15717            fs as SaSint,
15718            f as SaSint,
15719            4,
15720        );
15721        assert_eq!(&threaded_sa[..m], &scalar_sa[..m]);
15722        assert_eq!(
15723            &threaded_sa[n + fs - m..n + fs],
15724            &scalar_sa[n + fs - m..n + fs]
15725        );
15726    }
15727
15728    #[test]
15729    fn libsais16x64_merge_lms_helpers_match_c() {
15730        let n = 10;
15731        let m = 3;
15732        let mut rust_t = vec![0; n as usize];
15733        rust_t[1] = SAINT_MIN | 11;
15734        rust_t[3] = SAINT_MIN | 22;
15735        rust_t[7] = SAINT_MIN | 33;
15736        let mut rust_sa = vec![0; n as usize];
15737        rust_sa[6..10].copy_from_slice(&[2, 5, 8, 9]);
15738        let mut c_t = rust_t.clone();
15739        let mut c_sa = rust_sa.clone();
15740        merge_unique_lms_suffixes_32s(&mut rust_t, &mut rust_sa, n, m, 0, 0, n as isize);
15741        unsafe {
15742            probe_libsais16x64_merge_unique_lms_suffixes_32s(
15743                c_t.as_mut_ptr(),
15744                c_sa.as_mut_ptr(),
15745                n,
15746                m,
15747                0,
15748                0,
15749                n,
15750            );
15751        }
15752        assert_eq!(rust_t, c_t);
15753        assert_eq!(rust_sa, c_sa);
15754
15755        let n = 10;
15756        let m = 5;
15757        let mut rust_sa = vec![9, 0, 8, 0, 0, 7, 31, 32, 33, 34];
15758        let mut c_sa = rust_sa.clone();
15759        merge_nonunique_lms_suffixes_32s(&mut rust_sa, n, m, 2, 0, m as isize);
15760        unsafe {
15761            probe_libsais16x64_merge_nonunique_lms_suffixes_32s(c_sa.as_mut_ptr(), n, m, 2, 0, m);
15762        }
15763        assert_eq!(rust_sa, c_sa);
15764    }
15765
15766    #[test]
15767    fn libsais16x64_merge_lms_omp_wrappers_match_c() {
15768        let n = 12;
15769        let m = 4;
15770        let f = 2;
15771        let mut rust_t = vec![0; n as usize];
15772        rust_t[1] = SAINT_MIN | 11;
15773        rust_t[5] = SAINT_MIN | 22;
15774        let mut rust_sa = vec![0; n as usize];
15775        rust_sa[1] = 41;
15776        rust_sa[7..12].copy_from_slice(&[2, 6, 21, 22, 23]);
15777        let mut c_t = rust_t.clone();
15778        let mut c_sa = rust_sa.clone();
15779        merge_unique_lms_suffixes_32s_omp(&mut rust_t, &mut rust_sa, n, m, 1);
15780        unsafe {
15781            probe_libsais16x64_merge_unique_lms_suffixes_32s_omp(
15782                c_t.as_mut_ptr(),
15783                c_sa.as_mut_ptr(),
15784                n,
15785                m,
15786                1,
15787            );
15788        }
15789        assert_eq!(rust_t, c_t);
15790        assert_eq!(rust_sa, c_sa);
15791
15792        let mut rust_sa = vec![0, 41, 1, 0, 55, 66, 77, 2, 6, 21, 22, 23];
15793        let mut c_sa = rust_sa.clone();
15794        merge_nonunique_lms_suffixes_32s_omp(&mut rust_sa, n, m, f, 1);
15795        unsafe {
15796            probe_libsais16x64_merge_nonunique_lms_suffixes_32s_omp(c_sa.as_mut_ptr(), n, m, f, 1);
15797        }
15798        assert_eq!(rust_sa, c_sa);
15799
15800        let mut rust_t = vec![0; n as usize];
15801        rust_t[1] = SAINT_MIN | 11;
15802        rust_t[5] = SAINT_MIN | 22;
15803        let mut rust_sa = vec![0; n as usize];
15804        rust_sa[1] = 41;
15805        rust_sa[7..12].copy_from_slice(&[2, 6, 21, 22, 23]);
15806        let mut c_t = rust_t.clone();
15807        let mut c_sa = rust_sa.clone();
15808        merge_compacted_lms_suffixes_32s_omp(&mut rust_t, &mut rust_sa, n, m, f, 1);
15809        unsafe {
15810            probe_libsais16x64_merge_compacted_lms_suffixes_32s_omp(
15811                c_t.as_mut_ptr(),
15812                c_sa.as_mut_ptr(),
15813                n,
15814                m,
15815                f,
15816                1,
15817            );
15818        }
15819        assert_eq!(rust_t, c_t);
15820        assert_eq!(rust_sa, c_sa);
15821    }
15822
15823    #[test]
15824    fn libsais16x64_merge_lms_omp_wrappers_use_block_partition() {
15825        let n = 65_536usize;
15826        let m = 10_000usize;
15827        let mut scalar_t = vec![0; n];
15828        for i in (0..n).step_by(17) {
15829            scalar_t[i] = SAINT_MIN | (i as SaSint + 1);
15830        }
15831        let unique_count = scalar_t.iter().filter(|&&value| value < 0).count();
15832        let mut scalar_sa = vec![0; n];
15833        let source = n - m - 1;
15834        for i in 0..=unique_count {
15835            scalar_sa[source + i] = ((i * 13 + 7) % n) as SaSint;
15836        }
15837        let mut threaded_t = scalar_t.clone();
15838        let mut threaded_sa = scalar_sa.clone();
15839
15840        merge_unique_lms_suffixes_32s_omp(
15841            &mut scalar_t,
15842            &mut scalar_sa,
15843            n as SaSint,
15844            m as SaSint,
15845            1,
15846        );
15847        merge_unique_lms_suffixes_32s_omp(
15848            &mut threaded_t,
15849            &mut threaded_sa,
15850            n as SaSint,
15851            m as SaSint,
15852            4,
15853        );
15854        assert_eq!(threaded_t, scalar_t);
15855        assert_eq!(threaded_sa, scalar_sa);
15856
15857        let n = 131_072usize;
15858        let m = 65_536usize;
15859        let f = 100usize;
15860        let mut scalar_sa = vec![1; n];
15861        for i in (0..m).step_by(9) {
15862            scalar_sa[i] = 0;
15863        }
15864        let zero_count = scalar_sa[..m].iter().filter(|&&value| value == 0).count();
15865        let source = n - m - 1 + f;
15866        for i in 0..=zero_count {
15867            scalar_sa[source + i] = 2_000_000 + i as SaSint;
15868        }
15869        let mut threaded_sa = scalar_sa.clone();
15870
15871        merge_nonunique_lms_suffixes_32s_omp(
15872            &mut scalar_sa,
15873            n as SaSint,
15874            m as SaSint,
15875            f as SaSint,
15876            1,
15877        );
15878        merge_nonunique_lms_suffixes_32s_omp(
15879            &mut threaded_sa,
15880            n as SaSint,
15881            m as SaSint,
15882            f as SaSint,
15883            4,
15884        );
15885        assert_eq!(threaded_sa, scalar_sa);
15886    }
15887
15888    #[test]
15889    fn libsais16x64_radix_sort_lms_suffixes_32s_match_c() {
15890        let t = vec![0, 1, 2, 3, 1, 2, 3, 0];
15891        let mut rust_sa = vec![0, 0, 0, 0, 0, 1, 2, 3];
15892        let mut c_sa = rust_sa.clone();
15893        let mut rust_bucket = vec![0, 6, 7, 8];
15894        let mut c_bucket = rust_bucket.clone();
15895        radix_sort_lms_suffixes_32s_6k(&t, &mut rust_sa, &mut rust_bucket, 5, 3);
15896        unsafe {
15897            probe_libsais16x64_radix_sort_lms_suffixes_32s_6k(
15898                t.as_ptr(),
15899                c_sa.as_mut_ptr(),
15900                c_bucket.as_mut_ptr(),
15901                5,
15902                3,
15903            );
15904        }
15905        assert_eq!(rust_sa, c_sa);
15906        assert_eq!(rust_bucket, c_bucket);
15907
15908        let mut rust_sa = vec![0, 0, 0, 0, 0, 1, 2, 3];
15909        let mut c_sa = rust_sa.clone();
15910        let mut rust_bucket = vec![0, 0, 6, 0, 7, 0, 8, 0];
15911        let mut c_bucket = rust_bucket.clone();
15912        radix_sort_lms_suffixes_32s_2k(&t, &mut rust_sa, &mut rust_bucket, 5, 3);
15913        unsafe {
15914            probe_libsais16x64_radix_sort_lms_suffixes_32s_2k(
15915                t.as_ptr(),
15916                c_sa.as_mut_ptr(),
15917                c_bucket.as_mut_ptr(),
15918                5,
15919                3,
15920            );
15921        }
15922        assert_eq!(rust_sa, c_sa);
15923        assert_eq!(rust_bucket, c_bucket);
15924
15925        let mut cache = vec![ThreadCache::default(); 8];
15926        let sa = vec![0, 0, 0, 0, 0, 1, 2, 3];
15927        radix_sort_lms_suffixes_32s_block_gather(&t, &sa, &mut cache, 5, 3);
15928        assert_eq!(cache[5].index, 1);
15929        assert_eq!(cache[5].symbol, 1);
15930        assert_eq!(cache[6].index, 2);
15931        assert_eq!(cache[6].symbol, 2);
15932        assert_eq!(cache[7].index, 3);
15933        assert_eq!(cache[7].symbol, 3);
15934
15935        let mut bucket = vec![0, 6, 7, 8];
15936        radix_sort_lms_suffixes_32s_6k_block_sort(&mut bucket, &mut cache, 5, 3);
15937        assert_eq!(bucket, vec![0, 5, 6, 7]);
15938        assert_eq!(cache[5].symbol, 5);
15939        assert_eq!(cache[6].symbol, 6);
15940        assert_eq!(cache[7].symbol, 7);
15941
15942        let mut cache = vec![ThreadCache::default(); 8];
15943        radix_sort_lms_suffixes_32s_block_gather(&t, &sa, &mut cache, 5, 3);
15944        let mut bucket = vec![0, 0, 6, 0, 7, 0, 8, 0];
15945        radix_sort_lms_suffixes_32s_2k_block_sort(&mut bucket, &mut cache, 5, 3);
15946        assert_eq!(bucket, vec![0, 0, 5, 0, 6, 0, 7, 0]);
15947        assert_eq!(cache[5].symbol, 5);
15948        assert_eq!(cache[6].symbol, 6);
15949        assert_eq!(cache[7].symbol, 7);
15950
15951        let mut rust_sa = vec![0, 0, 0, 0, 0, 1, 2, 3];
15952        let mut c_sa = rust_sa.clone();
15953        let mut rust_bucket = vec![0, 6, 7, 8];
15954        let mut c_bucket = rust_bucket.clone();
15955        radix_sort_lms_suffixes_32s_6k_omp(&t, &mut rust_sa, 8, 4, &mut rust_bucket, 1);
15956        unsafe {
15957            probe_libsais16x64_radix_sort_lms_suffixes_32s_6k_omp(
15958                t.as_ptr(),
15959                c_sa.as_mut_ptr(),
15960                8,
15961                4,
15962                c_bucket.as_mut_ptr(),
15963                1,
15964            );
15965        }
15966        assert_eq!(rust_sa, c_sa);
15967        assert_eq!(rust_bucket, c_bucket);
15968
15969        let mut rust_sa = vec![0, 0, 0, 0, 0, 1, 2, 3];
15970        let mut c_sa = rust_sa.clone();
15971        let mut rust_bucket = vec![0, 0, 6, 0, 7, 0, 8, 0];
15972        let mut c_bucket = rust_bucket.clone();
15973        radix_sort_lms_suffixes_32s_2k_omp(&t, &mut rust_sa, 8, 4, &mut rust_bucket, 1);
15974        unsafe {
15975            probe_libsais16x64_radix_sort_lms_suffixes_32s_2k_omp(
15976                t.as_ptr(),
15977                c_sa.as_mut_ptr(),
15978                8,
15979                4,
15980                c_bucket.as_mut_ptr(),
15981                1,
15982            );
15983        }
15984        assert_eq!(rust_sa, c_sa);
15985        assert_eq!(rust_bucket, c_bucket);
15986
15987        let t = vec![2, 1, 3, 1, 0];
15988        let mut rust_sa = vec![0; t.len()];
15989        let mut c_sa = rust_sa.clone();
15990        let mut rust_bucket = vec![0, 2, 4, 5];
15991        let mut c_bucket = rust_bucket.clone();
15992        let rust_m =
15993            radix_sort_lms_suffixes_32s_1k(&t, &mut rust_sa, t.len() as SaSint, &mut rust_bucket);
15994        let c_m = unsafe {
15995            probe_libsais16x64_radix_sort_lms_suffixes_32s_1k(
15996                t.as_ptr(),
15997                c_sa.as_mut_ptr(),
15998                t.len() as SaSint,
15999                c_bucket.as_mut_ptr(),
16000            )
16001        };
16002        assert_eq!(rust_m, c_m);
16003        assert_eq!(rust_sa, c_sa);
16004        assert_eq!(rust_bucket, c_bucket);
16005    }
16006
16007    #[test]
16008    fn libsais16x64_radix_sort_set_markers_32s_match_c() {
16009        let mut rust_sa = vec![0; 8];
16010        let mut c_sa = rust_sa.clone();
16011        let mut induction_bucket = vec![1, 3, 5, 7];
16012        radix_sort_set_markers_32s_6k(&mut rust_sa, &induction_bucket, 0, 4);
16013        unsafe {
16014            probe_libsais16x64_radix_sort_set_markers_32s_6k(
16015                c_sa.as_mut_ptr(),
16016                induction_bucket.as_mut_ptr(),
16017                0,
16018                4,
16019            );
16020        }
16021        assert_eq!(rust_sa, c_sa);
16022
16023        let mut rust_sa = vec![0; 8];
16024        let mut c_sa = rust_sa.clone();
16025        radix_sort_set_markers_32s_6k_omp(&mut rust_sa, 5, &induction_bucket, 1);
16026        unsafe {
16027            probe_libsais16x64_radix_sort_set_markers_32s_6k_omp(
16028                c_sa.as_mut_ptr(),
16029                5,
16030                induction_bucket.as_mut_ptr(),
16031                1,
16032            );
16033        }
16034        assert_eq!(rust_sa, c_sa);
16035
16036        let mut rust_sa = vec![0; 8];
16037        let mut c_sa = rust_sa.clone();
16038        let mut induction_bucket = vec![1, 0, 3, 0, 5, 0, 7, 0];
16039        radix_sort_set_markers_32s_4k(&mut rust_sa, &induction_bucket, 0, 4);
16040        unsafe {
16041            probe_libsais16x64_radix_sort_set_markers_32s_4k(
16042                c_sa.as_mut_ptr(),
16043                induction_bucket.as_mut_ptr(),
16044                0,
16045                4,
16046            );
16047        }
16048        assert_eq!(rust_sa, c_sa);
16049
16050        let mut rust_sa = vec![0; 8];
16051        let mut c_sa = rust_sa.clone();
16052        radix_sort_set_markers_32s_4k_omp(&mut rust_sa, 5, &induction_bucket, 1);
16053        unsafe {
16054            probe_libsais16x64_radix_sort_set_markers_32s_4k_omp(
16055                c_sa.as_mut_ptr(),
16056                5,
16057                induction_bucket.as_mut_ptr(),
16058                1,
16059            );
16060        }
16061        assert_eq!(rust_sa, c_sa);
16062    }
16063
16064    #[test]
16065    fn libsais16x64_radix_sort_set_markers_32s_omp_partitions_large_inputs() {
16066        let k = 65_600usize;
16067        let induction_bucket_6k: Vec<SaSint> = (0..k).map(|i| i as SaSint).collect();
16068        let mut single = vec![0; k];
16069        let mut threaded = vec![0; k];
16070        radix_sort_set_markers_32s_6k_omp(&mut single, k as SaSint, &induction_bucket_6k, 1);
16071        radix_sort_set_markers_32s_6k_omp(&mut threaded, k as SaSint, &induction_bucket_6k, 4);
16072        assert_eq!(threaded, single);
16073
16074        let mut induction_bucket_4k = vec![0; 2 * k];
16075        for i in 0..k {
16076            induction_bucket_4k[buckets_index2(i, 0)] = i as SaSint;
16077        }
16078        let mut single = vec![0; k];
16079        let mut threaded = vec![0; k];
16080        radix_sort_set_markers_32s_4k_omp(&mut single, k as SaSint, &induction_bucket_4k, 1);
16081        radix_sort_set_markers_32s_4k_omp(&mut threaded, k as SaSint, &induction_bucket_4k, 4);
16082        assert_eq!(threaded, single);
16083    }
16084
16085    #[test]
16086    fn libsais16x64_partial_sorting_32s_helpers_match_c() {
16087        let k = 3;
16088        let mut rust_sa = vec![0, SAINT_MIN, 2, SAINT_MIN, 4, SAINT_MIN];
16089        let mut c_sa = rust_sa.clone();
16090        let mut buckets = vec![0; 6 * k as usize];
16091        buckets[buckets_index4(1, 0)] = 3;
16092        buckets[buckets_index4(2, 0)] = 6;
16093        buckets[4 * k as usize + buckets_index2(0, 0)] = 0;
16094        buckets[4 * k as usize + buckets_index2(1, 0)] = 1;
16095        partial_sorting_shift_markers_32s_6k_omp(&mut rust_sa, k, &buckets, 1);
16096        unsafe {
16097            probe_libsais16x64_partial_sorting_shift_markers_32s_6k_omp(
16098                c_sa.as_mut_ptr(),
16099                k,
16100                buckets.as_ptr(),
16101                1,
16102            );
16103        }
16104        assert_eq!(rust_sa, c_sa);
16105
16106        let mut rust_sa = vec![
16107            1 | SUFFIX_GROUP_MARKER,
16108            2,
16109            3 | SUFFIX_GROUP_MARKER,
16110            4 | SUFFIX_GROUP_MARKER,
16111            5,
16112            6,
16113        ];
16114        let mut c_sa = rust_sa.clone();
16115        partial_sorting_shift_markers_32s_4k(&mut rust_sa, 6);
16116        unsafe { probe_libsais16x64_partial_sorting_shift_markers_32s_4k(c_sa.as_mut_ptr(), 6) };
16117        assert_eq!(rust_sa, c_sa);
16118
16119        let mut rust_buckets = vec![0; 6 * k as usize];
16120        for (i, value) in rust_buckets[4 * k as usize..].iter_mut().enumerate() {
16121            *value = 100 + i as SaSint;
16122        }
16123        let mut c_buckets = rust_buckets.clone();
16124        partial_sorting_shift_buckets_32s_6k(k, &mut rust_buckets);
16125        unsafe {
16126            probe_libsais16x64_partial_sorting_shift_buckets_32s_6k(k, c_buckets.as_mut_ptr())
16127        };
16128        assert_eq!(rust_buckets, c_buckets);
16129
16130        let mut rust_sa = vec![1 | SUFFIX_GROUP_MARKER, -3, 5 | SUFFIX_GROUP_MARKER, -7];
16131        let mut c_sa = rust_sa.clone();
16132        let rust_l = partial_sorting_gather_lms_suffixes_32s_4k(&mut rust_sa, 0, 4);
16133        let c_l = unsafe {
16134            probe_libsais16x64_partial_sorting_gather_lms_suffixes_32s_4k(c_sa.as_mut_ptr(), 0, 4)
16135        };
16136        assert_eq!(rust_l, c_l);
16137        assert_eq!(rust_sa, c_sa);
16138
16139        let mut rust_sa = vec![1, -3, 5, -7];
16140        let mut c_sa = rust_sa.clone();
16141        let rust_l = partial_sorting_gather_lms_suffixes_32s_1k(&mut rust_sa, 0, 4);
16142        let c_l = unsafe {
16143            probe_libsais16x64_partial_sorting_gather_lms_suffixes_32s_1k(c_sa.as_mut_ptr(), 0, 4)
16144        };
16145        assert_eq!(rust_l, c_l);
16146        assert_eq!(rust_sa, c_sa);
16147
16148        let mut rust_state = alloc_thread_state(1).unwrap();
16149        let mut rust_sa = vec![1 | SUFFIX_GROUP_MARKER, -3, 5 | SUFFIX_GROUP_MARKER, -7];
16150        let mut c_sa = rust_sa.clone();
16151        partial_sorting_gather_lms_suffixes_32s_4k_omp(&mut rust_sa, 4, 1, &mut rust_state);
16152        unsafe {
16153            probe_libsais16x64_partial_sorting_gather_lms_suffixes_32s_4k_omp(
16154                c_sa.as_mut_ptr(),
16155                4,
16156                1,
16157            );
16158        }
16159        assert_eq!(rust_sa, c_sa);
16160
16161        let mut rust_state = alloc_thread_state(1).unwrap();
16162        let mut rust_sa = vec![1, -3, 5, -7];
16163        let mut c_sa = rust_sa.clone();
16164        partial_sorting_gather_lms_suffixes_32s_1k_omp(&mut rust_sa, 4, 1, &mut rust_state);
16165        unsafe {
16166            probe_libsais16x64_partial_sorting_gather_lms_suffixes_32s_1k_omp(
16167                c_sa.as_mut_ptr(),
16168                4,
16169                1,
16170            );
16171        }
16172        assert_eq!(rust_sa, c_sa);
16173    }
16174
16175    #[test]
16176    fn libsais16x64_partial_sorting_gather_lms_suffixes_32s_omp_uses_block_partition() {
16177        let n = 65_536usize;
16178        let mut base_4k = vec![0; n];
16179        let mut base_1k = vec![0; n];
16180        for i in 0..n {
16181            let value = (i as SaSint + 1) & SAINT_MAX;
16182            base_4k[i] = if i % 7 == 0 {
16183                value | SAINT_MIN | SUFFIX_GROUP_MARKER
16184            } else if i % 11 == 0 {
16185                value | SUFFIX_GROUP_MARKER
16186            } else {
16187                value
16188            };
16189            base_1k[i] = if i % 7 == 0 { value | SAINT_MIN } else { value };
16190        }
16191        let lms_count = base_1k.iter().filter(|&&v| v < 0).count();
16192
16193        let mut scalar = base_4k.clone();
16194        let mut threaded = base_4k;
16195        let mut scalar_state = alloc_thread_state(1).unwrap();
16196        let mut threaded_state = alloc_thread_state(4).unwrap();
16197        partial_sorting_gather_lms_suffixes_32s_4k_omp(
16198            &mut scalar,
16199            n as SaSint,
16200            1,
16201            &mut scalar_state,
16202        );
16203        partial_sorting_gather_lms_suffixes_32s_4k_omp(
16204            &mut threaded,
16205            n as SaSint,
16206            4,
16207            &mut threaded_state,
16208        );
16209        assert_eq!(&threaded[..lms_count], &scalar[..lms_count]);
16210
16211        let mut scalar = base_1k.clone();
16212        let mut threaded = base_1k;
16213        partial_sorting_gather_lms_suffixes_32s_1k_omp(
16214            &mut scalar,
16215            n as SaSint,
16216            1,
16217            &mut scalar_state,
16218        );
16219        partial_sorting_gather_lms_suffixes_32s_1k_omp(
16220            &mut threaded,
16221            n as SaSint,
16222            4,
16223            &mut threaded_state,
16224        );
16225        assert_eq!(&threaded[..lms_count], &scalar[..lms_count]);
16226    }
16227
16228    #[test]
16229    fn libsais16x64_partial_sorting_32s_block_helpers_behave_like_upstream_shapes() {
16230        let t = vec![0, 1, 2, 1, 0];
16231        let k = 3;
16232
16233        let mut sa = vec![0, 4 | SAINT_MIN, 0];
16234        let mut cache = vec![ThreadCache::default(); sa.len()];
16235        partial_sorting_scan_right_to_left_32s_6k_block_gather(&t, &mut sa, &mut cache, 1, 1);
16236        assert_eq!(cache[1].index, 4 | SAINT_MIN);
16237        assert_eq!(cache[1].symbol, buckets_index4(1, 1) as SaSint);
16238
16239        let mut sa = vec![0, 4 | SUFFIX_GROUP_MARKER, 0];
16240        let mut cache = vec![ThreadCache::default(); sa.len()];
16241        partial_sorting_scan_right_to_left_32s_4k_block_gather(&t, &mut sa, &mut cache, 1, 1);
16242        assert_eq!(sa[1], 0);
16243        assert_eq!(cache[1].index, 4 | SUFFIX_GROUP_MARKER);
16244        assert_eq!(cache[1].symbol, buckets_index2(1, 1) as SaSint);
16245
16246        let mut sa = vec![0, 4, 0];
16247        let mut cache = vec![ThreadCache::default(); sa.len()];
16248        partial_sorting_scan_right_to_left_32s_1k_block_gather(&t, &mut sa, &mut cache, 1, 1);
16249        assert_eq!(sa[1], 0);
16250        assert_eq!(cache[1].index, 3 | SAINT_MIN);
16251        assert_eq!(cache[1].symbol, 1);
16252
16253        let mut sa = vec![4 | SAINT_MIN, 0, 0];
16254        let mut cache = vec![ThreadCache::default(); sa.len()];
16255        partial_sorting_scan_left_to_right_32s_6k_block_gather(&t, &mut sa, &mut cache, 0, 1);
16256        assert_eq!(cache[0].index, 4 | SAINT_MIN);
16257        assert_eq!(cache[0].symbol, buckets_index4(1, 1) as SaSint);
16258
16259        let mut sa = vec![4 | SUFFIX_GROUP_MARKER, 0, 0];
16260        let mut cache = vec![ThreadCache::default(); sa.len()];
16261        partial_sorting_scan_left_to_right_32s_4k_block_gather(&t, &mut sa, &mut cache, 0, 1);
16262        assert_eq!(sa[0], 0);
16263        assert_eq!(cache[0].index, 4 | SUFFIX_GROUP_MARKER);
16264        assert_eq!(cache[0].symbol, buckets_index2(1, 0) as SaSint);
16265
16266        let mut sa = vec![4, 0, 0];
16267        let mut cache = vec![ThreadCache::default(); sa.len()];
16268        partial_sorting_scan_left_to_right_32s_1k_block_gather(&t, &mut sa, &mut cache, 0, 1);
16269        assert_eq!(sa[0], 0);
16270        assert_eq!(cache[0].index, 3);
16271        assert_eq!(cache[0].symbol, 1);
16272
16273        let mut cache = vec![ThreadCache::default(); 3];
16274        cache[1].index = 4 | SAINT_MIN;
16275        cache[1].symbol = buckets_index4(1, 1) as SaSint;
16276        let mut buckets = vec![0; 4 * k];
16277        buckets[buckets_index4(1, 1)] = 2;
16278        let d = partial_sorting_scan_right_to_left_32s_6k_block_sort(
16279            &t,
16280            &mut buckets,
16281            0,
16282            &mut cache,
16283            1,
16284            1,
16285        );
16286        assert_eq!(d, 1);
16287        assert_eq!(cache[1].index, 3 | SAINT_MIN);
16288        assert_eq!(buckets[buckets_index4(1, 1)], 1);
16289        assert_eq!(buckets[buckets_index4(1, 1) + 2], 1);
16290
16291        let mut cache = vec![ThreadCache::default(); 3];
16292        cache[0].index = 4 | SAINT_MIN;
16293        cache[0].symbol = buckets_index4(1, 1) as SaSint;
16294        let mut buckets = vec![0; 4 * k];
16295        buckets[buckets_index4(1, 1)] = 1;
16296        let d = partial_sorting_scan_left_to_right_32s_6k_block_sort(
16297            &t,
16298            &mut buckets,
16299            0,
16300            &mut cache,
16301            0,
16302            1,
16303        );
16304        assert_eq!(d, 1);
16305        assert_eq!(cache[0].index, 3 | SAINT_MIN);
16306        assert_eq!(buckets[buckets_index4(1, 1)], 2);
16307        assert_eq!(buckets[buckets_index4(1, 1) + 2], 1);
16308
16309        let mut cache = vec![ThreadCache::default(); 3];
16310        cache[1].index = 4 | SUFFIX_GROUP_MARKER;
16311        cache[1].symbol = buckets_index2(1, 1) as SaSint;
16312        let mut buckets = vec![0; 4 * k];
16313        buckets[3 * k + 1] = 2;
16314        let d = partial_sorting_scan_right_to_left_32s_4k_block_sort(
16315            &t,
16316            k as SaSint,
16317            &mut buckets,
16318            0,
16319            &mut cache,
16320            1,
16321            1,
16322        );
16323        assert_eq!(d, 1);
16324        assert_eq!(cache[1].symbol, 1);
16325        assert_eq!(buckets[3 * k + 1], 1);
16326
16327        let mut cache = vec![ThreadCache::default(); 3];
16328        cache[0].index = 4 | SUFFIX_GROUP_MARKER;
16329        cache[0].symbol = buckets_index2(1, 0) as SaSint;
16330        let mut buckets = vec![0; 4 * k];
16331        buckets[2 * k + 1] = 1;
16332        let d = partial_sorting_scan_left_to_right_32s_4k_block_sort(
16333            &t,
16334            k as SaSint,
16335            &mut buckets,
16336            0,
16337            &mut cache,
16338            0,
16339            1,
16340        );
16341        assert_eq!(d, 1);
16342        assert_eq!(cache[0].symbol, 1);
16343        assert_eq!(buckets[2 * k + 1], 2);
16344
16345        let mut cache = vec![ThreadCache::default(); 3];
16346        cache[1].index = 4;
16347        cache[1].symbol = 1;
16348        let mut buckets = vec![0; k];
16349        buckets[1] = 2;
16350        partial_sorting_scan_right_to_left_32s_1k_block_sort(&t, &mut buckets, &mut cache, 1, 1);
16351        assert_eq!(cache[1].symbol, 1);
16352        assert_eq!(buckets[1], 1);
16353
16354        let mut cache = vec![ThreadCache::default(); 3];
16355        cache[0].index = 4;
16356        cache[0].symbol = 1;
16357        let mut buckets = vec![0; k];
16358        buckets[1] = 1;
16359        partial_sorting_scan_left_to_right_32s_1k_block_sort(&t, &mut buckets, &mut cache, 0, 1);
16360        assert_eq!(cache[0].symbol, 1);
16361        assert_eq!(buckets[1], 2);
16362    }
16363
16364    #[test]
16365    fn libsais16x64_partial_sorting_scan_32s_match_c() {
16366        let t = vec![0, 1, 2, 1, 3, 0];
16367        let k = 4;
16368
16369        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 9, 9];
16370        let mut c_sa = rust_sa.clone();
16371        let mut rust_buckets = vec![0; 6 * k as usize];
16372        rust_buckets[buckets_index4(2, 0)] = 4;
16373        rust_buckets[buckets_index4(1, 1)] = 5;
16374        let mut c_buckets = rust_buckets.clone();
16375        let rust_d =
16376            partial_sorting_scan_left_to_right_32s_6k(&t, &mut rust_sa, &mut rust_buckets, 0, 0, 2);
16377        let c_d = unsafe {
16378            probe_libsais16x64_partial_sorting_scan_left_to_right_32s_6k(
16379                t.as_ptr(),
16380                c_sa.as_mut_ptr(),
16381                c_buckets.as_mut_ptr(),
16382                0,
16383                0,
16384                2,
16385            )
16386        };
16387        assert_eq!(rust_d, c_d);
16388        assert_eq!(rust_sa, c_sa);
16389        assert_eq!(rust_buckets, c_buckets);
16390
16391        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 9, 9];
16392        let mut c_sa = rust_sa.clone();
16393        let mut rust_buckets = vec![0; 4 * k as usize];
16394        rust_buckets[2 * k as usize + 2] = 4;
16395        rust_buckets[2 * k as usize + 1] = 5;
16396        let mut c_buckets = rust_buckets.clone();
16397        let rust_d = partial_sorting_scan_left_to_right_32s_4k(
16398            &t,
16399            &mut rust_sa,
16400            k,
16401            &mut rust_buckets,
16402            0,
16403            0,
16404            2,
16405        );
16406        let c_d = unsafe {
16407            probe_libsais16x64_partial_sorting_scan_left_to_right_32s_4k(
16408                t.as_ptr(),
16409                c_sa.as_mut_ptr(),
16410                k,
16411                c_buckets.as_mut_ptr(),
16412                0,
16413                0,
16414                2,
16415            )
16416        };
16417        assert_eq!(rust_d, c_d);
16418        assert_eq!(rust_sa, c_sa);
16419        assert_eq!(rust_buckets, c_buckets);
16420
16421        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 9, 9];
16422        let mut c_sa = rust_sa.clone();
16423        let mut rust_buckets = vec![0, 5, 4, 0];
16424        let mut c_buckets = rust_buckets.clone();
16425        partial_sorting_scan_left_to_right_32s_1k(&t, &mut rust_sa, &mut rust_buckets, 0, 2);
16426        unsafe {
16427            probe_libsais16x64_partial_sorting_scan_left_to_right_32s_1k(
16428                t.as_ptr(),
16429                c_sa.as_mut_ptr(),
16430                c_buckets.as_mut_ptr(),
16431                0,
16432                2,
16433            );
16434        }
16435        assert_eq!(rust_sa, c_sa);
16436        assert_eq!(rust_buckets, c_buckets);
16437
16438        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 9, 9];
16439        let mut c_sa = rust_sa.clone();
16440        let mut rust_buckets = vec![0; 6 * k as usize];
16441        rust_buckets[buckets_index4(2, 0)] = 7;
16442        rust_buckets[buckets_index4(1, 1)] = 6;
16443        let mut c_buckets = rust_buckets.clone();
16444        let rust_d =
16445            partial_sorting_scan_right_to_left_32s_6k(&t, &mut rust_sa, &mut rust_buckets, 0, 0, 2);
16446        let c_d = unsafe {
16447            probe_libsais16x64_partial_sorting_scan_right_to_left_32s_6k(
16448                t.as_ptr(),
16449                c_sa.as_mut_ptr(),
16450                c_buckets.as_mut_ptr(),
16451                0,
16452                0,
16453                2,
16454            )
16455        };
16456        assert_eq!(rust_d, c_d);
16457        assert_eq!(rust_sa, c_sa);
16458        assert_eq!(rust_buckets, c_buckets);
16459
16460        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 9, 9];
16461        let mut c_sa = rust_sa.clone();
16462        let mut rust_buckets = vec![0; 4 * k as usize];
16463        rust_buckets[3 * k as usize + 2] = 7;
16464        rust_buckets[3 * k as usize + 1] = 6;
16465        let mut c_buckets = rust_buckets.clone();
16466        let rust_d = partial_sorting_scan_right_to_left_32s_4k(
16467            &t,
16468            &mut rust_sa,
16469            k,
16470            &mut rust_buckets,
16471            0,
16472            0,
16473            2,
16474        );
16475        let c_d = unsafe {
16476            probe_libsais16x64_partial_sorting_scan_right_to_left_32s_4k(
16477                t.as_ptr(),
16478                c_sa.as_mut_ptr(),
16479                k,
16480                c_buckets.as_mut_ptr(),
16481                0,
16482                0,
16483                2,
16484            )
16485        };
16486        assert_eq!(rust_d, c_d);
16487        assert_eq!(rust_sa, c_sa);
16488        assert_eq!(rust_buckets, c_buckets);
16489
16490        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 9, 9];
16491        let mut c_sa = rust_sa.clone();
16492        let mut rust_buckets = vec![0, 6, 7, 0];
16493        let mut c_buckets = rust_buckets.clone();
16494        partial_sorting_scan_right_to_left_32s_1k(&t, &mut rust_sa, &mut rust_buckets, 0, 2);
16495        unsafe {
16496            probe_libsais16x64_partial_sorting_scan_right_to_left_32s_1k(
16497                t.as_ptr(),
16498                c_sa.as_mut_ptr(),
16499                c_buckets.as_mut_ptr(),
16500                0,
16501                2,
16502            );
16503        }
16504        assert_eq!(rust_sa, c_sa);
16505        assert_eq!(rust_buckets, c_buckets);
16506
16507        let mut state = alloc_thread_state(1).unwrap();
16508        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 7, 9];
16509        let mut c_sa = rust_sa.clone();
16510        let mut rust_buckets = vec![0; 6 * k as usize];
16511        rust_buckets[buckets_index4(2, 0)] = 4;
16512        rust_buckets[buckets_index4(1, 1)] = 5;
16513        rust_buckets[buckets_index4(3, 0)] = 6;
16514        let mut c_buckets = rust_buckets.clone();
16515        let rust_d = partial_sorting_scan_left_to_right_32s_6k_omp(
16516            &t,
16517            &mut rust_sa,
16518            5,
16519            &mut rust_buckets,
16520            2,
16521            0,
16522            1,
16523            &mut state,
16524        );
16525        let c_d = unsafe {
16526            probe_libsais16x64_partial_sorting_scan_left_to_right_32s_6k_omp(
16527                t.as_ptr(),
16528                c_sa.as_mut_ptr(),
16529                5,
16530                c_buckets.as_mut_ptr(),
16531                2,
16532                0,
16533                1,
16534            )
16535        };
16536        assert_eq!(rust_d, c_d);
16537        assert_eq!(rust_sa, c_sa);
16538        assert_eq!(rust_buckets, c_buckets);
16539
16540        let mut state = alloc_thread_state(1).unwrap();
16541        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 7, 9];
16542        let mut c_sa = rust_sa.clone();
16543        let mut rust_buckets = vec![0; 4 * k as usize];
16544        rust_buckets[2 * k as usize + 2] = 4;
16545        rust_buckets[2 * k as usize + 1] = 5;
16546        rust_buckets[2 * k as usize + 3] = 6;
16547        let mut c_buckets = rust_buckets.clone();
16548        let rust_d = partial_sorting_scan_left_to_right_32s_4k_omp(
16549            &t,
16550            &mut rust_sa,
16551            5,
16552            k,
16553            &mut rust_buckets,
16554            0,
16555            1,
16556            &mut state,
16557        );
16558        let c_d = unsafe {
16559            probe_libsais16x64_partial_sorting_scan_left_to_right_32s_4k_omp(
16560                t.as_ptr(),
16561                c_sa.as_mut_ptr(),
16562                5,
16563                k,
16564                c_buckets.as_mut_ptr(),
16565                0,
16566                1,
16567            )
16568        };
16569        assert_eq!(rust_d, c_d);
16570        assert_eq!(rust_sa, c_sa);
16571        assert_eq!(rust_buckets, c_buckets);
16572
16573        let mut state = alloc_thread_state(1).unwrap();
16574        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 7, 9];
16575        let mut c_sa = rust_sa.clone();
16576        let mut rust_buckets = vec![0, 5, 4, 6];
16577        let mut c_buckets = rust_buckets.clone();
16578        partial_sorting_scan_left_to_right_32s_1k_omp(
16579            &t,
16580            &mut rust_sa,
16581            5,
16582            &mut rust_buckets,
16583            1,
16584            &mut state,
16585        );
16586        unsafe {
16587            probe_libsais16x64_partial_sorting_scan_left_to_right_32s_1k_omp(
16588                t.as_ptr(),
16589                c_sa.as_mut_ptr(),
16590                5,
16591                c_buckets.as_mut_ptr(),
16592                1,
16593            );
16594        }
16595        assert_eq!(rust_sa, c_sa);
16596        assert_eq!(rust_buckets, c_buckets);
16597
16598        let mut state = alloc_thread_state(1).unwrap();
16599        let mut rust_sa = vec![0, 0, 3, 4, 9, 9, 9, 9];
16600        let mut c_sa = rust_sa.clone();
16601        let mut rust_buckets = vec![0; 6 * k as usize];
16602        rust_buckets[buckets_index4(2, 0)] = 7;
16603        rust_buckets[buckets_index4(1, 1)] = 6;
16604        let mut c_buckets = rust_buckets.clone();
16605        let rust_d = partial_sorting_scan_right_to_left_32s_6k_omp(
16606            &t,
16607            &mut rust_sa,
16608            5,
16609            &mut rust_buckets,
16610            1,
16611            1,
16612            0,
16613            1,
16614            &mut state,
16615        );
16616        let c_d = unsafe {
16617            probe_libsais16x64_partial_sorting_scan_right_to_left_32s_6k_omp(
16618                t.as_ptr(),
16619                c_sa.as_mut_ptr(),
16620                5,
16621                c_buckets.as_mut_ptr(),
16622                1,
16623                1,
16624                0,
16625                1,
16626            )
16627        };
16628        assert_eq!(rust_d, c_d);
16629        assert_eq!(rust_sa, c_sa);
16630        assert_eq!(rust_buckets, c_buckets);
16631
16632        let mut state = alloc_thread_state(1).unwrap();
16633        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 9, 9];
16634        let mut c_sa = rust_sa.clone();
16635        let mut rust_buckets = vec![0; 4 * k as usize];
16636        rust_buckets[3 * k as usize + 2] = 7;
16637        rust_buckets[3 * k as usize + 1] = 6;
16638        let mut c_buckets = rust_buckets.clone();
16639        let rust_d = partial_sorting_scan_right_to_left_32s_4k_omp(
16640            &t,
16641            &mut rust_sa,
16642            2,
16643            k,
16644            &mut rust_buckets,
16645            0,
16646            1,
16647            &mut state,
16648        );
16649        let c_d = unsafe {
16650            probe_libsais16x64_partial_sorting_scan_right_to_left_32s_4k_omp(
16651                t.as_ptr(),
16652                c_sa.as_mut_ptr(),
16653                2,
16654                k,
16655                c_buckets.as_mut_ptr(),
16656                0,
16657                1,
16658            )
16659        };
16660        assert_eq!(rust_d, c_d);
16661        assert_eq!(rust_sa, c_sa);
16662        assert_eq!(rust_buckets, c_buckets);
16663
16664        let mut state = alloc_thread_state(1).unwrap();
16665        let mut rust_sa = vec![3, 4, 0, 0, 9, 9, 9, 9];
16666        let mut c_sa = rust_sa.clone();
16667        let mut rust_buckets = vec![0, 6, 7, 0];
16668        let mut c_buckets = rust_buckets.clone();
16669        partial_sorting_scan_right_to_left_32s_1k_omp(
16670            &t,
16671            &mut rust_sa,
16672            2,
16673            &mut rust_buckets,
16674            1,
16675            &mut state,
16676        );
16677        unsafe {
16678            probe_libsais16x64_partial_sorting_scan_right_to_left_32s_1k_omp(
16679                t.as_ptr(),
16680                c_sa.as_mut_ptr(),
16681                2,
16682                c_buckets.as_mut_ptr(),
16683                1,
16684            );
16685        }
16686        assert_eq!(rust_sa, c_sa);
16687        assert_eq!(rust_buckets, c_buckets);
16688    }
16689
16690    #[test]
16691    fn libsais16x64_place_lms_suffixes_histogram_32s_match_c() {
16692        let n = 12;
16693        let k = 4;
16694        let m = 4;
16695        let mut rust_sa = vec![101, 102, 103, 104, 9, 9, 9, 9, 9, 9, 9, 9];
16696        let mut c_sa = rust_sa.clone();
16697        let mut buckets = vec![0; 2 * k as usize];
16698        buckets[buckets_index2(1, 0)] = 7;
16699        buckets[buckets_index2(1, 1)] = 2;
16700        buckets[buckets_index2(2, 0)] = 10;
16701        buckets[buckets_index2(2, 1)] = 1;
16702        place_lms_suffixes_histogram_32s_2k(&mut rust_sa, n, k, m, &buckets);
16703        unsafe {
16704            probe_libsais16x64_place_lms_suffixes_histogram_32s_2k(
16705                c_sa.as_mut_ptr(),
16706                n,
16707                k,
16708                m,
16709                buckets.as_ptr(),
16710            );
16711        }
16712        assert_eq!(rust_sa, c_sa);
16713
16714        let mut rust_sa = vec![101, 102, 103, 104, 9, 9, 9, 9, 9, 9, 9, 9];
16715        let mut c_sa = rust_sa.clone();
16716        let mut buckets = vec![0; 4 * k as usize];
16717        buckets[buckets_index2(1, 1)] = 2;
16718        buckets[buckets_index2(2, 1)] = 1;
16719        buckets[3 * k as usize + 1] = 7;
16720        buckets[3 * k as usize + 2] = 10;
16721        place_lms_suffixes_histogram_32s_4k(&mut rust_sa, n, k, m, &buckets);
16722        unsafe {
16723            probe_libsais16x64_place_lms_suffixes_histogram_32s_4k(
16724                c_sa.as_mut_ptr(),
16725                n,
16726                k,
16727                m,
16728                buckets.as_ptr(),
16729            );
16730        }
16731        assert_eq!(rust_sa, c_sa);
16732
16733        let mut rust_sa = vec![101, 102, 103, 104, 9, 9, 9, 9, 9, 9, 9, 9];
16734        let mut c_sa = rust_sa.clone();
16735        let mut buckets = vec![0; 6 * k as usize];
16736        buckets[buckets_index4(1, 1)] = 2;
16737        buckets[buckets_index4(2, 1)] = 1;
16738        buckets[5 * k as usize + 1] = 7;
16739        buckets[5 * k as usize + 2] = 10;
16740        place_lms_suffixes_histogram_32s_6k(&mut rust_sa, n, k, m, &buckets);
16741        unsafe {
16742            probe_libsais16x64_place_lms_suffixes_histogram_32s_6k(
16743                c_sa.as_mut_ptr(),
16744                n,
16745                k,
16746                m,
16747                buckets.as_ptr(),
16748            );
16749        }
16750        assert_eq!(rust_sa, c_sa);
16751    }
16752
16753    #[test]
16754    fn libsais16x64_count_gather_lms_suffixes_32s_match_c() {
16755        let t = vec![2, 1, 3, 1, 2, 0, 1, 0];
16756        let n = t.len() as SaSint;
16757        let k = 4;
16758
16759        let mut rust_sa = vec![0; t.len()];
16760        let mut c_sa = rust_sa.clone();
16761        let rust_m = gather_lms_suffixes_32s(&t, &mut rust_sa, n);
16762        let c_m =
16763            unsafe { probe_libsais16x64_gather_lms_suffixes_32s(t.as_ptr(), c_sa.as_mut_ptr(), n) };
16764        assert_eq!(rust_m, c_m);
16765        assert_eq!(rust_sa, c_sa);
16766
16767        let compact_t = vec![2, SAINT_MIN | 1, 3, 1, SAINT_MIN | 2, 0, 1, 0];
16768        let mut rust_sa = vec![0; compact_t.len()];
16769        let mut c_sa = rust_sa.clone();
16770        let rust_m = gather_compacted_lms_suffixes_32s(&compact_t, &mut rust_sa, n);
16771        let c_m = unsafe {
16772            probe_libsais16x64_gather_compacted_lms_suffixes_32s(
16773                compact_t.as_ptr(),
16774                c_sa.as_mut_ptr(),
16775                n,
16776            )
16777        };
16778        assert_eq!(rust_m, c_m);
16779        assert_eq!(rust_sa, c_sa);
16780
16781        let mut rust_buckets = vec![99; 2 * k as usize];
16782        let mut c_buckets = rust_buckets.clone();
16783        count_lms_suffixes_32s_2k(&t, n, k, &mut rust_buckets);
16784        unsafe {
16785            probe_libsais16x64_count_lms_suffixes_32s_2k(t.as_ptr(), n, k, c_buckets.as_mut_ptr());
16786        }
16787        assert_eq!(rust_buckets, c_buckets);
16788
16789        let mut rust_sa = vec![0; t.len()];
16790        let mut c_sa = rust_sa.clone();
16791        let mut rust_buckets = vec![0; 2 * k as usize];
16792        let mut c_buckets = rust_buckets.clone();
16793        let rust_m = count_and_gather_lms_suffixes_32s_2k(
16794            &t,
16795            &mut rust_sa,
16796            n,
16797            k,
16798            &mut rust_buckets,
16799            0,
16800            n as isize,
16801        );
16802        let c_m = unsafe {
16803            probe_libsais16x64_count_and_gather_lms_suffixes_32s_2k(
16804                t.as_ptr(),
16805                c_sa.as_mut_ptr(),
16806                n,
16807                k,
16808                c_buckets.as_mut_ptr(),
16809                0,
16810                n,
16811            )
16812        };
16813        assert_eq!(rust_m, c_m);
16814        assert_eq!(rust_sa, c_sa);
16815        assert_eq!(rust_buckets, c_buckets);
16816
16817        let mut rust_sa = vec![0; compact_t.len()];
16818        let mut c_sa = rust_sa.clone();
16819        let mut rust_buckets = vec![0; 2 * k as usize];
16820        let mut c_buckets = rust_buckets.clone();
16821        let rust_m = count_and_gather_compacted_lms_suffixes_32s_2k(
16822            &compact_t,
16823            &mut rust_sa,
16824            n,
16825            k,
16826            &mut rust_buckets,
16827            0,
16828            n as isize,
16829        );
16830        let c_m = unsafe {
16831            probe_libsais16x64_count_and_gather_compacted_lms_suffixes_32s_2k(
16832                compact_t.as_ptr(),
16833                c_sa.as_mut_ptr(),
16834                n,
16835                k,
16836                c_buckets.as_mut_ptr(),
16837                0,
16838                n,
16839            )
16840        };
16841        assert_eq!(rust_m, c_m);
16842        assert_eq!(rust_sa, c_sa);
16843        assert_eq!(rust_buckets, c_buckets);
16844    }
16845
16846    #[test]
16847    fn libsais16x64_small_openmp_leaf_helpers_match_upstream_shapes() {
16848        let sa = [-1, 0, 3, SAINT_MIN, 0, 7, -5];
16849        assert_eq!(count_negative_marked_suffixes(&sa, 1, 5), 1);
16850        assert_eq!(count_zero_marked_suffixes(&sa, 1, 5), 2);
16851
16852        let mut buckets = vec![1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9, 0, 10, 11, 12, 0];
16853        accumulate_counts_s32_4(&mut buckets, 12, 3, 4);
16854        assert_eq!(&buckets[12..15], &[22, 26, 30]);
16855
16856        let mut many = Vec::new();
16857        for bucket in 0..10 {
16858            many.extend([bucket, bucket + 1, bucket + 2, 0]);
16859        }
16860        accumulate_counts_s32(&mut many, 36, 3, 4, 10);
16861        assert_eq!(&many[36..39], &[45, 55, 65]);
16862
16863        let t = [1, SAINT_MIN | 2, 0];
16864        let mut compacted_buckets = vec![0; 6];
16865        count_compacted_lms_suffixes_32s_2k(&t, t.len() as SaSint, 3, &mut compacted_buckets);
16866        assert_eq!(compacted_buckets, vec![1, 0, 1, 0, 0, 1]);
16867
16868        let unique_sa = [0, 2, 4, 6, 0, -10, 20, -30];
16869        assert_eq!(count_unique_suffixes(&unique_sa, 4, 0, 4), 2);
16870
16871        let s = [10u32, 11, 12, 13];
16872        let mut d = [0u64; 4];
16873        convert_32u_to_64u(&s, &mut d, 1, 2);
16874        assert_eq!(d, [0, 11, 12, 0]);
16875
16876        let mut words = [10u32, 11, 12, 13, 99, 99, 99, 99];
16877        convert_inplace_32u_to_64u(&mut words, 0, 4);
16878        assert_eq!(words, [10, 0, 11, 0, 12, 0, 13, 0]);
16879        convert_inplace_64u_to_32u(&mut words, 0, 4);
16880        assert_eq!(&words[..4], &[10, 11, 12, 13]);
16881
16882        let mut words = [20u32, 21, 22, 23, 99, 99, 99, 99];
16883        convert_inplace_32u_to_64u_omp(&mut words, 4, 2);
16884        assert_eq!(words, [20, 0, 21, 0, 22, 0, 23, 0]);
16885
16886        assert_eq!(get_bucket_stride(20_000, 1000, 4), 1024);
16887        assert_eq!(get_bucket_stride(3024, 1001, 4), 1008);
16888        assert_eq!(get_bucket_stride(3000, 1001, 4), 1001);
16889    }
16890
16891    #[test]
16892    fn libsais16x64_count_gather_lms_suffixes_32s_omp_wrappers_match_c() {
16893        let t = vec![2, 1, 3, 1, 2, 0, 1, 0];
16894        let n = t.len() as SaSint;
16895        let k = 4;
16896        let mut rust_sa = vec![0; t.len()];
16897        let mut c_sa = rust_sa.clone();
16898        let mut rust_buckets = vec![0; 2 * k as usize];
16899        let mut c_buckets = rust_buckets.clone();
16900        let mut rust_state = alloc_thread_state(1).unwrap();
16901        let rust_m = count_and_gather_lms_suffixes_32s_2k_omp(
16902            &t,
16903            &mut rust_sa,
16904            n,
16905            k,
16906            &mut rust_buckets,
16907            0,
16908            1,
16909            &mut rust_state,
16910        );
16911        let c_m = unsafe {
16912            probe_libsais16x64_count_and_gather_lms_suffixes_32s_2k_omp(
16913                t.as_ptr(),
16914                c_sa.as_mut_ptr(),
16915                n,
16916                k,
16917                c_buckets.as_mut_ptr(),
16918                0,
16919                1,
16920            )
16921        };
16922        assert_eq!(rust_m, c_m);
16923        assert_eq!(rust_sa, c_sa);
16924        assert_eq!(rust_buckets, c_buckets);
16925
16926        let compact_t = vec![2, SAINT_MIN | 1, 3, 1, SAINT_MIN | 2, 0, 1, 0];
16927        let mut rust_sa = vec![0; compact_t.len()];
16928        let mut c_sa = rust_sa.clone();
16929        let mut rust_buckets = vec![0; 2 * k as usize];
16930        let mut c_buckets = rust_buckets.clone();
16931        let mut rust_state = alloc_thread_state(1).unwrap();
16932        count_and_gather_compacted_lms_suffixes_32s_2k_omp(
16933            &compact_t,
16934            &mut rust_sa,
16935            n,
16936            k,
16937            &mut rust_buckets,
16938            0,
16939            1,
16940            &mut rust_state,
16941        );
16942        unsafe {
16943            probe_libsais16x64_count_and_gather_compacted_lms_suffixes_32s_2k_omp(
16944                compact_t.as_ptr(),
16945                c_sa.as_mut_ptr(),
16946                n,
16947                k,
16948                c_buckets.as_mut_ptr(),
16949                0,
16950                1,
16951            );
16952        }
16953        assert_eq!(rust_sa, c_sa);
16954        assert_eq!(rust_buckets, c_buckets);
16955    }
16956
16957    #[test]
16958    fn libsais16x64_count_gather_lms_suffixes_32s_4k_match_c() {
16959        let t = vec![2, 1, 3, 1, 2, 0, 1, 0];
16960        let n = t.len() as SaSint;
16961        let k = 4;
16962
16963        let mut rust_buckets = vec![77; 4 * k as usize];
16964        let mut c_buckets = vec![0; 4 * k as usize];
16965        let mut c_sa_for_count = vec![0; t.len()];
16966        count_lms_suffixes_32s_4k(&t, n, k, &mut rust_buckets);
16967        unsafe {
16968            probe_libsais16x64_count_and_gather_lms_suffixes_32s_4k(
16969                t.as_ptr(),
16970                c_sa_for_count.as_mut_ptr(),
16971                n,
16972                k,
16973                c_buckets.as_mut_ptr(),
16974                0,
16975                n,
16976            );
16977        }
16978        assert_eq!(rust_buckets, c_buckets);
16979
16980        let mut rust_sa = vec![0; t.len()];
16981        let mut c_sa = rust_sa.clone();
16982        let mut rust_buckets = vec![0; 4 * k as usize];
16983        let mut c_buckets = rust_buckets.clone();
16984        let rust_m = count_and_gather_lms_suffixes_32s_4k(
16985            &t,
16986            &mut rust_sa,
16987            n,
16988            k,
16989            &mut rust_buckets,
16990            0,
16991            n as isize,
16992        );
16993        let c_m = unsafe {
16994            probe_libsais16x64_count_and_gather_lms_suffixes_32s_4k(
16995                t.as_ptr(),
16996                c_sa.as_mut_ptr(),
16997                n,
16998                k,
16999                c_buckets.as_mut_ptr(),
17000                0,
17001                n,
17002            )
17003        };
17004        assert_eq!(rust_m, c_m);
17005        assert_eq!(rust_sa, c_sa);
17006        assert_eq!(rust_buckets, c_buckets);
17007
17008        let mut rust_sa = vec![0; t.len()];
17009        let mut c_sa = rust_sa.clone();
17010        let mut rust_buckets = vec![0; 4 * k as usize];
17011        let mut c_buckets = rust_buckets.clone();
17012        let mut rust_state = alloc_thread_state(1).unwrap();
17013        let rust_m = count_and_gather_lms_suffixes_32s_4k_omp(
17014            &t,
17015            &mut rust_sa,
17016            n,
17017            k,
17018            &mut rust_buckets,
17019            0,
17020            1,
17021            &mut rust_state,
17022        );
17023        let c_m = unsafe {
17024            probe_libsais16x64_count_and_gather_lms_suffixes_32s_4k_omp(
17025                t.as_ptr(),
17026                c_sa.as_mut_ptr(),
17027                n,
17028                k,
17029                c_buckets.as_mut_ptr(),
17030                0,
17031                1,
17032            )
17033        };
17034        assert_eq!(rust_m, c_m);
17035        assert_eq!(rust_sa, c_sa);
17036        assert_eq!(rust_buckets, c_buckets);
17037
17038        let mut rust_buckets = vec![91; k as usize];
17039        let mut c_buckets = rust_buckets.clone();
17040        count_suffixes_32s(&t, n, k, &mut rust_buckets);
17041        unsafe {
17042            probe_libsais16x64_count_suffixes_32s(t.as_ptr(), n, k, c_buckets.as_mut_ptr());
17043        }
17044        assert_eq!(rust_buckets, c_buckets);
17045    }
17046
17047    #[test]
17048    fn libsais16x64_initialize_buckets_32s_match_c() {
17049        let k = 4;
17050
17051        let base_6k = vec![
17052            1, 2, 0, 1, 0, 1, 2, 0, 3, 0, 1, 1, 2, 1, 0, 0, 9, 9, 9, 9, 8, 8, 8, 8,
17053        ];
17054        let mut rust = base_6k.clone();
17055        let mut c = base_6k.clone();
17056        initialize_buckets_start_and_end_32s_6k(k, &mut rust);
17057        unsafe { probe_libsais16x64_initialize_buckets_start_and_end_32s_6k(k, c.as_mut_ptr()) };
17058        assert_eq!(rust, c);
17059
17060        let base_4k = vec![1, 2, 0, 1, 3, 0, 2, 1, 9, 9, 9, 9, 8, 8, 8, 8];
17061        let mut rust = base_4k.clone();
17062        let mut c = base_4k.clone();
17063        initialize_buckets_start_and_end_32s_4k(k, &mut rust);
17064        unsafe { probe_libsais16x64_initialize_buckets_start_and_end_32s_4k(k, c.as_mut_ptr()) };
17065        assert_eq!(rust, c);
17066
17067        let base_2k = vec![1, 2, 0, 1, 3, 0, 2, 1];
17068        let mut rust = base_2k.clone();
17069        let mut c = base_2k.clone();
17070        initialize_buckets_end_32s_2k(k, &mut rust);
17071        unsafe { probe_libsais16x64_initialize_buckets_end_32s_2k(k, c.as_mut_ptr()) };
17072        assert_eq!(rust, c);
17073
17074        let mut rust = base_2k.clone();
17075        let mut c = base_2k.clone();
17076        initialize_buckets_start_and_end_32s_2k(k, &mut rust);
17077        unsafe { probe_libsais16x64_initialize_buckets_start_and_end_32s_2k(k, c.as_mut_ptr()) };
17078        assert_eq!(rust, c);
17079
17080        let base_1k = vec![2, 1, 3, 2];
17081        let mut rust = base_1k.clone();
17082        let mut c = base_1k.clone();
17083        initialize_buckets_start_32s_1k(k, &mut rust);
17084        unsafe { probe_libsais16x64_initialize_buckets_start_32s_1k(k, c.as_mut_ptr()) };
17085        assert_eq!(rust, c);
17086
17087        let mut rust = base_1k.clone();
17088        let mut c = base_1k.clone();
17089        initialize_buckets_end_32s_1k(k, &mut rust);
17090        unsafe { probe_libsais16x64_initialize_buckets_end_32s_1k(k, c.as_mut_ptr()) };
17091        assert_eq!(rust, c);
17092
17093        let t = vec![2, 1, 3, 1, 2, 0, 1, 0];
17094        let mut rust = vec![1, 2, 0, 1, 3, 0, 2, 1];
17095        let mut c = rust.clone();
17096        initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(&t, k, &mut rust, 4);
17097        unsafe {
17098            probe_libsais16x64_initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
17099                t.as_ptr(),
17100                k,
17101                c.as_mut_ptr(),
17102                4,
17103            );
17104        }
17105        assert_eq!(rust, c);
17106
17107        let mut rust = vec![
17108            1, 2, 0, 1, 3, 0, 2, 1, 1, 0, 2, 0, 0, 1, 1, 0, 9, 9, 9, 9, 8, 8, 8, 8,
17109        ];
17110        let mut c = rust.clone();
17111        let rust_sum = initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(&t, k, &mut rust, 4);
17112        let c_sum = unsafe {
17113            probe_libsais16x64_initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
17114                t.as_ptr(),
17115                k,
17116                c.as_mut_ptr(),
17117                4,
17118            )
17119        };
17120        assert_eq!(rust_sum, c_sum);
17121        assert_eq!(rust, c);
17122
17123        let mut rust = base_4k.clone();
17124        let mut c = base_4k;
17125        initialize_buckets_for_radix_and_partial_sorting_32s_4k(&t, k, &mut rust, 4);
17126        unsafe {
17127            probe_libsais16x64_initialize_buckets_for_radix_and_partial_sorting_32s_4k(
17128                t.as_ptr(),
17129                k,
17130                c.as_mut_ptr(),
17131                4,
17132            );
17133        }
17134        assert_eq!(rust, c);
17135    }
17136
17137    #[test]
17138    fn libsais16x64_place_lms_suffixes_interval_32s_match_c() {
17139        let n = 12;
17140        let k = 4;
17141        let m = 4;
17142
17143        let mut rust_sa = vec![101, 102, 103, 104, 9, 9, 9, 9, 9, 9, 9, 9];
17144        let mut c_sa = rust_sa.clone();
17145        let mut buckets = vec![0; 4 * k as usize];
17146        buckets[buckets_index2(0, 1)] = 2;
17147        buckets[buckets_index2(1, 1)] = 2;
17148        buckets[buckets_index2(2, 1)] = 3;
17149        buckets[buckets_index2(2, 1) + buckets_index2(1, 0)] = 4;
17150        buckets[3 * k as usize + 1] = 7;
17151        buckets[3 * k as usize + 2] = 10;
17152        place_lms_suffixes_interval_32s_4k(&mut rust_sa, n, k, m, &buckets);
17153        unsafe {
17154            probe_libsais16x64_place_lms_suffixes_interval_32s_4k(
17155                c_sa.as_mut_ptr(),
17156                n,
17157                k,
17158                m,
17159                buckets.as_ptr(),
17160            );
17161        }
17162        assert_eq!(rust_sa, c_sa);
17163
17164        let mut rust_sa = vec![101, 102, 103, 104, 9, 9, 9, 9, 9, 9, 9, 9];
17165        let mut c_sa = rust_sa.clone();
17166        let mut buckets = vec![0; 2 * k as usize];
17167        buckets[buckets_index2(1, 0)] = 7;
17168        buckets[buckets_index2(0, 1)] = 1;
17169        buckets[buckets_index2(1, 1)] = 1;
17170        buckets[buckets_index2(2, 0)] = 10;
17171        buckets[buckets_index2(2, 1)] = 2;
17172        buckets[buckets_index2(3, 1)] = 3;
17173        place_lms_suffixes_interval_32s_2k(&mut rust_sa, n, k, m, &buckets);
17174        unsafe {
17175            probe_libsais16x64_place_lms_suffixes_interval_32s_2k(
17176                c_sa.as_mut_ptr(),
17177                n,
17178                k,
17179                m,
17180                buckets.as_ptr(),
17181            );
17182        }
17183        assert_eq!(rust_sa, c_sa);
17184
17185        let t = vec![0, 1, 2, 1, 2, 3, 1, 3, 0, 0, 0, 0];
17186        let mut rust_sa = vec![1, 3, 4, 7, 9, 9, 9, 9, 9, 9, 9, 9];
17187        let mut c_sa = rust_sa.clone();
17188        let rust_buckets = vec![0, 3, 6, 10];
17189        let mut c_buckets = rust_buckets.clone();
17190        place_lms_suffixes_interval_32s_1k(&t, &mut rust_sa, k, m, &rust_buckets);
17191        unsafe {
17192            probe_libsais16x64_place_lms_suffixes_interval_32s_1k(
17193                t.as_ptr(),
17194                c_sa.as_mut_ptr(),
17195                k,
17196                m,
17197                c_buckets.as_mut_ptr(),
17198            );
17199        }
17200        assert_eq!(rust_sa, c_sa);
17201        assert_eq!(rust_buckets, c_buckets);
17202    }
17203
17204    #[test]
17205    fn libsais16x64_renumber_and_mark_distinct_lms_suffixes_32s_1k_matches_c() {
17206        let rust_t = vec![2, 1, 3, 1, 2, 0, 1, 0];
17207        let n = rust_t.len() as SaSint;
17208        let mut probe_sa = vec![0; rust_t.len()];
17209        let m = gather_lms_suffixes_32s(&rust_t, &mut probe_sa, n);
17210        let mut rust_sa = vec![0; rust_t.len()];
17211        let mut c_t = rust_t.clone();
17212        let mut c_sa = rust_sa.clone();
17213
17214        let rust_name =
17215            renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(&rust_t, &mut rust_sa, n, m, 1);
17216        let c_name = unsafe {
17217            probe_libsais16x64_renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(
17218                c_t.as_mut_ptr(),
17219                c_sa.as_mut_ptr(),
17220                n,
17221                m,
17222                1,
17223            )
17224        };
17225        assert_eq!(rust_name, c_name);
17226        assert_eq!(rust_t, c_t);
17227        assert_eq!(rust_sa, c_sa);
17228    }
17229
17230    #[test]
17231    fn libsais16x64_reconstruct_compacted_lms_suffixes_32s_match_c() {
17232        let n = 8;
17233        let k = 4;
17234        let fs = 0;
17235        let f = 0;
17236        let mut m_probe_sa = vec![0; n as usize];
17237        let m = gather_lms_suffixes_32s(&[2, 1, 3, 1, 2, 0, 1, 0], &mut m_probe_sa, n);
17238
17239        let mut rust_t = vec![2, 1, 3, 1, 2, 0, 1, 0];
17240        let mut c_t = rust_t.clone();
17241        let mut rust_sa = vec![0; n as usize];
17242        let mut c_sa = rust_sa.clone();
17243        let mut rust_buckets = vec![0; 2 * k as usize];
17244        let mut c_buckets = rust_buckets.clone();
17245        let mut rust_thread_state = alloc_thread_state(1).unwrap();
17246        reconstruct_compacted_lms_suffixes_32s_2k_omp(
17247            &mut rust_t,
17248            &mut rust_sa,
17249            n,
17250            k,
17251            m,
17252            fs,
17253            f,
17254            &mut rust_buckets,
17255            0,
17256            1,
17257            &mut rust_thread_state,
17258        );
17259        unsafe {
17260            probe_libsais16x64_reconstruct_compacted_lms_suffixes_32s_2k_omp(
17261                c_t.as_mut_ptr(),
17262                c_sa.as_mut_ptr(),
17263                n,
17264                k,
17265                m,
17266                fs,
17267                f,
17268                c_buckets.as_mut_ptr(),
17269                0,
17270                1,
17271            );
17272        }
17273        assert_eq!(rust_t, c_t);
17274        assert_eq!(rust_sa, c_sa);
17275        assert_eq!(rust_buckets, c_buckets);
17276
17277        let mut rust_t = vec![2, 1, 3, 1, 2, 0, 1, 0];
17278        let mut c_t = rust_t.clone();
17279        let mut rust_sa = vec![0; n as usize];
17280        let mut c_sa = rust_sa.clone();
17281        reconstruct_compacted_lms_suffixes_32s_1k_omp(&mut rust_t, &mut rust_sa, n, m, fs, f, 1);
17282        unsafe {
17283            probe_libsais16x64_reconstruct_compacted_lms_suffixes_32s_1k_omp(
17284                c_t.as_mut_ptr(),
17285                c_sa.as_mut_ptr(),
17286                n,
17287                m,
17288                fs,
17289                f,
17290                1,
17291            );
17292        }
17293        assert_eq!(rust_t, c_t);
17294        assert_eq!(rust_sa, c_sa);
17295    }
17296
17297    #[test]
17298    fn libsais16x64_partial_omp_wrappers_match_c() {
17299        let (text, mut rust_sa, mut rust_buckets) = partial_scan_fixture();
17300        let mut c_sa = rust_sa.clone();
17301        let mut c_buckets = rust_buckets.clone();
17302
17303        let rust_d = partial_sorting_scan_left_to_right_16u_omp(
17304            &text,
17305            &mut rust_sa,
17306            text.len() as SaSint,
17307            8,
17308            &mut rust_buckets,
17309            5,
17310            3,
17311            1,
17312        );
17313        let c_d = unsafe {
17314            probe_libsais16x64_partial_sorting_scan_left_to_right_16u_omp(
17315                text.as_ptr(),
17316                c_sa.as_mut_ptr(),
17317                text.len() as SaSint,
17318                8,
17319                c_buckets.as_mut_ptr(),
17320                5,
17321                3,
17322                1,
17323            )
17324        };
17325        assert_eq!(rust_d, c_d);
17326        assert_eq!(rust_sa, c_sa);
17327        assert_eq!(rust_buckets, c_buckets);
17328
17329        let (text, mut rust_sa, mut rust_buckets) = partial_scan_fixture();
17330        rust_sa[6..10].copy_from_slice(&[3, 5 | SAINT_MIN, 7, 9 | SAINT_MIN]);
17331        let mut c_sa = rust_sa.clone();
17332        let mut c_buckets = rust_buckets.clone();
17333        partial_sorting_scan_right_to_left_16u_omp(
17334            &text,
17335            &mut rust_sa,
17336            text.len() as SaSint,
17337            8,
17338            &mut rust_buckets,
17339            0,
17340            5,
17341            3,
17342            1,
17343        );
17344        unsafe {
17345            probe_libsais16x64_partial_sorting_scan_right_to_left_16u_omp(
17346                text.as_ptr(),
17347                c_sa.as_mut_ptr(),
17348                text.len() as SaSint,
17349                8,
17350                c_buckets.as_mut_ptr(),
17351                0,
17352                5,
17353                3,
17354                1,
17355            );
17356        }
17357        assert_eq!(rust_sa, c_sa);
17358        assert_eq!(rust_buckets, c_buckets);
17359
17360        let (text, mut rust_sa, mut rust_buckets) = partial_scan_fixture();
17361        rust_sa[6..10].copy_from_slice(&[3, 5 | SAINT_MIN, 7, 9 | SAINT_MIN]);
17362        let mut c_sa = rust_sa.clone();
17363        let mut c_buckets = rust_buckets.clone();
17364        partial_gsa_scan_right_to_left_16u_omp(
17365            &text,
17366            &mut rust_sa,
17367            text.len() as SaSint,
17368            8,
17369            &mut rust_buckets,
17370            0,
17371            5,
17372            3,
17373            1,
17374        );
17375        unsafe {
17376            probe_libsais16x64_partial_gsa_scan_right_to_left_16u_omp(
17377                text.as_ptr(),
17378                c_sa.as_mut_ptr(),
17379                text.len() as SaSint,
17380                8,
17381                c_buckets.as_mut_ptr(),
17382                0,
17383                5,
17384                3,
17385                1,
17386            );
17387        }
17388        assert_eq!(rust_sa, c_sa);
17389        assert_eq!(rust_buckets, c_buckets);
17390    }
17391
17392    #[test]
17393    fn libsais16x64_final_omp_wrappers_match_c() {
17394        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
17395        let mut c_sa = rust_sa.clone();
17396        let mut c_bucket = rust_bucket.clone();
17397        final_bwt_scan_left_to_right_16u_omp(
17398            &text,
17399            &mut rust_sa,
17400            text.len() as SaSint,
17401            8,
17402            &mut rust_bucket,
17403            1,
17404        );
17405        unsafe {
17406            probe_libsais16x64_final_bwt_scan_left_to_right_16u_omp(
17407                text.as_ptr(),
17408                c_sa.as_mut_ptr(),
17409                text.len() as SaSint,
17410                8,
17411                c_bucket.as_mut_ptr(),
17412                1,
17413            );
17414        }
17415        assert_eq!(rust_sa, c_sa);
17416        assert_eq!(rust_bucket, c_bucket);
17417
17418        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
17419        let mut c_sa = rust_sa.clone();
17420        let mut c_bucket = rust_bucket.clone();
17421        let mut rust_i = vec![-1; 8];
17422        let mut c_i = rust_i.clone();
17423        final_bwt_aux_scan_left_to_right_16u_omp(
17424            &text,
17425            &mut rust_sa,
17426            text.len() as SaSint,
17427            8,
17428            1,
17429            &mut rust_i,
17430            &mut rust_bucket,
17431            1,
17432        );
17433        unsafe {
17434            probe_libsais16x64_final_bwt_aux_scan_left_to_right_16u_omp(
17435                text.as_ptr(),
17436                c_sa.as_mut_ptr(),
17437                text.len() as SaSint,
17438                8,
17439                1,
17440                c_i.as_mut_ptr(),
17441                c_bucket.as_mut_ptr(),
17442                1,
17443            );
17444        }
17445        assert_eq!(rust_sa, c_sa);
17446        assert_eq!(rust_bucket, c_bucket);
17447        assert_eq!(rust_i, c_i);
17448
17449        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
17450        let mut c_sa = rust_sa.clone();
17451        let mut c_bucket = rust_bucket.clone();
17452        final_sorting_scan_left_to_right_16u_omp(
17453            &text,
17454            &mut rust_sa,
17455            text.len() as SaSint,
17456            8,
17457            &mut rust_bucket,
17458            1,
17459        );
17460        unsafe {
17461            probe_libsais16x64_final_sorting_scan_left_to_right_16u_omp(
17462                text.as_ptr(),
17463                c_sa.as_mut_ptr(),
17464                text.len() as SaSint,
17465                8,
17466                c_bucket.as_mut_ptr(),
17467                1,
17468            );
17469        }
17470        assert_eq!(rust_sa, c_sa);
17471        assert_eq!(rust_bucket, c_bucket);
17472
17473        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
17474        let mut c_sa = rust_sa.clone();
17475        let mut c_bucket = rust_bucket.clone();
17476        let rust_index = final_bwt_scan_right_to_left_16u_omp(
17477            &text,
17478            &mut rust_sa,
17479            text.len() as SaSint,
17480            8,
17481            &mut rust_bucket,
17482            1,
17483        );
17484        let c_index = unsafe {
17485            probe_libsais16x64_final_bwt_scan_right_to_left_16u_omp(
17486                text.as_ptr(),
17487                c_sa.as_mut_ptr(),
17488                text.len() as SaSint,
17489                8,
17490                c_bucket.as_mut_ptr(),
17491                1,
17492            )
17493        };
17494        assert_eq!(rust_index, c_index);
17495        assert_eq!(rust_sa, c_sa);
17496        assert_eq!(rust_bucket, c_bucket);
17497
17498        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
17499        let mut c_sa = rust_sa.clone();
17500        let mut c_bucket = rust_bucket.clone();
17501        let mut rust_i = vec![-1; 8];
17502        let mut c_i = rust_i.clone();
17503        final_bwt_aux_scan_right_to_left_16u_omp(
17504            &text,
17505            &mut rust_sa,
17506            text.len() as SaSint,
17507            8,
17508            1,
17509            &mut rust_i,
17510            &mut rust_bucket,
17511            1,
17512        );
17513        unsafe {
17514            probe_libsais16x64_final_bwt_aux_scan_right_to_left_16u_omp(
17515                text.as_ptr(),
17516                c_sa.as_mut_ptr(),
17517                text.len() as SaSint,
17518                8,
17519                1,
17520                c_i.as_mut_ptr(),
17521                c_bucket.as_mut_ptr(),
17522                1,
17523            );
17524        }
17525        assert_eq!(rust_sa, c_sa);
17526        assert_eq!(rust_bucket, c_bucket);
17527        assert_eq!(rust_i, c_i);
17528
17529        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
17530        let mut c_sa = rust_sa.clone();
17531        let mut c_bucket = rust_bucket.clone();
17532        final_sorting_scan_right_to_left_16u_omp(&text, &mut rust_sa, 0, 6, 8, &mut rust_bucket, 1);
17533        unsafe {
17534            probe_libsais16x64_final_sorting_scan_right_to_left_16u_omp(
17535                text.as_ptr(),
17536                c_sa.as_mut_ptr(),
17537                0,
17538                6,
17539                8,
17540                c_bucket.as_mut_ptr(),
17541                1,
17542            );
17543        }
17544        assert_eq!(rust_sa, c_sa);
17545        assert_eq!(rust_bucket, c_bucket);
17546
17547        let (text, mut rust_sa, mut rust_bucket) = final_scan_fixture();
17548        let mut c_sa = rust_sa.clone();
17549        let mut c_bucket = rust_bucket.clone();
17550        final_gsa_scan_right_to_left_16u_omp(&text, &mut rust_sa, 0, 6, 8, &mut rust_bucket, 1);
17551        unsafe {
17552            probe_libsais16x64_final_gsa_scan_right_to_left_16u_omp(
17553                text.as_ptr(),
17554                c_sa.as_mut_ptr(),
17555                0,
17556                6,
17557                8,
17558                c_bucket.as_mut_ptr(),
17559                1,
17560            );
17561        }
17562        assert_eq!(rust_sa, c_sa);
17563        assert_eq!(rust_bucket, c_bucket);
17564    }
17565
17566    #[test]
17567    fn libsais16x64_matches_bruteforce() {
17568        let t = [3, 1, 4, 1, 5, 9, 0, 2];
17569        let mut sa = vec![0; t.len()];
17570        let mut freq = vec![0; ALPHABET_SIZE];
17571        assert_eq!(libsais16x64(&t, &mut sa, 0, Some(&mut freq)), 0);
17572        assert_eq!(sa, brute_sa(&t));
17573        assert_eq!(freq[1], 2);
17574        assert_eq!(freq[9], 1);
17575    }
17576
17577    #[test]
17578    fn libsais16x64_bwt_round_trips() {
17579        let t = [2, 1, 3, 1, 2, 4, 1, 0];
17580        let mut bwt = vec![0; t.len()];
17581        let mut work = vec![0; t.len()];
17582        let primary = libsais16x64_bwt(&t, &mut bwt, &mut work, 0, None);
17583        assert!(primary > 0);
17584
17585        let mut restored = vec![0; t.len()];
17586        assert_eq!(
17587            libsais16x64_unbwt(&bwt, &mut restored, &mut work, None, primary),
17588            0
17589        );
17590        assert_eq!(restored, t);
17591    }
17592
17593    #[test]
17594    fn libsais16x64_plcp_lcp_are_consistent() {
17595        let t = [2, 1, 2, 1, 0];
17596        let sa = brute_sa(&t);
17597        let mut plcp = vec![0; t.len()];
17598        let mut lcp = vec![0; t.len()];
17599        assert_eq!(libsais16x64_plcp(&t, &sa, &mut plcp), 0);
17600        assert_eq!(libsais16x64_lcp(&plcp, &sa, &mut lcp), 0);
17601        assert_eq!(lcp[0], 0);
17602
17603        let mut named_plcp = vec![0; t.len()];
17604        assert_eq!(
17605            compute_phi_omp(&sa, &mut named_plcp, t.len() as SaSint, 1),
17606            0
17607        );
17608        assert_eq!(
17609            compute_plcp_omp(&t, &mut named_plcp, t.len() as SaSint, 1),
17610            0
17611        );
17612        assert_eq!(named_plcp, plcp);
17613
17614        let mut named_lcp = vec![0; t.len()];
17615        assert_eq!(
17616            compute_lcp_omp(&named_plcp, &sa, &mut named_lcp, t.len() as SaSint, 1),
17617            0
17618        );
17619        assert_eq!(named_lcp, lcp);
17620
17621        let mut gsa_plcp = vec![0; t.len()];
17622        let mut named_gsa_plcp = vec![0; t.len()];
17623        assert_eq!(libsais16x64_plcp_gsa(&t, &sa, &mut gsa_plcp), 0);
17624        assert_eq!(
17625            compute_phi_omp(&sa, &mut named_gsa_plcp, t.len() as SaSint, 1),
17626            0
17627        );
17628        assert_eq!(
17629            compute_plcp_gsa_omp(&t, &mut named_gsa_plcp, t.len() as SaSint, 1),
17630            0
17631        );
17632        assert_eq!(named_gsa_plcp, gsa_plcp);
17633    }
17634
17635    #[test]
17636    fn libsais16x64_bwt_copy_16u_omp_uses_block_partition_for_large_inputs() {
17637        let n = 65_600usize;
17638        let a: Vec<SaSint> = (0..n).map(|i| (i * 17) as SaSint).collect();
17639        let mut threaded = vec![0; n];
17640        let mut sequential = vec![0; n];
17641
17642        bwt_copy_16u_omp(&mut threaded, &a, n as SaSint, 4);
17643        bwt_copy_16u(&mut sequential, &a, n as SaSint);
17644
17645        assert_eq!(threaded, sequential);
17646    }
17647
17648    #[test]
17649    fn libsais16x64_plcp_lcp_omp_wrappers_match_single_thread_on_large_inputs() {
17650        let n = 65_600usize;
17651        let text: Vec<u16> = (0..n).map(|i| 1 + (i % 251) as u16).collect();
17652        let sa: Vec<SaSint> = (0..n as SaSint).collect();
17653
17654        let mut plcp_single = vec![0; n];
17655        let mut plcp_threaded = vec![0; n];
17656        assert_eq!(compute_phi_omp(&sa, &mut plcp_single, n as SaSint, 1), 0);
17657        assert_eq!(compute_phi_omp(&sa, &mut plcp_threaded, n as SaSint, 4), 0);
17658        assert_eq!(plcp_threaded, plcp_single);
17659
17660        assert_eq!(compute_plcp_omp(&text, &mut plcp_single, n as SaSint, 1), 0);
17661        assert_eq!(
17662            compute_plcp_omp(&text, &mut plcp_threaded, n as SaSint, 4),
17663            0
17664        );
17665        assert_eq!(plcp_threaded, plcp_single);
17666
17667        let mut lcp_single = vec![0; n];
17668        let mut lcp_threaded = vec![0; n];
17669        assert_eq!(
17670            compute_lcp_omp(&plcp_single, &sa, &mut lcp_single, n as SaSint, 1),
17671            0
17672        );
17673        assert_eq!(
17674            compute_lcp_omp(&plcp_threaded, &sa, &mut lcp_threaded, n as SaSint, 4),
17675            0
17676        );
17677        assert_eq!(lcp_threaded, lcp_single);
17678    }
17679
17680    #[test]
17681    fn libsais16x64_context_allocates_upstream_shaped_buffers() {
17682        let ctx = create_ctx().unwrap();
17683        assert_eq!(ctx.threads, 1);
17684        assert_eq!(ctx.buckets.len(), 8 * ALPHABET_SIZE);
17685        assert!(ctx.thread_state.is_none());
17686
17687        let ctx = create_ctx_omp(2).unwrap();
17688        assert_eq!(ctx.threads, 2);
17689        assert_eq!(ctx.buckets.len(), 8 * ALPHABET_SIZE);
17690        let thread_state = ctx.thread_state.as_ref().unwrap();
17691        assert_eq!(thread_state.len(), 2);
17692        assert_eq!(thread_state[0].buckets.len(), 4 * ALPHABET_SIZE);
17693        assert_eq!(thread_state[0].cache_entries, PER_THREAD_CACHE_SIZE);
17694
17695        let ctx = create_ctx_omp(0).unwrap();
17696        assert_eq!(ctx.threads, 1);
17697        assert!(ctx.thread_state.is_none());
17698    }
17699
17700    #[test]
17701    fn libsais16x64_unbwt_context_allocates_upstream_shaped_buffers() {
17702        let ctx = unbwt_create_ctx().unwrap();
17703        assert_eq!(ctx.threads, 1);
17704        assert_eq!(ctx.bucket2.len(), ALPHABET_SIZE);
17705        assert_eq!(ctx.fastbits.len(), 1 + (1 << UNBWT_FASTBITS));
17706        assert!(ctx.buckets.is_none());
17707
17708        let ctx = unbwt_create_ctx_omp(3).unwrap();
17709        assert_eq!(ctx.threads, 3);
17710        assert_eq!(ctx.bucket2.len(), ALPHABET_SIZE);
17711        assert_eq!(ctx.fastbits.len(), 1 + (1 << UNBWT_FASTBITS));
17712        assert_eq!(ctx.buckets.as_ref().unwrap().len(), 3 * ALPHABET_SIZE);
17713    }
17714
17715    #[test]
17716    fn libsais16x64_named_unbwt_helpers_follow_decode_shapes() {
17717        let t = [0, 1, 2];
17718        let mut p = vec![usize::MAX; 4];
17719        let mut bucket2 = vec![0; ALPHABET_SIZE];
17720        bucket2[0] = 1;
17721        bucket2[1] = 2;
17722        bucket2[2] = 3;
17723        unbwt_calculate_P(&t, &mut p, &mut bucket2, 2, 1, 3);
17724        assert_eq!(p[2], 1);
17725        assert_eq!(p[3], 3);
17726
17727        let p = [1usize, 2, 0];
17728        let mut bucket2 = vec![3; ALPHABET_SIZE];
17729        bucket2[0] = 1;
17730        bucket2[1] = 2;
17731        bucket2[2] = 3;
17732        let fastbits = vec![0; 3];
17733
17734        let mut u = vec![99; 3];
17735        let mut i0 = 0;
17736        unbwt_decode_1(&mut u, &p, &bucket2, &fastbits, 0, &mut i0, 3);
17737        assert_eq!(u, vec![0, 1, 2]);
17738        assert_eq!(i0, 0);
17739
17740        let mut u = vec![99; 6];
17741        let (mut i0, mut i1) = (0, 1);
17742        unbwt_decode_2(&mut u, &p, &bucket2, &fastbits, 0, 3, &mut i0, &mut i1, 2);
17743        assert_eq!(&u[..2], &[0, 1]);
17744        assert_eq!(&u[3..5], &[1, 2]);
17745        assert_eq!((i0, i1), (2, 0));
17746
17747        let mut u = vec![99; 8];
17748        let mut cursors = [0; 8];
17749        unbwt_decode_8(&mut u, &p, &bucket2, &fastbits, 0, 1, &mut cursors, 1);
17750        assert_eq!(u, vec![0; 8]);
17751        assert_eq!(cursors, [1; 8]);
17752    }
17753
17754    #[test]
17755    fn libsais16x64_unbwt_init_parallel_uses_block_partition() {
17756        let n = 70_003usize;
17757        let t: Vec<u16> = (0..n)
17758            .map(|i| ((i.wrapping_mul(37).wrapping_add(i >> 3)) % 251) as u16)
17759            .collect();
17760        let i = [12_345];
17761
17762        let mut single_p = vec![0; n + 1];
17763        let mut threaded_p = vec![0; n + 1];
17764        let mut single_bucket2 = vec![0; ALPHABET_SIZE];
17765        let mut threaded_bucket2 = vec![0; ALPHABET_SIZE];
17766        let mut single_fastbits = vec![0; 1 + (1 << UNBWT_FASTBITS)];
17767        let mut threaded_fastbits = vec![0; 1 + (1 << UNBWT_FASTBITS)];
17768        let mut buckets = vec![0; 4 * ALPHABET_SIZE];
17769
17770        unbwt_init_single(
17771            &t,
17772            &mut single_p,
17773            None,
17774            &i,
17775            &mut single_bucket2,
17776            &mut single_fastbits,
17777        );
17778        unbwt_init_parallel(
17779            &t,
17780            &mut threaded_p,
17781            None,
17782            &i,
17783            &mut threaded_bucket2,
17784            &mut threaded_fastbits,
17785            &mut buckets,
17786            4,
17787        );
17788
17789        assert_eq!(threaded_p, single_p);
17790        assert_eq!(threaded_bucket2, single_bucket2);
17791        assert_eq!(threaded_fastbits, single_fastbits);
17792    }
17793
17794    fn assert_libsais16x64_matches_c(text: &[u16]) {
17795        let mut rust_sa = vec![0; text.len()];
17796        let mut c_sa = vec![0; text.len()];
17797
17798        let rust_rc = libsais16x64(text, &mut rust_sa, 0, None);
17799        let c_rc = unsafe {
17800            probe_public_libsais16x64(text.as_ptr(), c_sa.as_mut_ptr(), text.len() as SaSint, 0)
17801        };
17802
17803        assert_eq!(rust_rc, c_rc);
17804        assert_eq!(rust_sa, c_sa);
17805    }
17806
17807    fn assert_libsais16x64_gsa_matches_c(text: &[u16]) {
17808        let mut rust_sa = vec![0; text.len()];
17809        let mut c_sa = vec![0; text.len()];
17810
17811        let rust_rc = libsais16x64_gsa(text, &mut rust_sa, 0, None);
17812        let c_rc = unsafe {
17813            probe_public_libsais16x64_gsa(text.as_ptr(), c_sa.as_mut_ptr(), text.len() as SaSint, 0)
17814        };
17815
17816        assert_eq!(rust_rc, c_rc);
17817        assert_eq!(rust_sa, c_sa);
17818    }
17819
17820    fn assert_libsais16x64_long_matches_c_with_fs(text: &[SaSint], k: SaSint, fs: SaSint) {
17821        let mut rust_t = text.to_vec();
17822        let mut c_t = text.to_vec();
17823        let mut rust_sa = vec![0; text.len() + fs as usize];
17824        let mut c_sa = vec![0; text.len() + fs as usize];
17825
17826        let rust_rc = libsais16x64_long(&mut rust_t, &mut rust_sa, k, fs);
17827        let c_rc = unsafe {
17828            probe_public_libsais16x64_long(
17829                c_t.as_mut_ptr(),
17830                c_sa.as_mut_ptr(),
17831                c_t.len() as SaSint,
17832                k,
17833                fs,
17834            )
17835        };
17836
17837        assert_eq!(rust_rc, c_rc);
17838        assert_eq!(rust_t, c_t);
17839        assert_eq!(rust_sa, c_sa);
17840    }
17841
17842    fn assert_libsais16x64_long_matches_c(text: &[SaSint], k: SaSint) {
17843        assert_libsais16x64_long_matches_c_with_fs(text, k, 0);
17844    }
17845
17846    fn make_main_32s_stress_text(len: usize, alphabet: SaSint) -> Vec<SaSint> {
17847        let mut state: u32 = 0x1357_9bdf;
17848        let mut t = Vec::with_capacity(len + 1);
17849
17850        for i in 0..len {
17851            state = state.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
17852            let mut value = ((state >> 16) % (alphabet as u32 - 1)) as SaSint + 1;
17853            if i % 17 < 8 {
17854                value = ((i / 17) as SaSint % 11) + 1;
17855            }
17856            if i % 29 < 10 {
17857                value = (((i / 29) as SaSint * 3) % 19) + 1;
17858            }
17859            if i % 64 >= 48 {
17860                value = t[i - 48];
17861            }
17862            t.push(value);
17863        }
17864
17865        t.push(0);
17866        t
17867    }
17868
17869    fn make_recursive_main_32s_text(repeats: usize) -> Vec<SaSint> {
17870        let motif = [9, 4, 9, 2, 9, 4, 9, 1];
17871        let mut t = Vec::with_capacity(repeats * motif.len() + 1);
17872        for _ in 0..repeats {
17873            t.extend_from_slice(&motif);
17874        }
17875        t.push(0);
17876        t
17877    }
17878
17879    fn assert_main_32s_entry_matches_c(mut t: Vec<SaSint>, k: SaSint, fs: SaSint) {
17880        let n = t.len() as SaSint;
17881        let threads = 1;
17882        let mut sa = vec![0; t.len() + fs as usize];
17883        let initial_t = t.clone();
17884        let initial_sa = sa.clone();
17885
17886        let c_result = unsafe {
17887            probe_libsais16x64_main_32s_entry(t.as_mut_ptr(), sa.as_mut_ptr(), n, k, fs, threads)
17888        };
17889        let c_t = t.clone();
17890        let c_sa = sa.clone();
17891
17892        t.copy_from_slice(&initial_t);
17893        sa.copy_from_slice(&initial_sa);
17894
17895        let mut thread_state = alloc_thread_state(threads).unwrap();
17896        let rust_result = main_32s_entry(
17897            t.as_mut_ptr(),
17898            &mut sa,
17899            n,
17900            k,
17901            fs,
17902            threads,
17903            &mut thread_state,
17904        );
17905
17906        assert_eq!(rust_result, c_result);
17907        assert_eq!(t, c_t);
17908        assert_eq!(&sa[..n as usize], &c_sa[..n as usize]);
17909        if fs == 0 {
17910            assert_eq!(sa, c_sa);
17911        }
17912    }
17913
17914    #[test]
17915    fn libsais16x64_main_32s_entry_matches_c_for_local_32s_paths() {
17916        assert_main_32s_entry_matches_c(make_main_32s_stress_text(1024, 300), 300, 2048);
17917        assert_main_32s_entry_matches_c(make_main_32s_stress_text(1024, 400), 400, 2048);
17918        assert_main_32s_entry_matches_c(make_main_32s_stress_text(1024, 700), 700, 2048);
17919        assert_main_32s_entry_matches_c(make_main_32s_stress_text(1024, 1501), 1501, 2048);
17920        assert_main_32s_entry_matches_c(make_recursive_main_32s_text(24), 300, 0);
17921        assert_main_32s_entry_matches_c(make_recursive_main_32s_text(24), 1501, 0);
17922    }
17923
17924    fn assert_libsais16x64_bwt_matches_c(text: &[u16]) {
17925        let mut rust_u = vec![0; text.len()];
17926        let mut rust_a = vec![0; text.len()];
17927        let mut c_u = vec![0; text.len()];
17928        let mut c_a = vec![0; text.len()];
17929
17930        let rust_rc = libsais16x64_bwt(text, &mut rust_u, &mut rust_a, 0, None);
17931        let c_rc = unsafe {
17932            probe_public_libsais16x64_bwt(
17933                text.as_ptr(),
17934                c_u.as_mut_ptr(),
17935                c_a.as_mut_ptr(),
17936                text.len() as SaSint,
17937                0,
17938            )
17939        };
17940
17941        assert_eq!(rust_rc, c_rc);
17942        assert_eq!(rust_u, c_u);
17943    }
17944
17945    fn assert_libsais16x64_bwt_aux_matches_c(text: &[u16], r: SaSint) {
17946        let aux_len = if text.is_empty() {
17947            0
17948        } else {
17949            (text.len() - 1) / r as usize + 1
17950        };
17951        let mut rust_u = vec![0; text.len()];
17952        let mut rust_a = vec![0; text.len()];
17953        let mut rust_i = vec![0; aux_len];
17954        let mut c_u = vec![0; text.len()];
17955        let mut c_a = vec![0; text.len()];
17956        let mut c_i = vec![0; aux_len];
17957
17958        let rust_rc = libsais16x64_bwt_aux(text, &mut rust_u, &mut rust_a, 0, None, r, &mut rust_i);
17959        let c_rc = unsafe {
17960            probe_public_libsais16x64_bwt_aux(
17961                text.as_ptr(),
17962                c_u.as_mut_ptr(),
17963                c_a.as_mut_ptr(),
17964                text.len() as SaSint,
17965                0,
17966                r,
17967                c_i.as_mut_ptr(),
17968            )
17969        };
17970
17971        assert_eq!(rust_rc, c_rc);
17972        assert_eq!(rust_u, c_u);
17973        assert_eq!(rust_i, c_i);
17974    }
17975
17976    fn assert_libsais16x64_freq_outputs_match_c(text: &[u16], gsa_text: &[u16]) {
17977        let mut rust_sa = vec![0; text.len()];
17978        let mut c_sa = vec![0; text.len()];
17979        let mut rust_freq = vec![-1; ALPHABET_SIZE];
17980        let mut c_freq = vec![-1; ALPHABET_SIZE];
17981
17982        let rust_rc = libsais16x64(text, &mut rust_sa, 0, Some(&mut rust_freq));
17983        let c_rc = unsafe {
17984            probe_public_libsais16x64_freq(
17985                text.as_ptr(),
17986                c_sa.as_mut_ptr(),
17987                text.len() as SaSint,
17988                0,
17989                c_freq.as_mut_ptr(),
17990            )
17991        };
17992        assert_eq!(rust_rc, c_rc);
17993        assert_eq!(rust_sa, c_sa);
17994        assert_eq!(rust_freq, c_freq);
17995
17996        let mut rust_gsa = vec![0; gsa_text.len()];
17997        let mut c_gsa = vec![0; gsa_text.len()];
17998        rust_freq.fill(-1);
17999        c_freq.fill(-1);
18000        let rust_rc = libsais16x64_gsa(gsa_text, &mut rust_gsa, 0, Some(&mut rust_freq));
18001        let c_rc = unsafe {
18002            probe_public_libsais16x64_gsa_freq(
18003                gsa_text.as_ptr(),
18004                c_gsa.as_mut_ptr(),
18005                gsa_text.len() as SaSint,
18006                0,
18007                c_freq.as_mut_ptr(),
18008            )
18009        };
18010        assert_eq!(rust_rc, c_rc);
18011        assert_eq!(rust_gsa, c_gsa);
18012        assert_eq!(rust_freq, c_freq);
18013
18014        let mut rust_u = vec![0; text.len()];
18015        let mut rust_a = vec![0; text.len()];
18016        let mut c_u = vec![0; text.len()];
18017        let mut c_a = vec![0; text.len()];
18018        rust_freq.fill(-1);
18019        c_freq.fill(-1);
18020        let rust_rc = libsais16x64_bwt(text, &mut rust_u, &mut rust_a, 0, Some(&mut rust_freq));
18021        let c_rc = unsafe {
18022            probe_public_libsais16x64_bwt_freq(
18023                text.as_ptr(),
18024                c_u.as_mut_ptr(),
18025                c_a.as_mut_ptr(),
18026                text.len() as SaSint,
18027                0,
18028                c_freq.as_mut_ptr(),
18029            )
18030        };
18031        assert_eq!(rust_rc, c_rc);
18032        assert_eq!(rust_u, c_u);
18033        assert_eq!(rust_freq, c_freq);
18034
18035        let r = 4;
18036        let aux_len = (text.len() - 1) / r as usize + 1;
18037        let mut rust_i = vec![0; aux_len];
18038        let mut c_i = vec![0; aux_len];
18039        rust_freq.fill(-1);
18040        c_freq.fill(-1);
18041        let rust_rc = libsais16x64_bwt_aux(
18042            text,
18043            &mut rust_u,
18044            &mut rust_a,
18045            0,
18046            Some(&mut rust_freq),
18047            r,
18048            &mut rust_i,
18049        );
18050        let c_rc = unsafe {
18051            probe_public_libsais16x64_bwt_aux_freq(
18052                text.as_ptr(),
18053                c_u.as_mut_ptr(),
18054                c_a.as_mut_ptr(),
18055                text.len() as SaSint,
18056                0,
18057                c_freq.as_mut_ptr(),
18058                r,
18059                c_i.as_mut_ptr(),
18060            )
18061        };
18062        assert_eq!(rust_rc, c_rc);
18063        assert_eq!(rust_u, c_u);
18064        assert_eq!(rust_i, c_i);
18065        assert_eq!(rust_freq, c_freq);
18066    }
18067
18068    fn assert_libsais16x64_unbwt_matches_c(text: &[u16]) {
18069        let mut bwt = vec![0; text.len()];
18070        let mut work = vec![0; text.len()];
18071        let primary = libsais16x64_bwt(text, &mut bwt, &mut work, 0, None);
18072        assert!(primary >= 0);
18073
18074        let mut rust_u = vec![0; text.len()];
18075        let mut rust_a = vec![0; text.len() + 1];
18076        let mut c_u = vec![0; text.len()];
18077        let mut c_a = vec![0; text.len() + 1];
18078
18079        let rust_rc = libsais16x64_unbwt(&bwt, &mut rust_u, &mut rust_a, None, primary);
18080        let c_rc = unsafe {
18081            probe_public_libsais16x64_unbwt(
18082                bwt.as_ptr(),
18083                c_u.as_mut_ptr(),
18084                c_a.as_mut_ptr(),
18085                bwt.len() as SaSint,
18086                primary,
18087            )
18088        };
18089
18090        assert_eq!(rust_rc, c_rc);
18091        assert_eq!(rust_u, c_u);
18092        assert_eq!(rust_u, text);
18093    }
18094
18095    fn assert_libsais16x64_unbwt_aux_matches_c(text: &[u16], r: SaSint) {
18096        let mut bwt = vec![0; text.len()];
18097        let mut work = vec![0; text.len()];
18098        let mut aux = vec![0; (text.len() - 1) / r as usize + 1];
18099        let bwt_rc = libsais16x64_bwt_aux(text, &mut bwt, &mut work, 0, None, r, &mut aux);
18100        assert_eq!(bwt_rc, 0);
18101
18102        let mut rust_u = vec![0; text.len()];
18103        let mut rust_a = vec![0; text.len() + 1];
18104        let mut c_u = vec![0; text.len()];
18105        let mut c_a = vec![0; text.len() + 1];
18106
18107        let rust_rc = libsais16x64_unbwt_aux(&bwt, &mut rust_u, &mut rust_a, None, r, &aux);
18108        let c_rc = unsafe {
18109            probe_public_libsais16x64_unbwt_aux(
18110                bwt.as_ptr(),
18111                c_u.as_mut_ptr(),
18112                c_a.as_mut_ptr(),
18113                bwt.len() as SaSint,
18114                r,
18115                aux.as_ptr(),
18116            )
18117        };
18118
18119        assert_eq!(rust_rc, c_rc);
18120        assert_eq!(rust_u, c_u);
18121        assert_eq!(rust_u, text);
18122    }
18123
18124    fn assert_libsais16x64_unbwt_freq_matches_c(text: &[u16]) {
18125        let mut freq = vec![0; ALPHABET_SIZE];
18126        let mut bwt = vec![0; text.len()];
18127        let mut work = vec![0; text.len()];
18128        let primary = libsais16x64_bwt(text, &mut bwt, &mut work, 0, Some(&mut freq));
18129        assert!(primary >= 0);
18130
18131        let mut rust_u = vec![0; text.len()];
18132        let mut rust_a = vec![0; text.len() + 1];
18133        let mut c_u = vec![0; text.len()];
18134        let mut c_a = vec![0; text.len() + 1];
18135
18136        let rust_rc = libsais16x64_unbwt(&bwt, &mut rust_u, &mut rust_a, Some(&freq), primary);
18137        let c_rc = unsafe {
18138            probe_public_libsais16x64_unbwt_freq(
18139                bwt.as_ptr(),
18140                c_u.as_mut_ptr(),
18141                c_a.as_mut_ptr(),
18142                bwt.len() as SaSint,
18143                freq.as_ptr(),
18144                primary,
18145            )
18146        };
18147        assert_eq!(rust_rc, c_rc);
18148        assert_eq!(rust_u, c_u);
18149        assert_eq!(rust_u, text);
18150
18151        let r = 4;
18152        let mut aux = vec![0; (text.len() - 1) / r as usize + 1];
18153        let bwt_rc =
18154            libsais16x64_bwt_aux(text, &mut bwt, &mut work, 0, Some(&mut freq), r, &mut aux);
18155        assert_eq!(bwt_rc, 0);
18156
18157        rust_u.fill(0);
18158        rust_a.fill(0);
18159        c_u.fill(0);
18160        c_a.fill(0);
18161        let rust_rc = libsais16x64_unbwt_aux(&bwt, &mut rust_u, &mut rust_a, Some(&freq), r, &aux);
18162        let c_rc = unsafe {
18163            probe_public_libsais16x64_unbwt_aux_freq(
18164                bwt.as_ptr(),
18165                c_u.as_mut_ptr(),
18166                c_a.as_mut_ptr(),
18167                bwt.len() as SaSint,
18168                freq.as_ptr(),
18169                r,
18170                aux.as_ptr(),
18171            )
18172        };
18173        assert_eq!(rust_rc, c_rc);
18174        assert_eq!(rust_u, c_u);
18175        assert_eq!(rust_u, text);
18176    }
18177
18178    fn assert_libsais16x64_plcp_lcp_matches_c(text: &[u16]) {
18179        let mut sa = vec![0; text.len()];
18180        assert_eq!(libsais16x64(text, &mut sa, 0, None), 0);
18181
18182        let mut rust_plcp = vec![0; text.len()];
18183        let mut c_plcp = vec![0; text.len()];
18184        let rust_rc = libsais16x64_plcp(text, &sa, &mut rust_plcp);
18185        let c_rc = unsafe {
18186            probe_public_libsais16x64_plcp(
18187                text.as_ptr(),
18188                sa.as_ptr(),
18189                c_plcp.as_mut_ptr(),
18190                text.len() as SaSint,
18191            )
18192        };
18193        assert_eq!(rust_rc, c_rc);
18194        assert_eq!(rust_plcp, c_plcp);
18195
18196        let mut rust_lcp = vec![0; text.len()];
18197        let mut c_lcp = vec![0; text.len()];
18198        let rust_rc = libsais16x64_lcp(&rust_plcp, &sa, &mut rust_lcp);
18199        let c_rc = unsafe {
18200            probe_public_libsais16x64_lcp(
18201                c_plcp.as_ptr(),
18202                sa.as_ptr(),
18203                c_lcp.as_mut_ptr(),
18204                text.len() as SaSint,
18205            )
18206        };
18207        assert_eq!(rust_rc, c_rc);
18208        assert_eq!(rust_lcp, c_lcp);
18209    }
18210
18211    fn assert_libsais16x64_plcp_gsa_matches_c(text: &[u16]) {
18212        let mut sa = vec![0; text.len()];
18213        assert_eq!(libsais16x64_gsa(text, &mut sa, 0, None), 0);
18214
18215        let mut rust_plcp = vec![0; text.len()];
18216        let mut c_plcp = vec![0; text.len()];
18217        let rust_rc = libsais16x64_plcp_gsa(text, &sa, &mut rust_plcp);
18218        let c_rc = unsafe {
18219            probe_public_libsais16x64_plcp_gsa(
18220                text.as_ptr(),
18221                sa.as_ptr(),
18222                c_plcp.as_mut_ptr(),
18223                text.len() as SaSint,
18224            )
18225        };
18226        assert_eq!(rust_rc, c_rc);
18227        assert_eq!(rust_plcp, c_plcp);
18228    }
18229
18230    #[test]
18231    fn public_libsais16x64_matches_upstream_c() {
18232        for text in [
18233            [].as_slice(),
18234            &[1][..],
18235            &[2, 1, 3, 1, 2, 0],
18236            &[2, 1, 3, 1, 2, 4, 1, 0],
18237            &[65_535, 1, 65_534, 1, 0],
18238            &[7, 7, 7, 7, 7, 0],
18239        ] {
18240            assert_libsais16x64_matches_c(text);
18241        }
18242    }
18243
18244    #[test]
18245    fn public_libsais16x64_bwt_matches_upstream_c() {
18246        for text in [
18247            [].as_slice(),
18248            &[1][..],
18249            &[2, 1, 3, 1, 2, 0],
18250            &[2, 1, 3, 1, 2, 4, 1, 0],
18251            &[65_535, 1, 65_534, 1, 0],
18252            &[7, 7, 7, 7, 7, 0],
18253        ] {
18254            assert_libsais16x64_bwt_matches_c(text);
18255        }
18256    }
18257
18258    #[test]
18259    fn public_libsais16x64_gsa_matches_upstream_c() {
18260        for text in [&[0][..], &[2, 1, 0], &[2, 1, 0, 3, 1, 0], &[7, 7, 0, 7, 0]] {
18261            assert_libsais16x64_gsa_matches_c(text);
18262        }
18263    }
18264
18265    #[test]
18266    fn public_libsais16x64_long_matches_upstream_c() {
18267        for (text, k) in [
18268            (&[][..], 0),
18269            (&[0][..], 1),
18270            (&[1, 2, 1, 0][..], 3),
18271            (&[2, 1, 2, 1, 0][..], 3),
18272            (&[3, 3, 3, 2, 1, 0][..], 4),
18273        ] {
18274            assert_libsais16x64_long_matches_c(text, k);
18275        }
18276
18277        assert_libsais16x64_long_matches_c_with_fs(&[2, 1, 3, 1, 2, 0], 4, 64);
18278    }
18279
18280    #[test]
18281    fn public_libsais16x64_plcp_lcp_matches_upstream_c() {
18282        for text in [
18283            &[2, 1, 3, 1, 2, 0][..],
18284            &[2, 1, 3, 1, 2, 4, 1, 0],
18285            &[65_535, 1, 65_534, 1, 0],
18286            &[7, 7, 7, 7, 7, 0],
18287        ] {
18288            assert_libsais16x64_plcp_lcp_matches_c(text);
18289        }
18290    }
18291
18292    #[test]
18293    fn public_libsais16x64_plcp_gsa_matches_upstream_c() {
18294        for text in [&[0][..], &[2, 1, 0], &[2, 1, 0, 3, 1, 0], &[7, 7, 0, 7, 0]] {
18295            assert_libsais16x64_plcp_gsa_matches_c(text);
18296        }
18297    }
18298
18299    #[test]
18300    fn public_libsais16x64_bwt_aux_matches_upstream_c() {
18301        for text in [
18302            &[2, 1, 3, 1, 2, 0][..],
18303            &[2, 1, 3, 1, 2, 4, 1, 0],
18304            &[65_535, 1, 65_534, 1, 0],
18305            &[7, 7, 7, 7, 7, 0],
18306        ] {
18307            assert_libsais16x64_bwt_aux_matches_c(text, 4);
18308        }
18309    }
18310
18311    #[test]
18312    fn public_libsais16x64_frequency_outputs_match_upstream_c() {
18313        assert_libsais16x64_freq_outputs_match_c(&[65_535, 1, 2, 1, 0], &[65_535, 1, 0, 2, 1, 0]);
18314    }
18315
18316    #[test]
18317    fn public_libsais16x64_unbwt_with_frequency_matches_upstream_c() {
18318        assert_libsais16x64_unbwt_freq_matches_c(&[65_535, 1, 2, 1, 0]);
18319    }
18320
18321    #[test]
18322    fn public_libsais16x64_unbwt_matches_upstream_c() {
18323        for text in [
18324            &[1][..],
18325            &[2, 1, 3, 1, 2, 0],
18326            &[2, 1, 3, 1, 2, 4, 1, 0],
18327            &[65_535, 1, 65_534, 1, 0],
18328            &[7, 7, 7, 7, 7, 0],
18329        ] {
18330            assert_libsais16x64_unbwt_matches_c(text);
18331        }
18332    }
18333
18334    #[test]
18335    fn public_libsais16x64_unbwt_aux_matches_upstream_c() {
18336        for text in [
18337            &[2, 1, 3, 1, 2, 0][..],
18338            &[2, 1, 3, 1, 2, 4, 1, 0],
18339            &[65_535, 1, 65_534, 1, 0],
18340            &[7, 7, 7, 7, 7, 0],
18341        ] {
18342            assert_libsais16x64_unbwt_aux_matches_c(text, 4);
18343        }
18344    }
18345
18346    #[test]
18347    fn public_libsais16x64_unbwt_aux_exercises_decode_dispatch_cases() {
18348        for len in [2usize, 5, 9, 13, 17, 21, 25, 29, 33, 37] {
18349            let text = (0..len)
18350                .map(|i| ((i * 37 + 11) % 65_535 + 1) as u16)
18351                .collect::<Vec<_>>();
18352            assert_libsais16x64_unbwt_aux_matches_c(&text, 4);
18353        }
18354    }
18355
18356    #[test]
18357    fn libsais16x64_lcp_helpers_reject_invalid_suffix_entries() {
18358        let text = [2, 1, 2, 1, 0];
18359        let mut plcp = vec![0; text.len()];
18360        let mut lcp = vec![0; text.len()];
18361
18362        assert_eq!(libsais16x64_plcp(&text, &[0, 1, -1, 3, 4], &mut plcp), -1);
18363        assert_eq!(libsais16x64_plcp(&text, &[0, 1, 2, 3, 5], &mut plcp), -1);
18364        assert_eq!(libsais16x64_lcp(&plcp, &[0, 1, -1, 3, 4], &mut lcp), -1);
18365        assert_eq!(libsais16x64_lcp(&plcp, &[0, 1, 2, 3, 5], &mut lcp), -1);
18366    }
18367
18368    #[test]
18369    fn libsais16x64_rejects_invalid_public_arguments() {
18370        let text = [2, 1, 3, 1, 2, 0];
18371        let int_text = [1, 2, 1, 0];
18372        let mut int_text_for_short_sa = int_text.to_vec();
18373        let mut int_text_for_negative_fs = int_text.to_vec();
18374        let mut int_text_for_alias = int_text.to_vec();
18375        let mut sa = vec![0; text.len() - 1];
18376        let mut int_sa = vec![0; int_text.len() - 1];
18377        let mut full_int_sa = vec![0; int_text.len()];
18378        let mut freq = vec![0; ALPHABET_SIZE - 1];
18379        let mut u = vec![0; text.len() - 1];
18380        let mut a = vec![0; text.len() - 1];
18381        let mut full_u = vec![0; text.len()];
18382        let mut full_a = vec![0; text.len()];
18383        let mut aux = vec![0; 1];
18384
18385        assert_eq!(libsais16x64(&text, &mut sa, 0, None), -1);
18386        assert_eq!(libsais16x64(&text, &mut full_a, 0, Some(&mut freq)), -1);
18387        assert_eq!(libsais16x64_gsa(&[1, 2, 3], &mut full_a[..3], 0, None), -1);
18388        assert_eq!(
18389            libsais16x64_long(&mut int_text_for_short_sa, &mut int_sa, 3, 0),
18390            -1
18391        );
18392        assert_eq!(
18393            libsais16x64_long(&mut int_text_for_negative_fs, &mut full_int_sa, 3, -1),
18394            -1
18395        );
18396        assert_eq!(
18397            libsais16x64_int(&mut int_text_for_alias, &mut full_int_sa, 3, -1),
18398            -1
18399        );
18400        assert_eq!(libsais16x64_bwt(&text, &mut u, &mut full_a, 0, None), -1);
18401        assert_eq!(libsais16x64_bwt(&text, &mut full_u, &mut a, 0, None), -1);
18402        assert_eq!(
18403            libsais16x64_bwt_aux(&text, &mut full_u, &mut full_a, 0, None, 0, &mut aux),
18404            -1
18405        );
18406        assert_eq!(
18407            libsais16x64_bwt_aux(&text, &mut full_u, &mut full_a, 0, None, 3, &mut aux),
18408            -1
18409        );
18410        assert_eq!(
18411            libsais16x64_bwt_aux(&text, &mut full_u, &mut full_a, 0, None, 4, &mut aux),
18412            -1
18413        );
18414        assert_eq!(create_ctx_omp(-1), None);
18415        assert_eq!(unbwt_create_ctx_omp(-1), None);
18416    }
18417
18418    #[test]
18419    fn libsais16x64_unbwt_rejects_invalid_public_arguments() {
18420        let text = [2, 1, 3, 1, 2, 0];
18421        let mut bwt = vec![0; text.len()];
18422        let mut work = vec![0; text.len()];
18423        let primary = libsais16x64_bwt(&text, &mut bwt, &mut work, 0, None);
18424
18425        let mut short_u = vec![0; text.len() - 1];
18426        let mut short_a = vec![0; text.len() - 1];
18427        let mut full_u = vec![0; text.len()];
18428        let mut full_a = vec![0; text.len()];
18429        let short_freq = vec![0; ALPHABET_SIZE - 1];
18430        let short_aux = vec![primary];
18431        let bad_aux = vec![0, 0];
18432        let good_aux = vec![primary, 4];
18433
18434        assert_eq!(
18435            libsais16x64_unbwt(&bwt, &mut short_u, &mut full_a, None, primary),
18436            -1
18437        );
18438        assert_eq!(
18439            libsais16x64_unbwt(&bwt, &mut full_u, &mut short_a, None, primary),
18440            -1
18441        );
18442        assert_eq!(
18443            libsais16x64_unbwt(&bwt, &mut full_u, &mut full_a, Some(&short_freq), primary),
18444            -1
18445        );
18446        assert_eq!(
18447            libsais16x64_unbwt(&bwt, &mut full_u, &mut full_a, None, 0),
18448            -1
18449        );
18450        assert_eq!(
18451            libsais16x64_unbwt(
18452                &bwt,
18453                &mut full_u,
18454                &mut full_a,
18455                None,
18456                text.len() as SaSint + 1
18457            ),
18458            -1
18459        );
18460        assert_eq!(
18461            libsais16x64_unbwt_aux(&bwt, &mut full_u, &mut full_a, None, 0, &good_aux),
18462            -1
18463        );
18464        assert_eq!(
18465            libsais16x64_unbwt_aux(&bwt, &mut full_u, &mut full_a, None, 3, &good_aux),
18466            -1
18467        );
18468        assert_eq!(
18469            libsais16x64_unbwt_aux(&bwt, &mut full_u, &mut full_a, None, 4, &short_aux),
18470            -1
18471        );
18472        assert_eq!(
18473            libsais16x64_unbwt_aux(&bwt, &mut full_u, &mut full_a, None, 4, &bad_aux),
18474            -1
18475        );
18476    }
18477
18478    #[test]
18479    fn libsais16x64_ctx_rejects_invalid_public_arguments() {
18480        let text = [2, 1, 3, 1, 2, 0];
18481        let mut ctx = create_ctx().unwrap();
18482        let mut sa = vec![0; text.len() - 1];
18483        let mut freq = vec![0; ALPHABET_SIZE - 1];
18484        let mut u = vec![0; text.len() - 1];
18485        let mut a = vec![0; text.len() - 1];
18486        let mut full_u = vec![0; text.len()];
18487        let mut full_a = vec![0; text.len()];
18488        let mut aux = vec![0; 1];
18489
18490        assert_eq!(libsais16x64_ctx(&mut ctx, &text, &mut sa, 0, None), -1);
18491        assert_eq!(
18492            libsais16x64_ctx(&mut ctx, &text, &mut full_a, 0, Some(&mut freq)),
18493            -1
18494        );
18495        assert_eq!(
18496            libsais16x64_gsa_ctx(&mut ctx, &[1, 2, 3], &mut full_a[..3], 0, None),
18497            -1
18498        );
18499        assert_eq!(
18500            libsais16x64_bwt_ctx(&mut ctx, &text, &mut u, &mut full_a, 0, None),
18501            -1
18502        );
18503        assert_eq!(
18504            libsais16x64_bwt_ctx(&mut ctx, &text, &mut full_u, &mut a, 0, None),
18505            -1
18506        );
18507        assert_eq!(
18508            libsais16x64_bwt_aux_ctx(
18509                &mut ctx,
18510                &text,
18511                &mut full_u,
18512                &mut full_a,
18513                0,
18514                None,
18515                0,
18516                &mut aux
18517            ),
18518            -1
18519        );
18520        assert_eq!(
18521            libsais16x64_bwt_aux_ctx(
18522                &mut ctx,
18523                &text,
18524                &mut full_u,
18525                &mut full_a,
18526                0,
18527                None,
18528                3,
18529                &mut aux
18530            ),
18531            -1
18532        );
18533        assert_eq!(
18534            libsais16x64_bwt_aux_ctx(
18535                &mut ctx,
18536                &text,
18537                &mut full_u,
18538                &mut full_a,
18539                0,
18540                None,
18541                4,
18542                &mut aux
18543            ),
18544            -1
18545        );
18546
18547        let mut default_ctx = Context::default();
18548        assert_eq!(
18549            libsais16x64_ctx(&mut default_ctx, &text, &mut full_a, 0, None),
18550            -2
18551        );
18552
18553        let mut bad_bucket_ctx = create_ctx().unwrap();
18554        bad_bucket_ctx.buckets.clear();
18555        assert_eq!(
18556            libsais16x64_ctx(&mut bad_bucket_ctx, &text, &mut full_a, 0, None),
18557            -2
18558        );
18559
18560        let mut short_thread_state_ctx = create_ctx_omp(2).unwrap();
18561        short_thread_state_ctx
18562            .thread_state
18563            .as_mut()
18564            .unwrap()
18565            .truncate(1);
18566        assert_eq!(
18567            libsais16x64_ctx(&mut short_thread_state_ctx, &text, &mut full_a, 0, None),
18568            -2
18569        );
18570    }
18571
18572    #[test]
18573    fn libsais16x64_unbwt_ctx_rejects_invalid_public_arguments() {
18574        let text = [2, 1, 3, 1, 2, 0];
18575        let mut bwt = vec![0; text.len()];
18576        let mut work = vec![0; text.len()];
18577        let primary = libsais16x64_bwt(&text, &mut bwt, &mut work, 0, None);
18578        let mut ctx = unbwt_create_ctx().unwrap();
18579
18580        let mut short_u = vec![0; text.len() - 1];
18581        let mut short_a = vec![0; text.len() - 1];
18582        let mut full_u = vec![0; text.len()];
18583        let mut full_a = vec![0; text.len()];
18584        let short_freq = vec![0; ALPHABET_SIZE - 1];
18585        let short_aux = vec![primary];
18586        let bad_aux = vec![0, 0];
18587        let good_aux = vec![primary, 4];
18588
18589        assert_eq!(
18590            libsais16x64_unbwt_ctx(&mut ctx, &bwt, &mut short_u, &mut full_a, None, primary),
18591            -1
18592        );
18593        assert_eq!(
18594            libsais16x64_unbwt_ctx(&mut ctx, &bwt, &mut full_u, &mut short_a, None, primary),
18595            -1
18596        );
18597        assert_eq!(
18598            libsais16x64_unbwt_ctx(
18599                &mut ctx,
18600                &bwt,
18601                &mut full_u,
18602                &mut full_a,
18603                Some(&short_freq),
18604                primary
18605            ),
18606            -1
18607        );
18608        assert_eq!(
18609            libsais16x64_unbwt_ctx(&mut ctx, &bwt, &mut full_u, &mut full_a, None, 0),
18610            -1
18611        );
18612        assert_eq!(
18613            libsais16x64_unbwt_aux_ctx(
18614                &mut ctx,
18615                &bwt,
18616                &mut full_u,
18617                &mut full_a,
18618                None,
18619                0,
18620                &good_aux
18621            ),
18622            -1
18623        );
18624        assert_eq!(
18625            libsais16x64_unbwt_aux_ctx(
18626                &mut ctx,
18627                &bwt,
18628                &mut full_u,
18629                &mut full_a,
18630                None,
18631                3,
18632                &good_aux
18633            ),
18634            -1
18635        );
18636        assert_eq!(
18637            libsais16x64_unbwt_aux_ctx(
18638                &mut ctx,
18639                &bwt,
18640                &mut full_u,
18641                &mut full_a,
18642                None,
18643                4,
18644                &short_aux
18645            ),
18646            -1
18647        );
18648        assert_eq!(
18649            libsais16x64_unbwt_aux_ctx(&mut ctx, &bwt, &mut full_u, &mut full_a, None, 4, &bad_aux),
18650            -1
18651        );
18652    }
18653
18654    #[test]
18655    fn libsais16x64_context_wrappers_match_direct_calls() {
18656        let text = [2, 1, 3, 1, 2, 0];
18657        let mut ctx = create_ctx().unwrap();
18658
18659        let mut direct_sa = vec![0; text.len()];
18660        let mut ctx_sa = vec![0; text.len()];
18661        assert_eq!(libsais16x64(&text, &mut direct_sa, 0, None), 0);
18662        assert_eq!(libsais16x64_ctx(&mut ctx, &text, &mut ctx_sa, 0, None), 0);
18663        assert_eq!(ctx_sa, direct_sa);
18664
18665        let mut direct_bwt = vec![0; text.len()];
18666        let mut direct_work = vec![0; text.len()];
18667        let mut ctx_bwt = vec![0; text.len()];
18668        let mut ctx_work = vec![0; text.len()];
18669        assert_eq!(
18670            libsais16x64_bwt(&text, &mut direct_bwt, &mut direct_work, 0, None),
18671            libsais16x64_bwt_ctx(&mut ctx, &text, &mut ctx_bwt, &mut ctx_work, 0, None)
18672        );
18673        assert_eq!(ctx_bwt, direct_bwt);
18674
18675        let mut direct_aux = vec![0; 2];
18676        let mut ctx_aux = vec![0; 2];
18677        assert_eq!(
18678            libsais16x64_bwt_aux(
18679                &text,
18680                &mut direct_bwt,
18681                &mut direct_work,
18682                0,
18683                None,
18684                4,
18685                &mut direct_aux
18686            ),
18687            libsais16x64_bwt_aux_ctx(
18688                &mut ctx,
18689                &text,
18690                &mut ctx_bwt,
18691                &mut ctx_work,
18692                0,
18693                None,
18694                4,
18695                &mut ctx_aux
18696            )
18697        );
18698        assert_eq!(ctx_bwt, direct_bwt);
18699        assert_eq!(ctx_aux, direct_aux);
18700    }
18701
18702    #[test]
18703    fn libsais16x64_unbwt_context_wrappers_match_direct_calls() {
18704        let text = [2, 1, 3, 1, 2, 0];
18705        let mut bwt = vec![0; text.len()];
18706        let mut work = vec![0; text.len()];
18707        let primary = libsais16x64_bwt(&text, &mut bwt, &mut work, 0, None);
18708
18709        let mut ctx = unbwt_create_ctx().unwrap();
18710        let mut direct = vec![0; text.len()];
18711        let mut direct_work = vec![0; text.len()];
18712        let mut via_ctx = vec![0; text.len()];
18713        let mut ctx_work = vec![0; text.len()];
18714
18715        assert_eq!(
18716            libsais16x64_unbwt(&bwt, &mut direct, &mut direct_work, None, primary),
18717            0
18718        );
18719        assert_eq!(
18720            libsais16x64_unbwt_ctx(&mut ctx, &bwt, &mut via_ctx, &mut ctx_work, None, primary),
18721            0
18722        );
18723        assert_eq!(via_ctx, direct);
18724
18725        let mut aux = vec![0; 2];
18726        assert_eq!(
18727            libsais16x64_bwt_aux(&text, &mut bwt, &mut work, 0, None, 4, &mut aux),
18728            0
18729        );
18730        assert_eq!(
18731            libsais16x64_unbwt_aux(&bwt, &mut direct, &mut direct_work, None, 4, &aux),
18732            0
18733        );
18734        assert_eq!(
18735            libsais16x64_unbwt_aux_ctx(&mut ctx, &bwt, &mut via_ctx, &mut ctx_work, None, 4, &aux),
18736            0
18737        );
18738        assert_eq!(via_ctx, direct);
18739    }
18740
18741    #[test]
18742    fn libsais16x64_ctx_frequency_wrappers_match_direct_calls() {
18743        let text = [2, 1, 3, 1, 2, 0];
18744        let gsa_text = [2, 1, 0, 3, 1, 0];
18745        let mut ctx = create_ctx().unwrap();
18746
18747        let mut direct_sa = vec![0; text.len()];
18748        let mut ctx_sa = vec![0; text.len()];
18749        let mut direct_freq = vec![-1; ALPHABET_SIZE];
18750        let mut ctx_freq = vec![-1; ALPHABET_SIZE];
18751        assert_eq!(
18752            libsais16x64(&text, &mut direct_sa, 0, Some(&mut direct_freq)),
18753            0
18754        );
18755        assert_eq!(
18756            libsais16x64_ctx(&mut ctx, &text, &mut ctx_sa, 0, Some(&mut ctx_freq)),
18757            0
18758        );
18759        assert_eq!(ctx_sa, direct_sa);
18760        assert_eq!(ctx_freq, direct_freq);
18761
18762        let mut direct_gsa = vec![0; gsa_text.len()];
18763        let mut ctx_gsa = vec![0; gsa_text.len()];
18764        direct_freq.fill(-1);
18765        ctx_freq.fill(-1);
18766        assert_eq!(
18767            libsais16x64_gsa(&gsa_text, &mut direct_gsa, 0, Some(&mut direct_freq)),
18768            0
18769        );
18770        assert_eq!(
18771            libsais16x64_gsa_ctx(&mut ctx, &gsa_text, &mut ctx_gsa, 0, Some(&mut ctx_freq)),
18772            0
18773        );
18774        assert_eq!(ctx_gsa, direct_gsa);
18775        assert_eq!(ctx_freq, direct_freq);
18776
18777        let mut direct_bwt = vec![0; text.len()];
18778        let mut direct_work = vec![0; text.len()];
18779        let mut ctx_bwt = vec![0; text.len()];
18780        let mut ctx_work = vec![0; text.len()];
18781        direct_freq.fill(-1);
18782        ctx_freq.fill(-1);
18783        assert_eq!(
18784            libsais16x64_bwt(
18785                &text,
18786                &mut direct_bwt,
18787                &mut direct_work,
18788                0,
18789                Some(&mut direct_freq)
18790            ),
18791            libsais16x64_bwt_ctx(
18792                &mut ctx,
18793                &text,
18794                &mut ctx_bwt,
18795                &mut ctx_work,
18796                0,
18797                Some(&mut ctx_freq)
18798            )
18799        );
18800        assert_eq!(ctx_bwt, direct_bwt);
18801        assert_eq!(ctx_freq, direct_freq);
18802
18803        let mut direct_aux = vec![0; 2];
18804        let mut ctx_aux = vec![0; 2];
18805        direct_freq.fill(-1);
18806        ctx_freq.fill(-1);
18807        assert_eq!(
18808            libsais16x64_bwt_aux(
18809                &text,
18810                &mut direct_bwt,
18811                &mut direct_work,
18812                0,
18813                Some(&mut direct_freq),
18814                4,
18815                &mut direct_aux
18816            ),
18817            libsais16x64_bwt_aux_ctx(
18818                &mut ctx,
18819                &text,
18820                &mut ctx_bwt,
18821                &mut ctx_work,
18822                0,
18823                Some(&mut ctx_freq),
18824                4,
18825                &mut ctx_aux
18826            )
18827        );
18828        assert_eq!(ctx_bwt, direct_bwt);
18829        assert_eq!(ctx_aux, direct_aux);
18830        assert_eq!(ctx_freq, direct_freq);
18831    }
18832
18833    #[test]
18834    fn libsais16x64_unbwt_ctx_frequency_wrappers_match_direct_calls() {
18835        let text = [2, 1, 3, 1, 2, 0];
18836        let mut freq = vec![0; ALPHABET_SIZE];
18837        let mut bwt = vec![0; text.len()];
18838        let mut work = vec![0; text.len()];
18839        let primary = libsais16x64_bwt(&text, &mut bwt, &mut work, 0, Some(&mut freq));
18840        assert!(primary >= 0);
18841
18842        let mut ctx = unbwt_create_ctx().unwrap();
18843        let mut direct = vec![0; text.len()];
18844        let mut direct_work = vec![0; text.len() + 1];
18845        let mut via_ctx = vec![0; text.len()];
18846        let mut ctx_work = vec![0; text.len() + 1];
18847        assert_eq!(
18848            libsais16x64_unbwt(&bwt, &mut direct, &mut direct_work, Some(&freq), primary),
18849            libsais16x64_unbwt_ctx(
18850                &mut ctx,
18851                &bwt,
18852                &mut via_ctx,
18853                &mut ctx_work,
18854                Some(&freq),
18855                primary
18856            )
18857        );
18858        assert_eq!(via_ctx, direct);
18859        assert_eq!(via_ctx, text);
18860
18861        let mut aux = vec![0; (text.len() - 1) / 4 + 1];
18862        assert_eq!(
18863            libsais16x64_bwt_aux(&text, &mut bwt, &mut work, 0, Some(&mut freq), 4, &mut aux),
18864            0
18865        );
18866        direct.fill(0);
18867        direct_work.fill(0);
18868        via_ctx.fill(0);
18869        ctx_work.fill(0);
18870        assert_eq!(
18871            libsais16x64_unbwt_aux(&bwt, &mut direct, &mut direct_work, Some(&freq), 4, &aux),
18872            libsais16x64_unbwt_aux_ctx(
18873                &mut ctx,
18874                &bwt,
18875                &mut via_ctx,
18876                &mut ctx_work,
18877                Some(&freq),
18878                4,
18879                &aux
18880            )
18881        );
18882        assert_eq!(via_ctx, direct);
18883        assert_eq!(via_ctx, text);
18884    }
18885
18886    #[test]
18887    fn libsais16x64_omp_wrappers_match_direct_calls_and_reject_negative_threads() {
18888        let text = [2, 1, 3, 1, 2, 0];
18889        let gsa_text = [2, 1, 0, 3, 1, 0];
18890        let mut direct_sa = vec![0; text.len()];
18891        let mut omp_sa = vec![0; text.len()];
18892        assert_eq!(libsais16x64(&text, &mut direct_sa, 0, None), 0);
18893        assert_eq!(libsais16x64_omp(&text, &mut omp_sa, 0, None, 2), 0);
18894        assert_eq!(omp_sa, direct_sa);
18895        assert_eq!(libsais16x64_omp(&text, &mut omp_sa, 0, None, -1), -1);
18896
18897        let mut direct_gsa = vec![0; gsa_text.len()];
18898        let mut omp_gsa = vec![0; gsa_text.len()];
18899        assert_eq!(libsais16x64_gsa(&gsa_text, &mut direct_gsa, 0, None), 0);
18900        assert_eq!(libsais16x64_gsa_omp(&gsa_text, &mut omp_gsa, 0, None, 2), 0);
18901        assert_eq!(omp_gsa, direct_gsa);
18902        assert_eq!(
18903            libsais16x64_gsa_omp(&gsa_text, &mut omp_gsa, 0, None, -1),
18904            -1
18905        );
18906
18907        let int_text = [1, 2, 1, 0];
18908        let mut direct_int_text = int_text.to_vec();
18909        let mut omp_int_text = int_text.to_vec();
18910        let mut direct_int_sa = vec![0; int_text.len()];
18911        let mut omp_int_sa = vec![0; int_text.len()];
18912        assert_eq!(
18913            libsais16x64_long(&mut direct_int_text, &mut direct_int_sa, 3, 0),
18914            0
18915        );
18916        assert_eq!(
18917            libsais16x64_long_omp(&mut omp_int_text, &mut omp_int_sa, 3, 0, 2),
18918            0
18919        );
18920        assert_eq!(omp_int_text, direct_int_text);
18921        assert_eq!(omp_int_sa, direct_int_sa);
18922        assert_eq!(
18923            libsais16x64_long_omp(&mut omp_int_text, &mut omp_int_sa, 3, 0, -1),
18924            -1
18925        );
18926
18927        let mut direct_bwt = vec![0; text.len()];
18928        let mut direct_work = vec![0; text.len()];
18929        let mut omp_bwt = vec![0; text.len()];
18930        let mut omp_work = vec![0; text.len()];
18931        assert_eq!(
18932            libsais16x64_bwt(&text, &mut direct_bwt, &mut direct_work, 0, None),
18933            libsais16x64_bwt_omp(&text, &mut omp_bwt, &mut omp_work, 0, None, 2)
18934        );
18935        assert_eq!(omp_bwt, direct_bwt);
18936        assert_eq!(
18937            libsais16x64_bwt_omp(&text, &mut omp_bwt, &mut omp_work, 0, None, -1),
18938            -1
18939        );
18940
18941        let mut direct_aux = vec![0; 2];
18942        let mut omp_aux = vec![0; 2];
18943        assert_eq!(
18944            libsais16x64_bwt_aux(
18945                &text,
18946                &mut direct_bwt,
18947                &mut direct_work,
18948                0,
18949                None,
18950                4,
18951                &mut direct_aux
18952            ),
18953            libsais16x64_bwt_aux_omp(
18954                &text,
18955                &mut omp_bwt,
18956                &mut omp_work,
18957                0,
18958                None,
18959                4,
18960                &mut omp_aux,
18961                2
18962            )
18963        );
18964        assert_eq!(omp_bwt, direct_bwt);
18965        assert_eq!(omp_aux, direct_aux);
18966        assert_eq!(
18967            libsais16x64_bwt_aux_omp(
18968                &text,
18969                &mut omp_bwt,
18970                &mut omp_work,
18971                0,
18972                None,
18973                4,
18974                &mut omp_aux,
18975                -1
18976            ),
18977            -1
18978        );
18979    }
18980
18981    #[test]
18982    fn libsais16x64_omp_frequency_wrappers_match_direct_calls() {
18983        let text = [2, 1, 3, 1, 2, 0];
18984        let gsa_text = [2, 1, 0, 3, 1, 0];
18985        let mut direct_sa = vec![0; text.len()];
18986        let mut omp_sa = vec![0; text.len()];
18987        let mut direct_freq = vec![-1; ALPHABET_SIZE];
18988        let mut omp_freq = vec![-1; ALPHABET_SIZE];
18989        assert_eq!(
18990            libsais16x64(&text, &mut direct_sa, 0, Some(&mut direct_freq)),
18991            0
18992        );
18993        assert_eq!(
18994            libsais16x64_omp(&text, &mut omp_sa, 0, Some(&mut omp_freq), 2),
18995            0
18996        );
18997        assert_eq!(omp_sa, direct_sa);
18998        assert_eq!(omp_freq, direct_freq);
18999
19000        let mut direct_gsa = vec![0; gsa_text.len()];
19001        let mut omp_gsa = vec![0; gsa_text.len()];
19002        direct_freq.fill(-1);
19003        omp_freq.fill(-1);
19004        assert_eq!(
19005            libsais16x64_gsa(&gsa_text, &mut direct_gsa, 0, Some(&mut direct_freq)),
19006            0
19007        );
19008        assert_eq!(
19009            libsais16x64_gsa_omp(&gsa_text, &mut omp_gsa, 0, Some(&mut omp_freq), 2),
19010            0
19011        );
19012        assert_eq!(omp_gsa, direct_gsa);
19013        assert_eq!(omp_freq, direct_freq);
19014
19015        let mut direct_bwt = vec![0; text.len()];
19016        let mut direct_work = vec![0; text.len()];
19017        let mut omp_bwt = vec![0; text.len()];
19018        let mut omp_work = vec![0; text.len()];
19019        direct_freq.fill(-1);
19020        omp_freq.fill(-1);
19021        assert_eq!(
19022            libsais16x64_bwt(
19023                &text,
19024                &mut direct_bwt,
19025                &mut direct_work,
19026                0,
19027                Some(&mut direct_freq)
19028            ),
19029            libsais16x64_bwt_omp(
19030                &text,
19031                &mut omp_bwt,
19032                &mut omp_work,
19033                0,
19034                Some(&mut omp_freq),
19035                2
19036            )
19037        );
19038        assert_eq!(omp_bwt, direct_bwt);
19039        assert_eq!(omp_freq, direct_freq);
19040
19041        let mut direct_aux = vec![0; 2];
19042        let mut omp_aux = vec![0; 2];
19043        direct_freq.fill(-1);
19044        omp_freq.fill(-1);
19045        assert_eq!(
19046            libsais16x64_bwt_aux(
19047                &text,
19048                &mut direct_bwt,
19049                &mut direct_work,
19050                0,
19051                Some(&mut direct_freq),
19052                4,
19053                &mut direct_aux
19054            ),
19055            libsais16x64_bwt_aux_omp(
19056                &text,
19057                &mut omp_bwt,
19058                &mut omp_work,
19059                0,
19060                Some(&mut omp_freq),
19061                4,
19062                &mut omp_aux,
19063                2
19064            )
19065        );
19066        assert_eq!(omp_bwt, direct_bwt);
19067        assert_eq!(omp_aux, direct_aux);
19068        assert_eq!(omp_freq, direct_freq);
19069    }
19070
19071    #[test]
19072    fn libsais16x64_unbwt_omp_frequency_wrappers_match_direct_calls() {
19073        let text = [2, 1, 3, 1, 2, 0];
19074        let mut freq = vec![0; ALPHABET_SIZE];
19075        let mut bwt = vec![0; text.len()];
19076        let mut work = vec![0; text.len()];
19077        let primary = libsais16x64_bwt(&text, &mut bwt, &mut work, 0, Some(&mut freq));
19078        assert!(primary >= 0);
19079
19080        let mut direct = vec![0; text.len()];
19081        let mut direct_work = vec![0; text.len() + 1];
19082        let mut omp = vec![0; text.len()];
19083        let mut omp_work = vec![0; text.len() + 1];
19084        assert_eq!(
19085            libsais16x64_unbwt(&bwt, &mut direct, &mut direct_work, Some(&freq), primary),
19086            libsais16x64_unbwt_omp(&bwt, &mut omp, &mut omp_work, Some(&freq), primary, 2)
19087        );
19088        assert_eq!(omp, direct);
19089        assert_eq!(omp, text);
19090
19091        let mut aux = vec![0; (text.len() - 1) / 4 + 1];
19092        assert_eq!(
19093            libsais16x64_bwt_aux(&text, &mut bwt, &mut work, 0, Some(&mut freq), 4, &mut aux),
19094            0
19095        );
19096        direct.fill(0);
19097        direct_work.fill(0);
19098        omp.fill(0);
19099        omp_work.fill(0);
19100        assert_eq!(
19101            libsais16x64_unbwt_aux(&bwt, &mut direct, &mut direct_work, Some(&freq), 4, &aux),
19102            libsais16x64_unbwt_aux_omp(&bwt, &mut omp, &mut omp_work, Some(&freq), 4, &aux, 2)
19103        );
19104        assert_eq!(omp, direct);
19105        assert_eq!(omp, text);
19106    }
19107
19108    #[test]
19109    fn libsais16x64_lcp_and_unbwt_omp_wrappers_match_direct_calls() {
19110        let text = [2, 1, 3, 1, 2, 0];
19111        let mut sa = vec![0; text.len()];
19112        assert_eq!(libsais16x64(&text, &mut sa, 0, None), 0);
19113
19114        let mut direct_plcp = vec![0; text.len()];
19115        let mut omp_plcp = vec![0; text.len()];
19116        assert_eq!(libsais16x64_plcp(&text, &sa, &mut direct_plcp), 0);
19117        assert_eq!(libsais16x64_plcp_omp(&text, &sa, &mut omp_plcp, 2), 0);
19118        assert_eq!(omp_plcp, direct_plcp);
19119        assert_eq!(libsais16x64_plcp_omp(&text, &sa, &mut omp_plcp, -1), -1);
19120
19121        let gsa_text = [2, 1, 0, 1, 2, 0];
19122        let mut gsa = vec![0; gsa_text.len()];
19123        assert_eq!(libsais16x64_gsa(&gsa_text, &mut gsa, 0, None), 0);
19124        let mut direct_gsa_plcp = vec![0; gsa_text.len()];
19125        let mut omp_gsa_plcp = vec![0; gsa_text.len()];
19126        assert_eq!(
19127            libsais16x64_plcp_gsa(&gsa_text, &gsa, &mut direct_gsa_plcp),
19128            0
19129        );
19130        assert_eq!(
19131            libsais16x64_plcp_gsa_omp(&gsa_text, &gsa, &mut omp_gsa_plcp, 2),
19132            0
19133        );
19134        assert_eq!(omp_gsa_plcp, direct_gsa_plcp);
19135        assert_eq!(
19136            libsais16x64_plcp_gsa_omp(&gsa_text, &gsa, &mut omp_gsa_plcp, -1),
19137            -1
19138        );
19139
19140        let mut direct_lcp = vec![0; text.len()];
19141        let mut omp_lcp = vec![0; text.len()];
19142        assert_eq!(libsais16x64_lcp(&direct_plcp, &sa, &mut direct_lcp), 0);
19143        assert_eq!(libsais16x64_lcp_omp(&direct_plcp, &sa, &mut omp_lcp, 2), 0);
19144        assert_eq!(omp_lcp, direct_lcp);
19145        assert_eq!(
19146            libsais16x64_lcp_omp(&direct_plcp, &sa, &mut omp_lcp, -1),
19147            -1
19148        );
19149
19150        let mut bwt = vec![0; text.len()];
19151        let mut work = vec![0; text.len()];
19152        let primary = libsais16x64_bwt(&text, &mut bwt, &mut work, 0, None);
19153        let mut direct = vec![0; text.len()];
19154        let mut omp = vec![0; text.len()];
19155        let mut direct_work = vec![0; text.len()];
19156        let mut omp_work = vec![0; text.len()];
19157        assert_eq!(
19158            libsais16x64_unbwt(&bwt, &mut direct, &mut direct_work, None, primary),
19159            0
19160        );
19161        assert_eq!(
19162            libsais16x64_unbwt_omp(&bwt, &mut omp, &mut omp_work, None, primary, 2),
19163            0
19164        );
19165        assert_eq!(omp, direct);
19166        assert_eq!(
19167            libsais16x64_unbwt_omp(&bwt, &mut omp, &mut omp_work, None, primary, -1),
19168            -1
19169        );
19170    }
19171}