1use std::marker::PhantomData;
10use std::mem;
11
12use rayon::prelude::*;
13
14pub mod libsais16;
15pub mod libsais16x64;
16pub mod libsais64;
17pub use libsais16::{libsais16, SaSint as SaSint16, SaUint as SaUint16};
18pub use libsais16x64::{libsais16x64, SaSint as SaSint16x64, SaUint as SaUint16x64};
19pub use libsais64::{libsais64, SaSint as SaSint64, SaUint as SaUint64};
20
21pub type SaSint = i32;
22pub type SaUint = u32;
23pub type FastSint = isize;
24pub type FastUint = usize;
25
26pub const SAINT_BIT: u32 = 32;
27pub const SAINT_MAX: SaSint = i32::MAX;
28pub const SAINT_MIN: SaSint = i32::MIN;
29
30pub const ALPHABET_SIZE: usize = 1usize << 8;
31pub const UNBWT_FASTBITS: usize = 17;
32
33#[inline(always)]
37pub(crate) fn libsais_prefetchr<T>(ptr: *const T) {
38 #[cfg(target_arch = "x86_64")]
39 unsafe {
40 std::arch::x86_64::_mm_prefetch(ptr as *const i8, std::arch::x86_64::_MM_HINT_T0);
41 }
42 #[cfg(not(target_arch = "x86_64"))]
43 {
44 let _ = ptr;
45 }
46}
47
48#[inline(always)]
52pub(crate) fn libsais_prefetchw<T>(ptr: *const T) {
53 #[cfg(target_arch = "x86_64")]
54 unsafe {
55 std::arch::x86_64::_mm_prefetch(ptr as *const i8, std::arch::x86_64::_MM_HINT_T0);
56 }
57 #[cfg(not(target_arch = "x86_64"))]
58 {
59 let _ = ptr;
60 }
61}
62
63pub const SUFFIX_GROUP_BIT: u32 = SAINT_BIT - 1;
64pub const SUFFIX_GROUP_MARKER: SaSint = 1_i32 << (SUFFIX_GROUP_BIT - 1);
65
66pub const LIBSAIS_LOCAL_BUFFER_SIZE: usize = 2000;
67pub const LIBSAIS_PER_THREAD_CACHE_SIZE: usize = 24_576;
68
69pub const LIBSAIS_FLAGS_NONE: SaSint = 0;
70pub const LIBSAIS_FLAGS_BWT: SaSint = 1;
71pub const LIBSAIS_FLAGS_GSA: SaSint = 2;
72
73pub(crate) fn run_rayon_with_threads<R: Send>(threads: usize, f: impl FnOnce() -> R + Send) -> R {
83 if threads <= 1 || rayon::current_thread_index().is_some() {
84 return f();
85 }
86 match rayon::ThreadPoolBuilder::new().num_threads(threads).build() {
87 Ok(pool) => pool.install(f),
88 Err(_) => f(),
89 }
90}
91
92#[derive(Copy, Clone)]
107pub(crate) struct SyncMutPtr<T> {
108 ptr: *mut T,
109 len: usize,
110}
111
112unsafe impl<T: Send> Send for SyncMutPtr<T> {}
113unsafe impl<T: Send> Sync for SyncMutPtr<T> {}
114
115impl<T> SyncMutPtr<T> {
116 pub(crate) fn new(slice: &mut [T]) -> Self {
117 Self {
118 ptr: slice.as_mut_ptr(),
119 len: slice.len(),
120 }
121 }
122
123 #[allow(clippy::mut_from_ref)]
130 pub(crate) unsafe fn as_slice<'a>(&'a self) -> &'a mut [T] {
131 std::slice::from_raw_parts_mut(self.ptr, self.len)
132 }
133}
134
135#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
136pub struct ThreadCache {
137 pub symbol: SaSint,
138 pub index: SaSint,
139}
140
141#[derive(Clone, Debug, PartialEq, Eq)]
142pub struct ThreadState {
143 pub position: FastSint,
144 pub count: FastSint,
145 pub m: FastSint,
146 pub last_lms_suffix: FastSint,
147 pub buckets: Vec<SaSint>,
148 pub cache: Vec<ThreadCache>,
149}
150
151impl ThreadState {
152 fn new() -> Self {
153 Self {
154 position: 0,
155 count: 0,
156 m: 0,
157 last_lms_suffix: 0,
158 buckets: vec![0; 4 * ALPHABET_SIZE],
159 cache: vec![ThreadCache::default(); LIBSAIS_PER_THREAD_CACHE_SIZE],
160 }
161 }
162}
163
164#[derive(Clone, Debug, PartialEq, Eq)]
165pub struct Context {
166 pub buckets: Vec<SaSint>,
167 pub thread_state: Option<Vec<ThreadState>>,
168 pub threads: FastSint,
169}
170
171#[derive(Clone, Debug, PartialEq, Eq)]
172pub struct UnbwtContext {
173 pub bucket2: Vec<SaUint>,
174 pub fastbits: Vec<u16>,
175 pub buckets: Option<Vec<SaUint>>,
176 pub threads: FastSint,
177}
178
179#[doc(hidden)]
181pub fn buckets_index2(c: FastUint, s: FastUint) -> FastUint {
182 (c << 1) + s
183}
184
185#[doc(hidden)]
187pub fn buckets_index4(c: FastUint, s: FastUint) -> FastUint {
188 (c << 2) + s
189}
190
191#[doc(hidden)]
193pub fn align_up(value: usize, alignment: usize) -> usize {
194 debug_assert!(alignment.is_power_of_two());
195 (value + alignment - 1) & !(alignment - 1)
196}
197
198#[doc(hidden)]
200pub fn alloc_thread_state(threads: SaSint) -> Option<Vec<ThreadState>> {
201 if threads <= 0 {
202 return None;
203 }
204
205 let len = usize::try_from(threads).ok()?;
206 Some((0..len).map(|_| ThreadState::new()).collect())
207}
208
209#[doc(hidden)]
211pub fn create_ctx_main(threads: SaSint) -> Option<Context> {
212 if threads <= 0 {
213 return None;
214 }
215
216 let thread_state = if threads > 1 {
217 Some(alloc_thread_state(threads)?)
218 } else {
219 None
220 };
221
222 Some(Context {
223 buckets: vec![0; 8 * ALPHABET_SIZE],
224 thread_state,
225 threads: threads as FastSint,
226 })
227}
228
229pub fn create_ctx() -> Option<Context> {
235 create_ctx_main(1)
236}
237
238pub fn free_ctx(_ctx: Context) {}
243
244#[doc(hidden)]
246pub fn unbwt_create_ctx_main(threads: SaSint) -> Option<UnbwtContext> {
247 if threads <= 0 {
248 return None;
249 }
250
251 let buckets = if threads > 1 {
252 let len = usize::try_from(threads).ok()? * (ALPHABET_SIZE + ALPHABET_SIZE * ALPHABET_SIZE);
253 Some(vec![0; len])
254 } else {
255 None
256 };
257
258 Some(UnbwtContext {
259 bucket2: vec![0; ALPHABET_SIZE * ALPHABET_SIZE],
260 fastbits: vec![0; 1 + (1 << UNBWT_FASTBITS)],
261 buckets,
262 threads: threads as FastSint,
263 })
264}
265
266#[doc(hidden)]
268pub fn unbwt_free_ctx_main(_ctx: UnbwtContext) {}
269
270pub fn unbwt_create_ctx() -> Option<UnbwtContext> {
276 unbwt_create_ctx_main(1)
277}
278
279pub fn unbwt_free_ctx(_ctx: UnbwtContext) {}
284
285#[doc(hidden)]
287pub fn count_negative_marked_suffixes(
288 sa: &[SaSint],
289 block_start: FastSint,
290 block_size: FastSint,
291) -> SaSint {
292 block_slice(sa, block_start, block_size)
293 .iter()
294 .map(|&value| SaSint::from(value < 0))
295 .sum()
296}
297
298#[doc(hidden)]
300pub fn count_zero_marked_suffixes(
301 sa: &[SaSint],
302 block_start: FastSint,
303 block_size: FastSint,
304) -> SaSint {
305 block_slice(sa, block_start, block_size)
306 .iter()
307 .map(|&value| SaSint::from(value == 0))
308 .sum()
309}
310
311#[doc(hidden)]
313pub fn place_cached_suffixes(
314 sa: &mut [SaSint],
315 cache: &[ThreadCache],
316 block_start: FastSint,
317 block_size: FastSint,
318) {
319 let start = usize::try_from(block_start).expect("block_start must be non-negative");
320 let len = usize::try_from(block_size).expect("block_size must be non-negative");
321 let entries = if cache.len() >= start + len {
322 &cache[start..start + len]
323 } else {
324 &cache[..len]
325 };
326
327 for entry in entries {
328 let slot = usize::try_from(entry.symbol).expect("cache symbol must be non-negative");
329 sa[slot] = entry.index;
330 }
331}
332
333#[doc(hidden)]
335pub fn compact_and_place_cached_suffixes(
336 sa: &mut [SaSint],
337 cache: &mut [ThreadCache],
338 block_start: FastSint,
339 block_size: FastSint,
340) {
341 let start = usize::try_from(block_start).expect("block_start must be non-negative");
342 let len = usize::try_from(block_size).expect("block_size must be non-negative");
343 let read_start = if cache.len() >= start + len { start } else { 0 };
344 let read_end = read_start + len;
345
346 let mut write = read_start;
347 for read in read_start..read_end {
348 let entry = cache[read];
349 if entry.symbol >= 0 {
350 cache[write] = entry;
351 write += 1;
352 }
353 }
354
355 place_cached_suffixes(sa, cache, block_start, (write - read_start) as FastSint);
356}
357
358#[doc(hidden)]
360pub fn flip_suffix_markers_omp(sa: &mut [SaSint], l: SaSint, threads: SaSint) {
361 let len = usize::try_from(l).expect("l must be non-negative");
362 let omp_num_threads = if threads > 1 && l >= 65_536 {
363 usize::try_from(threads).expect("threads must be non-negative")
364 } else {
365 1
366 };
367 if omp_num_threads > 1 {
368 let chunk_size = ((len / omp_num_threads) & !15usize).max(16);
369 run_rayon_with_threads(omp_num_threads, || {
370 sa[..len].par_chunks_mut(chunk_size).for_each(|chunk| {
371 for value in chunk {
372 *value ^= SAINT_MIN;
373 }
374 });
375 });
376 return;
377 }
378
379 let omp_block_stride = (len / omp_num_threads) & !15usize;
380 for omp_thread_num in 0..omp_num_threads {
381 let omp_block_start = omp_thread_num * omp_block_stride;
382 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
383 omp_block_stride
384 } else {
385 len - omp_block_start
386 };
387 for value in &mut sa[omp_block_start..omp_block_start + omp_block_size] {
388 *value ^= SAINT_MIN;
389 }
390 }
391}
392
393#[doc(hidden)]
395pub fn gather_lms_suffixes_8u(
396 t: &[u8],
397 sa: &mut [SaSint],
398 n: SaSint,
399 mut m: FastSint,
400 omp_block_start: FastSint,
401 omp_block_size: FastSint,
402) {
403 if omp_block_size <= 0 {
404 return;
405 }
406
407 let n = usize::try_from(n).expect("n must be non-negative");
408 let block_start =
409 usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
410 let block_size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
411
412 let mut j = block_start + block_size;
413 let mut c0 = t[block_start + block_size - 1] as FastSint;
414 let mut c1 = -1;
415 while j < n {
416 c1 = t[j] as FastSint;
417 if c1 != c0 {
418 break;
419 }
420 j += 1;
421 }
422
423 let mut f0 = usize::from(c0 >= c1);
424 let mut f1: usize;
425 let mut i = block_start + block_size - 2;
426 let limit = block_start + 3;
427
428 while i >= limit {
429 c1 = t[i] as FastSint;
430 f1 = usize::from(c1 > (c0 - f0 as FastSint));
431 sa[usize::try_from(m).expect("m must be non-negative")] = (i + 1) as SaSint;
432 m -= (f1 & !f0) as FastSint;
433
434 c0 = t[i - 1] as FastSint;
435 f0 = usize::from(c0 > (c1 - f1 as FastSint));
436 sa[usize::try_from(m).expect("m must be non-negative")] = i as SaSint;
437 m -= (f0 & !f1) as FastSint;
438
439 c1 = t[i - 2] as FastSint;
440 f1 = usize::from(c1 > (c0 - f0 as FastSint));
441 sa[usize::try_from(m).expect("m must be non-negative")] = (i - 1) as SaSint;
442 m -= (f1 & !f0) as FastSint;
443
444 c0 = t[i - 3] as FastSint;
445 f0 = usize::from(c0 > (c1 - f1 as FastSint));
446 sa[usize::try_from(m).expect("m must be non-negative")] = (i - 2) as SaSint;
447 m -= (f0 & !f1) as FastSint;
448
449 if i < 4 {
450 break;
451 }
452 i -= 4;
453 }
454
455 let tail_limit = limit - 3;
456 while i >= tail_limit {
457 c1 = c0;
458 c0 = t[i] as FastSint;
459 f1 = f0;
460 f0 = usize::from(c0 > (c1 - f1 as FastSint));
461 sa[usize::try_from(m).expect("m must be non-negative")] = (i + 1) as SaSint;
462 m -= (f0 & !f1) as FastSint;
463 if i == 0 {
464 break;
465 }
466 i -= 1;
467 }
468
469 sa[usize::try_from(m).expect("m must be non-negative")] = (i + 1) as SaSint;
470}
471
472#[doc(hidden)]
474pub fn gather_lms_suffixes_8u_omp(
475 t: &[u8],
476 sa: &mut [SaSint],
477 n: SaSint,
478 threads: SaSint,
479 thread_state: &mut [ThreadState],
480) {
481 let n_usize = usize::try_from(n).expect("n must be non-negative");
482 let omp_num_threads = if threads > 1 && n >= 65_536 {
483 usize::try_from(threads)
484 .expect("threads must be non-negative")
485 .min(thread_state.len())
486 .max(1)
487 } else {
488 1
489 };
490 if omp_num_threads == 1 {
491 gather_lms_suffixes_8u(t, sa, n, n as FastSint - 1, 0, n as FastSint);
492 return;
493 }
494
495 let omp_block_stride = (n_usize / omp_num_threads) & !15usize;
496 let mut suffix_counts_after = vec![0 as FastSint; omp_num_threads];
497 let mut m = 0 as FastSint;
498 for omp_thread_num in (0..omp_num_threads).rev() {
499 suffix_counts_after[omp_thread_num] = m;
500 m += thread_state[omp_thread_num].m;
501 }
502
503 let sa_ptr = SyncMutPtr::new(sa);
504 let suffix_counts_after_slice: &[FastSint] = &suffix_counts_after;
505 let last_lms_suffixes: Vec<FastSint> = thread_state[..omp_num_threads]
506 .iter()
507 .map(|s| {
508 if s.m > 0 {
509 s.last_lms_suffix
510 } else {
511 FastSint::MIN
512 }
513 })
514 .collect();
515
516 run_rayon_with_threads(omp_num_threads, || {
517 (0..omp_num_threads)
518 .into_par_iter()
519 .for_each(|omp_thread_num| {
520 let omp_block_start = omp_thread_num * omp_block_stride;
521 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
522 omp_block_stride
523 } else {
524 n_usize - omp_block_start
525 };
526 let sa = unsafe { sa_ptr.as_slice() };
529 gather_lms_suffixes_8u(
530 t,
531 sa,
532 n,
533 n as FastSint - 1 - suffix_counts_after_slice[omp_thread_num],
534 omp_block_start as FastSint,
535 omp_block_size as FastSint,
536 );
537 });
538 });
539
540 for omp_thread_num in 0..omp_num_threads {
541 if last_lms_suffixes[omp_thread_num] != FastSint::MIN {
542 let dst = usize::try_from(n as FastSint - 1 - suffix_counts_after[omp_thread_num])
543 .expect("destination must be non-negative");
544 sa[dst] = last_lms_suffixes[omp_thread_num] as SaSint;
545 }
546 }
547}
548
549#[doc(hidden)]
551pub fn gather_lms_suffixes_32s(t: &[SaSint], sa: &mut [SaSint], n: SaSint) -> SaSint {
552 let n_usize = usize::try_from(n).expect("n must be non-negative");
553 let mut i = n as FastSint - 2;
554 let mut m = n_usize - 1;
555 let mut f0 = 1usize;
556 let mut f1: usize;
557 let mut c0 = t[n_usize - 1] as FastSint;
558 let mut c1: FastSint;
559
560 while i >= 3 {
561 c1 = t[i as usize] as FastSint;
562 f1 = usize::from(c1 > (c0 - f0 as FastSint));
563 sa[m] = (i + 1) as SaSint;
564 m -= f1 & !f0;
565
566 c0 = t[(i - 1) as usize] as FastSint;
567 f0 = usize::from(c0 > (c1 - f1 as FastSint));
568 sa[m] = i as SaSint;
569 m -= f0 & !f1;
570
571 c1 = t[(i - 2) as usize] as FastSint;
572 f1 = usize::from(c1 > (c0 - f0 as FastSint));
573 sa[m] = (i - 1) as SaSint;
574 m -= f1 & !f0;
575
576 c0 = t[(i - 3) as usize] as FastSint;
577 f0 = usize::from(c0 > (c1 - f1 as FastSint));
578 sa[m] = (i - 2) as SaSint;
579 m -= f0 & !f1;
580
581 i -= 4;
582 }
583
584 while i >= 0 {
585 c1 = c0;
586 c0 = t[i as usize] as FastSint;
587 f1 = f0;
588 f0 = usize::from(c0 > (c1 - f1 as FastSint));
589 sa[m] = (i + 1) as SaSint;
590 m -= f0 & !f1;
591 i -= 1;
592 }
593
594 (n_usize - 1 - m) as SaSint
595}
596
597#[doc(hidden)]
599pub fn gather_compacted_lms_suffixes_32s(t: &[SaSint], sa: &mut [SaSint], n: SaSint) -> SaSint {
600 let n_usize = usize::try_from(n).expect("n must be non-negative");
601 let mut i = n as FastSint - 2;
602 let mut m = n_usize - 1;
603 let mut f0 = 1usize;
604 let mut f1: usize;
605 let mut c0 = t[n_usize - 1] as FastSint;
606 let mut c1: FastSint;
607
608 while i >= 3 {
609 c1 = t[i as usize] as FastSint;
610 f1 = usize::from(c1 > (c0 - f0 as FastSint));
611 sa[m] = (i + 1) as SaSint;
612 m -= f1 & !f0 & usize::from(c0 >= 0);
613
614 c0 = t[(i - 1) as usize] as FastSint;
615 f0 = usize::from(c0 > (c1 - f1 as FastSint));
616 sa[m] = i as SaSint;
617 m -= f0 & !f1 & usize::from(c1 >= 0);
618
619 c1 = t[(i - 2) as usize] as FastSint;
620 f1 = usize::from(c1 > (c0 - f0 as FastSint));
621 sa[m] = (i - 1) as SaSint;
622 m -= f1 & !f0 & usize::from(c0 >= 0);
623
624 c0 = t[(i - 3) as usize] as FastSint;
625 f0 = usize::from(c0 > (c1 - f1 as FastSint));
626 sa[m] = (i - 2) as SaSint;
627 m -= f0 & !f1 & usize::from(c1 >= 0);
628
629 i -= 4;
630 }
631
632 while i >= 0 {
633 c1 = c0;
634 c0 = t[i as usize] as FastSint;
635 f1 = f0;
636 f0 = usize::from(c0 > (c1 - f1 as FastSint));
637 sa[m] = (i + 1) as SaSint;
638 m -= f0 & !f1 & usize::from(c1 >= 0);
639 i -= 1;
640 }
641
642 (n_usize - 1 - m) as SaSint
643}
644
645#[doc(hidden)]
647pub fn count_lms_suffixes_32s_4k(t: &[SaSint], n: SaSint, k: SaSint, buckets: &mut [SaSint]) {
648 buckets.fill(0);
649 let n_usize = usize::try_from(n).expect("n must be non-negative");
650 let _k_usize = usize::try_from(k).expect("k must be non-negative");
651 let mut i = n as FastSint - 2;
652 let mut f0 = 1usize;
653 let mut f1: usize;
654 let mut c0 = t[n_usize - 1] as FastSint;
655 let mut c1: FastSint;
656
657 while i >= 3 {
658 c1 = t[i as usize] as FastSint;
659 f1 = usize::from(c1 > (c0 - f0 as FastSint));
660 buckets[buckets_index4((c0 as SaSint & SAINT_MAX) as usize, f0 + f0 + f1)] += 1;
661
662 c0 = t[(i - 1) as usize] as FastSint;
663 f0 = usize::from(c0 > (c1 - f1 as FastSint));
664 buckets[buckets_index4((c1 as SaSint & SAINT_MAX) as usize, f1 + f1 + f0)] += 1;
665
666 c1 = t[(i - 2) as usize] as FastSint;
667 f1 = usize::from(c1 > (c0 - f0 as FastSint));
668 buckets[buckets_index4((c0 as SaSint & SAINT_MAX) as usize, f0 + f0 + f1)] += 1;
669
670 c0 = t[(i - 3) as usize] as FastSint;
671 f0 = usize::from(c0 > (c1 - f1 as FastSint));
672 buckets[buckets_index4((c1 as SaSint & SAINT_MAX) as usize, f1 + f1 + f0)] += 1;
673
674 i -= 4;
675 }
676
677 while i >= 0 {
678 c1 = c0;
679 c0 = t[i as usize] as FastSint;
680 f1 = f0;
681 f0 = usize::from(c0 > (c1 - f1 as FastSint));
682 buckets[buckets_index4((c1 as SaSint & SAINT_MAX) as usize, f1 + f1 + f0)] += 1;
683 i -= 1;
684 }
685
686 buckets[buckets_index4((c0 as SaSint & SAINT_MAX) as usize, f0 + f0)] += 1;
687}
688
689#[doc(hidden)]
691pub fn count_lms_suffixes_32s_2k(t: &[SaSint], n: SaSint, k: SaSint, buckets: &mut [SaSint]) {
692 buckets.fill(0);
693 let n_usize = usize::try_from(n).expect("n must be non-negative");
694 let _k_usize = usize::try_from(k).expect("k must be non-negative");
695 let mut i = n as FastSint - 2;
696 let mut f0 = 1usize;
697 let mut f1: usize;
698 let mut c0 = t[n_usize - 1] as FastSint;
699 let mut c1: FastSint;
700
701 while i >= 3 {
702 c1 = t[i as usize] as FastSint;
703 f1 = usize::from(c1 > (c0 - f0 as FastSint));
704 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
705
706 c0 = t[(i - 1) as usize] as FastSint;
707 f0 = usize::from(c0 > (c1 - f1 as FastSint));
708 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
709
710 c1 = t[(i - 2) as usize] as FastSint;
711 f1 = usize::from(c1 > (c0 - f0 as FastSint));
712 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
713
714 c0 = t[(i - 3) as usize] as FastSint;
715 f0 = usize::from(c0 > (c1 - f1 as FastSint));
716 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
717
718 i -= 4;
719 }
720
721 while i >= 0 {
722 c1 = c0;
723 c0 = t[i as usize] as FastSint;
724 f1 = f0;
725 f0 = usize::from(c0 > (c1 - f1 as FastSint));
726 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
727 i -= 1;
728 }
729
730 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, 0)] += 1;
731}
732
733#[doc(hidden)]
735pub fn count_compacted_lms_suffixes_32s_2k(
736 t: &[SaSint],
737 n: SaSint,
738 k: SaSint,
739 buckets: &mut [SaSint],
740) {
741 buckets.fill(0);
742 let n_usize = usize::try_from(n).expect("n must be non-negative");
743 let _k_usize = usize::try_from(k).expect("k must be non-negative");
744 let mut i = n as FastSint - 2;
745 let mut f0 = 1usize;
746 let mut f1: usize;
747 let mut c0 = t[n_usize - 1] as FastSint;
748 let mut c1: FastSint;
749
750 while i >= 3 {
751 c1 = t[i as usize] as FastSint;
752 f1 = usize::from(c1 > (c0 - f0 as FastSint));
753 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
754
755 c0 = t[(i - 1) as usize] as FastSint;
756 f0 = usize::from(c0 > (c1 - f1 as FastSint));
757 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
758
759 c1 = t[(i - 2) as usize] as FastSint;
760 f1 = usize::from(c1 > (c0 - f0 as FastSint));
761 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
762
763 c0 = t[(i - 3) as usize] as FastSint;
764 f0 = usize::from(c0 > (c1 - f1 as FastSint));
765 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
766
767 i -= 4;
768 }
769
770 while i >= 0 {
771 c1 = c0;
772 c0 = t[i as usize] as FastSint;
773 f1 = f0;
774 f0 = usize::from(c0 > (c1 - f1 as FastSint));
775 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
776 i -= 1;
777 }
778
779 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, 0)] += 1;
780}
781
782#[doc(hidden)]
784pub fn count_and_gather_lms_suffixes_8u(
785 t: &[u8],
786 sa: &mut [SaSint],
787 n: SaSint,
788 buckets: &mut [SaSint],
789 omp_block_start: FastSint,
790 omp_block_size: FastSint,
791) -> SaSint {
792 buckets.fill(0);
793 let n = n as FastSint;
794 let mut m = omp_block_start + omp_block_size - 1;
795
796 if omp_block_size > 0 {
797 let prefetch_distance = 256 as FastSint;
798 let mut j = m + 1;
799 let mut c0 = t[m as usize] as FastSint;
800 let mut c1 = -1;
801 while j < n {
802 c1 = t[j as usize] as FastSint;
803 if c1 != c0 {
804 break;
805 }
806 j += 1;
807 }
808
809 let mut f0 = usize::from(c0 >= c1);
810 let mut f1: usize;
811 let mut i = m - 1;
812 let limit = omp_block_start + 3;
813
814 while i >= limit {
815 let _prefetch_index = i - prefetch_distance;
816 c1 = t[i as usize] as FastSint;
817 f1 = usize::from(c1 > (c0 - f0 as FastSint));
818 sa[m as usize] = (i + 1) as SaSint;
819 m -= (f1 & !f0) as FastSint;
820 buckets[buckets_index4((c0 as SaSint & SAINT_MAX) as usize, f0 + f0 + f1)] += 1;
821
822 c0 = t[(i - 1) as usize] as FastSint;
823 f0 = usize::from(c0 > (c1 - f1 as FastSint));
824 sa[m as usize] = i as SaSint;
825 m -= (f0 & !f1) as FastSint;
826 buckets[buckets_index4((c1 as SaSint & SAINT_MAX) as usize, f1 + f1 + f0)] += 1;
827
828 c1 = t[(i - 2) as usize] as FastSint;
829 f1 = usize::from(c1 > (c0 - f0 as FastSint));
830 sa[m as usize] = (i - 1) as SaSint;
831 m -= (f1 & !f0) as FastSint;
832 buckets[buckets_index4((c0 as SaSint & SAINT_MAX) as usize, f0 + f0 + f1)] += 1;
833
834 c0 = t[(i - 3) as usize] as FastSint;
835 f0 = usize::from(c0 > (c1 - f1 as FastSint));
836 sa[m as usize] = (i - 2) as SaSint;
837 m -= (f0 & !f1) as FastSint;
838 buckets[buckets_index4((c1 as SaSint & SAINT_MAX) as usize, f1 + f1 + f0)] += 1;
839
840 i -= 4;
841 }
842
843 let tail_limit = limit - 3;
844 while i >= tail_limit {
845 c1 = c0;
846 c0 = t[i as usize] as FastSint;
847 f1 = f0;
848 f0 = usize::from(c0 > (c1 - f1 as FastSint));
849 sa[m as usize] = (i + 1) as SaSint;
850 m -= (f0 & !f1) as FastSint;
851 buckets[buckets_index4((c1 as SaSint & SAINT_MAX) as usize, f1 + f1 + f0)] += 1;
852 i -= 1;
853 }
854
855 c1 = if i >= 0 {
856 t[i as usize] as FastSint
857 } else {
858 -1
859 };
860 f1 = usize::from(c1 > (c0 - f0 as FastSint));
861 sa[m as usize] = (i + 1) as SaSint;
862 m -= (f1 & !f0) as FastSint;
863 buckets[buckets_index4((c0 as SaSint & SAINT_MAX) as usize, f0 + f0 + f1)] += 1;
864 }
865
866 (omp_block_start + omp_block_size - 1 - m) as SaSint
867}
868
869#[doc(hidden)]
871pub fn count_and_gather_lms_suffixes_8u_omp(
872 t: &[u8],
873 sa: &mut [SaSint],
874 n: SaSint,
875 buckets: &mut [SaSint],
876 threads: SaSint,
877 thread_state: &mut [ThreadState],
878) -> SaSint {
879 let mut m = 0;
880 let n_usize = usize::try_from(n).expect("n must be non-negative");
881 let omp_num_threads = if threads > 1 && n >= 65_536 {
882 usize::try_from(threads)
883 .expect("threads must be non-negative")
884 .min(thread_state.len())
885 .max(1)
886 } else {
887 1
888 };
889 let omp_block_stride = (n_usize / omp_num_threads) & !15usize;
890
891 if omp_num_threads == 1 {
892 return count_and_gather_lms_suffixes_8u(t, sa, n, buckets, 0, n as FastSint);
893 }
894
895 let sa_ptr = SyncMutPtr::new(sa);
896 run_rayon_with_threads(omp_num_threads, || {
897 thread_state[..omp_num_threads]
898 .par_iter_mut()
899 .enumerate()
900 .for_each(|(omp_thread_num, state)| {
901 let omp_block_start = omp_thread_num * omp_block_stride;
902 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
903 omp_block_stride
904 } else {
905 n_usize - omp_block_start
906 };
907
908 let sa = unsafe { sa_ptr.as_slice() };
912
913 state.position = FastSint::try_from(omp_block_start + omp_block_size)
914 .expect("position must fit FastSint");
915 state.m = FastSint::try_from(count_and_gather_lms_suffixes_8u(
916 t,
917 sa,
918 n,
919 &mut state.buckets,
920 FastSint::try_from(omp_block_start).expect("block start must fit FastSint"),
921 FastSint::try_from(omp_block_size).expect("block size must fit FastSint"),
922 ))
923 .expect("m must fit FastSint");
924
925 if state.m > 0 {
926 let position =
927 usize::try_from(state.position).expect("position must be non-negative");
928 state.last_lms_suffix =
929 FastSint::try_from(sa[position - 1]).expect("suffix must fit FastSint");
930 }
931 });
932 });
933
934 buckets.fill(0);
935
936 for tnum in (0..omp_num_threads).rev() {
937 let state = &mut thread_state[tnum];
938 m += SaSint::try_from(state.m).expect("m must fit SaSint");
939
940 if tnum + 1 < omp_num_threads && state.m > 0 {
941 let position = usize::try_from(state.position).expect("position must be non-negative");
942 let count = usize::try_from(state.m).expect("m must be non-negative");
943 let dst = n_usize - usize::try_from(m).expect("m must be non-negative");
944 sa.copy_within(position - count..position, dst);
945 }
946
947 for s in 0..4 * ALPHABET_SIZE {
948 let a = buckets[s];
949 let b = state.buckets[s];
950 buckets[s] = a + b;
951 state.buckets[s] = a;
952 }
953 }
954
955 m
956}
957
958#[doc(hidden)]
960pub fn count_and_gather_lms_suffixes_32s_4k(
961 t: &[SaSint],
962 sa: &mut [SaSint],
963 n: SaSint,
964 k: SaSint,
965 buckets: &mut [SaSint],
966 omp_block_start: FastSint,
967 omp_block_size: FastSint,
968) -> SaSint {
969 buckets.fill(0);
970 let n = n as FastSint;
971 let _k = k as FastSint;
972 let mut m = omp_block_start + omp_block_size - 1;
973
974 if omp_block_size > 0 {
975 let prefetch_distance = 64 as FastSint;
976 let mut j = m + 1;
977 let mut c0 = t[m as usize] as FastSint;
978 let mut c1 = -1;
979
980 while j < n {
981 c1 = t[j as usize] as FastSint;
982 if c1 != c0 {
983 break;
984 }
985 j += 1;
986 }
987
988 let mut f0 = usize::from(c0 >= c1);
989 let mut f1: usize;
990 let mut i = m - 1;
991 let limit = omp_block_start + prefetch_distance + 3;
992
993 while i >= limit {
994 let _prefetch_index = i - 2 * prefetch_distance;
995 c1 = t[i as usize] as FastSint;
996 f1 = usize::from(c1 > (c0 - f0 as FastSint));
997 sa[m as usize] = (i + 1) as SaSint;
998 m -= (f1 & !f0) as FastSint;
999 buckets[buckets_index4((c0 as SaSint & SAINT_MAX) as usize, f0 + f0 + f1)] += 1;
1000
1001 c0 = t[(i - 1) as usize] as FastSint;
1002 f0 = usize::from(c0 > (c1 - f1 as FastSint));
1003 sa[m as usize] = i as SaSint;
1004 m -= (f0 & !f1) as FastSint;
1005 buckets[buckets_index4((c1 as SaSint & SAINT_MAX) as usize, f1 + f1 + f0)] += 1;
1006
1007 c1 = t[(i - 2) as usize] as FastSint;
1008 f1 = usize::from(c1 > (c0 - f0 as FastSint));
1009 sa[m as usize] = (i - 1) as SaSint;
1010 m -= (f1 & !f0) as FastSint;
1011 buckets[buckets_index4((c0 as SaSint & SAINT_MAX) as usize, f0 + f0 + f1)] += 1;
1012
1013 c0 = t[(i - 3) as usize] as FastSint;
1014 f0 = usize::from(c0 > (c1 - f1 as FastSint));
1015 sa[m as usize] = (i - 2) as SaSint;
1016 m -= (f0 & !f1) as FastSint;
1017 buckets[buckets_index4((c1 as SaSint & SAINT_MAX) as usize, f1 + f1 + f0)] += 1;
1018
1019 i -= 4;
1020 }
1021
1022 let tail_limit = omp_block_start;
1023 while i >= tail_limit {
1024 c1 = c0;
1025 c0 = t[i as usize] as FastSint;
1026 f1 = f0;
1027 f0 = usize::from(c0 > (c1 - f1 as FastSint));
1028 sa[m as usize] = (i + 1) as SaSint;
1029 m -= (f0 & !f1) as FastSint;
1030 buckets[buckets_index4((c1 as SaSint & SAINT_MAX) as usize, f1 + f1 + f0)] += 1;
1031 i -= 1;
1032 }
1033
1034 c1 = if i >= 0 {
1035 t[i as usize] as FastSint
1036 } else {
1037 -1
1038 };
1039 f1 = usize::from(c1 > (c0 - f0 as FastSint));
1040 sa[m as usize] = (i + 1) as SaSint;
1041 m -= (f1 & !f0) as FastSint;
1042 buckets[buckets_index4((c0 as SaSint & SAINT_MAX) as usize, f0 + f0 + f1)] += 1;
1043 }
1044
1045 (omp_block_start + omp_block_size - 1 - m) as SaSint
1046}
1047
1048#[doc(hidden)]
1050pub fn count_and_gather_lms_suffixes_32s_2k(
1051 t: &[SaSint],
1052 sa: &mut [SaSint],
1053 n: SaSint,
1054 k: SaSint,
1055 buckets: &mut [SaSint],
1056 omp_block_start: FastSint,
1057 omp_block_size: FastSint,
1058) -> SaSint {
1059 buckets.fill(0);
1060 let n = n as FastSint;
1061 let _k = k as FastSint;
1062 let mut m = omp_block_start + omp_block_size - 1;
1063
1064 if omp_block_size > 0 {
1065 let prefetch_distance = 64 as FastSint;
1066 let mut j = m + 1;
1067 let mut c0 = t[m as usize] as FastSint;
1068 let mut c1 = -1;
1069
1070 while j < n {
1071 c1 = t[j as usize] as FastSint;
1072 if c1 != c0 {
1073 break;
1074 }
1075 j += 1;
1076 }
1077
1078 let mut f0 = usize::from(c0 >= c1);
1079 let mut f1: usize;
1080 let mut i = m - 1;
1081 let limit = omp_block_start + prefetch_distance + 3;
1082
1083 while i >= limit {
1084 let _prefetch_index = i - 2 * prefetch_distance;
1085 c1 = t[i as usize] as FastSint;
1086 f1 = usize::from(c1 > (c0 - f0 as FastSint));
1087 sa[m as usize] = (i + 1) as SaSint;
1088 m -= (f1 & !f0) as FastSint;
1089 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
1090
1091 c0 = t[(i - 1) as usize] as FastSint;
1092 f0 = usize::from(c0 > (c1 - f1 as FastSint));
1093 sa[m as usize] = i as SaSint;
1094 m -= (f0 & !f1) as FastSint;
1095 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
1096
1097 c1 = t[(i - 2) as usize] as FastSint;
1098 f1 = usize::from(c1 > (c0 - f0 as FastSint));
1099 sa[m as usize] = (i - 1) as SaSint;
1100 m -= (f1 & !f0) as FastSint;
1101 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
1102
1103 c0 = t[(i - 3) as usize] as FastSint;
1104 f0 = usize::from(c0 > (c1 - f1 as FastSint));
1105 sa[m as usize] = (i - 2) as SaSint;
1106 m -= (f0 & !f1) as FastSint;
1107 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
1108
1109 i -= 4;
1110 }
1111
1112 let tail_limit = omp_block_start;
1113 while i >= tail_limit {
1114 c1 = c0;
1115 c0 = t[i as usize] as FastSint;
1116 f1 = f0;
1117 f0 = usize::from(c0 > (c1 - f1 as FastSint));
1118 sa[m as usize] = (i + 1) as SaSint;
1119 m -= (f0 & !f1) as FastSint;
1120 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
1121 i -= 1;
1122 }
1123
1124 c1 = if i >= 0 {
1125 t[i as usize] as FastSint
1126 } else {
1127 -1
1128 };
1129 f1 = usize::from(c1 > (c0 - f0 as FastSint));
1130 sa[m as usize] = (i + 1) as SaSint;
1131 m -= (f1 & !f0) as FastSint;
1132 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
1133 }
1134
1135 (omp_block_start + omp_block_size - 1 - m) as SaSint
1136}
1137
1138#[doc(hidden)]
1140pub fn count_and_gather_compacted_lms_suffixes_32s_2k(
1141 t: &[SaSint],
1142 sa: &mut [SaSint],
1143 n: SaSint,
1144 k: SaSint,
1145 buckets: &mut [SaSint],
1146 omp_block_start: FastSint,
1147 omp_block_size: FastSint,
1148) -> SaSint {
1149 buckets.fill(0);
1150 let n_usize = usize::try_from(n).expect("n must be non-negative");
1151 let _k_usize = usize::try_from(k).expect("k must be non-negative");
1152 let block_start =
1153 usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
1154 let block_size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
1155 let mut m = block_start + block_size - 1;
1156
1157 if omp_block_size > 0 {
1158 let mut j = m + 1;
1159 let mut c0 = t[m] as FastSint;
1160 let mut c1 = -1;
1161
1162 while j < n_usize {
1163 c1 = t[j] as FastSint;
1164 if c1 != c0 {
1165 break;
1166 }
1167 j += 1;
1168 }
1169
1170 let mut f0 = usize::from(c0 >= c1);
1171 let mut f1: usize;
1172 let mut i = m as FastSint - 1;
1173 let limit = block_start as FastSint + 3;
1174
1175 while i >= limit {
1176 c1 = t[i as usize] as FastSint;
1177 f1 = usize::from(c1 > (c0 - f0 as FastSint));
1178 sa[m] = (i + 1) as SaSint;
1179 m -= f1 & !f0 & usize::from(c0 >= 0);
1180 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
1181
1182 c0 = t[(i - 1) as usize] as FastSint;
1183 f0 = usize::from(c0 > (c1 - f1 as FastSint));
1184 sa[m] = i as SaSint;
1185 m -= f0 & !f1 & usize::from(c1 >= 0);
1186 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
1187
1188 c1 = t[(i - 2) as usize] as FastSint;
1189 f1 = usize::from(c1 > (c0 - f0 as FastSint));
1190 sa[m] = (i - 1) as SaSint;
1191 m -= f1 & !f0 & usize::from(c0 >= 0);
1192 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
1193
1194 c0 = t[(i - 3) as usize] as FastSint;
1195 f0 = usize::from(c0 > (c1 - f1 as FastSint));
1196 sa[m] = (i - 2) as SaSint;
1197 m -= f0 & !f1 & usize::from(c1 >= 0);
1198 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
1199
1200 i -= 4;
1201 }
1202
1203 let tail_limit = block_start as FastSint;
1204 while i >= tail_limit {
1205 c1 = c0;
1206 c0 = t[i as usize] as FastSint;
1207 f1 = f0;
1208 f0 = usize::from(c0 > (c1 - f1 as FastSint));
1209 sa[m] = (i + 1) as SaSint;
1210 m -= f0 & !f1 & usize::from(c1 >= 0);
1211 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
1212 i -= 1;
1213 }
1214
1215 c1 = if i >= 0 {
1216 t[i as usize] as FastSint
1217 } else {
1218 -1
1219 };
1220 f1 = usize::from(c1 > (c0 - f0 as FastSint));
1221 sa[m] = (i + 1) as SaSint;
1222 m -= f1 & !f0 & usize::from(c0 >= 0);
1223 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
1224 }
1225
1226 (block_start + block_size - 1 - m) as SaSint
1227}
1228
1229#[doc(hidden)]
1231pub fn get_bucket_stride(
1232 free_space: FastSint,
1233 bucket_size: FastSint,
1234 num_buckets: FastSint,
1235) -> FastSint {
1236 let bucket_size_1024 = (bucket_size + 1023) & (-1024);
1237 if free_space / (num_buckets - 1) >= bucket_size_1024 {
1238 return bucket_size_1024;
1239 }
1240 let bucket_size_16 = (bucket_size + 15) & (-16);
1241 if free_space / (num_buckets - 1) >= bucket_size_16 {
1242 return bucket_size_16;
1243 }
1244 bucket_size
1245}
1246
1247#[doc(hidden)]
1249pub fn count_and_gather_lms_suffixes_32s_4k_nofs_omp(
1250 t: &[SaSint],
1251 sa: &mut [SaSint],
1252 n: SaSint,
1253 k: SaSint,
1254 buckets: &mut [SaSint],
1255 threads: SaSint,
1256) -> SaSint {
1257 let m;
1258 let omp_num_threads = if threads > 1 && n >= 65_536 { 2 } else { 1 };
1259
1260 if omp_num_threads == 1 {
1261 m = count_and_gather_lms_suffixes_32s_4k(t, sa, n, k, buckets, 0, n as FastSint);
1262 } else {
1263 count_lms_suffixes_32s_4k(t, n, k, buckets);
1264 m = gather_lms_suffixes_32s(t, sa, n);
1265 }
1266
1267 m
1268}
1269
1270#[doc(hidden)]
1272pub fn count_and_gather_lms_suffixes_32s_2k_nofs_omp(
1273 t: &[SaSint],
1274 sa: &mut [SaSint],
1275 n: SaSint,
1276 k: SaSint,
1277 buckets: &mut [SaSint],
1278 threads: SaSint,
1279) -> SaSint {
1280 let m;
1281 let omp_num_threads = if threads > 1 && n >= 65_536 { 2 } else { 1 };
1282
1283 if omp_num_threads == 1 {
1284 m = count_and_gather_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as FastSint);
1285 } else {
1286 count_lms_suffixes_32s_2k(t, n, k, buckets);
1287 m = gather_lms_suffixes_32s(t, sa, n);
1288 }
1289
1290 m
1291}
1292
1293#[doc(hidden)]
1295pub fn count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(
1296 t: &[SaSint],
1297 sa: &mut [SaSint],
1298 n: SaSint,
1299 k: SaSint,
1300 buckets: &mut [SaSint],
1301 threads: SaSint,
1302) -> SaSint {
1303 let m;
1304 let omp_num_threads = if threads > 1 && n >= 65_536 { 2 } else { 1 };
1305
1306 if omp_num_threads == 1 {
1307 m = count_and_gather_compacted_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as FastSint);
1308 } else {
1309 count_compacted_lms_suffixes_32s_2k(t, n, k, buckets);
1310 m = gather_compacted_lms_suffixes_32s(t, sa, n);
1311 }
1312
1313 m
1314}
1315
1316#[doc(hidden)]
1318pub fn count_and_gather_lms_suffixes_32s_4k_fs_omp(
1319 t: &[SaSint],
1320 sa: &mut [SaSint],
1321 n: SaSint,
1322 k: SaSint,
1323 buckets: &mut [SaSint],
1324 local_buckets: SaSint,
1325 threads: SaSint,
1326 thread_state: &mut [ThreadState],
1327) -> SaSint {
1328 let n_usize = usize::try_from(n).expect("n must be non-negative");
1329 let k_usize = usize::try_from(k).expect("k must be non-negative");
1330 let omp_num_threads = usize::try_from(threads).expect("threads must be non-negative");
1331 let bucket_size = FastSint::try_from(4 * k_usize).expect("bucket size must fit FastSint");
1332
1333 if omp_num_threads <= 1 || n < 65_536 {
1334 return count_and_gather_lms_suffixes_32s_4k(t, sa, n, k, buckets, 0, n as FastSint);
1335 }
1336
1337 let omp_block_stride = (n_usize / omp_num_threads) & !15usize;
1338 let free_space = if local_buckets == 1 {
1339 FastSint::try_from(LIBSAIS_LOCAL_BUFFER_SIZE).expect("free space must fit FastSint")
1340 } else if local_buckets > 1 {
1341 FastSint::try_from(local_buckets).expect("free space must fit FastSint")
1342 } else {
1343 FastSint::try_from(buckets.len()).expect("free space must fit FastSint")
1344 };
1345 let bucket_stride = get_bucket_stride(
1346 free_space,
1347 bucket_size,
1348 FastSint::try_from(omp_num_threads).expect("thread count must fit FastSint"),
1349 );
1350 let bucket_size_usize = usize::try_from(bucket_size).expect("bucket size must be non-negative");
1351 let bucket_stride_usize =
1352 usize::try_from(bucket_stride).expect("bucket stride must be non-negative");
1353 let workspace_len =
1354 bucket_size_usize + bucket_stride_usize.saturating_mul(omp_num_threads.saturating_sub(1));
1355 let mut workspace = vec![0; workspace_len];
1356
1357 {
1358 let sa_ptr = SyncMutPtr::new(sa);
1359 let ws_ptr = SyncMutPtr::new(&mut workspace);
1360 run_rayon_with_threads(omp_num_threads, || {
1361 thread_state[..omp_num_threads]
1362 .par_iter_mut()
1363 .enumerate()
1364 .for_each(|(omp_thread_num, state)| {
1365 let omp_block_start = omp_thread_num * omp_block_stride;
1366 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
1367 omp_block_stride
1368 } else {
1369 n_usize - omp_block_start
1370 };
1371 let workspace_end = workspace_len - omp_thread_num * bucket_stride_usize;
1372 let workspace_start = workspace_end - bucket_size_usize;
1373 let sa = unsafe { sa_ptr.as_slice() };
1376 let workspace = unsafe { ws_ptr.as_slice() };
1377 let count = count_and_gather_lms_suffixes_32s_4k(
1378 t,
1379 sa,
1380 n,
1381 k,
1382 &mut workspace[workspace_start..workspace_end],
1383 omp_block_start as FastSint,
1384 omp_block_size as FastSint,
1385 );
1386 state.position = (omp_block_start + omp_block_size) as FastSint;
1387 state.count = count as FastSint;
1388 });
1389 });
1390 }
1391
1392 let mut m = 0;
1393 for t in (0..omp_num_threads).rev() {
1394 m += thread_state[t].count as SaSint;
1395
1396 if t + 1 != omp_num_threads && thread_state[t].count > 0 {
1397 let src_end =
1398 usize::try_from(thread_state[t].position).expect("position must be non-negative");
1399 let src_start = src_end
1400 - usize::try_from(thread_state[t].count).expect("count must be non-negative");
1401 let dst_start = usize::try_from(n - m).expect("destination must be non-negative");
1402 sa.copy_within(src_start..src_end, dst_start);
1403 }
1404 }
1405
1406 let omp_num_threads = omp_num_threads - 1;
1407 let omp_block_stride = (bucket_size_usize / omp_num_threads) & !15usize;
1408 {
1409 let ws_ptr = SyncMutPtr::new(&mut workspace);
1410 run_rayon_with_threads(omp_num_threads, || {
1411 (0..omp_num_threads)
1412 .into_par_iter()
1413 .for_each(|omp_thread_num| {
1414 let omp_block_start = omp_thread_num * omp_block_stride;
1415 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
1416 omp_block_stride
1417 } else {
1418 bucket_size_usize - omp_block_start
1419 };
1420 let workspace = unsafe { ws_ptr.as_slice() };
1425 accumulate_counts_s32(
1426 &mut workspace[omp_block_start..],
1427 omp_block_size as FastSint,
1428 bucket_stride,
1429 FastSint::try_from(omp_num_threads + 1)
1430 .expect("thread count must fit FastSint"),
1431 );
1432 });
1433 });
1434 }
1435
1436 let accumulated_start = omp_num_threads * bucket_stride_usize;
1437 buckets[..bucket_size_usize]
1438 .copy_from_slice(&workspace[accumulated_start..accumulated_start + bucket_size_usize]);
1439 m
1440}
1441
1442#[doc(hidden)]
1444pub fn count_and_gather_lms_suffixes_32s_2k_fs_omp(
1445 t: &[SaSint],
1446 sa: &mut [SaSint],
1447 n: SaSint,
1448 k: SaSint,
1449 buckets: &mut [SaSint],
1450 local_buckets: SaSint,
1451 threads: SaSint,
1452 thread_state: &mut [ThreadState],
1453) -> SaSint {
1454 let n_usize = usize::try_from(n).expect("n must be non-negative");
1455 let k_usize = usize::try_from(k).expect("k must be non-negative");
1456 let omp_num_threads = usize::try_from(threads).expect("threads must be non-negative");
1457 let bucket_size = FastSint::try_from(2 * k_usize).expect("bucket size must fit FastSint");
1458
1459 if omp_num_threads <= 1 || n < 65_536 {
1460 return count_and_gather_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as FastSint);
1461 }
1462
1463 let omp_block_stride = (n_usize / omp_num_threads) & !15usize;
1464 let free_space = if local_buckets == 1 {
1465 FastSint::try_from(LIBSAIS_LOCAL_BUFFER_SIZE).expect("free space must fit FastSint")
1466 } else if local_buckets > 1 {
1467 FastSint::try_from(local_buckets).expect("free space must fit FastSint")
1468 } else {
1469 FastSint::try_from(buckets.len()).expect("free space must fit FastSint")
1470 };
1471 let bucket_stride = get_bucket_stride(
1472 free_space,
1473 bucket_size,
1474 FastSint::try_from(omp_num_threads).expect("thread count must fit FastSint"),
1475 );
1476 let bucket_size_usize = usize::try_from(bucket_size).expect("bucket size must be non-negative");
1477 let bucket_stride_usize =
1478 usize::try_from(bucket_stride).expect("bucket stride must be non-negative");
1479 let workspace_len =
1480 bucket_size_usize + bucket_stride_usize.saturating_mul(omp_num_threads.saturating_sub(1));
1481 let mut workspace = vec![0; workspace_len];
1482
1483 {
1484 let sa_ptr = SyncMutPtr::new(sa);
1485 let ws_ptr = SyncMutPtr::new(&mut workspace);
1486 run_rayon_with_threads(omp_num_threads, || {
1487 thread_state[..omp_num_threads]
1488 .par_iter_mut()
1489 .enumerate()
1490 .for_each(|(omp_thread_num, state)| {
1491 let omp_block_start = omp_thread_num * omp_block_stride;
1492 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
1493 omp_block_stride
1494 } else {
1495 n_usize - omp_block_start
1496 };
1497 let workspace_end = workspace_len - omp_thread_num * bucket_stride_usize;
1498 let workspace_start = workspace_end - bucket_size_usize;
1499 let sa = unsafe { sa_ptr.as_slice() };
1501 let workspace = unsafe { ws_ptr.as_slice() };
1502 let count = count_and_gather_lms_suffixes_32s_2k(
1503 t,
1504 sa,
1505 n,
1506 k,
1507 &mut workspace[workspace_start..workspace_end],
1508 omp_block_start as FastSint,
1509 omp_block_size as FastSint,
1510 );
1511 state.position = (omp_block_start + omp_block_size) as FastSint;
1512 state.count = count as FastSint;
1513 });
1514 });
1515 }
1516
1517 let mut m = 0;
1518 for t in (0..omp_num_threads).rev() {
1519 m += thread_state[t].count as SaSint;
1520 if t + 1 != omp_num_threads && thread_state[t].count > 0 {
1521 let src_end =
1522 usize::try_from(thread_state[t].position).expect("position must be non-negative");
1523 let src_start = src_end
1524 - usize::try_from(thread_state[t].count).expect("count must be non-negative");
1525 let dst_start = usize::try_from(n - m).expect("destination must be non-negative");
1526 sa.copy_within(src_start..src_end, dst_start);
1527 }
1528 }
1529
1530 let omp_num_threads = omp_num_threads - 1;
1531 let omp_block_stride = (bucket_size_usize / omp_num_threads) & !15usize;
1532 {
1533 let ws_ptr = SyncMutPtr::new(&mut workspace);
1534 run_rayon_with_threads(omp_num_threads, || {
1535 (0..omp_num_threads)
1536 .into_par_iter()
1537 .for_each(|omp_thread_num| {
1538 let omp_block_start = omp_thread_num * omp_block_stride;
1539 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
1540 omp_block_stride
1541 } else {
1542 bucket_size_usize - omp_block_start
1543 };
1544 let workspace = unsafe { ws_ptr.as_slice() };
1546 accumulate_counts_s32(
1547 &mut workspace[omp_block_start..],
1548 omp_block_size as FastSint,
1549 bucket_stride,
1550 FastSint::try_from(omp_num_threads + 1)
1551 .expect("thread count must fit FastSint"),
1552 );
1553 });
1554 });
1555 }
1556
1557 let accumulated_start = omp_num_threads * bucket_stride_usize;
1558 buckets[..bucket_size_usize]
1559 .copy_from_slice(&workspace[accumulated_start..accumulated_start + bucket_size_usize]);
1560 m
1561}
1562
1563#[doc(hidden)]
1565pub fn count_and_gather_compacted_lms_suffixes_32s_2k_fs_omp(
1566 t: &[SaSint],
1567 sa: &mut [SaSint],
1568 n: SaSint,
1569 k: SaSint,
1570 buckets: &mut [SaSint],
1571 _local_buckets: SaSint,
1572 threads: SaSint,
1573 thread_state: &mut [ThreadState],
1574) {
1575 let n_usize = usize::try_from(n).expect("n must be non-negative");
1576 let k_usize = usize::try_from(k).expect("k must be non-negative");
1577 let thread_count = usize::try_from(threads).expect("threads must be non-negative");
1578 let bucket_size = 2 * k_usize;
1579
1580 if thread_count <= 1 || n < 65_536 {
1581 let _ =
1582 count_and_gather_compacted_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as FastSint);
1583 return;
1584 }
1585
1586 if thread_state.len() < thread_count || sa.len() < 2 * n_usize {
1587 let _ =
1588 count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(t, sa, n, k, buckets, threads);
1589 return;
1590 }
1591
1592 let omp_block_stride = (n_usize / thread_count) & !15usize;
1593 let free_space = if _local_buckets != 0 {
1594 FastSint::try_from(LIBSAIS_LOCAL_BUFFER_SIZE).expect("free space must fit FastSint")
1595 } else {
1596 FastSint::try_from(buckets.len()).expect("free space must fit FastSint")
1597 };
1598 let bucket_stride = get_bucket_stride(
1599 free_space,
1600 FastSint::try_from(bucket_size).expect("bucket size must fit FastSint"),
1601 FastSint::try_from(thread_count).expect("thread count must fit FastSint"),
1602 );
1603 let bucket_stride_usize =
1604 usize::try_from(bucket_stride).expect("bucket stride must be non-negative");
1605 let workspace_len =
1606 bucket_size + bucket_stride_usize.saturating_mul(thread_count.saturating_sub(1));
1607 let mut workspace = vec![0; workspace_len];
1608
1609 let usable_thread_state_len = thread_count.min(thread_state.len());
1610
1611 {
1612 let sa_ptr = SyncMutPtr::new(sa);
1613 let ws_ptr = SyncMutPtr::new(&mut workspace);
1614 let state_ptr = SyncMutPtr::new(thread_state);
1615 run_rayon_with_threads(thread_count, || {
1616 (0..thread_count)
1617 .into_par_iter()
1618 .for_each(|omp_thread_num| {
1619 let omp_block_start = omp_thread_num * omp_block_stride;
1620 let omp_block_size = if omp_thread_num + 1 < thread_count {
1621 omp_block_stride
1622 } else {
1623 n_usize - omp_block_start
1624 };
1625
1626 let workspace_end = workspace_len - omp_thread_num * bucket_stride_usize;
1627 let workspace_start = workspace_end - bucket_size;
1628 let sa = unsafe { sa_ptr.as_slice() };
1632 let workspace = unsafe { ws_ptr.as_slice() };
1633 let count = count_and_gather_compacted_lms_suffixes_32s_2k(
1634 t,
1635 &mut sa[n_usize..],
1636 n,
1637 k,
1638 &mut workspace[workspace_start..workspace_end],
1639 omp_block_start as FastSint,
1640 omp_block_size as FastSint,
1641 );
1642
1643 if omp_thread_num < usable_thread_state_len {
1644 let states = unsafe { state_ptr.as_slice() };
1645 states[omp_thread_num].position =
1646 (omp_block_start + omp_block_size) as FastSint;
1647 states[omp_thread_num].count = count as FastSint;
1648 }
1649 });
1650 });
1651 }
1652
1653 let mut m = 0usize;
1654 for omp_thread_num in (0..thread_count).rev() {
1655 let count = usize::try_from(thread_state[omp_thread_num].count)
1656 .expect("count must be non-negative");
1657 m += count;
1658 if count > 0 {
1659 let position = usize::try_from(thread_state[omp_thread_num].position)
1660 .expect("position must be non-negative");
1661 let src_start = n_usize + position - count;
1662 let src_end = n_usize + position;
1663 let dst_start = n_usize - m;
1664 sa.copy_within(src_start..src_end, dst_start);
1665 }
1666 }
1667
1668 let accumulation_threads = thread_count;
1669 let omp_block_stride = (bucket_size / accumulation_threads) & !15usize;
1670 {
1671 let ws_ptr = SyncMutPtr::new(&mut workspace);
1672 run_rayon_with_threads(accumulation_threads, || {
1673 (0..accumulation_threads)
1674 .into_par_iter()
1675 .for_each(|omp_thread_num| {
1676 let omp_block_start = omp_thread_num * omp_block_stride;
1677 let omp_block_size = if omp_thread_num + 1 < accumulation_threads {
1678 omp_block_stride
1679 } else {
1680 bucket_size - omp_block_start
1681 };
1682 let workspace = unsafe { ws_ptr.as_slice() };
1684 accumulate_counts_s32(
1685 &mut workspace[omp_block_start..],
1686 omp_block_size as FastSint,
1687 bucket_stride,
1688 FastSint::try_from(thread_count).expect("thread count must fit FastSint"),
1689 );
1690 });
1691 });
1692 }
1693 let accumulated_start = (accumulation_threads - 1) * bucket_stride_usize;
1694 buckets[..bucket_size]
1695 .copy_from_slice(&workspace[accumulated_start..accumulated_start + bucket_size]);
1696}
1697
1698#[doc(hidden)]
1700pub fn count_and_gather_lms_suffixes_32s_4k_omp(
1701 t: &[SaSint],
1702 sa: &mut [SaSint],
1703 n: SaSint,
1704 k: SaSint,
1705 buckets: &mut [SaSint],
1706 local_buckets: SaSint,
1707 threads: SaSint,
1708 thread_state: &mut [ThreadState],
1709) -> SaSint {
1710 let free_space = if local_buckets > 1 {
1711 local_buckets as FastSint
1712 } else if local_buckets != 0 {
1713 LIBSAIS_LOCAL_BUFFER_SIZE as FastSint
1714 } else {
1715 FastSint::try_from(buckets.len()).expect("bucket length must fit FastSint")
1716 };
1717 let threads_fast = threads as FastSint;
1718 let mut max_threads = (free_space / (((4 * k as FastSint) + 15) & -16)).min(threads_fast);
1719
1720 if max_threads > 1 && n >= 65_536 && n / k >= 2 {
1721 let thread_cap = (n / (16 * k)) as FastSint;
1722 if max_threads > thread_cap {
1723 max_threads = thread_cap;
1724 }
1725 return count_and_gather_lms_suffixes_32s_4k_fs_omp(
1726 t,
1727 sa,
1728 n,
1729 k,
1730 buckets,
1731 local_buckets,
1732 max_threads.max(2) as SaSint,
1733 thread_state,
1734 );
1735 }
1736
1737 if threads > 1 && n >= 65_536 {
1738 count_lms_suffixes_32s_4k(t, n, k, buckets);
1739 gather_lms_suffixes_32s(t, sa, n)
1740 } else {
1741 count_and_gather_lms_suffixes_32s_4k(t, sa, n, k, buckets, 0, n as FastSint)
1742 }
1743}
1744
1745#[doc(hidden)]
1747pub fn count_and_gather_lms_suffixes_32s_2k_omp(
1748 t: &[SaSint],
1749 sa: &mut [SaSint],
1750 n: SaSint,
1751 k: SaSint,
1752 buckets: &mut [SaSint],
1753 local_buckets: SaSint,
1754 threads: SaSint,
1755 thread_state: &mut [ThreadState],
1756) -> SaSint {
1757 let free_space = if local_buckets > 1 {
1758 local_buckets as FastSint
1759 } else if local_buckets != 0 {
1760 LIBSAIS_LOCAL_BUFFER_SIZE as FastSint
1761 } else {
1762 FastSint::try_from(buckets.len()).expect("bucket length must fit FastSint")
1763 };
1764 let threads_fast = threads as FastSint;
1765 let mut max_threads = (free_space / (((2 * k as FastSint) + 15) & -16)).min(threads_fast);
1766
1767 if max_threads > 1 && n >= 65_536 && n / k >= 2 {
1768 let thread_cap = (n / (8 * k)) as FastSint;
1769 if max_threads > thread_cap {
1770 max_threads = thread_cap;
1771 }
1772 return count_and_gather_lms_suffixes_32s_2k_fs_omp(
1773 t,
1774 sa,
1775 n,
1776 k,
1777 buckets,
1778 local_buckets,
1779 max_threads.max(2) as SaSint,
1780 thread_state,
1781 );
1782 }
1783
1784 if threads > 1 && n >= 65_536 {
1785 count_lms_suffixes_32s_2k(t, n, k, buckets);
1786 gather_lms_suffixes_32s(t, sa, n)
1787 } else {
1788 count_and_gather_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as FastSint)
1789 }
1790}
1791
1792#[doc(hidden)]
1794pub fn count_and_gather_compacted_lms_suffixes_32s_2k_omp(
1795 t: &[SaSint],
1796 sa: &mut [SaSint],
1797 n: SaSint,
1798 k: SaSint,
1799 buckets: &mut [SaSint],
1800 local_buckets: SaSint,
1801 threads: SaSint,
1802 thread_state: &mut [ThreadState],
1803) {
1804 let free_space = if local_buckets != 0 {
1805 LIBSAIS_LOCAL_BUFFER_SIZE as FastSint
1806 } else {
1807 FastSint::try_from(buckets.len()).expect("bucket length must fit FastSint")
1808 };
1809 let threads_fast = threads as FastSint;
1810 let mut max_threads = (free_space / (((2 * k as FastSint) + 15) & -16)).min(threads_fast);
1811
1812 if local_buckets == 0 && max_threads > 1 && n >= 65_536 && n / k >= 2 {
1813 let thread_cap = (n / (8 * k)) as FastSint;
1814 if max_threads > thread_cap {
1815 max_threads = thread_cap;
1816 }
1817 count_and_gather_compacted_lms_suffixes_32s_2k_fs_omp(
1818 t,
1819 sa,
1820 n,
1821 k,
1822 buckets,
1823 local_buckets,
1824 max_threads.max(2) as SaSint,
1825 thread_state,
1826 );
1827 return;
1828 }
1829
1830 let _ = count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(t, sa, n, k, buckets, threads);
1831}
1832
1833#[doc(hidden)]
1835pub fn count_suffixes_32s(t: &[SaSint], n: SaSint, k: SaSint, buckets: &mut [SaSint]) {
1836 let n_usize = usize::try_from(n).expect("n must be non-negative");
1837 let k_usize = usize::try_from(k).expect("k must be non-negative");
1838 buckets[..k_usize].fill(0);
1839
1840 let mut i = 0usize;
1841 let mut j = n_usize.saturating_sub(7);
1842 while i < j {
1843 buckets[t[i] as usize] += 1;
1844 buckets[t[i + 1] as usize] += 1;
1845 buckets[t[i + 2] as usize] += 1;
1846 buckets[t[i + 3] as usize] += 1;
1847 buckets[t[i + 4] as usize] += 1;
1848 buckets[t[i + 5] as usize] += 1;
1849 buckets[t[i + 6] as usize] += 1;
1850 buckets[t[i + 7] as usize] += 1;
1851 i += 8;
1852 }
1853
1854 j += 7;
1855 while i < j {
1856 buckets[t[i] as usize] += 1;
1857 i += 1;
1858 }
1859}
1860
1861#[doc(hidden)]
1863pub fn initialize_buckets_start_and_end_8u(
1864 buckets: &mut [SaSint],
1865 freq: Option<&mut [SaSint]>,
1866) -> SaSint {
1867 let start_offset = 6 * ALPHABET_SIZE;
1868 let end_offset = 7 * ALPHABET_SIZE;
1869 let mut k = -1isize;
1870 let mut sum = 0;
1871
1872 match freq {
1873 Some(freq) => {
1874 for j in 0..ALPHABET_SIZE {
1875 let i = buckets_index4(j, 0);
1876 let total = buckets[i] + buckets[i + 1] + buckets[i + 2] + buckets[i + 3];
1877 buckets[start_offset + j] = sum;
1878 sum += total;
1879 buckets[end_offset + j] = sum;
1880 if total > 0 {
1881 k = j as isize;
1882 }
1883 freq[j] = total;
1884 }
1885 }
1886 None => {
1887 for j in 0..ALPHABET_SIZE {
1888 let i = buckets_index4(j, 0);
1889 let total = buckets[i] + buckets[i + 1] + buckets[i + 2] + buckets[i + 3];
1890 buckets[start_offset + j] = sum;
1891 sum += total;
1892 buckets[end_offset + j] = sum;
1893 if total > 0 {
1894 k = j as isize;
1895 }
1896 }
1897 }
1898 }
1899
1900 (k + 1) as SaSint
1901}
1902
1903#[doc(hidden)]
1905pub fn initialize_buckets_start_and_end_32s_6k(k: SaSint, buckets: &mut [SaSint]) {
1906 let k_usize = usize::try_from(k).expect("k must be non-negative");
1907 let start_offset = 4 * k_usize;
1908 let end_offset = 5 * k_usize;
1909 let mut sum = 0;
1910 for j in 0..k_usize {
1911 let i = buckets_index4(j, 0);
1912 buckets[start_offset + j] = sum;
1913 sum += buckets[i] + buckets[i + 1] + buckets[i + 2] + buckets[i + 3];
1914 buckets[end_offset + j] = sum;
1915 }
1916}
1917
1918#[doc(hidden)]
1920pub fn initialize_buckets_start_and_end_32s_4k(k: SaSint, buckets: &mut [SaSint]) {
1921 let k_usize = usize::try_from(k).expect("k must be non-negative");
1922 let start_offset = 2 * k_usize;
1923 let end_offset = 3 * k_usize;
1924 let mut sum = 0;
1925 for j in 0..k_usize {
1926 let i = buckets_index2(j, 0);
1927 buckets[start_offset + j] = sum;
1928 sum += buckets[i] + buckets[i + 1];
1929 buckets[end_offset + j] = sum;
1930 }
1931}
1932
1933#[doc(hidden)]
1935pub fn initialize_buckets_end_32s_2k(k: SaSint, buckets: &mut [SaSint]) {
1936 let k_usize = usize::try_from(k).expect("k must be non-negative");
1937 let mut sum0 = 0;
1938 for j in 0..k_usize {
1939 let i = buckets_index2(j, 0);
1940 sum0 += buckets[i] + buckets[i + 1];
1941 buckets[i] = sum0;
1942 }
1943}
1944
1945#[doc(hidden)]
1947pub fn initialize_buckets_start_and_end_32s_2k(k: SaSint, buckets: &mut [SaSint]) {
1948 let k_usize = usize::try_from(k).expect("k must be non-negative");
1949 for j in 0..k_usize {
1950 let i = buckets_index2(j, 0);
1951 buckets[j] = buckets[i];
1952 }
1953 buckets[k_usize] = 0;
1954 for j in 1..k_usize {
1955 buckets[k_usize + j] = buckets[j - 1];
1956 }
1957}
1958
1959#[doc(hidden)]
1961pub fn initialize_buckets_start_32s_1k(k: SaSint, buckets: &mut [SaSint]) {
1962 let k_usize = usize::try_from(k).expect("k must be non-negative");
1963 let mut sum = 0;
1964 for bucket in buckets.iter_mut().take(k_usize) {
1965 let tmp = *bucket;
1966 *bucket = sum;
1967 sum += tmp;
1968 }
1969}
1970
1971#[doc(hidden)]
1973pub fn initialize_buckets_end_32s_1k(k: SaSint, buckets: &mut [SaSint]) {
1974 let k_usize = usize::try_from(k).expect("k must be non-negative");
1975 let mut sum = 0;
1976 for bucket in buckets.iter_mut().take(k_usize) {
1977 sum += *bucket;
1978 *bucket = sum;
1979 }
1980}
1981
1982#[doc(hidden)]
1984pub fn initialize_buckets_for_lms_suffixes_radix_sort_8u(
1985 t: &[u8],
1986 buckets: &mut [SaSint],
1987 mut first_lms_suffix: SaSint,
1988) -> SaSint {
1989 let mut f0 = 0usize;
1990 let mut f1: usize;
1991 let mut c0 = t[first_lms_suffix as usize] as FastSint;
1992 let mut c1: FastSint;
1993
1994 while {
1995 first_lms_suffix -= 1;
1996 first_lms_suffix >= 0
1997 } {
1998 c1 = c0;
1999 c0 = t[first_lms_suffix as usize] as FastSint;
2000 f1 = f0;
2001 f0 = usize::from(c0 > (c1 - f1 as FastSint));
2002 let idx = 4 * c1 as usize + (f1 + f1 + f0);
2003 buckets[idx] -= 1;
2004 }
2005 buckets[4 * c0 as usize + (f0 + f0)] -= 1;
2006
2007 let temp_offset = 4 * ALPHABET_SIZE;
2008 let mut sum = 0;
2009 for j in 0..ALPHABET_SIZE {
2010 let i = 4 * j;
2011 let tj = 2 * j;
2012 buckets[temp_offset + tj + 1] = sum;
2013 sum += buckets[i + 1] + buckets[i + 3];
2014 buckets[temp_offset + tj] = sum;
2015 }
2016 sum
2017}
2018
2019#[doc(hidden)]
2021pub fn initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
2022 t: &[SaSint],
2023 k: SaSint,
2024 buckets: &mut [SaSint],
2025 first_lms_suffix: SaSint,
2026) {
2027 let _k_usize = usize::try_from(k).expect("k must be non-negative");
2028 buckets[buckets_index2(t[first_lms_suffix as usize] as usize, 0)] += 1;
2029 buckets[buckets_index2(t[first_lms_suffix as usize] as usize, 1)] -= 1;
2030
2031 let mut sum0 = 0;
2032 let mut sum1 = 0;
2033 for j in 0..usize::try_from(k).unwrap() {
2034 let i = buckets_index2(j, 0);
2035 sum0 += buckets[i] + buckets[i + 1];
2036 sum1 += buckets[i + 1];
2037 buckets[i] = sum0;
2038 buckets[i + 1] = sum1;
2039 }
2040}
2041
2042#[doc(hidden)]
2044pub fn initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
2045 t: &[SaSint],
2046 k: SaSint,
2047 buckets: &mut [SaSint],
2048 mut first_lms_suffix: SaSint,
2049) -> SaSint {
2050 let mut f0 = 0usize;
2051 let mut f1: usize;
2052 let mut c0 = t[first_lms_suffix as usize] as FastSint;
2053 let mut c1: FastSint;
2054
2055 while {
2056 first_lms_suffix -= 1;
2057 first_lms_suffix >= 0
2058 } {
2059 c1 = c0;
2060 c0 = t[first_lms_suffix as usize] as FastSint;
2061 f1 = f0;
2062 f0 = usize::from(c0 > (c1 - f1 as FastSint));
2063 buckets[4 * c1 as usize + (f1 + f1 + f0)] -= 1;
2064 }
2065 buckets[4 * c0 as usize + (f0 + f0)] -= 1;
2066
2067 let temp_offset = 4 * usize::try_from(k).unwrap();
2068 let mut sum = 0;
2069 for j in 0..usize::try_from(k).unwrap() {
2070 let i = 4 * j;
2071 sum += buckets[i + 1] + buckets[i + 3];
2072 buckets[temp_offset + j] = sum;
2073 }
2074 sum
2075}
2076
2077#[doc(hidden)]
2079pub fn initialize_buckets_for_radix_and_partial_sorting_32s_4k(
2080 t: &[SaSint],
2081 k: SaSint,
2082 buckets: &mut [SaSint],
2083 first_lms_suffix: SaSint,
2084) {
2085 let k_usize = usize::try_from(k).expect("k must be non-negative");
2086 let start_offset = 2 * k_usize;
2087 let end_offset = 3 * k_usize;
2088
2089 buckets[buckets_index2(t[first_lms_suffix as usize] as usize, 0)] += 1;
2090 buckets[buckets_index2(t[first_lms_suffix as usize] as usize, 1)] -= 1;
2091
2092 let mut sum0 = 0;
2093 let mut sum1 = 0;
2094 for j in 0..k_usize {
2095 let i = buckets_index2(j, 0);
2096 buckets[start_offset + j] = sum1;
2097 sum0 += buckets[i + 1];
2098 sum1 += buckets[i] + buckets[i + 1];
2099 buckets[i + 1] = sum0;
2100 buckets[end_offset + j] = sum1;
2101 }
2102}
2103
2104#[doc(hidden)]
2106pub fn radix_sort_lms_suffixes_8u(
2107 t: &[u8],
2108 sa: &mut [SaSint],
2109 induction_bucket: &mut [SaSint],
2110 omp_block_start: FastSint,
2111 omp_block_size: FastSint,
2112) {
2113 let prefetch_distance = 64 as FastSint;
2114 let mut i = omp_block_start + omp_block_size - 1;
2115 let mut j = omp_block_start + prefetch_distance + 3;
2116
2117 while i >= j {
2118 let p0 = sa[i as usize];
2119 let idx0 = buckets_index2(t[p0 as usize] as usize, 0);
2120 induction_bucket[idx0] -= 1;
2121 sa[induction_bucket[idx0] as usize] = p0;
2122
2123 let p1 = sa[(i - 1) as usize];
2124 let idx1 = buckets_index2(t[p1 as usize] as usize, 0);
2125 induction_bucket[idx1] -= 1;
2126 sa[induction_bucket[idx1] as usize] = p1;
2127
2128 let p2 = sa[(i - 2) as usize];
2129 let idx2 = buckets_index2(t[p2 as usize] as usize, 0);
2130 induction_bucket[idx2] -= 1;
2131 sa[induction_bucket[idx2] as usize] = p2;
2132
2133 let p3 = sa[(i - 3) as usize];
2134 let idx3 = buckets_index2(t[p3 as usize] as usize, 0);
2135 induction_bucket[idx3] -= 1;
2136 sa[induction_bucket[idx3] as usize] = p3;
2137
2138 i -= 4;
2139 }
2140
2141 j -= prefetch_distance + 3;
2142 while i >= j {
2143 let p = sa[i as usize];
2144 let idx = buckets_index2(t[p as usize] as usize, 0);
2145 induction_bucket[idx] -= 1;
2146 sa[induction_bucket[idx] as usize] = p;
2147 i -= 1;
2148 }
2149}
2150
2151#[doc(hidden)]
2153pub fn radix_sort_lms_suffixes_8u_omp(
2154 t: &[u8],
2155 sa: &mut [SaSint],
2156 n: SaSint,
2157 m: SaSint,
2158 flags: SaSint,
2159 buckets: &mut [SaSint],
2160 threads: SaSint,
2161 thread_state: &mut [ThreadState],
2162) {
2163 if (flags & LIBSAIS_FLAGS_GSA) != 0 {
2164 buckets[4 * ALPHABET_SIZE] -= 1;
2165 }
2166
2167 let omp_num_threads = if threads > 1 && n >= 65_536 && m >= 65_536 {
2168 usize::try_from(threads)
2169 .expect("threads must be non-negative")
2170 .min(thread_state.len())
2171 .max(1)
2172 } else {
2173 1
2174 };
2175
2176 if omp_num_threads == 1 {
2177 radix_sort_lms_suffixes_8u(
2178 t,
2179 sa,
2180 &mut buckets[4 * ALPHABET_SIZE..],
2181 n as FastSint - m as FastSint + 1,
2182 m as FastSint - 1,
2183 );
2184 return;
2185 }
2186
2187 let src_bucket: &[SaSint] = &buckets[4 * ALPHABET_SIZE..];
2188
2189 let m_values: Vec<FastSint> = thread_state[..omp_num_threads]
2194 .iter()
2195 .map(|s| s.m)
2196 .collect();
2197 let m_total = m as FastSint;
2198 let sa_ptr = SyncMutPtr::new(sa);
2199
2200 run_rayon_with_threads(omp_num_threads, || {
2201 thread_state[..omp_num_threads]
2202 .par_iter_mut()
2203 .enumerate()
2204 .for_each(|(thread_num, state)| {
2205 for (i, j) in (0..=buckets_index2(ALPHABET_SIZE - 1, 0))
2208 .step_by(buckets_index2(1, 0))
2209 .zip((buckets_index4(0, 1)..).step_by(buckets_index4(1, 0)))
2210 {
2211 state.buckets[i] = src_bucket[i] - state.buckets[j];
2212 }
2213
2214 let mut omp_block_start: FastSint = 0;
2215 for &other_m in m_values[thread_num..omp_num_threads].iter().rev() {
2216 omp_block_start += other_m;
2217 }
2218
2219 let mut omp_block_size = m_values[thread_num];
2220 if omp_block_start == m_total && omp_block_size > 0 {
2221 omp_block_start -= 1;
2222 omp_block_size -= 1;
2223 }
2224
2225 let sa = unsafe { sa_ptr.as_slice() };
2229 radix_sort_lms_suffixes_8u(
2230 t,
2231 sa,
2232 &mut state.buckets,
2233 n as FastSint - omp_block_start,
2234 omp_block_size,
2235 );
2236 });
2237 });
2238}
2239
2240#[doc(hidden)]
2242pub fn radix_sort_lms_suffixes_32s_6k(
2243 t: &[SaSint],
2244 sa: &mut [SaSint],
2245 induction_bucket: &mut [SaSint],
2246 omp_block_start: FastSint,
2247 omp_block_size: FastSint,
2248) {
2249 let prefetch_distance = 64 as FastSint;
2250 let mut i = omp_block_start + omp_block_size - 1;
2251 let mut j = omp_block_start + 2 * prefetch_distance + 3;
2252
2253 while i >= j {
2254 let p0 = sa[i as usize];
2255 let idx0 = t[p0 as usize] as usize;
2256 induction_bucket[idx0] -= 1;
2257 sa[induction_bucket[idx0] as usize] = p0;
2258
2259 let p1 = sa[(i - 1) as usize];
2260 let idx1 = t[p1 as usize] as usize;
2261 induction_bucket[idx1] -= 1;
2262 sa[induction_bucket[idx1] as usize] = p1;
2263
2264 let p2 = sa[(i - 2) as usize];
2265 let idx2 = t[p2 as usize] as usize;
2266 induction_bucket[idx2] -= 1;
2267 sa[induction_bucket[idx2] as usize] = p2;
2268
2269 let p3 = sa[(i - 3) as usize];
2270 let idx3 = t[p3 as usize] as usize;
2271 induction_bucket[idx3] -= 1;
2272 sa[induction_bucket[idx3] as usize] = p3;
2273
2274 i -= 4;
2275 }
2276
2277 j -= 2 * prefetch_distance + 3;
2278 while i >= j {
2279 let p = sa[i as usize];
2280 let idx = t[p as usize] as usize;
2281 induction_bucket[idx] -= 1;
2282 sa[induction_bucket[idx] as usize] = p;
2283 i -= 1;
2284 }
2285}
2286
2287#[doc(hidden)]
2289pub fn radix_sort_lms_suffixes_32s_2k(
2290 t: &[SaSint],
2291 sa: &mut [SaSint],
2292 induction_bucket: &mut [SaSint],
2293 omp_block_start: FastSint,
2294 omp_block_size: FastSint,
2295) {
2296 let prefetch_distance = 64 as FastSint;
2297 let mut i = omp_block_start + omp_block_size - 1;
2298 let mut j = omp_block_start + 2 * prefetch_distance + 3;
2299
2300 while i >= j {
2301 let p0 = sa[i as usize];
2302 let idx0 = buckets_index2(t[p0 as usize] as usize, 0);
2303 induction_bucket[idx0] -= 1;
2304 sa[induction_bucket[idx0] as usize] = p0;
2305
2306 let p1 = sa[(i - 1) as usize];
2307 let idx1 = buckets_index2(t[p1 as usize] as usize, 0);
2308 induction_bucket[idx1] -= 1;
2309 sa[induction_bucket[idx1] as usize] = p1;
2310
2311 let p2 = sa[(i - 2) as usize];
2312 let idx2 = buckets_index2(t[p2 as usize] as usize, 0);
2313 induction_bucket[idx2] -= 1;
2314 sa[induction_bucket[idx2] as usize] = p2;
2315
2316 let p3 = sa[(i - 3) as usize];
2317 let idx3 = buckets_index2(t[p3 as usize] as usize, 0);
2318 induction_bucket[idx3] -= 1;
2319 sa[induction_bucket[idx3] as usize] = p3;
2320
2321 i -= 4;
2322 }
2323
2324 j -= 2 * prefetch_distance + 3;
2325 while i >= j {
2326 let p = sa[i as usize];
2327 let idx = buckets_index2(t[p as usize] as usize, 0);
2328 induction_bucket[idx] -= 1;
2329 sa[induction_bucket[idx] as usize] = p;
2330 i -= 1;
2331 }
2332}
2333
2334#[doc(hidden)]
2336pub fn radix_sort_lms_suffixes_32s_block_gather(
2337 t: &[SaSint],
2338 sa: &[SaSint],
2339 cache: &mut [ThreadCache],
2340 omp_block_start: FastSint,
2341 omp_block_size: FastSint,
2342) {
2343 let start = usize::try_from(omp_block_start).expect("block start must be non-negative");
2344 let mut i = omp_block_start;
2345 let mut j = omp_block_start + omp_block_size - 64 - 3;
2346
2347 while i < j {
2348 for current in [i, i + 1, i + 2, i + 3] {
2349 let ci = current as usize - start;
2350 let index = sa[current as usize];
2351 cache[ci].index = index;
2352 cache[ci].symbol = t[index as usize];
2353 }
2354 i += 4;
2355 }
2356
2357 j += 64 + 3;
2358 while i < j {
2359 let ci = i as usize - start;
2360 let index = sa[i as usize];
2361 cache[ci].index = index;
2362 cache[ci].symbol = t[index as usize];
2363 i += 1;
2364 }
2365}
2366
2367#[doc(hidden)]
2369pub fn radix_sort_lms_suffixes_32s_6k_block_sort(
2370 induction_bucket: &mut [SaSint],
2371 cache: &mut [ThreadCache],
2372 omp_block_start: FastSint,
2373 omp_block_size: FastSint,
2374) {
2375 let start = usize::try_from(omp_block_start).expect("block start must be non-negative");
2376 let mut i = omp_block_start + omp_block_size - 1;
2377 let mut j = omp_block_start + 64 + 3;
2378
2379 while i >= j {
2380 for current in [i, i - 1, i - 2, i - 3] {
2381 let ci = current as usize - start;
2382 let v = cache[ci].symbol as usize;
2383 induction_bucket[v] -= 1;
2384 cache[ci].symbol = induction_bucket[v];
2385 }
2386 i -= 4;
2387 }
2388
2389 j -= 64 + 3;
2390 while i >= j {
2391 let ci = i as usize - start;
2392 let v = cache[ci].symbol as usize;
2393 induction_bucket[v] -= 1;
2394 cache[ci].symbol = induction_bucket[v];
2395 i -= 1;
2396 }
2397}
2398
2399#[doc(hidden)]
2401pub fn radix_sort_lms_suffixes_32s_2k_block_sort(
2402 induction_bucket: &mut [SaSint],
2403 cache: &mut [ThreadCache],
2404 omp_block_start: FastSint,
2405 omp_block_size: FastSint,
2406) {
2407 let start = usize::try_from(omp_block_start).expect("block start must be non-negative");
2408 let mut i = omp_block_start + omp_block_size - 1;
2409 let mut j = omp_block_start + 64 + 3;
2410
2411 while i >= j {
2412 for current in [i, i - 1, i - 2, i - 3] {
2413 let ci = current as usize - start;
2414 let v = buckets_index2(cache[ci].symbol as usize, 0);
2415 induction_bucket[v] -= 1;
2416 cache[ci].symbol = induction_bucket[v];
2417 }
2418 i -= 4;
2419 }
2420
2421 j -= 64 + 3;
2422 while i >= j {
2423 let ci = i as usize - start;
2424 let v = buckets_index2(cache[ci].symbol as usize, 0);
2425 induction_bucket[v] -= 1;
2426 cache[ci].symbol = induction_bucket[v];
2427 i -= 1;
2428 }
2429}
2430
2431#[doc(hidden)]
2433pub fn radix_sort_lms_suffixes_32s_6k_block_omp(
2434 t: &[SaSint],
2435 sa: &mut [SaSint],
2436 induction_bucket: &mut [SaSint],
2437 cache: &mut [ThreadCache],
2438 block_start: FastSint,
2439 block_size: FastSint,
2440 threads: SaSint,
2441) {
2442 if threads <= 1 || block_size < 16_384 {
2443 radix_sort_lms_suffixes_32s_6k(t, sa, induction_bucket, block_start, block_size);
2444 return;
2445 }
2446
2447 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
2448 let threads_usize = usize::try_from(threads)
2449 .expect("threads must be positive")
2450 .min(block_size_usize.max(1));
2451 let omp_block_stride = (block_size_usize / threads_usize) & !15usize;
2452
2453 {
2454 let sa_ro: &[SaSint] = sa;
2455 let t_ro: &[SaSint] = t;
2456 let cache_ptr = SyncMutPtr::new(cache);
2457 run_rayon_with_threads(threads_usize, || {
2458 (0..threads_usize)
2459 .into_par_iter()
2460 .for_each(|omp_thread_num| {
2461 let omp_block_start = omp_thread_num * omp_block_stride;
2462 let omp_block_size = if omp_thread_num + 1 < threads_usize {
2463 omp_block_stride
2464 } else {
2465 block_size_usize - omp_block_start
2466 };
2467 if omp_block_size > 0 {
2468 let cache = unsafe { cache_ptr.as_slice() };
2470 radix_sort_lms_suffixes_32s_block_gather(
2471 t_ro,
2472 sa_ro,
2473 &mut cache[omp_block_start..],
2474 block_start + omp_block_start as FastSint,
2475 omp_block_size as FastSint,
2476 );
2477 }
2478 });
2479 });
2480 }
2481
2482 radix_sort_lms_suffixes_32s_6k_block_sort(induction_bucket, cache, block_start, block_size);
2483
2484 {
2485 let sa_ptr = SyncMutPtr::new(sa);
2486 let cache_ro: &[ThreadCache] = cache;
2487 run_rayon_with_threads(threads_usize, || {
2488 (0..threads_usize)
2489 .into_par_iter()
2490 .for_each(|omp_thread_num| {
2491 let omp_block_start = omp_thread_num * omp_block_stride;
2492 let omp_block_size = if omp_thread_num + 1 < threads_usize {
2493 omp_block_stride
2494 } else {
2495 block_size_usize - omp_block_start
2496 };
2497 if omp_block_size > 0 {
2498 let sa = unsafe { sa_ptr.as_slice() };
2500 place_cached_suffixes(
2501 sa,
2502 &cache_ro[omp_block_start..],
2503 0,
2504 omp_block_size as FastSint,
2505 );
2506 }
2507 });
2508 });
2509 }
2510}
2511
2512#[doc(hidden)]
2514pub fn radix_sort_lms_suffixes_32s_2k_block_omp(
2515 t: &[SaSint],
2516 sa: &mut [SaSint],
2517 induction_bucket: &mut [SaSint],
2518 cache: &mut [ThreadCache],
2519 block_start: FastSint,
2520 block_size: FastSint,
2521 threads: SaSint,
2522) {
2523 if threads <= 1 || block_size < 16_384 {
2524 radix_sort_lms_suffixes_32s_2k(t, sa, induction_bucket, block_start, block_size);
2525 return;
2526 }
2527
2528 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
2529 let threads_usize = usize::try_from(threads)
2530 .expect("threads must be positive")
2531 .min(block_size_usize.max(1));
2532 let omp_block_stride = (block_size_usize / threads_usize) & !15usize;
2533
2534 {
2535 let sa_ro: &[SaSint] = sa;
2536 let t_ro: &[SaSint] = t;
2537 let cache_ptr = SyncMutPtr::new(cache);
2538 run_rayon_with_threads(threads_usize, || {
2539 (0..threads_usize)
2540 .into_par_iter()
2541 .for_each(|omp_thread_num| {
2542 let omp_block_start = omp_thread_num * omp_block_stride;
2543 let omp_block_size = if omp_thread_num + 1 < threads_usize {
2544 omp_block_stride
2545 } else {
2546 block_size_usize - omp_block_start
2547 };
2548 if omp_block_size > 0 {
2549 let cache = unsafe { cache_ptr.as_slice() };
2551 radix_sort_lms_suffixes_32s_block_gather(
2552 t_ro,
2553 sa_ro,
2554 &mut cache[omp_block_start..],
2555 block_start + omp_block_start as FastSint,
2556 omp_block_size as FastSint,
2557 );
2558 }
2559 });
2560 });
2561 }
2562
2563 radix_sort_lms_suffixes_32s_2k_block_sort(induction_bucket, cache, block_start, block_size);
2564
2565 {
2566 let sa_ptr = SyncMutPtr::new(sa);
2567 let cache_ro: &[ThreadCache] = cache;
2568 run_rayon_with_threads(threads_usize, || {
2569 (0..threads_usize)
2570 .into_par_iter()
2571 .for_each(|omp_thread_num| {
2572 let omp_block_start = omp_thread_num * omp_block_stride;
2573 let omp_block_size = if omp_thread_num + 1 < threads_usize {
2574 omp_block_stride
2575 } else {
2576 block_size_usize - omp_block_start
2577 };
2578 if omp_block_size > 0 {
2579 let sa = unsafe { sa_ptr.as_slice() };
2581 place_cached_suffixes(
2582 sa,
2583 &cache_ro[omp_block_start..],
2584 0,
2585 omp_block_size as FastSint,
2586 );
2587 }
2588 });
2589 });
2590 }
2591}
2592
2593#[doc(hidden)]
2595pub fn radix_sort_lms_suffixes_32s_6k_omp(
2596 t: &[SaSint],
2597 sa: &mut [SaSint],
2598 n: SaSint,
2599 m: SaSint,
2600 induction_bucket: &mut [SaSint],
2601 threads: SaSint,
2602 _thread_state: &mut [ThreadState],
2603) {
2604 if threads <= 1 || m < 65_536 {
2605 radix_sort_lms_suffixes_32s_6k(
2606 t,
2607 sa,
2608 induction_bucket,
2609 n as FastSint - m as FastSint + 1,
2610 m as FastSint - 1,
2611 );
2612 return;
2613 }
2614
2615 let threads_usize = usize::try_from(threads).expect("threads must be positive");
2616 let mut cache = vec![ThreadCache::default(); threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE];
2617 let mut block_start = 0usize;
2618 let m_usize = usize::try_from(m).expect("m must be non-negative");
2619 let n_usize = usize::try_from(n).expect("n must be non-negative");
2620 let last = m_usize - 1;
2621
2622 while block_start < last {
2623 let block_end = (block_start + threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE).min(last);
2624 radix_sort_lms_suffixes_32s_6k_block_omp(
2625 t,
2626 sa,
2627 induction_bucket,
2628 &mut cache,
2629 (n_usize - block_end) as FastSint,
2630 (block_end - block_start) as FastSint,
2631 threads,
2632 );
2633 block_start = block_end;
2634 }
2635}
2636
2637#[doc(hidden)]
2639pub fn radix_sort_lms_suffixes_32s_2k_omp(
2640 t: &[SaSint],
2641 sa: &mut [SaSint],
2642 n: SaSint,
2643 m: SaSint,
2644 induction_bucket: &mut [SaSint],
2645 threads: SaSint,
2646 _thread_state: &mut [ThreadState],
2647) {
2648 if threads <= 1 || m < 65_536 {
2649 radix_sort_lms_suffixes_32s_2k(
2650 t,
2651 sa,
2652 induction_bucket,
2653 n as FastSint - m as FastSint + 1,
2654 m as FastSint - 1,
2655 );
2656 return;
2657 }
2658
2659 let threads_usize = usize::try_from(threads).expect("threads must be positive");
2660 let mut cache = vec![ThreadCache::default(); threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE];
2661 let mut block_start = 0usize;
2662 let m_usize = usize::try_from(m).expect("m must be non-negative");
2663 let n_usize = usize::try_from(n).expect("n must be non-negative");
2664 let last = m_usize - 1;
2665
2666 while block_start < last {
2667 let block_end = (block_start + threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE).min(last);
2668 radix_sort_lms_suffixes_32s_2k_block_omp(
2669 t,
2670 sa,
2671 induction_bucket,
2672 &mut cache,
2673 (n_usize - block_end) as FastSint,
2674 (block_end - block_start) as FastSint,
2675 threads,
2676 );
2677 block_start = block_end;
2678 }
2679}
2680
2681#[doc(hidden)]
2683pub fn radix_sort_lms_suffixes_32s_1k(
2684 t: &[SaSint],
2685 sa: &mut [SaSint],
2686 n: SaSint,
2687 buckets: &mut [SaSint],
2688) -> SaSint {
2689 let n_usize = usize::try_from(n).expect("n must be non-negative");
2690 let mut i = n as FastSint - 2;
2691 let mut m = 0;
2692 let mut f0 = 1usize;
2693 let mut f1: usize;
2694 let mut c0 = t[n_usize - 1] as FastSint;
2695 let mut c1: FastSint;
2696 let mut c2 = 0 as FastSint;
2697
2698 while i >= 67 {
2699 c1 = t[i as usize] as FastSint;
2700 f1 = usize::from(c1 > (c0 - f0 as FastSint));
2701 if (f1 & !f0) != 0 {
2702 c2 = c0;
2703 buckets[c2 as usize] -= 1;
2704 sa[buckets[c2 as usize] as usize] = (i + 1) as SaSint;
2705 m += 1;
2706 }
2707
2708 c0 = t[(i - 1) as usize] as FastSint;
2709 f0 = usize::from(c0 > (c1 - f1 as FastSint));
2710 if (f0 & !f1) != 0 {
2711 c2 = c1;
2712 buckets[c2 as usize] -= 1;
2713 sa[buckets[c2 as usize] as usize] = i as SaSint;
2714 m += 1;
2715 }
2716
2717 c1 = t[(i - 2) as usize] as FastSint;
2718 f1 = usize::from(c1 > (c0 - f0 as FastSint));
2719 if (f1 & !f0) != 0 {
2720 c2 = c0;
2721 buckets[c2 as usize] -= 1;
2722 sa[buckets[c2 as usize] as usize] = (i - 1) as SaSint;
2723 m += 1;
2724 }
2725
2726 c0 = t[(i - 3) as usize] as FastSint;
2727 f0 = usize::from(c0 > (c1 - f1 as FastSint));
2728 if (f0 & !f1) != 0 {
2729 c2 = c1;
2730 buckets[c2 as usize] -= 1;
2731 sa[buckets[c2 as usize] as usize] = (i - 2) as SaSint;
2732 m += 1;
2733 }
2734
2735 i -= 4;
2736 }
2737
2738 while i >= 0 {
2739 c1 = c0;
2740 c0 = t[i as usize] as FastSint;
2741 f1 = f0;
2742 f0 = usize::from(c0 > (c1 - f1 as FastSint));
2743 if (f0 & !f1) != 0 {
2744 c2 = c1;
2745 buckets[c2 as usize] -= 1;
2746 sa[buckets[c2 as usize] as usize] = (i + 1) as SaSint;
2747 m += 1;
2748 }
2749 i -= 1;
2750 }
2751
2752 if m > 1 {
2753 sa[buckets[c2 as usize] as usize] = 0;
2754 }
2755
2756 m
2757}
2758
2759#[doc(hidden)]
2761pub fn radix_sort_set_markers_32s_6k(
2762 sa: &mut [SaSint],
2763 induction_bucket: &[SaSint],
2764 omp_block_start: FastSint,
2765 omp_block_size: FastSint,
2766) {
2767 let mut i = omp_block_start;
2768 let mut j = omp_block_start + omp_block_size - 67;
2769
2770 while i < j {
2771 sa[induction_bucket[i as usize] as usize] |= SAINT_MIN;
2772 sa[induction_bucket[(i + 1) as usize] as usize] |= SAINT_MIN;
2773 sa[induction_bucket[(i + 2) as usize] as usize] |= SAINT_MIN;
2774 sa[induction_bucket[(i + 3) as usize] as usize] |= SAINT_MIN;
2775 i += 4;
2776 }
2777
2778 j += 67;
2779 while i < j {
2780 sa[induction_bucket[i as usize] as usize] |= SAINT_MIN;
2781 i += 1;
2782 }
2783}
2784
2785#[doc(hidden)]
2787pub fn radix_sort_set_markers_32s_4k(
2788 sa: &mut [SaSint],
2789 induction_bucket: &[SaSint],
2790 omp_block_start: FastSint,
2791 omp_block_size: FastSint,
2792) {
2793 let mut i = omp_block_start;
2794 let mut j = omp_block_start + omp_block_size - 67;
2795
2796 while i < j {
2797 sa[induction_bucket[buckets_index2(i as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2798 sa[induction_bucket[buckets_index2((i + 1) as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2799 sa[induction_bucket[buckets_index2((i + 2) as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2800 sa[induction_bucket[buckets_index2((i + 3) as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2801 i += 4;
2802 }
2803
2804 j += 67;
2805 while i < j {
2806 sa[induction_bucket[buckets_index2(i as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2807 i += 1;
2808 }
2809}
2810
2811#[doc(hidden)]
2813pub fn radix_sort_set_markers_32s_6k_omp(
2814 sa: &mut [SaSint],
2815 k: SaSint,
2816 induction_bucket: &[SaSint],
2817 threads: SaSint,
2818) {
2819 if k <= 1 {
2820 return;
2821 }
2822
2823 if threads <= 1 || k < 65_536 {
2824 radix_sort_set_markers_32s_6k(sa, induction_bucket, 0, k as FastSint - 1);
2825 return;
2826 }
2827
2828 let threads_usize = usize::try_from(threads).expect("threads must be positive");
2829 let last = usize::try_from(k - 1).expect("k must be positive");
2830 let stride = (last / threads_usize) & !15usize;
2831
2832 {
2833 let sa_ptr = SyncMutPtr::new(sa);
2834 run_rayon_with_threads(threads_usize, || {
2835 (0..threads_usize).into_par_iter().for_each(|thread| {
2836 let start = thread * stride;
2837 let end = if thread + 1 == threads_usize {
2838 last
2839 } else {
2840 start + stride
2841 };
2842 if end > start {
2843 let sa = unsafe { sa_ptr.as_slice() };
2845 radix_sort_set_markers_32s_6k(
2846 sa,
2847 induction_bucket,
2848 start as FastSint,
2849 (end - start) as FastSint,
2850 );
2851 }
2852 });
2853 });
2854 }
2855}
2856
2857#[doc(hidden)]
2859pub fn radix_sort_set_markers_32s_4k_omp(
2860 sa: &mut [SaSint],
2861 k: SaSint,
2862 induction_bucket: &[SaSint],
2863 threads: SaSint,
2864) {
2865 if k <= 1 {
2866 return;
2867 }
2868
2869 if threads <= 1 || k < 65_536 {
2870 radix_sort_set_markers_32s_4k(sa, induction_bucket, 0, k as FastSint - 1);
2871 return;
2872 }
2873
2874 let threads_usize = usize::try_from(threads).expect("threads must be positive");
2875 let last = usize::try_from(k - 1).expect("k must be positive");
2876 let stride = (last / threads_usize) & !15usize;
2877
2878 {
2879 let sa_ptr = SyncMutPtr::new(sa);
2880 run_rayon_with_threads(threads_usize, || {
2881 (0..threads_usize).into_par_iter().for_each(|thread| {
2882 let start = thread * stride;
2883 let end = if thread + 1 == threads_usize {
2884 last
2885 } else {
2886 start + stride
2887 };
2888 if end > start {
2889 let sa = unsafe { sa_ptr.as_slice() };
2891 radix_sort_set_markers_32s_4k(
2892 sa,
2893 induction_bucket,
2894 start as FastSint,
2895 (end - start) as FastSint,
2896 );
2897 }
2898 });
2899 });
2900 }
2901}
2902
2903#[doc(hidden)]
2905pub fn initialize_buckets_for_partial_sorting_8u(
2906 t: &[u8],
2907 buckets: &mut [SaSint],
2908 first_lms_suffix: SaSint,
2909 left_suffixes_count: SaSint,
2910) {
2911 let temp_offset = 4 * ALPHABET_SIZE;
2912 buckets[buckets_index4(t[first_lms_suffix as usize] as usize, 1)] += 1;
2913
2914 let mut sum0 = left_suffixes_count + 1;
2915 let mut sum1 = 0;
2916 for j in 0..ALPHABET_SIZE {
2917 let i = buckets_index4(j, 0);
2918 let tj = buckets_index2(j, 0);
2919 buckets[temp_offset + tj] = sum0;
2920 sum0 += buckets[i] + buckets[i + 2];
2921 sum1 += buckets[i + 1];
2922 buckets[tj] = sum0;
2923 buckets[tj + 1] = sum1;
2924 }
2925}
2926
2927#[doc(hidden)]
2929pub fn initialize_buckets_for_partial_sorting_32s_6k(
2930 t: &[SaSint],
2931 k: SaSint,
2932 buckets: &mut [SaSint],
2933 first_lms_suffix: SaSint,
2934 left_suffixes_count: SaSint,
2935) {
2936 let k_usize = usize::try_from(k).expect("k must be non-negative");
2937 let temp_offset = 4 * k_usize;
2938 let first_symbol = t[first_lms_suffix as usize] as usize;
2939 let mut sum0 = left_suffixes_count + 1;
2940 let mut sum1 = 0;
2941 let mut sum2 = 0;
2942
2943 for j in 0..first_symbol {
2944 let i = buckets_index4(j, 0);
2945 let tj = buckets_index2(j, 0);
2946 let ss = buckets[i];
2947 let ls = buckets[i + 1];
2948 let sl = buckets[i + 2];
2949 let ll = buckets[i + 3];
2950
2951 buckets[i] = sum0;
2952 buckets[i + 1] = sum2;
2953 buckets[i + 2] = 0;
2954 buckets[i + 3] = 0;
2955
2956 sum0 += ss + sl;
2957 sum1 += ls;
2958 sum2 += ls + ll;
2959
2960 buckets[temp_offset + tj] = sum0;
2961 buckets[temp_offset + tj + 1] = sum1;
2962 }
2963
2964 sum1 += 1;
2965 for j in first_symbol..k_usize {
2966 let i = buckets_index4(j, 0);
2967 let tj = buckets_index2(j, 0);
2968 let ss = buckets[i];
2969 let ls = buckets[i + 1];
2970 let sl = buckets[i + 2];
2971 let ll = buckets[i + 3];
2972
2973 buckets[i] = sum0;
2974 buckets[i + 1] = sum2;
2975 buckets[i + 2] = 0;
2976 buckets[i + 3] = 0;
2977
2978 sum0 += ss + sl;
2979 sum1 += ls;
2980 sum2 += ls + ll;
2981
2982 buckets[temp_offset + tj] = sum0;
2983 buckets[temp_offset + tj + 1] = sum1;
2984 }
2985}
2986
2987#[doc(hidden)]
2989pub fn partial_sorting_scan_left_to_right_8u(
2990 t: &[u8],
2991 sa: &mut [SaSint],
2992 buckets: &mut [SaSint],
2993 mut d: SaSint,
2994 omp_block_start: FastSint,
2995 omp_block_size: FastSint,
2996) -> SaSint {
2997 let induction_offset = 4 * ALPHABET_SIZE;
2998 let distinct_offset = 2 * ALPHABET_SIZE;
2999 let prefetch_distance = 64 as FastSint;
3000 let mut i = omp_block_start;
3001 let mut j = if omp_block_size > prefetch_distance + 1 {
3002 omp_block_start + omp_block_size - prefetch_distance - 1
3003 } else {
3004 omp_block_start
3005 };
3006
3007 let sa_ptr = sa.as_ptr();
3008 let t_ptr = t.as_ptr();
3009 let prefetch_distance_us = prefetch_distance as usize;
3010 while i < j {
3011 let i_us = i as usize;
3012 libsais_prefetchr(sa_ptr.wrapping_add(i_us + 2 * prefetch_distance_us));
3013 let pf0 = (sa[i_us + prefetch_distance_us] & SAINT_MAX) as usize;
3014 libsais_prefetchr(t_ptr.wrapping_add(pf0).wrapping_sub(1));
3015 libsais_prefetchr(t_ptr.wrapping_add(pf0).wrapping_sub(2));
3016 let pf1 = (sa[i_us + prefetch_distance_us + 1] & SAINT_MAX) as usize;
3017 libsais_prefetchr(t_ptr.wrapping_add(pf1).wrapping_sub(1));
3018 libsais_prefetchr(t_ptr.wrapping_add(pf1).wrapping_sub(2));
3019
3020 let mut p0 = sa[i as usize];
3021 d += SaSint::from(p0 < 0);
3022 p0 &= SAINT_MAX;
3023 let v0 = buckets_index2(
3024 t[(p0 - 1) as usize] as usize,
3025 usize::from(t[(p0 - 2) as usize] >= t[(p0 - 1) as usize]),
3026 );
3027 let pos0 = buckets[induction_offset + v0] as usize;
3028 sa[pos0] = (p0 - 1) | (((buckets[distinct_offset + v0] != d) as SaSint) << (SAINT_BIT - 1));
3029 buckets[induction_offset + v0] += 1;
3030 buckets[distinct_offset + v0] = d;
3031
3032 let mut p1 = sa[(i + 1) as usize];
3033 d += SaSint::from(p1 < 0);
3034 p1 &= SAINT_MAX;
3035 let v1 = buckets_index2(
3036 t[(p1 - 1) as usize] as usize,
3037 usize::from(t[(p1 - 2) as usize] >= t[(p1 - 1) as usize]),
3038 );
3039 let pos1 = buckets[induction_offset + v1] as usize;
3040 sa[pos1] = (p1 - 1) | (((buckets[distinct_offset + v1] != d) as SaSint) << (SAINT_BIT - 1));
3041 buckets[induction_offset + v1] += 1;
3042 buckets[distinct_offset + v1] = d;
3043
3044 i += 2;
3045 }
3046
3047 j = omp_block_start + omp_block_size;
3048 while i < j {
3049 let mut p = sa[i as usize];
3050 d += SaSint::from(p < 0);
3051 p &= SAINT_MAX;
3052 let v = buckets_index2(
3053 t[(p - 1) as usize] as usize,
3054 usize::from(t[(p - 2) as usize] >= t[(p - 1) as usize]),
3055 );
3056 let pos = buckets[induction_offset + v] as usize;
3057 sa[pos] = (p - 1) | (((buckets[distinct_offset + v] != d) as SaSint) << (SAINT_BIT - 1));
3058 buckets[induction_offset + v] += 1;
3059 buckets[distinct_offset + v] = d;
3060 i += 1;
3061 }
3062
3063 d
3064}
3065
3066#[doc(hidden)]
3068pub fn partial_sorting_scan_left_to_right_8u_omp(
3069 t: &[u8],
3070 sa: &mut [SaSint],
3071 n: SaSint,
3072 k: SaSint,
3073 buckets: &mut [SaSint],
3074 left_suffixes_count: SaSint,
3075 mut d: SaSint,
3076 threads: SaSint,
3077 thread_state: &mut [ThreadState],
3078) -> SaSint {
3079 let v = buckets_index2(
3080 t[(n - 1) as usize] as usize,
3081 usize::from(t[(n - 2) as usize] >= t[(n - 1) as usize]),
3082 );
3083 let induction_offset = 4 * ALPHABET_SIZE;
3084 let distinct_offset = 2 * ALPHABET_SIZE;
3085 let pos = buckets[induction_offset + v] as usize;
3086 sa[pos] = (n - 1) | SAINT_MIN;
3087 buckets[induction_offset + v] += 1;
3088 d += 1;
3089 buckets[distinct_offset + v] = d;
3090
3091 if threads == 1 || left_suffixes_count < 65_536 {
3092 return partial_sorting_scan_left_to_right_8u(
3093 t,
3094 sa,
3095 buckets,
3096 d,
3097 0,
3098 left_suffixes_count as FastSint,
3099 );
3100 }
3101
3102 let mut block_start = 0usize;
3103 let left_suffixes_count =
3104 usize::try_from(left_suffixes_count).expect("left_suffixes_count must be non-negative");
3105 let threads_usize = usize::try_from(threads)
3106 .expect("threads must be non-negative")
3107 .min(thread_state.len())
3108 .max(1);
3109 while block_start < left_suffixes_count {
3110 if sa[block_start] == 0 {
3111 block_start += 1;
3112 } else {
3113 let mut block_max_end =
3114 block_start + threads_usize * (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * threads_usize);
3115 if block_max_end > left_suffixes_count {
3116 block_max_end = left_suffixes_count;
3117 }
3118 let mut block_end = block_start + 1;
3119 while block_end < block_max_end && sa[block_end] != 0 {
3120 block_end += 1;
3121 }
3122 let block_size = block_end - block_start;
3123
3124 if block_size < 32 {
3125 while block_start < block_end {
3126 let p = sa[block_start];
3127 d += SaSint::from(p < 0);
3128 let p = p & SAINT_MAX;
3129 let v = buckets_index2(
3130 t[(p - 1) as usize] as usize,
3131 usize::from(t[(p - 2) as usize] >= t[(p - 1) as usize]),
3132 );
3133 let pos = buckets[induction_offset + v] as usize;
3134 sa[pos] = (p - 1)
3135 | (((buckets[distinct_offset + v] != d) as SaSint) << (SAINT_BIT - 1));
3136 buckets[induction_offset + v] += 1;
3137 buckets[distinct_offset + v] = d;
3138 block_start += 1;
3139 }
3140 } else {
3141 d = partial_sorting_scan_left_to_right_8u_block_omp(
3142 t,
3143 sa,
3144 k,
3145 buckets,
3146 d,
3147 block_start as FastSint,
3148 block_size as FastSint,
3149 threads,
3150 thread_state,
3151 );
3152 block_start = block_end;
3153 }
3154 }
3155 }
3156
3157 d
3158}
3159
3160#[doc(hidden)]
3162pub fn partial_sorting_scan_left_to_right_32s_6k(
3163 t: &[SaSint],
3164 sa: &mut [SaSint],
3165 buckets: &mut [SaSint],
3166 mut d: SaSint,
3167 omp_block_start: FastSint,
3168 omp_block_size: FastSint,
3169) -> SaSint {
3170 let prefetch_distance: FastSint = 64;
3171
3172 let mut i = omp_block_start;
3173 let mut j = omp_block_start + omp_block_size - 2 * prefetch_distance - 1;
3174 let sa_ptr = sa.as_ptr();
3175 let t_ptr = t.as_ptr();
3176 let buckets_ptr = buckets.as_ptr();
3177 let prefetch_distance_us = prefetch_distance as usize;
3178 while i < j {
3179 let i_us = i as usize;
3180 libsais_prefetchr(sa_ptr.wrapping_add(i_us + 3 * prefetch_distance_us));
3181 let pa = (sa[i_us + 2 * prefetch_distance_us] & SAINT_MAX) as usize;
3182 libsais_prefetchr(t_ptr.wrapping_add(pa).wrapping_sub(1));
3183 libsais_prefetchr(t_ptr.wrapping_add(pa).wrapping_sub(2));
3184 let pb = (sa[i_us + 2 * prefetch_distance_us + 1] & SAINT_MAX) as usize;
3185 libsais_prefetchr(t_ptr.wrapping_add(pb).wrapping_sub(1));
3186 libsais_prefetchr(t_ptr.wrapping_add(pb).wrapping_sub(2));
3187 let pc = (sa[i_us + prefetch_distance_us] & SAINT_MAX) as usize;
3188 let vc = buckets_index4(t[pc - usize::from(pc > 0)] as usize, 0);
3189 libsais_prefetchw(buckets_ptr.wrapping_add(vc));
3190 let pd = (sa[i_us + prefetch_distance_us + 1] & SAINT_MAX) as usize;
3191 let vd = buckets_index4(t[pd - usize::from(pd > 0)] as usize, 0);
3192 libsais_prefetchw(buckets_ptr.wrapping_add(vd));
3193
3194 let mut p0 = sa[i as usize];
3195 d += SaSint::from(p0 < 0);
3196 p0 &= SAINT_MAX;
3197 let p0u = p0 as usize;
3198 let v0 = buckets_index4(t[p0u - 1] as usize, usize::from(t[p0u - 2] >= t[p0u - 1]));
3199 let pos0 = buckets[v0] as usize;
3200 sa[pos0] = (p0 - 1) | (((buckets[2 + v0] != d) as SaSint) << (SAINT_BIT - 1));
3201 buckets[v0] += 1;
3202 buckets[2 + v0] = d;
3203
3204 let mut p1 = sa[(i + 1) as usize];
3205 d += SaSint::from(p1 < 0);
3206 p1 &= SAINT_MAX;
3207 let p1u = p1 as usize;
3208 let v1 = buckets_index4(t[p1u - 1] as usize, usize::from(t[p1u - 2] >= t[p1u - 1]));
3209 let pos1 = buckets[v1] as usize;
3210 sa[pos1] = (p1 - 1) | (((buckets[2 + v1] != d) as SaSint) << (SAINT_BIT - 1));
3211 buckets[v1] += 1;
3212 buckets[2 + v1] = d;
3213
3214 i += 2;
3215 }
3216
3217 j += 2 * prefetch_distance + 1;
3218 while i < j {
3219 let mut p = sa[i as usize];
3220 d += SaSint::from(p < 0);
3221 p &= SAINT_MAX;
3222 let pu = p as usize;
3223 let v = buckets_index4(t[pu - 1] as usize, usize::from(t[pu - 2] >= t[pu - 1]));
3224 let pos = buckets[v] as usize;
3225 sa[pos] = (p - 1) | (((buckets[2 + v] != d) as SaSint) << (SAINT_BIT - 1));
3226 buckets[v] += 1;
3227 buckets[2 + v] = d;
3228 i += 1;
3229 }
3230
3231 d
3232}
3233
3234#[doc(hidden)]
3236pub fn partial_sorting_scan_left_to_right_32s_4k(
3237 t: &[SaSint],
3238 sa: &mut [SaSint],
3239 k: SaSint,
3240 buckets: &mut [SaSint],
3241 mut d: SaSint,
3242 omp_block_start: FastSint,
3243 omp_block_size: FastSint,
3244) -> SaSint {
3245 let k_usize = usize::try_from(k).expect("k must be non-negative");
3246 let prefetch_distance: FastSint = 64;
3247 let induction_offset = 2 * k_usize;
3248 let mut i = omp_block_start;
3249 let mut j = omp_block_start + omp_block_size - 2 * prefetch_distance - 1;
3250
3251 while i < j {
3252 let i0 = i as usize;
3253 let mut p0 = sa[i0];
3254 sa[i0] = p0 & SAINT_MAX;
3255 if p0 > 0 {
3256 sa[i0] = 0;
3257 d += p0 >> (SUFFIX_GROUP_BIT - 1);
3258 p0 &= !SUFFIX_GROUP_MARKER;
3259 let p0u = p0 as usize;
3260 let c0 = t[p0u - 1];
3261 let f0 = usize::from(t[p0u - 2] < c0);
3262 let v0 = buckets_index2(c0 as usize, f0);
3263 let c0u = c0 as usize;
3264 let pos0 = buckets[induction_offset + c0u] as usize;
3265 sa[pos0] = (p0 - 1)
3266 | ((f0 as SaSint) << (SAINT_BIT - 1))
3267 | (((buckets[v0] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3268 buckets[induction_offset + c0u] += 1;
3269 buckets[v0] = d;
3270 }
3271
3272 let i1 = (i + 1) as usize;
3273 let mut p1 = sa[i1];
3274 sa[i1] = p1 & SAINT_MAX;
3275 if p1 > 0 {
3276 sa[i1] = 0;
3277 d += p1 >> (SUFFIX_GROUP_BIT - 1);
3278 p1 &= !SUFFIX_GROUP_MARKER;
3279 let p1u = p1 as usize;
3280 let c1 = t[p1u - 1];
3281 let f1 = usize::from(t[p1u - 2] < c1);
3282 let v1 = buckets_index2(c1 as usize, f1);
3283 let c1u = c1 as usize;
3284 let pos1 = buckets[induction_offset + c1u] as usize;
3285 sa[pos1] = (p1 - 1)
3286 | ((f1 as SaSint) << (SAINT_BIT - 1))
3287 | (((buckets[v1] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3288 buckets[induction_offset + c1u] += 1;
3289 buckets[v1] = d;
3290 }
3291
3292 i += 2;
3293 }
3294
3295 j += 2 * prefetch_distance + 1;
3296 while i < j {
3297 let iu = i as usize;
3298 let mut p = sa[iu];
3299 sa[iu] = p & SAINT_MAX;
3300 if p > 0 {
3301 sa[iu] = 0;
3302 d += p >> (SUFFIX_GROUP_BIT - 1);
3303 p &= !SUFFIX_GROUP_MARKER;
3304 let pu = p as usize;
3305 let c = t[pu - 1];
3306 let f = usize::from(t[pu - 2] < c);
3307 let v = buckets_index2(c as usize, f);
3308 let cu = c as usize;
3309 let pos = buckets[induction_offset + cu] as usize;
3310 sa[pos] = (p - 1)
3311 | ((f as SaSint) << (SAINT_BIT - 1))
3312 | (((buckets[v] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3313 buckets[induction_offset + cu] += 1;
3314 buckets[v] = d;
3315 }
3316 i += 1;
3317 }
3318
3319 d
3320}
3321
3322#[doc(hidden)]
3324pub fn partial_sorting_scan_left_to_right_32s_1k(
3325 t: &[SaSint],
3326 sa: &mut [SaSint],
3327 induction_bucket: &mut [SaSint],
3328 omp_block_start: FastSint,
3329 omp_block_size: FastSint,
3330) {
3331 let prefetch_distance = 64 as FastSint;
3332 let mut i = omp_block_start;
3333 let mut j = omp_block_start + omp_block_size - 2 * prefetch_distance - 1;
3334
3335 while i < j {
3336 let p0 = sa[i as usize];
3337 sa[i as usize] = p0 & SAINT_MAX;
3338 if p0 > 0 {
3339 sa[i as usize] = 0;
3340 let c0 = t[(p0 - 1) as usize] as usize;
3341 let pos0 = induction_bucket[c0] as usize;
3342 induction_bucket[c0] += 1;
3343 sa[pos0] = (p0 - 1)
3344 | ((usize::from(t[(p0 - 2) as usize] < t[(p0 - 1) as usize]) as SaSint)
3345 << (SAINT_BIT - 1));
3346 }
3347
3348 let p1 = sa[(i + 1) as usize];
3349 sa[(i + 1) as usize] = p1 & SAINT_MAX;
3350 if p1 > 0 {
3351 sa[(i + 1) as usize] = 0;
3352 let c1 = t[(p1 - 1) as usize] as usize;
3353 let pos1 = induction_bucket[c1] as usize;
3354 induction_bucket[c1] += 1;
3355 sa[pos1] = (p1 - 1)
3356 | ((usize::from(t[(p1 - 2) as usize] < t[(p1 - 1) as usize]) as SaSint)
3357 << (SAINT_BIT - 1));
3358 }
3359
3360 i += 2;
3361 }
3362
3363 j += 2 * prefetch_distance + 1;
3364 while i < j {
3365 let p = sa[i as usize];
3366 sa[i as usize] = p & SAINT_MAX;
3367 if p > 0 {
3368 sa[i as usize] = 0;
3369 let c = t[(p - 1) as usize] as usize;
3370 let pos = induction_bucket[c] as usize;
3371 induction_bucket[c] += 1;
3372 sa[pos] = (p - 1)
3373 | ((usize::from(t[(p - 2) as usize] < t[(p - 1) as usize]) as SaSint)
3374 << (SAINT_BIT - 1));
3375 }
3376 i += 1;
3377 }
3378}
3379
3380#[doc(hidden)]
3382pub fn partial_sorting_scan_left_to_right_32s_6k_omp(
3383 t: &[SaSint],
3384 sa: &mut [SaSint],
3385 n: SaSint,
3386 buckets: &mut [SaSint],
3387 left_suffixes_count: SaSint,
3388 mut d: SaSint,
3389 threads: SaSint,
3390 thread_state: &mut [ThreadState],
3391) -> SaSint {
3392 let v = buckets_index4(
3393 t[(n - 1) as usize] as usize,
3394 usize::from(t[(n - 2) as usize] >= t[(n - 1) as usize]),
3395 );
3396 let pos = buckets[v] as usize;
3397 sa[pos] = (n - 1) | SAINT_MIN;
3398 buckets[v] += 1;
3399 d += 1;
3400 buckets[2 + v] = d;
3401 if threads == 1 || left_suffixes_count < 65_536 {
3402 return partial_sorting_scan_left_to_right_32s_6k(
3403 t,
3404 sa,
3405 buckets,
3406 d,
3407 0,
3408 left_suffixes_count as FastSint,
3409 );
3410 }
3411 if thread_state.is_empty() {
3412 return partial_sorting_scan_left_to_right_32s_6k(
3413 t,
3414 sa,
3415 buckets,
3416 d,
3417 0,
3418 left_suffixes_count as FastSint,
3419 );
3420 }
3421
3422 let left_suffixes_count =
3423 usize::try_from(left_suffixes_count).expect("left_suffixes_count must be non-negative");
3424 let threads_usize = usize::try_from(threads)
3425 .expect("threads must be non-negative")
3426 .max(1);
3427 let mut block_start = 0usize;
3428 let block_span = threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE;
3429 let mut cache = vec![ThreadCache::default(); block_span];
3430 while block_start < left_suffixes_count {
3431 let mut block_end = block_start + block_span;
3432 if block_end > left_suffixes_count {
3433 block_end = left_suffixes_count;
3434 }
3435
3436 d = partial_sorting_scan_left_to_right_32s_6k_block_omp(
3437 t,
3438 sa,
3439 buckets,
3440 d,
3441 &mut cache,
3442 block_start as FastSint,
3443 (block_end - block_start) as FastSint,
3444 threads,
3445 );
3446
3447 block_start = block_end;
3448 }
3449
3450 d
3451}
3452
3453#[doc(hidden)]
3455pub fn partial_sorting_scan_left_to_right_32s_4k_omp(
3456 t: &[SaSint],
3457 sa: &mut [SaSint],
3458 n: SaSint,
3459 k: SaSint,
3460 buckets: &mut [SaSint],
3461 mut d: SaSint,
3462 threads: SaSint,
3463 thread_state: &mut [ThreadState],
3464) -> SaSint {
3465 let k_usize = usize::try_from(k).expect("k must be non-negative");
3466 let induction_offset = 2 * k_usize;
3467 let distinct_offset = 0usize;
3468 let symbol = t[(n - 1) as usize] as usize;
3469 let is_s = usize::from(t[(n - 2) as usize] < t[(n - 1) as usize]);
3470 let pos = buckets[induction_offset + symbol] as usize;
3471 sa[pos] = (n - 1) | ((is_s as SaSint) << (SAINT_BIT - 1)) | SUFFIX_GROUP_MARKER;
3472 buckets[induction_offset + symbol] += 1;
3473 d += 1;
3474 buckets[distinct_offset + buckets_index2(symbol, is_s)] = d;
3475
3476 if threads == 1 || n < 65_536 {
3477 d = partial_sorting_scan_left_to_right_32s_4k(t, sa, k, buckets, d, 0, n as FastSint);
3478 } else {
3479 if thread_state.is_empty() {
3480 return partial_sorting_scan_left_to_right_32s_4k(
3481 t,
3482 sa,
3483 k,
3484 buckets,
3485 d,
3486 0,
3487 n as FastSint,
3488 );
3489 }
3490 let mut block_start = 0usize;
3491 let n_usize = usize::try_from(n).expect("n must be non-negative");
3492 let threads_usize = usize::try_from(threads)
3493 .expect("threads must be non-negative")
3494 .max(1);
3495 let chunk_capacity = threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE;
3496 let mut cache = vec![ThreadCache::default(); chunk_capacity];
3497
3498 while block_start < n_usize {
3499 let mut block_end = block_start + chunk_capacity;
3500 if block_end > n_usize {
3501 block_end = n_usize;
3502 }
3503
3504 d = partial_sorting_scan_left_to_right_32s_4k_block_omp(
3505 t,
3506 sa,
3507 k,
3508 buckets,
3509 d,
3510 &mut cache,
3511 block_start as FastSint,
3512 (block_end - block_start) as FastSint,
3513 threads,
3514 );
3515
3516 block_start = block_end;
3517 }
3518 }
3519
3520 d
3521}
3522
3523#[doc(hidden)]
3525pub fn partial_sorting_scan_left_to_right_32s_1k_omp(
3526 t: &[SaSint],
3527 sa: &mut [SaSint],
3528 n: SaSint,
3529 buckets: &mut [SaSint],
3530 threads: SaSint,
3531 thread_state: &mut [ThreadState],
3532) {
3533 let symbol = t[(n - 1) as usize] as usize;
3534 let pos = buckets[symbol] as usize;
3535 sa[pos] = (n - 1)
3536 | ((usize::from(t[(n - 2) as usize] < t[(n - 1) as usize]) as SaSint) << (SAINT_BIT - 1));
3537 buckets[symbol] += 1;
3538 if threads == 1 || n < 65_536 {
3539 partial_sorting_scan_left_to_right_32s_1k(t, sa, buckets, 0, n as FastSint);
3540 } else {
3541 if thread_state.is_empty() {
3542 partial_sorting_scan_left_to_right_32s_1k(t, sa, buckets, 0, n as FastSint);
3543 return;
3544 }
3545 let n_usize = usize::try_from(n).expect("n must be non-negative");
3546 let threads_usize = usize::try_from(threads)
3547 .expect("threads must be non-negative")
3548 .max(1);
3549 let mut block_start = 0usize;
3550 let block_span = threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE;
3551 let mut cache = vec![ThreadCache::default(); block_span];
3552
3553 while block_start < n_usize {
3554 let mut block_end = block_start + block_span;
3555 if block_end > n_usize {
3556 block_end = n_usize;
3557 }
3558
3559 partial_sorting_scan_left_to_right_32s_1k_block_omp(
3560 t,
3561 sa,
3562 buckets,
3563 &mut cache,
3564 block_start as FastSint,
3565 (block_end - block_start) as FastSint,
3566 threads,
3567 );
3568
3569 block_start = block_end;
3570 }
3571 }
3572}
3573
3574#[doc(hidden)]
3576pub fn partial_sorting_scan_left_to_right_8u_block_prepare(
3577 t: &[u8],
3578 sa: &[SaSint],
3579 k: SaSint,
3580 buckets: &mut [SaSint],
3581 cache: &mut [ThreadCache],
3582 omp_block_start: FastSint,
3583 omp_block_size: FastSint,
3584) -> (FastSint, FastSint) {
3585 let k_usize = usize::try_from(k).expect("k must be non-negative");
3586 buckets[..2 * k_usize].fill(0);
3587 buckets[2 * k_usize..4 * k_usize].fill(0);
3588
3589 let mut i = omp_block_start;
3590 let mut j = omp_block_start + omp_block_size - 65;
3591 let mut count = 0usize;
3592 let mut d: SaSint = 1;
3593
3594 while i < j {
3595 let mut p0 = sa[i as usize];
3596 cache[count].index = p0;
3597 d += SaSint::from(p0 < 0);
3598 p0 &= SAINT_MAX;
3599 let v0 = buckets_index2(
3600 t[(p0 - 1) as usize] as usize,
3601 usize::from(t[(p0 - 2) as usize] >= t[(p0 - 1) as usize]),
3602 );
3603 cache[count].symbol = v0 as SaSint;
3604 count += 1;
3605 buckets[v0] += 1;
3606 buckets[2 * k_usize + v0] = d;
3607
3608 let mut p1 = sa[(i + 1) as usize];
3609 cache[count].index = p1;
3610 d += SaSint::from(p1 < 0);
3611 p1 &= SAINT_MAX;
3612 let v1 = buckets_index2(
3613 t[(p1 - 1) as usize] as usize,
3614 usize::from(t[(p1 - 2) as usize] >= t[(p1 - 1) as usize]),
3615 );
3616 cache[count].symbol = v1 as SaSint;
3617 count += 1;
3618 buckets[v1] += 1;
3619 buckets[2 * k_usize + v1] = d;
3620
3621 i += 2;
3622 }
3623
3624 j += 65;
3625 while i < j {
3626 let mut p = sa[i as usize];
3627 cache[count].index = p;
3628 d += SaSint::from(p < 0);
3629 p &= SAINT_MAX;
3630 let v = buckets_index2(
3631 t[(p - 1) as usize] as usize,
3632 usize::from(t[(p - 2) as usize] >= t[(p - 1) as usize]),
3633 );
3634 cache[count].symbol = v as SaSint;
3635 count += 1;
3636 buckets[v] += 1;
3637 buckets[2 * k_usize + v] = d;
3638 i += 1;
3639 }
3640
3641 (d as FastSint - 1, count as FastSint)
3642}
3643
3644#[doc(hidden)]
3646pub fn partial_sorting_scan_left_to_right_8u_block_place(
3647 sa: &mut [SaSint],
3648 buckets: &mut [SaSint],
3649 k: SaSint,
3650 cache: &[ThreadCache],
3651 count: FastSint,
3652 mut d: SaSint,
3653) {
3654 let split = 2 * usize::try_from(k).expect("k must be non-negative");
3655 let (induction_bucket, distinct_names) = buckets.split_at_mut(split);
3656
3657 let mut i = 0usize;
3658 let mut j = usize::try_from(count)
3659 .expect("count must be non-negative")
3660 .saturating_sub(1);
3661 while i < j {
3662 let p0 = cache[i].index;
3663 d += SaSint::from(p0 < 0);
3664 let v0 = cache[i].symbol as usize;
3665 let pos0 = induction_bucket[v0] as usize;
3666 sa[pos0] = (p0 - 1) | (((distinct_names[v0] != d) as SaSint) << (SAINT_BIT - 1));
3667 induction_bucket[v0] += 1;
3668 distinct_names[v0] = d;
3669
3670 let p1 = cache[i + 1].index;
3671 d += SaSint::from(p1 < 0);
3672 let v1 = cache[i + 1].symbol as usize;
3673 let pos1 = induction_bucket[v1] as usize;
3674 sa[pos1] = (p1 - 1) | (((distinct_names[v1] != d) as SaSint) << (SAINT_BIT - 1));
3675 induction_bucket[v1] += 1;
3676 distinct_names[v1] = d;
3677
3678 i += 2;
3679 }
3680
3681 j += 1;
3682 while i < j {
3683 let p = cache[i].index;
3684 d += SaSint::from(p < 0);
3685 let v = cache[i].symbol as usize;
3686 let pos = induction_bucket[v] as usize;
3687 sa[pos] = (p - 1) | (((distinct_names[v] != d) as SaSint) << (SAINT_BIT - 1));
3688 induction_bucket[v] += 1;
3689 distinct_names[v] = d;
3690 i += 1;
3691 }
3692}
3693
3694#[doc(hidden)]
3696pub fn partial_sorting_scan_left_to_right_8u_block_omp(
3697 t: &[u8],
3698 sa: &mut [SaSint],
3699 k: SaSint,
3700 buckets: &mut [SaSint],
3701 d: SaSint,
3702 block_start: FastSint,
3703 block_size: FastSint,
3704 threads: SaSint,
3705 thread_state: &mut [ThreadState],
3706) -> SaSint {
3707 let mut d = d;
3708 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
3709 let k_usize = usize::try_from(k).expect("k must be non-negative");
3710 let omp_num_threads = if threads > 1 && block_size_usize >= 64 * k_usize.max(256) {
3711 usize::try_from(threads)
3712 .expect("threads must be non-negative")
3713 .min(thread_state.len())
3714 .max(1)
3715 } else {
3716 1
3717 };
3718 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
3719
3720 if omp_num_threads == 1 {
3721 return partial_sorting_scan_left_to_right_8u(t, sa, buckets, d, block_start, block_size);
3722 }
3723
3724 {
3725 let sa_ro: &[SaSint] = sa;
3726 run_rayon_with_threads(omp_num_threads, || {
3727 thread_state[..omp_num_threads]
3728 .par_iter_mut()
3729 .enumerate()
3730 .for_each(|(omp_thread_num, state)| {
3731 let mut omp_block_start = omp_thread_num * omp_block_stride;
3732 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
3733 omp_block_stride
3734 } else {
3735 block_size_usize - omp_block_start
3736 };
3737 omp_block_start +=
3738 usize::try_from(block_start).expect("block_start must be non-negative");
3739
3740 let (position, count) = partial_sorting_scan_left_to_right_8u_block_prepare(
3741 t,
3742 sa_ro,
3743 k,
3744 &mut state.buckets,
3745 &mut state.cache,
3746 FastSint::try_from(omp_block_start).expect("block start must fit FastSint"),
3747 FastSint::try_from(omp_block_size).expect("block size must fit FastSint"),
3748 );
3749 state.position = position;
3750 state.count = count;
3751 });
3752 });
3753 }
3754
3755 let induction_offset = 4 * ALPHABET_SIZE;
3756 let distinct_offset = 2 * ALPHABET_SIZE;
3757 let (prefix, induction_tail) = buckets.split_at_mut(induction_offset);
3758 let induction_bucket = &mut induction_tail[..2 * k_usize];
3759 let distinct_names = &mut prefix[distinct_offset..distinct_offset + 2 * k_usize];
3760
3761 for tnum in 0..omp_num_threads {
3762 let state = &mut thread_state[tnum];
3763 let (temp_induction_bucket, temp_tail) = state.buckets.split_at_mut(2 * k_usize);
3764 let temp_distinct_names = &mut temp_tail[..2 * k_usize];
3765
3766 for c in 0..2 * k_usize {
3767 let a = induction_bucket[c];
3768 let b = temp_induction_bucket[c];
3769 induction_bucket[c] = a + b;
3770 temp_induction_bucket[c] = a;
3771 }
3772
3773 d -= 1;
3774 for c in 0..2 * k_usize {
3775 let a = distinct_names[c];
3776 let b = temp_distinct_names[c];
3777 let next_d = b + d;
3778 distinct_names[c] = if b > 0 { next_d } else { a };
3779 temp_distinct_names[c] = a;
3780 }
3781 d += 1 + SaSint::try_from(state.position).expect("position must fit SaSint");
3782 state.position = FastSint::try_from(d).expect("d must fit FastSint") - state.position;
3783 }
3784
3785 {
3786 let sa_ptr = SyncMutPtr::new(sa);
3787 run_rayon_with_threads(omp_num_threads, || {
3788 thread_state[..omp_num_threads]
3789 .par_iter_mut()
3790 .for_each(|state| {
3791 let sa = unsafe { sa_ptr.as_slice() };
3795 partial_sorting_scan_left_to_right_8u_block_place(
3796 sa,
3797 &mut state.buckets,
3798 k,
3799 &state.cache,
3800 state.count,
3801 state.position as SaSint,
3802 );
3803 });
3804 });
3805 }
3806
3807 d
3808}
3809
3810#[doc(hidden)]
3812pub fn partial_sorting_shift_markers_8u_omp(
3813 sa: &mut [SaSint],
3814 n: SaSint,
3815 buckets: &[SaSint],
3816 threads: SaSint,
3817) {
3818 let temp_bucket = &buckets[4 * ALPHABET_SIZE..];
3819 let thread_count = if threads > 1 && n >= 65536 {
3820 usize::try_from(threads).expect("threads must be positive")
3821 } else {
3822 1
3823 };
3824 let c_step = buckets_index2(1, 0) as isize;
3825 let c_min = buckets_index2(1, 0) as isize;
3826 let c_max = buckets_index2(ALPHABET_SIZE - 1, 0) as isize;
3827 {
3828 let sa_ptr = SyncMutPtr::new(sa);
3829 let buckets_ref: &[SaSint] = buckets;
3830 let temp_bucket_ref: &[SaSint] = temp_bucket;
3831 run_rayon_with_threads(thread_count, || {
3832 (0..thread_count).into_par_iter().for_each(|t| {
3833 let mut c = c_max - (t as isize * c_step);
3834 let sa = unsafe { sa_ptr.as_slice() };
3838 while c >= c_min {
3839 let c_usize = c as usize;
3840 let mut i = temp_bucket_ref[c_usize] as isize - 1;
3841 let mut j = buckets_ref[c_usize - buckets_index2(1, 0)] as isize + 3;
3842 let mut s = SAINT_MIN;
3843
3844 while i >= j {
3845 let p0 = sa[i as usize];
3846 let q0 = (p0 & SAINT_MIN) ^ s;
3847 s ^= q0;
3848 sa[i as usize] = p0 ^ q0;
3849
3850 let p1 = sa[(i - 1) as usize];
3851 let q1 = (p1 & SAINT_MIN) ^ s;
3852 s ^= q1;
3853 sa[(i - 1) as usize] = p1 ^ q1;
3854
3855 let p2 = sa[(i - 2) as usize];
3856 let q2 = (p2 & SAINT_MIN) ^ s;
3857 s ^= q2;
3858 sa[(i - 2) as usize] = p2 ^ q2;
3859
3860 let p3 = sa[(i - 3) as usize];
3861 let q3 = (p3 & SAINT_MIN) ^ s;
3862 s ^= q3;
3863 sa[(i - 3) as usize] = p3 ^ q3;
3864
3865 i -= 4;
3866 }
3867
3868 j -= 3;
3869 while i >= j {
3870 let p = sa[i as usize];
3871 let q = (p & SAINT_MIN) ^ s;
3872 s ^= q;
3873 sa[i as usize] = p ^ q;
3874 i -= 1;
3875 }
3876
3877 c -= c_step * thread_count as isize;
3878 }
3879 });
3880 });
3881 }
3882}
3883
3884#[doc(hidden)]
3886pub fn partial_sorting_shift_markers_32s_6k_omp(
3887 sa: &mut [SaSint],
3888 k: SaSint,
3889 buckets: &[SaSint],
3890 threads: SaSint,
3891) {
3892 let k_usize = usize::try_from(k).expect("k must be non-negative");
3893 let temp_bucket = &buckets[4 * k_usize..];
3894 let thread_count = if threads > 1 && k >= 65536 {
3895 usize::try_from(threads).expect("threads must be positive")
3896 } else {
3897 1
3898 };
3899 {
3900 let sa_ptr = SyncMutPtr::new(sa);
3901 let buckets_ref: &[SaSint] = buckets;
3902 let temp_bucket_ref: &[SaSint] = temp_bucket;
3903 run_rayon_with_threads(thread_count, || {
3904 (0..thread_count).into_par_iter().for_each(|t| {
3905 let mut c = k_usize as isize - 1 - t as isize;
3906 let sa = unsafe { sa_ptr.as_slice() };
3908 while c >= 1 {
3909 let c_usize = c as usize;
3910 let mut i = buckets_ref[buckets_index4(c_usize, 0)] as isize - 1;
3911 let mut j = temp_bucket_ref[buckets_index2(c_usize - 1, 0)] as isize + 3;
3912 let mut s = SAINT_MIN;
3913
3914 while i >= j {
3915 let p0 = sa[i as usize];
3916 let q0 = (p0 & SAINT_MIN) ^ s;
3917 s ^= q0;
3918 sa[i as usize] = p0 ^ q0;
3919
3920 let p1 = sa[(i - 1) as usize];
3921 let q1 = (p1 & SAINT_MIN) ^ s;
3922 s ^= q1;
3923 sa[(i - 1) as usize] = p1 ^ q1;
3924
3925 let p2 = sa[(i - 2) as usize];
3926 let q2 = (p2 & SAINT_MIN) ^ s;
3927 s ^= q2;
3928 sa[(i - 2) as usize] = p2 ^ q2;
3929
3930 let p3 = sa[(i - 3) as usize];
3931 let q3 = (p3 & SAINT_MIN) ^ s;
3932 s ^= q3;
3933 sa[(i - 3) as usize] = p3 ^ q3;
3934
3935 i -= 4;
3936 }
3937
3938 j -= 3;
3939 while i >= j {
3940 let p = sa[i as usize];
3941 let q = (p & SAINT_MIN) ^ s;
3942 s ^= q;
3943 sa[i as usize] = p ^ q;
3944 i -= 1;
3945 }
3946
3947 c -= thread_count as isize;
3948 }
3949 });
3950 });
3951 }
3952}
3953
3954#[doc(hidden)]
3956pub fn partial_sorting_shift_markers_32s_4k(sa: &mut [SaSint], n: SaSint) {
3957 let mut i = n as isize - 1;
3958 let mut s = SUFFIX_GROUP_MARKER;
3959 while i >= 3 {
3960 let p0 = sa[i as usize];
3961 let q0 =
3962 ((p0 & SUFFIX_GROUP_MARKER) ^ s) & (((p0 > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3963 s ^= q0;
3964 sa[i as usize] = p0 ^ q0;
3965
3966 let p1 = sa[(i - 1) as usize];
3967 let q1 =
3968 ((p1 & SUFFIX_GROUP_MARKER) ^ s) & (((p1 > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3969 s ^= q1;
3970 sa[(i - 1) as usize] = p1 ^ q1;
3971
3972 let p2 = sa[(i - 2) as usize];
3973 let q2 =
3974 ((p2 & SUFFIX_GROUP_MARKER) ^ s) & (((p2 > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3975 s ^= q2;
3976 sa[(i - 2) as usize] = p2 ^ q2;
3977
3978 let p3 = sa[(i - 3) as usize];
3979 let q3 =
3980 ((p3 & SUFFIX_GROUP_MARKER) ^ s) & (((p3 > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3981 s ^= q3;
3982 sa[(i - 3) as usize] = p3 ^ q3;
3983
3984 i -= 4;
3985 }
3986
3987 while i >= 0 {
3988 let p = sa[i as usize];
3989 let q = ((p & SUFFIX_GROUP_MARKER) ^ s) & (((p > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3990 s ^= q;
3991 sa[i as usize] = p ^ q;
3992 i -= 1;
3993 }
3994}
3995
3996#[doc(hidden)]
3998pub fn partial_sorting_shift_buckets_32s_6k(k: SaSint, buckets: &mut [SaSint]) {
3999 let k_usize = usize::try_from(k).expect("k must be non-negative");
4000 let temp_offset = 4 * k_usize;
4001 for i in 0..k_usize {
4002 let src = buckets_index2(i, 0);
4003 let dst = 2 * src;
4004 buckets[dst] = buckets[temp_offset + src];
4005 buckets[dst + 1] = buckets[temp_offset + src + 1];
4006 }
4007}
4008
4009#[doc(hidden)]
4011pub fn partial_sorting_scan_right_to_left_8u(
4012 t: &[u8],
4013 sa: &mut [SaSint],
4014 buckets: &mut [SaSint],
4015 mut d: SaSint,
4016 omp_block_start: FastSint,
4017 omp_block_size: FastSint,
4018) -> SaSint {
4019 if omp_block_size <= 0 {
4020 return d;
4021 }
4022
4023 let prefetch_distance = 64usize;
4024 let (induction_bucket, distinct_names_all) = buckets.split_at_mut(2 * ALPHABET_SIZE);
4025 let distinct_names = &mut distinct_names_all[..2 * ALPHABET_SIZE];
4026
4027 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
4028 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
4029 let mut i = start + size - 1;
4030 let mut j = start + prefetch_distance + 1;
4031
4032 let sa_ptr = sa.as_ptr();
4033 let t_ptr = t.as_ptr();
4034 while i >= j {
4035 libsais_prefetchr(sa_ptr.wrapping_add(i.wrapping_sub(2 * prefetch_distance)));
4036 let pf0 = (sa[i - prefetch_distance] & SAINT_MAX) as usize;
4037 libsais_prefetchr(t_ptr.wrapping_add(pf0).wrapping_sub(1));
4038 libsais_prefetchr(t_ptr.wrapping_add(pf0).wrapping_sub(2));
4039 let pf1 = (sa[i - prefetch_distance - 1] & SAINT_MAX) as usize;
4040 libsais_prefetchr(t_ptr.wrapping_add(pf1).wrapping_sub(1));
4041 libsais_prefetchr(t_ptr.wrapping_add(pf1).wrapping_sub(2));
4042
4043 let mut p0 = sa[i];
4044 d += SaSint::from(p0 < 0);
4045 p0 &= SAINT_MAX;
4046
4047 let p0_usize = p0 as usize;
4048 let v0 = buckets_index2(
4049 t[p0_usize - 1] as usize,
4050 usize::from(t[p0_usize - 2] > t[p0_usize - 1]),
4051 );
4052
4053 induction_bucket[v0] -= 1;
4054 let slot0 = induction_bucket[v0] as usize;
4055 sa[slot0] = (p0 - 1) | (((distinct_names[v0] != d) as SaSint) << (SAINT_BIT - 1));
4056 distinct_names[v0] = d;
4057
4058 let mut p1 = sa[i - 1];
4059 d += SaSint::from(p1 < 0);
4060 p1 &= SAINT_MAX;
4061
4062 let p1_usize = p1 as usize;
4063 let v1 = buckets_index2(
4064 t[p1_usize - 1] as usize,
4065 usize::from(t[p1_usize - 2] > t[p1_usize - 1]),
4066 );
4067
4068 induction_bucket[v1] -= 1;
4069 let slot1 = induction_bucket[v1] as usize;
4070 sa[slot1] = (p1 - 1) | (((distinct_names[v1] != d) as SaSint) << (SAINT_BIT - 1));
4071 distinct_names[v1] = d;
4072
4073 i -= 2;
4074 }
4075
4076 j = if start + prefetch_distance < start + size {
4077 start
4078 } else {
4079 start
4080 };
4081 while i >= j {
4082 let mut p = sa[i];
4083 d += SaSint::from(p < 0);
4084 p &= SAINT_MAX;
4085
4086 let p_usize = p as usize;
4087 let v = buckets_index2(
4088 t[p_usize - 1] as usize,
4089 usize::from(t[p_usize - 2] > t[p_usize - 1]),
4090 );
4091
4092 induction_bucket[v] -= 1;
4093 let slot = induction_bucket[v] as usize;
4094 sa[slot] = (p - 1) | (((distinct_names[v] != d) as SaSint) << (SAINT_BIT - 1));
4095 distinct_names[v] = d;
4096
4097 if i == 0 {
4098 break;
4099 }
4100 i -= 1;
4101 }
4102
4103 d
4104}
4105
4106#[doc(hidden)]
4108pub fn partial_gsa_scan_right_to_left_8u(
4109 t: &[u8],
4110 sa: &mut [SaSint],
4111 buckets: &mut [SaSint],
4112 mut d: SaSint,
4113 omp_block_start: FastSint,
4114 omp_block_size: FastSint,
4115) -> SaSint {
4116 if omp_block_size <= 0 {
4117 return d;
4118 }
4119
4120 let prefetch_distance = 64usize;
4121 let (induction_bucket, distinct_names_all) = buckets.split_at_mut(2 * ALPHABET_SIZE);
4122 let distinct_names = &mut distinct_names_all[..2 * ALPHABET_SIZE];
4123
4124 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
4125 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
4126 let mut i = start + size - 1;
4127 let mut j = start + prefetch_distance + 1;
4128
4129 while i >= j {
4130 let mut p0 = sa[i];
4131 d += SaSint::from(p0 < 0);
4132 p0 &= SAINT_MAX;
4133
4134 let p0_usize = p0 as usize;
4135 let v0 = buckets_index2(
4136 t[p0_usize - 1] as usize,
4137 usize::from(t[p0_usize - 2] > t[p0_usize - 1]),
4138 );
4139
4140 if v0 != 1 {
4141 induction_bucket[v0] -= 1;
4142 let slot0 = induction_bucket[v0] as usize;
4143 sa[slot0] = (p0 - 1) | (((distinct_names[v0] != d) as SaSint) << (SAINT_BIT - 1));
4144 distinct_names[v0] = d;
4145 }
4146
4147 let mut p1 = sa[i - 1];
4148 d += SaSint::from(p1 < 0);
4149 p1 &= SAINT_MAX;
4150
4151 let p1_usize = p1 as usize;
4152 let v1 = buckets_index2(
4153 t[p1_usize - 1] as usize,
4154 usize::from(t[p1_usize - 2] > t[p1_usize - 1]),
4155 );
4156
4157 if v1 != 1 {
4158 induction_bucket[v1] -= 1;
4159 let slot1 = induction_bucket[v1] as usize;
4160 sa[slot1] = (p1 - 1) | (((distinct_names[v1] != d) as SaSint) << (SAINT_BIT - 1));
4161 distinct_names[v1] = d;
4162 }
4163
4164 i -= 2;
4165 }
4166
4167 j = start;
4168 while i >= j {
4169 let mut p = sa[i];
4170 d += SaSint::from(p < 0);
4171 p &= SAINT_MAX;
4172
4173 let p_usize = p as usize;
4174 let v = buckets_index2(
4175 t[p_usize - 1] as usize,
4176 usize::from(t[p_usize - 2] > t[p_usize - 1]),
4177 );
4178
4179 if v != 1 {
4180 induction_bucket[v] -= 1;
4181 let slot = induction_bucket[v] as usize;
4182 sa[slot] = (p - 1) | (((distinct_names[v] != d) as SaSint) << (SAINT_BIT - 1));
4183 distinct_names[v] = d;
4184 }
4185
4186 if i == 0 {
4187 break;
4188 }
4189 i -= 1;
4190 }
4191
4192 d
4193}
4194
4195#[doc(hidden)]
4197pub fn partial_sorting_scan_right_to_left_8u_block_prepare(
4198 t: &[u8],
4199 sa: &[SaSint],
4200 k: SaSint,
4201 buckets: &mut [SaSint],
4202 cache: &mut [ThreadCache],
4203 omp_block_start: FastSint,
4204 omp_block_size: FastSint,
4205) -> (FastSint, FastSint) {
4206 let k_usize = usize::try_from(k).expect("k must be non-negative");
4207 let (induction_bucket, distinct_names_all) = buckets.split_at_mut(2 * k_usize);
4208 let distinct_names = &mut distinct_names_all[..2 * k_usize];
4209 induction_bucket.fill(0);
4210 distinct_names.fill(0);
4211
4212 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
4213 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
4214 let mut count = 0usize;
4215 let mut d = 1;
4216
4217 let mut i = start + size;
4218 while i > start {
4219 i -= 1;
4220
4221 let mut p = sa[i];
4222 cache[count].index = p;
4223 d += SaSint::from(p < 0);
4224 p &= SAINT_MAX;
4225
4226 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
4227 let v = buckets_index2(
4228 t[p_usize - 1] as usize,
4229 usize::from(t[p_usize - 2] > t[p_usize - 1]),
4230 );
4231
4232 cache[count].symbol = v as SaSint;
4233 induction_bucket[v] += 1;
4234 distinct_names[v] = d;
4235 count += 1;
4236 }
4237
4238 ((d - 1) as FastSint, count as FastSint)
4239}
4240
4241#[doc(hidden)]
4243pub fn partial_sorting_scan_right_to_left_8u_block_place(
4244 sa: &mut [SaSint],
4245 buckets: &mut [SaSint],
4246 k: SaSint,
4247 cache: &[ThreadCache],
4248 count: FastSint,
4249 mut d: SaSint,
4250) {
4251 let split = 2 * usize::try_from(k).expect("k must be non-negative");
4252 let (induction_bucket, distinct_names) = buckets.split_at_mut(split);
4253
4254 let count = usize::try_from(count).expect("count must be non-negative");
4255 for entry in &cache[..count] {
4256 let p = entry.index;
4257 d += SaSint::from(p < 0);
4258 let v = usize::try_from(entry.symbol).expect("cache symbol must be non-negative");
4259 induction_bucket[v] -= 1;
4260 let slot = usize::try_from(induction_bucket[v]).expect("bucket slot must be non-negative");
4261 sa[slot] = (p - 1) | (((distinct_names[v] != d) as SaSint) << (SAINT_BIT - 1));
4262 distinct_names[v] = d;
4263 }
4264}
4265
4266#[doc(hidden)]
4268pub fn partial_gsa_scan_right_to_left_8u_block_place(
4269 sa: &mut [SaSint],
4270 buckets: &mut [SaSint],
4271 k: SaSint,
4272 cache: &[ThreadCache],
4273 count: FastSint,
4274 mut d: SaSint,
4275) {
4276 let split = 2 * usize::try_from(k).expect("k must be non-negative");
4277 let (induction_bucket, distinct_names) = buckets.split_at_mut(split);
4278
4279 let count = usize::try_from(count).expect("count must be non-negative");
4280 for entry in &cache[..count] {
4281 let p = entry.index;
4282 d += SaSint::from(p < 0);
4283 let v = usize::try_from(entry.symbol).expect("cache symbol must be non-negative");
4284 if v != 1 {
4285 induction_bucket[v] -= 1;
4286 let slot =
4287 usize::try_from(induction_bucket[v]).expect("bucket slot must be non-negative");
4288 sa[slot] = (p - 1) | (((distinct_names[v] != d) as SaSint) << (SAINT_BIT - 1));
4289 distinct_names[v] = d;
4290 }
4291 }
4292}
4293
4294#[doc(hidden)]
4296pub fn partial_sorting_scan_right_to_left_8u_block_omp(
4297 t: &[u8],
4298 sa: &mut [SaSint],
4299 k: SaSint,
4300 buckets: &mut [SaSint],
4301 d: SaSint,
4302 block_start: FastSint,
4303 block_size: FastSint,
4304 threads: SaSint,
4305 thread_state: &mut [ThreadState],
4306) -> SaSint {
4307 let mut d = d;
4308 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
4309 let k_usize = usize::try_from(k).expect("k must be non-negative");
4310 let omp_num_threads = if threads > 1 && block_size_usize >= 64 * k_usize.max(256) {
4311 usize::try_from(threads)
4312 .expect("threads must be non-negative")
4313 .min(thread_state.len())
4314 .max(1)
4315 } else {
4316 1
4317 };
4318 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
4319
4320 if omp_num_threads == 1 {
4321 return partial_sorting_scan_right_to_left_8u(t, sa, buckets, d, block_start, block_size);
4322 }
4323
4324 {
4325 let sa_ro: &[SaSint] = sa;
4326 run_rayon_with_threads(omp_num_threads, || {
4327 thread_state[..omp_num_threads]
4328 .par_iter_mut()
4329 .enumerate()
4330 .for_each(|(omp_thread_num, state)| {
4331 let mut omp_block_start = omp_thread_num * omp_block_stride;
4332 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4333 omp_block_stride
4334 } else {
4335 block_size_usize - omp_block_start
4336 };
4337 omp_block_start +=
4338 usize::try_from(block_start).expect("block_start must be non-negative");
4339
4340 let (position, count) = partial_sorting_scan_right_to_left_8u_block_prepare(
4341 t,
4342 sa_ro,
4343 k,
4344 &mut state.buckets,
4345 &mut state.cache,
4346 FastSint::try_from(omp_block_start).expect("block start must fit FastSint"),
4347 FastSint::try_from(omp_block_size).expect("block size must fit FastSint"),
4348 );
4349 state.position = position;
4350 state.count = count;
4351 });
4352 });
4353 }
4354
4355 let distinct_offset = 2 * ALPHABET_SIZE;
4356 let (induction_bucket, distinct_tail) = buckets.split_at_mut(distinct_offset);
4357 let distinct_names = &mut distinct_tail[..2 * k_usize];
4358
4359 for tnum in (0..omp_num_threads).rev() {
4360 let state = &mut thread_state[tnum];
4361 let (temp_induction_bucket, temp_tail) = state.buckets.split_at_mut(2 * k_usize);
4362 let temp_distinct_names = &mut temp_tail[..2 * k_usize];
4363
4364 for c in 0..2 * k_usize {
4365 let a = induction_bucket[c];
4366 let b = temp_induction_bucket[c];
4367 induction_bucket[c] = a - b;
4368 temp_induction_bucket[c] = a;
4369 }
4370
4371 d -= 1;
4372 for c in 0..2 * k_usize {
4373 let a = distinct_names[c];
4374 let b = temp_distinct_names[c];
4375 let next_d = b + d;
4376 distinct_names[c] = if b > 0 { next_d } else { a };
4377 temp_distinct_names[c] = a;
4378 }
4379 d += 1 + SaSint::try_from(state.position).expect("position must fit SaSint");
4380 state.position = FastSint::try_from(d).expect("d must fit FastSint") - state.position;
4381 }
4382
4383 {
4384 let sa_ptr = SyncMutPtr::new(sa);
4385 run_rayon_with_threads(omp_num_threads, || {
4386 thread_state[..omp_num_threads]
4387 .par_iter_mut()
4388 .for_each(|state| {
4389 let sa = unsafe { sa_ptr.as_slice() };
4392 partial_sorting_scan_right_to_left_8u_block_place(
4393 sa,
4394 &mut state.buckets,
4395 k,
4396 &state.cache,
4397 state.count,
4398 state.position as SaSint,
4399 );
4400 });
4401 });
4402 }
4403
4404 d
4405}
4406
4407#[doc(hidden)]
4409pub fn partial_gsa_scan_right_to_left_8u_block_omp(
4410 t: &[u8],
4411 sa: &mut [SaSint],
4412 k: SaSint,
4413 buckets: &mut [SaSint],
4414 d: SaSint,
4415 block_start: FastSint,
4416 block_size: FastSint,
4417 threads: SaSint,
4418 thread_state: &mut [ThreadState],
4419) -> SaSint {
4420 let mut d = d;
4421 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
4422 let k_usize = usize::try_from(k).expect("k must be non-negative");
4423 let omp_num_threads = if threads > 1 && block_size_usize >= 64 * k_usize.max(256) {
4424 usize::try_from(threads)
4425 .expect("threads must be non-negative")
4426 .min(thread_state.len())
4427 .max(1)
4428 } else {
4429 1
4430 };
4431 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
4432
4433 if omp_num_threads == 1 {
4434 return partial_gsa_scan_right_to_left_8u(t, sa, buckets, d, block_start, block_size);
4435 }
4436
4437 {
4438 let sa_ro: &[SaSint] = sa;
4439 run_rayon_with_threads(omp_num_threads, || {
4440 thread_state[..omp_num_threads]
4441 .par_iter_mut()
4442 .enumerate()
4443 .for_each(|(omp_thread_num, state)| {
4444 let mut omp_block_start = omp_thread_num * omp_block_stride;
4445 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4446 omp_block_stride
4447 } else {
4448 block_size_usize - omp_block_start
4449 };
4450 omp_block_start +=
4451 usize::try_from(block_start).expect("block_start must be non-negative");
4452
4453 let (position, count) = partial_sorting_scan_right_to_left_8u_block_prepare(
4454 t,
4455 sa_ro,
4456 k,
4457 &mut state.buckets,
4458 &mut state.cache,
4459 FastSint::try_from(omp_block_start).expect("block start must fit FastSint"),
4460 FastSint::try_from(omp_block_size).expect("block size must fit FastSint"),
4461 );
4462 state.position = position;
4463 state.count = count;
4464 });
4465 });
4466 }
4467
4468 let distinct_offset = 2 * ALPHABET_SIZE;
4469 let (induction_bucket, distinct_tail) = buckets.split_at_mut(distinct_offset);
4470 let distinct_names = &mut distinct_tail[..2 * k_usize];
4471
4472 for tnum in (0..omp_num_threads).rev() {
4473 let state = &mut thread_state[tnum];
4474 let (temp_induction_bucket, temp_tail) = state.buckets.split_at_mut(2 * k_usize);
4475 let temp_distinct_names = &mut temp_tail[..2 * k_usize];
4476
4477 for c in 0..2 * k_usize {
4478 let a = induction_bucket[c];
4479 let b = temp_induction_bucket[c];
4480 induction_bucket[c] = a - b;
4481 temp_induction_bucket[c] = a;
4482 }
4483
4484 d -= 1;
4485 for c in 0..2 * k_usize {
4486 let a = distinct_names[c];
4487 let b = temp_distinct_names[c];
4488 let next_d = b + d;
4489 distinct_names[c] = if b > 0 { next_d } else { a };
4490 temp_distinct_names[c] = a;
4491 }
4492 d += 1 + SaSint::try_from(state.position).expect("position must fit SaSint");
4493 state.position = FastSint::try_from(d).expect("d must fit FastSint") - state.position;
4494 }
4495
4496 {
4497 let sa_ptr = SyncMutPtr::new(sa);
4498 run_rayon_with_threads(omp_num_threads, || {
4499 thread_state[..omp_num_threads]
4500 .par_iter_mut()
4501 .for_each(|state| {
4502 let sa = unsafe { sa_ptr.as_slice() };
4505 partial_gsa_scan_right_to_left_8u_block_place(
4506 sa,
4507 &mut state.buckets,
4508 k,
4509 &state.cache,
4510 state.count,
4511 state.position as SaSint,
4512 );
4513 });
4514 });
4515 }
4516
4517 d
4518}
4519
4520#[doc(hidden)]
4522pub fn partial_sorting_scan_right_to_left_8u_omp(
4523 t: &[u8],
4524 sa: &mut [SaSint],
4525 n: SaSint,
4526 k: SaSint,
4527 buckets: &mut [SaSint],
4528 first_lms_suffix: SaSint,
4529 left_suffixes_count: SaSint,
4530 mut d: SaSint,
4531 threads: SaSint,
4532 thread_state: &mut [ThreadState],
4533) {
4534 let scan_start = left_suffixes_count as FastSint + 1;
4535 let scan_end = n as FastSint - first_lms_suffix as FastSint;
4536
4537 if threads == 1 || (scan_end - scan_start) < 65_536 {
4538 let _ = partial_sorting_scan_right_to_left_8u(
4539 t,
4540 sa,
4541 buckets,
4542 d,
4543 scan_start,
4544 scan_end - scan_start,
4545 );
4546 return;
4547 }
4548
4549 let distinct_offset = 2 * ALPHABET_SIZE;
4550
4551 let mut block_start = usize::try_from(scan_end - 1).expect("scan end must be positive");
4552 let scan_start_usize = usize::try_from(scan_start).expect("scan_start must be non-negative");
4553 let threads_usize = usize::try_from(threads)
4554 .expect("threads must be non-negative")
4555 .min(thread_state.len())
4556 .max(1);
4557
4558 while block_start >= scan_start_usize {
4559 if sa[block_start] == 0 {
4560 if block_start == 0 {
4561 break;
4562 }
4563 block_start -= 1;
4564 } else {
4565 let mut block_max_end = block_start.saturating_sub(
4566 threads_usize * (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * threads_usize),
4567 );
4568 if block_max_end + 1 < scan_start_usize {
4569 block_max_end = scan_start_usize.saturating_sub(1);
4570 }
4571 let mut block_end = block_start - 1;
4572 while block_end > block_max_end && sa[block_end] != 0 {
4573 block_end -= 1;
4574 }
4575 let block_size = block_start - block_end;
4576
4577 if block_size < 32 {
4578 while block_start > block_end {
4579 let p = sa[block_start];
4580 d += SaSint::from(p < 0);
4581 let p = p & SAINT_MAX;
4582 let v = buckets_index2(
4583 t[(p - 1) as usize] as usize,
4584 usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
4585 );
4586 buckets[v] -= 1;
4587 let slot =
4588 usize::try_from(buckets[v]).expect("bucket slot must be non-negative");
4589 sa[slot] = (p - 1)
4590 | (((buckets[distinct_offset + v] != d) as SaSint) << (SAINT_BIT - 1));
4591 buckets[distinct_offset + v] = d;
4592
4593 if block_start == 0 {
4594 break;
4595 }
4596 block_start -= 1;
4597 }
4598 } else {
4599 d = partial_sorting_scan_right_to_left_8u_block_omp(
4600 t,
4601 sa,
4602 k,
4603 buckets,
4604 d,
4605 FastSint::try_from(block_end + 1).expect("block start must fit FastSint"),
4606 FastSint::try_from(block_size).expect("block size must fit FastSint"),
4607 threads,
4608 thread_state,
4609 );
4610 block_start = block_end;
4611 }
4612 }
4613 }
4614}
4615
4616#[doc(hidden)]
4618pub fn partial_gsa_scan_right_to_left_8u_omp(
4619 t: &[u8],
4620 sa: &mut [SaSint],
4621 n: SaSint,
4622 k: SaSint,
4623 buckets: &mut [SaSint],
4624 first_lms_suffix: SaSint,
4625 left_suffixes_count: SaSint,
4626 mut d: SaSint,
4627 threads: SaSint,
4628 thread_state: &mut [ThreadState],
4629) {
4630 let scan_start = left_suffixes_count as FastSint + 1;
4631 let scan_end = n as FastSint - first_lms_suffix as FastSint;
4632
4633 if threads == 1 || (scan_end - scan_start) < 65_536 {
4634 let _ =
4635 partial_gsa_scan_right_to_left_8u(t, sa, buckets, d, scan_start, scan_end - scan_start);
4636 return;
4637 }
4638
4639 let distinct_offset = 2 * ALPHABET_SIZE;
4640 let mut block_start = usize::try_from(scan_end - 1).expect("scan end must be positive");
4641 let scan_start_usize = usize::try_from(scan_start).expect("scan_start must be non-negative");
4642 let threads_usize = usize::try_from(threads)
4643 .expect("threads must be non-negative")
4644 .min(thread_state.len())
4645 .max(1);
4646
4647 while block_start >= scan_start_usize {
4648 if sa[block_start] == 0 {
4649 if block_start == 0 {
4650 break;
4651 }
4652 block_start -= 1;
4653 } else {
4654 let mut block_max_end = block_start.saturating_sub(
4655 threads_usize * (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * threads_usize),
4656 );
4657 if block_max_end + 1 < scan_start_usize {
4658 block_max_end = scan_start_usize.saturating_sub(1);
4659 }
4660 let mut block_end = block_start - 1;
4661 while block_end > block_max_end && sa[block_end] != 0 {
4662 block_end -= 1;
4663 }
4664 let block_size = block_start - block_end;
4665
4666 if block_size < 32 {
4667 while block_start > block_end {
4668 let p = sa[block_start];
4669 d += SaSint::from(p < 0);
4670 let p = p & SAINT_MAX;
4671 let v = buckets_index2(
4672 t[(p - 1) as usize] as usize,
4673 usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
4674 );
4675 if v != 1 {
4676 buckets[v] -= 1;
4677 let slot =
4678 usize::try_from(buckets[v]).expect("bucket slot must be non-negative");
4679 sa[slot] = (p - 1)
4680 | (((buckets[distinct_offset + v] != d) as SaSint) << (SAINT_BIT - 1));
4681 buckets[distinct_offset + v] = d;
4682 }
4683
4684 if block_start == 0 {
4685 break;
4686 }
4687 block_start -= 1;
4688 }
4689 } else {
4690 d = partial_gsa_scan_right_to_left_8u_block_omp(
4691 t,
4692 sa,
4693 k,
4694 buckets,
4695 d,
4696 FastSint::try_from(block_end + 1).expect("block start must fit FastSint"),
4697 FastSint::try_from(block_size).expect("block size must fit FastSint"),
4698 threads,
4699 thread_state,
4700 );
4701 block_start = block_end;
4702 }
4703 }
4704 }
4705}
4706
4707#[doc(hidden)]
4709pub fn partial_sorting_scan_right_to_left_32s_6k(
4710 t: &[SaSint],
4711 sa: &mut [SaSint],
4712 buckets: &mut [SaSint],
4713 mut d: SaSint,
4714 omp_block_start: FastSint,
4715 omp_block_size: FastSint,
4716) -> SaSint {
4717 if omp_block_size <= 0 {
4718 return d;
4719 }
4720
4721 let prefetch_distance: FastSint = 64;
4722 let mut i = omp_block_start + omp_block_size - 1;
4723 let mut j = omp_block_start + 2 * prefetch_distance + 1;
4724
4725 let sa_ptr = sa.as_ptr();
4726 let t_ptr = t.as_ptr();
4727 let buckets_ptr = buckets.as_ptr();
4728 let prefetch_distance_us = prefetch_distance as usize;
4729 while i >= j {
4730 let i_us = i as usize;
4731 libsais_prefetchr(sa_ptr.wrapping_add(i_us.wrapping_sub(3 * prefetch_distance_us)));
4732 let pa = (sa[i_us - 2 * prefetch_distance_us] & SAINT_MAX) as usize;
4733 libsais_prefetchr(t_ptr.wrapping_add(pa).wrapping_sub(1));
4734 libsais_prefetchr(t_ptr.wrapping_add(pa).wrapping_sub(2));
4735 let pb = (sa[i_us - 2 * prefetch_distance_us - 1] & SAINT_MAX) as usize;
4736 libsais_prefetchr(t_ptr.wrapping_add(pb).wrapping_sub(1));
4737 libsais_prefetchr(t_ptr.wrapping_add(pb).wrapping_sub(2));
4738 let pc = (sa[i_us - prefetch_distance_us] & SAINT_MAX) as usize;
4739 let vc = buckets_index4(t[pc - usize::from(pc > 0)] as usize, 0);
4740 libsais_prefetchw(buckets_ptr.wrapping_add(vc));
4741 let pd = (sa[i_us - prefetch_distance_us - 1] & SAINT_MAX) as usize;
4742 let vd = buckets_index4(t[pd - usize::from(pd > 0)] as usize, 0);
4743 libsais_prefetchw(buckets_ptr.wrapping_add(vd));
4744
4745 let mut p0 = sa[i as usize];
4746 d += SaSint::from(p0 < 0);
4747 p0 &= SAINT_MAX;
4748 let p0u = p0 as usize;
4749 let v0 = buckets_index4(t[p0u - 1] as usize, usize::from(t[p0u - 2] > t[p0u - 1]));
4750 buckets[v0] -= 1;
4751 let slot0 = buckets[v0] as usize;
4752 sa[slot0] = (p0 - 1) | (((buckets[2 + v0] != d) as SaSint) << (SAINT_BIT - 1));
4753 buckets[2 + v0] = d;
4754
4755 let mut p1 = sa[(i - 1) as usize];
4756 d += SaSint::from(p1 < 0);
4757 p1 &= SAINT_MAX;
4758 let p1u = p1 as usize;
4759 let v1 = buckets_index4(t[p1u - 1] as usize, usize::from(t[p1u - 2] > t[p1u - 1]));
4760 buckets[v1] -= 1;
4761 let slot1 = buckets[v1] as usize;
4762 sa[slot1] = (p1 - 1) | (((buckets[2 + v1] != d) as SaSint) << (SAINT_BIT - 1));
4763 buckets[2 + v1] = d;
4764
4765 i -= 2;
4766 }
4767
4768 j -= 2 * prefetch_distance + 1;
4769 while i >= j {
4770 let mut p = sa[i as usize];
4771 d += SaSint::from(p < 0);
4772 p &= SAINT_MAX;
4773 let pu = p as usize;
4774 let v = buckets_index4(t[pu - 1] as usize, usize::from(t[pu - 2] > t[pu - 1]));
4775
4776 buckets[v] -= 1;
4777 let slot = buckets[v] as usize;
4778 sa[slot] = (p - 1) | (((buckets[2 + v] != d) as SaSint) << (SAINT_BIT - 1));
4779 buckets[2 + v] = d;
4780 i -= 1;
4781 }
4782
4783 d
4784}
4785
4786#[doc(hidden)]
4788pub fn partial_sorting_scan_right_to_left_32s_4k(
4789 t: &[SaSint],
4790 sa: &mut [SaSint],
4791 k: SaSint,
4792 buckets: &mut [SaSint],
4793 mut d: SaSint,
4794 omp_block_start: FastSint,
4795 omp_block_size: FastSint,
4796) -> SaSint {
4797 if omp_block_size <= 0 {
4798 return d;
4799 }
4800
4801 let k_usize = usize::try_from(k).expect("k must be non-negative");
4802 let prefetch_distance: FastSint = 64;
4803 let induction_offset = 3 * k_usize;
4804
4805 let mut i = omp_block_start + omp_block_size - 1;
4806 let mut j = omp_block_start + 2 * prefetch_distance + 1;
4807
4808 while i >= j {
4809 let i0 = i as usize;
4810 let mut p0 = sa[i0];
4811 if p0 > 0 {
4812 sa[i0] = 0;
4813 d += p0 >> (SUFFIX_GROUP_BIT - 1);
4814 p0 &= !SUFFIX_GROUP_MARKER;
4815
4816 let p0u = p0 as usize;
4817 let c0 = t[p0u - 1];
4818 let f0 = usize::from(t[p0u - 2] > c0);
4819 let v0 = buckets_index2(c0 as usize, f0);
4820 let c0u = c0 as usize;
4821 buckets[induction_offset + c0u] -= 1;
4822 let slot0 = buckets[induction_offset + c0u] as usize;
4823 sa[slot0] = (p0 - 1)
4824 | ((f0 as SaSint) << (SAINT_BIT - 1))
4825 | (((buckets[v0] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
4826 buckets[v0] = d;
4827 }
4828
4829 let i1 = (i - 1) as usize;
4830 let mut p1 = sa[i1];
4831 if p1 > 0 {
4832 sa[i1] = 0;
4833 d += p1 >> (SUFFIX_GROUP_BIT - 1);
4834 p1 &= !SUFFIX_GROUP_MARKER;
4835
4836 let p1u = p1 as usize;
4837 let c1 = t[p1u - 1];
4838 let f1 = usize::from(t[p1u - 2] > c1);
4839 let v1 = buckets_index2(c1 as usize, f1);
4840 let c1u = c1 as usize;
4841 buckets[induction_offset + c1u] -= 1;
4842 let slot1 = buckets[induction_offset + c1u] as usize;
4843 sa[slot1] = (p1 - 1)
4844 | ((f1 as SaSint) << (SAINT_BIT - 1))
4845 | (((buckets[v1] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
4846 buckets[v1] = d;
4847 }
4848
4849 i -= 2;
4850 }
4851
4852 j -= 2 * prefetch_distance + 1;
4853 while i >= j {
4854 let iu = i as usize;
4855 let mut p = sa[iu];
4856 if p > 0 {
4857 sa[iu] = 0;
4858 d += p >> (SUFFIX_GROUP_BIT - 1);
4859 p &= !SUFFIX_GROUP_MARKER;
4860
4861 let pu = p as usize;
4862 let c = t[pu - 1];
4863 let f = usize::from(t[pu - 2] > c);
4864 let v = buckets_index2(c as usize, f);
4865 let cu = c as usize;
4866 buckets[induction_offset + cu] -= 1;
4867 let slot = buckets[induction_offset + cu] as usize;
4868 sa[slot] = (p - 1)
4869 | ((f as SaSint) << (SAINT_BIT - 1))
4870 | (((buckets[v] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
4871 buckets[v] = d;
4872 }
4873 i -= 1;
4874 }
4875
4876 d
4877}
4878
4879#[doc(hidden)]
4881pub fn partial_sorting_scan_right_to_left_32s_1k(
4882 t: &[SaSint],
4883 sa: &mut [SaSint],
4884 induction_bucket: &mut [SaSint],
4885 omp_block_start: FastSint,
4886 omp_block_size: FastSint,
4887) {
4888 if omp_block_size <= 0 {
4889 return;
4890 }
4891
4892 let prefetch_distance = 64usize;
4893 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
4894 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
4895 let mut i = (start + size - 1) as isize;
4896 let mut j = (start + 2 * prefetch_distance + 1) as isize;
4897
4898 while i >= j {
4899 let p0 = sa[i as usize];
4900 if p0 > 0 {
4901 sa[i as usize] = 0;
4902 let p0_usize = usize::try_from(p0).expect("suffix index must be non-negative");
4903 let bucket_index0 =
4904 usize::try_from(t[p0_usize - 1]).expect("bucket symbol must be non-negative");
4905 induction_bucket[bucket_index0] -= 1;
4906 let slot0 = usize::try_from(induction_bucket[bucket_index0])
4907 .expect("bucket slot must be non-negative");
4908 sa[slot0] = (p0 - 1)
4909 | ((usize::from(t[p0_usize - 2] > t[p0_usize - 1]) as SaSint) << (SAINT_BIT - 1));
4910 }
4911 let p1 = sa[(i - 1) as usize];
4912 if p1 > 0 {
4913 sa[(i - 1) as usize] = 0;
4914 let p1_usize = usize::try_from(p1).expect("suffix index must be non-negative");
4915 let bucket_index1 =
4916 usize::try_from(t[p1_usize - 1]).expect("bucket symbol must be non-negative");
4917 induction_bucket[bucket_index1] -= 1;
4918 let slot1 = usize::try_from(induction_bucket[bucket_index1])
4919 .expect("bucket slot must be non-negative");
4920 sa[slot1] = (p1 - 1)
4921 | ((usize::from(t[p1_usize - 2] > t[p1_usize - 1]) as SaSint) << (SAINT_BIT - 1));
4922 }
4923
4924 i -= 2;
4925 }
4926
4927 j -= (2 * prefetch_distance + 1) as isize;
4928 while i >= j {
4929 let p = sa[i as usize];
4930 if p > 0 {
4931 sa[i as usize] = 0;
4932 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
4933 let bucket_index =
4934 usize::try_from(t[p_usize - 1]).expect("bucket symbol must be non-negative");
4935 induction_bucket[bucket_index] -= 1;
4936 let slot = usize::try_from(induction_bucket[bucket_index])
4937 .expect("bucket slot must be non-negative");
4938 sa[slot] = (p - 1)
4939 | ((usize::from(t[p_usize - 2] > t[p_usize - 1]) as SaSint) << (SAINT_BIT - 1));
4940 }
4941 if i == 0 {
4942 break;
4943 }
4944 i -= 1;
4945 }
4946}
4947
4948#[doc(hidden)]
4950pub fn partial_sorting_scan_right_to_left_32s_6k_block_gather(
4951 t: &[SaSint],
4952 sa: &[SaSint],
4953 cache: &mut [ThreadCache],
4954 omp_block_start: FastSint,
4955 omp_block_size: FastSint,
4956) {
4957 if omp_block_size <= 0 {
4958 return;
4959 }
4960
4961 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
4962 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
4963 for offset in 0..size {
4964 let i = start + offset;
4965 let mut p = sa[i];
4966 let mut symbol = 0usize;
4967 p &= SAINT_MAX;
4968 if p != 0 {
4969 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
4970 symbol = buckets_index4(
4971 usize::try_from(t[p_usize - 1]).expect("bucket symbol must be non-negative"),
4972 usize::from(t[p_usize - 2] > t[p_usize - 1]),
4973 );
4974 }
4975 cache[offset].index = sa[i];
4976 cache[offset].symbol = symbol as SaSint;
4977 }
4978}
4979
4980#[doc(hidden)]
4982pub fn partial_sorting_scan_right_to_left_32s_4k_block_gather(
4983 t: &[SaSint],
4984 sa: &mut [SaSint],
4985 cache: &mut [ThreadCache],
4986 omp_block_start: FastSint,
4987 omp_block_size: FastSint,
4988) {
4989 if omp_block_size <= 0 {
4990 return;
4991 }
4992
4993 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
4994 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
4995 for offset in 0..size {
4996 let i = start + offset;
4997 let mut symbol = SAINT_MIN;
4998 let mut p = sa[i];
4999 if p > 0 {
5000 sa[i] = 0;
5001 cache[offset].index = p;
5002 p &= !SUFFIX_GROUP_MARKER;
5003 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
5004 symbol = buckets_index2(
5005 usize::try_from(t[p_usize - 1]).expect("bucket symbol must be non-negative"),
5006 usize::from(t[p_usize - 2] > t[p_usize - 1]),
5007 ) as SaSint;
5008 }
5009 cache[offset].symbol = symbol;
5010 }
5011}
5012
5013#[doc(hidden)]
5015pub fn partial_sorting_scan_right_to_left_32s_1k_block_gather(
5016 t: &[SaSint],
5017 sa: &mut [SaSint],
5018 cache: &mut [ThreadCache],
5019 omp_block_start: FastSint,
5020 omp_block_size: FastSint,
5021) {
5022 if omp_block_size <= 0 {
5023 return;
5024 }
5025 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5026 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5027 for offset in 0..size {
5028 let i = start + offset;
5029 let mut symbol = SAINT_MIN;
5030 let p = sa[i];
5031 if p > 0 {
5032 sa[i] = 0;
5033 cache[offset].index = (p - 1)
5034 | ((usize::from(t[p as usize - 2] > t[p as usize - 1]) as SaSint)
5035 << (SAINT_BIT - 1));
5036 symbol = t[p as usize - 1];
5037 }
5038 cache[offset].symbol = symbol;
5039 }
5040}
5041
5042#[doc(hidden)]
5044pub fn partial_sorting_scan_right_to_left_32s_6k_block_sort(
5045 t: &[SaSint],
5046 buckets: &mut [SaSint],
5047 mut d: SaSint,
5048 cache: &mut [ThreadCache],
5049 omp_block_start: FastSint,
5050 omp_block_size: FastSint,
5051) -> SaSint {
5052 if omp_block_size <= 0 {
5053 return d;
5054 }
5055
5056 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5057 let mut i = size;
5058 while i > 0 {
5059 i -= 1;
5060
5061 let v = usize::try_from(cache[i].symbol).expect("cache symbol must be non-negative");
5062 let p = cache[i].index;
5063 d += SaSint::from(p < 0);
5064 buckets[v] -= 1;
5065 let target = buckets[v];
5066 cache[i].symbol = target;
5067 cache[i].index = (p - 1) | (((buckets[2 + v] != d) as SaSint) << (SAINT_BIT - 1));
5068 buckets[2 + v] = d;
5069
5070 let block_end = omp_block_start as SaSint + omp_block_size as SaSint;
5071 if target >= omp_block_start as SaSint && target < block_end {
5072 let s = usize::try_from(target - omp_block_start as SaSint)
5073 .expect("cache slot must be non-negative");
5074 let q = cache[i].index & SAINT_MAX;
5075 let q_usize = usize::try_from(q).expect("suffix index must be non-negative");
5076 cache[s].index = cache[i].index;
5077 cache[s].symbol = buckets_index4(
5078 usize::try_from(t[q_usize - 1]).expect("bucket symbol must be non-negative"),
5079 usize::from(t[q_usize - 2] > t[q_usize - 1]),
5080 ) as SaSint;
5081 }
5082 }
5083
5084 d
5085}
5086
5087#[doc(hidden)]
5089pub fn partial_sorting_scan_right_to_left_32s_4k_block_sort(
5090 t: &[SaSint],
5091 k: SaSint,
5092 buckets: &mut [SaSint],
5093 mut d: SaSint,
5094 cache: &mut [ThreadCache],
5095 omp_block_start: FastSint,
5096 omp_block_size: FastSint,
5097) -> SaSint {
5098 if omp_block_size <= 0 {
5099 return d;
5100 }
5101
5102 let k_usize = usize::try_from(k).expect("k must be non-negative");
5103 let (distinct_names, tail) = buckets.split_at_mut(2 * k_usize);
5104 let induction_bucket = &mut tail[k_usize..2 * k_usize];
5105
5106 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5107 let mut i = size;
5108 while i > 0 {
5109 i -= 1;
5110
5111 let v = cache[i].symbol;
5112 if v >= 0 {
5113 let p = cache[i].index;
5114 d += p >> (SUFFIX_GROUP_BIT - 1);
5115 let bucket_index = usize::try_from(v >> 1).expect("bucket symbol must be non-negative");
5116 induction_bucket[bucket_index] -= 1;
5117 let target = induction_bucket[bucket_index];
5118 cache[i].symbol = target;
5119 cache[i].index = (p - 1)
5120 | ((v & 1) << (SAINT_BIT - 1))
5121 | (((distinct_names
5122 [usize::try_from(v).expect("bucket symbol must be non-negative")]
5123 != d) as SaSint)
5124 << (SUFFIX_GROUP_BIT - 1));
5125 distinct_names[usize::try_from(v).expect("bucket symbol must be non-negative")] = d;
5126
5127 let block_end = omp_block_start as SaSint + omp_block_size as SaSint;
5128 if target >= omp_block_start as SaSint && target < block_end {
5129 let ni = usize::try_from(target - omp_block_start as SaSint)
5130 .expect("cache slot must be non-negative");
5131 let mut np = cache[i].index;
5132 if np > 0 {
5133 cache[i].index = 0;
5134 cache[ni].index = np;
5135 np &= !SUFFIX_GROUP_MARKER;
5136 let np_usize = usize::try_from(np).expect("suffix index must be non-negative");
5137 cache[ni].symbol = buckets_index2(
5138 usize::try_from(t[np_usize - 1])
5139 .expect("bucket symbol must be non-negative"),
5140 usize::from(t[np_usize - 2] > t[np_usize - 1]),
5141 ) as SaSint;
5142 }
5143 }
5144 }
5145 }
5146
5147 d
5148}
5149
5150#[doc(hidden)]
5152pub fn partial_sorting_scan_right_to_left_32s_1k_block_sort(
5153 t: &[SaSint],
5154 induction_bucket: &mut [SaSint],
5155 cache: &mut [ThreadCache],
5156 omp_block_start: FastSint,
5157 omp_block_size: FastSint,
5158) {
5159 if omp_block_size <= 0 {
5160 return;
5161 }
5162 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5163 let mut offset = size;
5164
5165 while offset > 0 {
5166 offset -= 1;
5167 let v = cache[offset].symbol;
5168 if v >= 0 {
5169 let bucket_index = v as usize;
5170 induction_bucket[bucket_index] -= 1;
5171 let target = induction_bucket[bucket_index];
5172 cache[offset].symbol = target;
5173 let block_end = omp_block_start as SaSint + omp_block_size as SaSint;
5174 if target >= omp_block_start as SaSint && target < block_end {
5175 let ni = usize::try_from(target - omp_block_start as SaSint)
5176 .expect("cache slot must be non-negative");
5177 let np = cache[offset].index;
5178 if np > 0 {
5179 cache[offset].index = 0;
5180 cache[ni].index = (np - 1)
5181 | ((usize::from(t[np as usize - 2] > t[np as usize - 1]) as SaSint)
5182 << (SAINT_BIT - 1));
5183 cache[ni].symbol = t[np as usize - 1];
5184 }
5185 }
5186 }
5187 }
5188}
5189
5190#[doc(hidden)]
5192pub fn partial_sorting_scan_right_to_left_32s_6k_block_omp(
5193 t: &[SaSint],
5194 sa: &mut [SaSint],
5195 buckets: &mut [SaSint],
5196 mut d: SaSint,
5197 cache: &mut [ThreadCache],
5198 block_start: FastSint,
5199 block_size: FastSint,
5200 threads: SaSint,
5201) -> SaSint {
5202 if block_size <= 0 {
5203 return d;
5204 }
5205 if threads == 1 || block_size < 16_384 {
5206 return partial_sorting_scan_right_to_left_32s_6k(
5207 t,
5208 sa,
5209 buckets,
5210 d,
5211 block_start,
5212 block_size,
5213 );
5214 }
5215
5216 let threads_usize = usize::try_from(threads)
5217 .expect("threads must be non-negative")
5218 .max(1);
5219 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
5220 let omp_num_threads = threads_usize.min(block_size_usize.max(1));
5221 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
5222
5223 {
5224 let sa_ro: &[SaSint] = sa;
5225 let t_ro: &[SaSint] = t;
5226 let cache_ptr = SyncMutPtr::new(cache);
5227 run_rayon_with_threads(omp_num_threads, || {
5228 (0..omp_num_threads)
5229 .into_par_iter()
5230 .for_each(|omp_thread_num| {
5231 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5232 omp_block_stride
5233 } else {
5234 block_size_usize - omp_thread_num * omp_block_stride
5235 };
5236 let omp_block_start = usize::try_from(block_start)
5237 .expect("block_start must be non-negative")
5238 + omp_thread_num * omp_block_stride;
5239 if omp_block_size > 0 {
5240 let cache = unsafe { cache_ptr.as_slice() };
5242 partial_sorting_scan_right_to_left_32s_6k_block_gather(
5243 t_ro,
5244 sa_ro,
5245 &mut cache[omp_thread_num * omp_block_stride
5246 ..omp_thread_num * omp_block_stride + omp_block_size],
5247 omp_block_start as FastSint,
5248 omp_block_size as FastSint,
5249 );
5250 }
5251 });
5252 });
5253 }
5254
5255 d = partial_sorting_scan_right_to_left_32s_6k_block_sort(
5256 t,
5257 buckets,
5258 d,
5259 &mut cache[..block_size_usize],
5260 block_start,
5261 block_size,
5262 );
5263
5264 {
5265 let sa_ptr = SyncMutPtr::new(sa);
5266 let cache_ro: &[ThreadCache] = cache;
5267 run_rayon_with_threads(omp_num_threads, || {
5268 (0..omp_num_threads)
5269 .into_par_iter()
5270 .for_each(|omp_thread_num| {
5271 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5272 omp_block_stride
5273 } else {
5274 block_size_usize - omp_thread_num * omp_block_stride
5275 };
5276 let cache_start = omp_thread_num * omp_block_stride;
5277 if omp_block_size > 0 {
5278 let sa = unsafe { sa_ptr.as_slice() };
5282 place_cached_suffixes(
5283 sa,
5284 &cache_ro[cache_start..],
5285 0,
5286 omp_block_size as FastSint,
5287 );
5288 }
5289 });
5290 });
5291 }
5292
5293 d
5294}
5295
5296#[doc(hidden)]
5298pub fn partial_sorting_scan_right_to_left_32s_4k_block_omp(
5299 t: &[SaSint],
5300 sa: &mut [SaSint],
5301 k: SaSint,
5302 buckets: &mut [SaSint],
5303 mut d: SaSint,
5304 cache: &mut [ThreadCache],
5305 block_start: FastSint,
5306 block_size: FastSint,
5307 threads: SaSint,
5308) -> SaSint {
5309 if block_size <= 0 {
5310 return d;
5311 }
5312 if threads == 1 || block_size < 16_384 {
5313 return partial_sorting_scan_right_to_left_32s_4k(
5314 t,
5315 sa,
5316 k,
5317 buckets,
5318 d,
5319 block_start,
5320 block_size,
5321 );
5322 }
5323
5324 let threads_usize = usize::try_from(threads)
5325 .expect("threads must be non-negative")
5326 .max(1);
5327 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
5328 let omp_num_threads = threads_usize.min(block_size_usize.max(1));
5329 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
5330
5331 {
5332 let sa_ptr = SyncMutPtr::new(sa);
5333 let t_ro: &[SaSint] = t;
5334 let cache_ptr = SyncMutPtr::new(cache);
5335 run_rayon_with_threads(omp_num_threads, || {
5336 (0..omp_num_threads)
5337 .into_par_iter()
5338 .for_each(|omp_thread_num| {
5339 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5340 omp_block_stride
5341 } else {
5342 block_size_usize - omp_thread_num * omp_block_stride
5343 };
5344 let omp_block_start = usize::try_from(block_start)
5345 .expect("block_start must be non-negative")
5346 + omp_thread_num * omp_block_stride;
5347 if omp_block_size > 0 {
5348 let cache = unsafe { cache_ptr.as_slice() };
5351 let sa = unsafe { sa_ptr.as_slice() };
5352 partial_sorting_scan_right_to_left_32s_4k_block_gather(
5353 t_ro,
5354 sa,
5355 &mut cache[omp_thread_num * omp_block_stride
5356 ..omp_thread_num * omp_block_stride + omp_block_size],
5357 omp_block_start as FastSint,
5358 omp_block_size as FastSint,
5359 );
5360 }
5361 });
5362 });
5363 }
5364
5365 d = partial_sorting_scan_right_to_left_32s_4k_block_sort(
5366 t,
5367 k,
5368 buckets,
5369 d,
5370 &mut cache[..block_size_usize],
5371 block_start,
5372 block_size,
5373 );
5374
5375 {
5376 let sa_ptr = SyncMutPtr::new(sa);
5377 let cache_ptr = SyncMutPtr::new(cache);
5378 run_rayon_with_threads(omp_num_threads, || {
5379 (0..omp_num_threads)
5380 .into_par_iter()
5381 .for_each(|omp_thread_num| {
5382 let omp_block_start = omp_thread_num * omp_block_stride;
5383 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5384 omp_block_stride
5385 } else {
5386 block_size_usize - omp_block_start
5387 };
5388 if omp_block_size > 0 {
5389 let sa = unsafe { sa_ptr.as_slice() };
5393 let cache = unsafe { cache_ptr.as_slice() };
5394 compact_and_place_cached_suffixes(
5395 sa,
5396 &mut cache[omp_block_start..],
5397 0,
5398 omp_block_size as FastSint,
5399 );
5400 }
5401 });
5402 });
5403 }
5404
5405 d
5406}
5407
5408#[doc(hidden)]
5410pub fn partial_sorting_scan_right_to_left_32s_1k_block_omp(
5411 t: &[SaSint],
5412 sa: &mut [SaSint],
5413 buckets: &mut [SaSint],
5414 cache: &mut [ThreadCache],
5415 block_start: FastSint,
5416 block_size: FastSint,
5417 threads: SaSint,
5418) {
5419 if block_size <= 0 {
5420 return;
5421 }
5422 if threads == 1 || block_size < 16_384 {
5423 partial_sorting_scan_right_to_left_32s_1k(t, sa, buckets, block_start, block_size);
5424 return;
5425 }
5426
5427 let threads_usize = usize::try_from(threads)
5428 .expect("threads must be non-negative")
5429 .max(1);
5430 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
5431 let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
5432 let omp_num_threads = threads_usize.min(block_size_usize.max(1));
5433 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
5434
5435 {
5436 let sa_ptr = SyncMutPtr::new(sa);
5437 let t_ro: &[SaSint] = t;
5438 let cache_ptr = SyncMutPtr::new(cache);
5439 run_rayon_with_threads(omp_num_threads, || {
5440 (0..omp_num_threads)
5441 .into_par_iter()
5442 .for_each(|omp_thread_num| {
5443 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5444 omp_block_stride
5445 } else {
5446 block_size_usize - omp_thread_num * omp_block_stride
5447 };
5448 let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
5449 if omp_block_size > 0 {
5450 let cache = unsafe { cache_ptr.as_slice() };
5452 let sa = unsafe { sa_ptr.as_slice() };
5453 partial_sorting_scan_right_to_left_32s_1k_block_gather(
5454 t_ro,
5455 sa,
5456 &mut cache[omp_thread_num * omp_block_stride
5457 ..omp_thread_num * omp_block_stride + omp_block_size],
5458 omp_block_start as FastSint,
5459 omp_block_size as FastSint,
5460 );
5461 }
5462 });
5463 });
5464 }
5465
5466 let cache = &mut cache[..block_size_usize];
5467 partial_sorting_scan_right_to_left_32s_1k_block_sort(
5468 t,
5469 buckets,
5470 cache,
5471 block_start,
5472 block_size,
5473 );
5474 {
5475 let sa_ptr = SyncMutPtr::new(sa);
5476 let cache_ptr = SyncMutPtr::new(cache);
5477 run_rayon_with_threads(omp_num_threads, || {
5478 (0..omp_num_threads)
5479 .into_par_iter()
5480 .for_each(|omp_thread_num| {
5481 let omp_block_start = omp_thread_num * omp_block_stride;
5482 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5483 omp_block_stride
5484 } else {
5485 block_size_usize - omp_block_start
5486 };
5487 if omp_block_size > 0 {
5488 let sa = unsafe { sa_ptr.as_slice() };
5491 let cache = unsafe { cache_ptr.as_slice() };
5492 compact_and_place_cached_suffixes(
5493 sa,
5494 &mut cache[omp_block_start..],
5495 0,
5496 omp_block_size as FastSint,
5497 );
5498 }
5499 });
5500 });
5501 }
5502}
5503
5504#[doc(hidden)]
5506pub fn partial_sorting_scan_left_to_right_32s_6k_block_gather(
5507 t: &[SaSint],
5508 sa: &mut [SaSint],
5509 cache: &mut [ThreadCache],
5510 omp_block_start: FastSint,
5511 omp_block_size: FastSint,
5512) {
5513 if omp_block_size <= 0 {
5514 return;
5515 }
5516
5517 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5518 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5519 for offset in 0..size {
5520 let i = start + offset;
5521 let p = sa[i];
5522 cache[offset].index = p;
5523 let q = p & SAINT_MAX;
5524 cache[offset].symbol = if q != 0 {
5525 buckets_index4(
5526 usize::try_from(t[q as usize - 1]).expect("bucket symbol must be non-negative"),
5527 usize::from(t[q as usize - 2] >= t[q as usize - 1]),
5528 ) as SaSint
5529 } else {
5530 0
5531 };
5532 }
5533}
5534
5535#[doc(hidden)]
5537pub fn partial_sorting_scan_left_to_right_32s_4k_block_gather(
5538 t: &[SaSint],
5539 sa: &mut [SaSint],
5540 cache: &mut [ThreadCache],
5541 omp_block_start: FastSint,
5542 omp_block_size: FastSint,
5543) {
5544 if omp_block_size <= 0 {
5545 return;
5546 }
5547
5548 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5549 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5550 for offset in 0..size {
5551 let i = start + offset;
5552 let mut symbol = SAINT_MIN;
5553 let mut p = sa[i];
5554 if p > 0 {
5555 cache[offset].index = p;
5556 p &= !SUFFIX_GROUP_MARKER;
5557 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
5558 symbol = buckets_index2(
5559 usize::try_from(t[p_usize - 1]).expect("bucket symbol must be non-negative"),
5560 usize::from(t[p_usize - 2] < t[p_usize - 1]),
5561 ) as SaSint;
5562 p = 0;
5563 }
5564 cache[offset].symbol = symbol;
5565 sa[i] = p & SAINT_MAX;
5566 }
5567}
5568
5569#[doc(hidden)]
5571pub fn partial_sorting_scan_left_to_right_32s_1k_block_gather(
5572 t: &[SaSint],
5573 sa: &mut [SaSint],
5574 cache: &mut [ThreadCache],
5575 omp_block_start: FastSint,
5576 omp_block_size: FastSint,
5577) {
5578 if omp_block_size <= 0 {
5579 return;
5580 }
5581 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5582 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5583 for offset in 0..size {
5584 let i = start + offset;
5585 let mut symbol = SAINT_MIN;
5586 let mut p = sa[i];
5587 if p > 0 {
5588 cache[offset].index = (p - 1)
5589 | ((usize::from(t[p as usize - 2] < t[p as usize - 1]) as SaSint)
5590 << (SAINT_BIT - 1));
5591 symbol = t[p as usize - 1];
5592 p = 0;
5593 }
5594 cache[offset].symbol = symbol;
5595 sa[i] = p & SAINT_MAX;
5596 }
5597}
5598
5599#[doc(hidden)]
5601pub fn partial_sorting_scan_left_to_right_32s_6k_block_sort(
5602 t: &[SaSint],
5603 buckets: &mut [SaSint],
5604 mut d: SaSint,
5605 cache: &mut [ThreadCache],
5606 omp_block_start: FastSint,
5607 omp_block_size: FastSint,
5608) -> SaSint {
5609 if omp_block_size <= 0 {
5610 return d;
5611 }
5612
5613 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5614 let block_end =
5615 start + usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5616
5617 let mut i = start;
5618 let mut j = block_end.saturating_sub(65);
5619 while i < j {
5620 let cache_i0 = i - start;
5621 let cache_i1 = cache_i0 + 1;
5622
5623 let v0 =
5624 usize::try_from(cache[cache_i0].symbol).expect("cache symbol must be non-negative");
5625 let p0 = cache[cache_i0].index;
5626 d += SaSint::from(p0 < 0);
5627 cache[cache_i0].symbol = buckets[v0];
5628 buckets[v0] += 1;
5629 cache[cache_i0].index =
5630 (p0 - 1) | ((SaSint::from(buckets[2 + v0] != d)) << (SAINT_BIT - 1));
5631 buckets[2 + v0] = d;
5632 if cache[cache_i0].symbol >= omp_block_start as SaSint
5633 && cache[cache_i0].symbol < block_end as SaSint
5634 {
5635 let s = usize::try_from(cache[cache_i0].symbol - omp_block_start as SaSint)
5636 .expect("cache slot must be non-negative");
5637 let q = cache[cache_i0].index & SAINT_MAX;
5638 cache[s].index = cache[cache_i0].index;
5639 let q_usize = usize::try_from(q).expect("suffix index must be non-negative");
5640 cache[s].symbol = buckets_index4(
5641 usize::try_from(t[q_usize - 1]).expect("bucket symbol must be non-negative"),
5642 usize::from(t[q_usize - 2] >= t[q_usize - 1]),
5643 ) as SaSint;
5644 }
5645
5646 let v1 =
5647 usize::try_from(cache[cache_i1].symbol).expect("cache symbol must be non-negative");
5648 let p1 = cache[cache_i1].index;
5649 d += SaSint::from(p1 < 0);
5650 cache[cache_i1].symbol = buckets[v1];
5651 buckets[v1] += 1;
5652 cache[cache_i1].index =
5653 (p1 - 1) | ((SaSint::from(buckets[2 + v1] != d)) << (SAINT_BIT - 1));
5654 buckets[2 + v1] = d;
5655 if cache[cache_i1].symbol >= omp_block_start as SaSint
5656 && cache[cache_i1].symbol < block_end as SaSint
5657 {
5658 let s = usize::try_from(cache[cache_i1].symbol - omp_block_start as SaSint)
5659 .expect("cache slot must be non-negative");
5660 let q = cache[cache_i1].index & SAINT_MAX;
5661 cache[s].index = cache[cache_i1].index;
5662 let q_usize = usize::try_from(q).expect("suffix index must be non-negative");
5663 cache[s].symbol = buckets_index4(
5664 usize::try_from(t[q_usize - 1]).expect("bucket symbol must be non-negative"),
5665 usize::from(t[q_usize - 2] >= t[q_usize - 1]),
5666 ) as SaSint;
5667 }
5668
5669 i += 2;
5670 }
5671
5672 j += 65;
5673 while i < j {
5674 let cache_i = i - start;
5675 let v = usize::try_from(cache[cache_i].symbol).expect("cache symbol must be non-negative");
5676 let p = cache[cache_i].index;
5677 d += SaSint::from(p < 0);
5678 cache[cache_i].symbol = buckets[v];
5679 buckets[v] += 1;
5680 cache[cache_i].index = (p - 1) | ((SaSint::from(buckets[2 + v] != d)) << (SAINT_BIT - 1));
5681 buckets[2 + v] = d;
5682 if cache[cache_i].symbol >= omp_block_start as SaSint
5683 && cache[cache_i].symbol < block_end as SaSint
5684 {
5685 let s = usize::try_from(cache[cache_i].symbol - omp_block_start as SaSint)
5686 .expect("cache slot must be non-negative");
5687 let q = cache[cache_i].index & SAINT_MAX;
5688 cache[s].index = cache[cache_i].index;
5689 let q_usize = usize::try_from(q).expect("suffix index must be non-negative");
5690 cache[s].symbol = buckets_index4(
5691 usize::try_from(t[q_usize - 1]).expect("bucket symbol must be non-negative"),
5692 usize::from(t[q_usize - 2] >= t[q_usize - 1]),
5693 ) as SaSint;
5694 }
5695 i += 1;
5696 }
5697
5698 d
5699}
5700
5701#[doc(hidden)]
5703pub fn partial_sorting_scan_left_to_right_32s_4k_block_sort(
5704 t: &[SaSint],
5705 k: SaSint,
5706 buckets: &mut [SaSint],
5707 mut d: SaSint,
5708 cache: &mut [ThreadCache],
5709 omp_block_start: FastSint,
5710 omp_block_size: FastSint,
5711) -> SaSint {
5712 if omp_block_size <= 0 {
5713 return d;
5714 }
5715
5716 let k_usize = usize::try_from(k).expect("k must be non-negative");
5717 let (distinct_names, tail) = buckets.split_at_mut(2 * k_usize);
5718 let induction_bucket = &mut tail[..k_usize];
5719
5720 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5721 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5722 let block_end = start + size;
5723
5724 for offset in 0..size {
5725 let v = cache[offset].symbol;
5726 if v >= 0 {
5727 let p = cache[offset].index;
5728 d += p >> (SUFFIX_GROUP_BIT - 1);
5729
5730 let bucket_index = usize::try_from(v >> 1).expect("bucket index must be non-negative");
5731 let v_usize = usize::try_from(v).expect("cache symbol must be non-negative");
5732 let target = induction_bucket[bucket_index];
5733 induction_bucket[bucket_index] += 1;
5734
5735 cache[offset].symbol = target;
5736 cache[offset].index = (p - 1)
5737 | ((v & 1) << (SAINT_BIT - 1))
5738 | (((distinct_names[v_usize] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
5739 distinct_names[v_usize] = d;
5740
5741 if target >= omp_block_start as SaSint && target < block_end as SaSint {
5742 let ni = usize::try_from(target - omp_block_start as SaSint)
5743 .expect("cache slot must be non-negative");
5744 let mut np = cache[offset].index;
5745 if np > 0 {
5746 cache[ni].index = np;
5747 np &= !SUFFIX_GROUP_MARKER;
5748 let np_usize = usize::try_from(np).expect("suffix index must be non-negative");
5749 cache[ni].symbol = buckets_index2(
5750 usize::try_from(t[np_usize - 1])
5751 .expect("bucket symbol must be non-negative"),
5752 usize::from(t[np_usize - 2] < t[np_usize - 1]),
5753 ) as SaSint;
5754 np = 0;
5755 }
5756 cache[offset].index = np & SAINT_MAX;
5757 }
5758 }
5759 }
5760
5761 d
5762}
5763
5764#[doc(hidden)]
5766pub fn partial_sorting_scan_left_to_right_32s_1k_block_sort(
5767 t: &[SaSint],
5768 induction_bucket: &mut [SaSint],
5769 cache: &mut [ThreadCache],
5770 omp_block_start: FastSint,
5771 omp_block_size: FastSint,
5772) {
5773 if omp_block_size <= 0 {
5774 return;
5775 }
5776 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5777 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5778 let block_end = start + size;
5779
5780 for offset in 0..size {
5781 let v = cache[offset].symbol;
5782 if v >= 0 {
5783 let v_usize = v as usize;
5784 let target = induction_bucket[v_usize];
5785 cache[offset].symbol = target;
5786 induction_bucket[v_usize] += 1;
5787 if target >= omp_block_start as SaSint && target < block_end as SaSint {
5788 let ni = usize::try_from(target - omp_block_start as SaSint)
5789 .expect("cache slot must be non-negative");
5790 let mut np = cache[offset].index;
5791 if np > 0 {
5792 cache[ni].index = (np - 1)
5793 | ((usize::from(t[np as usize - 2] < t[np as usize - 1]) as SaSint)
5794 << (SAINT_BIT - 1));
5795 cache[ni].symbol = t[np as usize - 1];
5796 np = 0;
5797 }
5798 cache[offset].index = np & SAINT_MAX;
5799 }
5800 }
5801 }
5802}
5803
5804#[doc(hidden)]
5806pub fn partial_sorting_scan_left_to_right_32s_6k_block_omp(
5807 t: &[SaSint],
5808 sa: &mut [SaSint],
5809 buckets: &mut [SaSint],
5810 d: SaSint,
5811 cache: &mut [ThreadCache],
5812 block_start: FastSint,
5813 block_size: FastSint,
5814 threads: SaSint,
5815) -> SaSint {
5816 if block_size <= 0 {
5817 return d;
5818 }
5819 if threads == 1 || block_size < 16_384 {
5820 return partial_sorting_scan_left_to_right_32s_6k(
5821 t,
5822 sa,
5823 buckets,
5824 d,
5825 block_start,
5826 block_size,
5827 );
5828 }
5829
5830 let threads_usize = usize::try_from(threads)
5831 .expect("threads must be non-negative")
5832 .max(1);
5833 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
5834 let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
5835 let omp_num_threads = threads_usize.min(block_size_usize.max(1));
5836 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
5837
5838 {
5839 let sa_ptr = SyncMutPtr::new(sa);
5840 let t_ro: &[SaSint] = t;
5841 let cache_ptr = SyncMutPtr::new(cache);
5842 run_rayon_with_threads(omp_num_threads, || {
5843 (0..omp_num_threads)
5844 .into_par_iter()
5845 .for_each(|omp_thread_num| {
5846 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5847 omp_block_stride
5848 } else {
5849 block_size_usize - omp_thread_num * omp_block_stride
5850 };
5851 let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
5852 if omp_block_size > 0 {
5853 let cache = unsafe { cache_ptr.as_slice() };
5855 let sa = unsafe { sa_ptr.as_slice() };
5856 partial_sorting_scan_left_to_right_32s_6k_block_gather(
5857 t_ro,
5858 sa,
5859 &mut cache[omp_thread_num * omp_block_stride
5860 ..omp_thread_num * omp_block_stride + omp_block_size],
5861 omp_block_start as FastSint,
5862 omp_block_size as FastSint,
5863 );
5864 }
5865 });
5866 });
5867 }
5868
5869 let d = partial_sorting_scan_left_to_right_32s_6k_block_sort(
5870 t,
5871 buckets,
5872 d,
5873 &mut cache[..block_size_usize],
5874 block_start,
5875 block_size,
5876 );
5877
5878 {
5879 let sa_ptr = SyncMutPtr::new(sa);
5880 let cache_ro: &[ThreadCache] = cache;
5881 run_rayon_with_threads(omp_num_threads, || {
5882 (0..omp_num_threads)
5883 .into_par_iter()
5884 .for_each(|omp_thread_num| {
5885 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5886 omp_block_stride
5887 } else {
5888 block_size_usize - omp_thread_num * omp_block_stride
5889 };
5890 if omp_block_size > 0 {
5891 let sa = unsafe { sa_ptr.as_slice() };
5893 place_cached_suffixes(
5894 sa,
5895 &cache_ro[omp_thread_num * omp_block_stride..],
5896 0,
5897 omp_block_size as FastSint,
5898 );
5899 }
5900 });
5901 });
5902 }
5903 d
5904}
5905
5906#[doc(hidden)]
5908pub fn partial_sorting_scan_left_to_right_32s_4k_block_omp(
5909 t: &[SaSint],
5910 sa: &mut [SaSint],
5911 k: SaSint,
5912 buckets: &mut [SaSint],
5913 d: SaSint,
5914 cache: &mut [ThreadCache],
5915 block_start: FastSint,
5916 block_size: FastSint,
5917 threads: SaSint,
5918) -> SaSint {
5919 if block_size <= 0 {
5920 return d;
5921 }
5922 if threads == 1 || block_size < 16_384 {
5923 return partial_sorting_scan_left_to_right_32s_4k(
5924 t,
5925 sa,
5926 k,
5927 buckets,
5928 d,
5929 block_start,
5930 block_size,
5931 );
5932 }
5933
5934 let threads_usize = usize::try_from(threads)
5935 .expect("threads must be non-negative")
5936 .max(1);
5937 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
5938 let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
5939 let omp_num_threads = threads_usize.min(block_size_usize.max(1));
5940 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
5941
5942 {
5943 let sa_ptr = SyncMutPtr::new(sa);
5944 let t_ro: &[SaSint] = t;
5945 let cache_ptr = SyncMutPtr::new(cache);
5946 run_rayon_with_threads(omp_num_threads, || {
5947 (0..omp_num_threads)
5948 .into_par_iter()
5949 .for_each(|omp_thread_num| {
5950 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5951 omp_block_stride
5952 } else {
5953 block_size_usize - omp_thread_num * omp_block_stride
5954 };
5955 let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
5956 if omp_block_size > 0 {
5957 let cache = unsafe { cache_ptr.as_slice() };
5959 let sa = unsafe { sa_ptr.as_slice() };
5960 partial_sorting_scan_left_to_right_32s_4k_block_gather(
5961 t_ro,
5962 sa,
5963 &mut cache[omp_thread_num * omp_block_stride
5964 ..omp_thread_num * omp_block_stride + omp_block_size],
5965 omp_block_start as FastSint,
5966 omp_block_size as FastSint,
5967 );
5968 }
5969 });
5970 });
5971 }
5972
5973 let cache = &mut cache[..block_size_usize];
5974 let d = partial_sorting_scan_left_to_right_32s_4k_block_sort(
5975 t,
5976 k,
5977 buckets,
5978 d,
5979 cache,
5980 block_start,
5981 block_size,
5982 );
5983
5984 {
5985 let sa_ptr = SyncMutPtr::new(sa);
5986 let cache_ptr = SyncMutPtr::new(cache);
5987 run_rayon_with_threads(omp_num_threads, || {
5988 (0..omp_num_threads)
5989 .into_par_iter()
5990 .for_each(|omp_thread_num| {
5991 let omp_block_start = omp_thread_num * omp_block_stride;
5992 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5993 omp_block_stride
5994 } else {
5995 block_size_usize - omp_block_start
5996 };
5997 if omp_block_size > 0 {
5998 let sa = unsafe { sa_ptr.as_slice() };
6000 let cache = unsafe { cache_ptr.as_slice() };
6001 compact_and_place_cached_suffixes(
6002 sa,
6003 &mut cache[omp_block_start..],
6004 0,
6005 omp_block_size as FastSint,
6006 );
6007 }
6008 });
6009 });
6010 }
6011
6012 d
6013}
6014
6015#[doc(hidden)]
6017pub fn partial_sorting_scan_left_to_right_32s_1k_block_omp(
6018 t: &[SaSint],
6019 sa: &mut [SaSint],
6020 buckets: &mut [SaSint],
6021 cache: &mut [ThreadCache],
6022 block_start: FastSint,
6023 block_size: FastSint,
6024 threads: SaSint,
6025) {
6026 if block_size <= 0 {
6027 return;
6028 }
6029 if threads == 1 || block_size < 16_384 {
6030 partial_sorting_scan_left_to_right_32s_1k(t, sa, buckets, block_start, block_size);
6031 return;
6032 }
6033
6034 let threads_usize = usize::try_from(threads)
6035 .expect("threads must be non-negative")
6036 .max(1);
6037 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
6038 let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
6039 let omp_num_threads = threads_usize.min(block_size_usize.max(1));
6040 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
6041
6042 {
6043 let sa_ptr = SyncMutPtr::new(sa);
6044 let t_ro: &[SaSint] = t;
6045 let cache_ptr = SyncMutPtr::new(cache);
6046 run_rayon_with_threads(omp_num_threads, || {
6047 (0..omp_num_threads)
6048 .into_par_iter()
6049 .for_each(|omp_thread_num| {
6050 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
6051 omp_block_stride
6052 } else {
6053 block_size_usize - omp_thread_num * omp_block_stride
6054 };
6055 let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
6056 if omp_block_size > 0 {
6057 let cache = unsafe { cache_ptr.as_slice() };
6059 let sa = unsafe { sa_ptr.as_slice() };
6060 partial_sorting_scan_left_to_right_32s_1k_block_gather(
6061 t_ro,
6062 sa,
6063 &mut cache[omp_thread_num * omp_block_stride
6064 ..omp_thread_num * omp_block_stride + omp_block_size],
6065 omp_block_start as FastSint,
6066 omp_block_size as FastSint,
6067 );
6068 }
6069 });
6070 });
6071 }
6072
6073 let cache = &mut cache[..block_size_usize];
6074 partial_sorting_scan_left_to_right_32s_1k_block_sort(
6075 t,
6076 buckets,
6077 cache,
6078 block_start,
6079 block_size,
6080 );
6081 {
6082 let sa_ptr = SyncMutPtr::new(sa);
6083 let cache_ptr = SyncMutPtr::new(cache);
6084 run_rayon_with_threads(omp_num_threads, || {
6085 (0..omp_num_threads)
6086 .into_par_iter()
6087 .for_each(|omp_thread_num| {
6088 let omp_block_start = omp_thread_num * omp_block_stride;
6089 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
6090 omp_block_stride
6091 } else {
6092 block_size_usize - omp_block_start
6093 };
6094 if omp_block_size > 0 {
6095 let sa = unsafe { sa_ptr.as_slice() };
6097 let cache = unsafe { cache_ptr.as_slice() };
6098 compact_and_place_cached_suffixes(
6099 sa,
6100 &mut cache[omp_block_start..],
6101 0,
6102 omp_block_size as FastSint,
6103 );
6104 }
6105 });
6106 });
6107 }
6108}
6109
6110#[doc(hidden)]
6112pub fn partial_sorting_scan_right_to_left_32s_6k_omp(
6113 t: &[SaSint],
6114 sa: &mut [SaSint],
6115 n: SaSint,
6116 buckets: &mut [SaSint],
6117 first_lms_suffix: SaSint,
6118 left_suffixes_count: SaSint,
6119 mut d: SaSint,
6120 threads: SaSint,
6121 thread_state: &mut [ThreadState],
6122) -> SaSint {
6123 let scan_start = left_suffixes_count as FastSint + 1;
6124 let scan_end = n as FastSint - first_lms_suffix as FastSint;
6125 if threads == 1 || (scan_end - scan_start) < 65_536 {
6126 return partial_sorting_scan_right_to_left_32s_6k(
6127 t,
6128 sa,
6129 buckets,
6130 d,
6131 scan_start,
6132 scan_end - scan_start,
6133 );
6134 }
6135 if thread_state.is_empty() {
6136 return partial_sorting_scan_right_to_left_32s_6k(
6137 t,
6138 sa,
6139 buckets,
6140 d,
6141 scan_start,
6142 scan_end - scan_start,
6143 );
6144 }
6145
6146 let threads_usize = usize::try_from(threads)
6147 .expect("threads must be non-negative")
6148 .max(1);
6149 let mut cache = vec![ThreadCache::default(); threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE];
6150 let mut block_start = scan_end - 1;
6151 let block_span = FastSint::try_from(threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE)
6152 .expect("block span must fit FastSint");
6153 while block_start >= scan_start {
6154 let mut block_end = block_start - block_span;
6155 if block_end < scan_start {
6156 block_end = scan_start - 1;
6157 }
6158
6159 d = partial_sorting_scan_right_to_left_32s_6k_block_omp(
6160 t,
6161 sa,
6162 buckets,
6163 d,
6164 &mut cache,
6165 block_end + 1,
6166 block_start - block_end,
6167 threads,
6168 );
6169
6170 if block_end < scan_start {
6171 break;
6172 }
6173 block_start = block_end;
6174 }
6175
6176 d
6177}
6178
6179#[doc(hidden)]
6181pub fn partial_sorting_scan_right_to_left_32s_4k_omp(
6182 t: &[SaSint],
6183 sa: &mut [SaSint],
6184 n: SaSint,
6185 k: SaSint,
6186 buckets: &mut [SaSint],
6187 mut d: SaSint,
6188 threads: SaSint,
6189 thread_state: &mut [ThreadState],
6190) -> SaSint {
6191 if threads == 1 || n < 65_536 {
6192 return partial_sorting_scan_right_to_left_32s_4k(t, sa, k, buckets, d, 0, n as FastSint);
6193 }
6194 if thread_state.is_empty() {
6195 return partial_sorting_scan_right_to_left_32s_4k(t, sa, k, buckets, d, 0, n as FastSint);
6196 }
6197 let threads_usize = usize::try_from(threads)
6198 .expect("threads must be non-negative")
6199 .max(1);
6200 let mut cache = vec![ThreadCache::default(); threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE];
6201 let mut block_start = FastSint::try_from(n).expect("n must fit FastSint") - 1;
6202 let block_span = FastSint::try_from(threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE)
6203 .expect("block span must fit FastSint");
6204 while block_start >= 0 {
6205 let mut block_end = block_start - block_span;
6206 if block_end < 0 {
6207 block_end = -1;
6208 }
6209
6210 d = partial_sorting_scan_right_to_left_32s_4k_block_omp(
6211 t,
6212 sa,
6213 k,
6214 buckets,
6215 d,
6216 &mut cache,
6217 block_end + 1,
6218 block_start - block_end,
6219 threads,
6220 );
6221
6222 if block_end < 0 {
6223 break;
6224 }
6225 block_start = block_end;
6226 }
6227
6228 d
6229}
6230
6231#[doc(hidden)]
6233pub fn partial_sorting_scan_right_to_left_32s_1k_omp(
6234 t: &[SaSint],
6235 sa: &mut [SaSint],
6236 n: SaSint,
6237 buckets: &mut [SaSint],
6238 threads: SaSint,
6239 thread_state: &mut [ThreadState],
6240) {
6241 if threads == 1 || n < 65_536 {
6242 partial_sorting_scan_right_to_left_32s_1k(t, sa, buckets, 0, n as FastSint);
6243 return;
6244 }
6245 if thread_state.is_empty() {
6246 partial_sorting_scan_right_to_left_32s_1k(t, sa, buckets, 0, n as FastSint);
6247 return;
6248 }
6249
6250 let threads_usize = usize::try_from(threads)
6251 .expect("threads must be non-negative")
6252 .max(1);
6253 let mut cache = vec![ThreadCache::default(); threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE];
6254 let mut block_start = FastSint::try_from(n).expect("n must fit FastSint") - 1;
6255 let block_span = FastSint::try_from(threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE)
6256 .expect("block span must fit FastSint");
6257 while block_start >= 0 {
6258 let mut block_end = block_start - block_span;
6259 if block_end < 0 {
6260 block_end = -1;
6261 }
6262
6263 partial_sorting_scan_right_to_left_32s_1k_block_omp(
6264 t,
6265 sa,
6266 buckets,
6267 &mut cache,
6268 block_end + 1,
6269 block_start - block_end,
6270 threads,
6271 );
6272
6273 if block_end < 0 {
6274 break;
6275 }
6276 block_start = block_end;
6277 }
6278}
6279
6280#[doc(hidden)]
6282pub fn partial_sorting_gather_lms_suffixes_32s_4k(
6283 sa: &mut [SaSint],
6284 omp_block_start: FastSint,
6285 omp_block_size: FastSint,
6286) -> FastSint {
6287 if omp_block_size <= 0 {
6288 return omp_block_start;
6289 }
6290
6291 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
6292 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
6293 let mut l = start;
6294
6295 for i in start..start + size {
6296 let s = sa[i] as SaUint;
6297 sa[l] = ((s.wrapping_sub(SUFFIX_GROUP_MARKER as SaUint)) & !(SUFFIX_GROUP_MARKER as SaUint))
6298 as SaSint;
6299 l += usize::from((s as SaSint) < 0);
6300 }
6301
6302 l as FastSint
6303}
6304
6305#[doc(hidden)]
6307pub fn partial_sorting_gather_lms_suffixes_32s_1k(
6308 sa: &mut [SaSint],
6309 omp_block_start: FastSint,
6310 omp_block_size: FastSint,
6311) -> FastSint {
6312 if omp_block_size <= 0 {
6313 return omp_block_start;
6314 }
6315
6316 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
6317 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
6318 let mut l = start;
6319
6320 for i in start..start + size {
6321 let s = sa[i];
6322 sa[l] = s & SAINT_MAX;
6323 l += usize::from(s < 0);
6324 }
6325
6326 l as FastSint
6327}
6328
6329#[doc(hidden)]
6331pub fn partial_sorting_gather_lms_suffixes_32s_4k_omp(
6332 sa: &mut [SaSint],
6333 n: SaSint,
6334 threads: SaSint,
6335 thread_state: &mut [ThreadState],
6336) {
6337 let n_usize = usize::try_from(n).expect("n must be non-negative");
6338 let omp_num_threads = if threads > 1 && n >= 65_536 {
6339 usize::try_from(threads)
6340 .expect("threads must be non-negative")
6341 .min(thread_state.len())
6342 .max(1)
6343 } else {
6344 1
6345 };
6346
6347 if omp_num_threads == 1 {
6348 let _ = partial_sorting_gather_lms_suffixes_32s_4k(sa, 0, n as FastSint);
6349 return;
6350 }
6351
6352 let omp_block_stride = (n_usize / omp_num_threads) & !15usize;
6353 for (thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
6354 let block_start = thread_num * omp_block_stride;
6355 let block_size = if thread_num + 1 < omp_num_threads {
6356 omp_block_stride
6357 } else {
6358 n_usize - block_start
6359 };
6360 state.position = block_start as FastSint;
6361 state.count = partial_sorting_gather_lms_suffixes_32s_4k(
6362 sa,
6363 block_start as FastSint,
6364 block_size as FastSint,
6365 ) - block_start as FastSint;
6366 }
6367
6368 let mut position = 0usize;
6369 for (thread_num, state) in thread_state.iter().take(omp_num_threads).enumerate() {
6370 let count = usize::try_from(state.count).expect("count must be non-negative");
6371 let src = usize::try_from(state.position).expect("position must be non-negative");
6372 if thread_num > 0 && count > 0 {
6373 sa.copy_within(src..src + count, position);
6374 }
6375 position += count;
6376 }
6377}
6378
6379#[doc(hidden)]
6381pub fn partial_sorting_gather_lms_suffixes_32s_1k_omp(
6382 sa: &mut [SaSint],
6383 n: SaSint,
6384 threads: SaSint,
6385 thread_state: &mut [ThreadState],
6386) {
6387 let n_usize = usize::try_from(n).expect("n must be non-negative");
6388 let omp_num_threads = if threads > 1 && n >= 65_536 {
6389 usize::try_from(threads)
6390 .expect("threads must be non-negative")
6391 .min(thread_state.len())
6392 .max(1)
6393 } else {
6394 1
6395 };
6396
6397 if omp_num_threads == 1 {
6398 let _ = partial_sorting_gather_lms_suffixes_32s_1k(sa, 0, n as FastSint);
6399 return;
6400 }
6401
6402 let omp_block_stride = (n_usize / omp_num_threads) & !15usize;
6403 for (thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
6404 let block_start = thread_num * omp_block_stride;
6405 let block_size = if thread_num + 1 < omp_num_threads {
6406 omp_block_stride
6407 } else {
6408 n_usize - block_start
6409 };
6410 state.position = block_start as FastSint;
6411 state.count = partial_sorting_gather_lms_suffixes_32s_1k(
6412 sa,
6413 block_start as FastSint,
6414 block_size as FastSint,
6415 ) - block_start as FastSint;
6416 }
6417
6418 let mut position = 0usize;
6419 for (thread_num, state) in thread_state.iter().take(omp_num_threads).enumerate() {
6420 let count = usize::try_from(state.count).expect("count must be non-negative");
6421 let src = usize::try_from(state.position).expect("position must be non-negative");
6422 if thread_num > 0 && count > 0 {
6423 sa.copy_within(src..src + count, position);
6424 }
6425 position += count;
6426 }
6427}
6428
6429#[doc(hidden)]
6431pub fn induce_partial_order_8u_omp(
6432 t: &[u8],
6433 sa: &mut [SaSint],
6434 n: SaSint,
6435 k: SaSint,
6436 flags: SaSint,
6437 buckets: &mut [SaSint],
6438 first_lms_suffix: SaSint,
6439 left_suffixes_count: SaSint,
6440 threads: SaSint,
6441 thread_state: &mut [ThreadState],
6442) {
6443 buckets[2 * ALPHABET_SIZE..4 * ALPHABET_SIZE].fill(0);
6444
6445 if (flags & LIBSAIS_FLAGS_GSA) != 0 {
6446 let left = 4 * ALPHABET_SIZE + buckets_index2(0, 1);
6447 let right = 4 * ALPHABET_SIZE + buckets_index2(1, 1);
6448 buckets[left] = buckets[right] - 1;
6449 flip_suffix_markers_omp(sa, buckets[left], threads);
6450 }
6451
6452 let d = partial_sorting_scan_left_to_right_8u_omp(
6453 t,
6454 sa,
6455 n,
6456 k,
6457 buckets,
6458 left_suffixes_count,
6459 0,
6460 threads,
6461 thread_state,
6462 );
6463 partial_sorting_shift_markers_8u_omp(sa, n, buckets, threads);
6464
6465 if (flags & LIBSAIS_FLAGS_GSA) != 0 {
6466 partial_gsa_scan_right_to_left_8u_omp(
6467 t,
6468 sa,
6469 n,
6470 k,
6471 buckets,
6472 first_lms_suffix,
6473 left_suffixes_count,
6474 d,
6475 threads,
6476 thread_state,
6477 );
6478
6479 if t[usize::try_from(first_lms_suffix).expect("first_lms_suffix must be non-negative")] == 0
6480 {
6481 let count = usize::try_from(buckets[buckets_index2(1, 1)] - 1)
6482 .expect("count must be non-negative");
6483 sa.copy_within(0..count, 1);
6484 sa[0] = first_lms_suffix | SAINT_MIN;
6485 }
6486
6487 buckets[buckets_index2(0, 1)] = 0;
6488 } else {
6489 partial_sorting_scan_right_to_left_8u_omp(
6490 t,
6491 sa,
6492 n,
6493 k,
6494 buckets,
6495 first_lms_suffix,
6496 left_suffixes_count,
6497 d,
6498 threads,
6499 thread_state,
6500 );
6501 }
6502}
6503
6504#[doc(hidden)]
6506pub fn induce_partial_order_32s_6k_omp(
6507 t: &[SaSint],
6508 sa: &mut [SaSint],
6509 n: SaSint,
6510 k: SaSint,
6511 buckets: &mut [SaSint],
6512 first_lms_suffix: SaSint,
6513 left_suffixes_count: SaSint,
6514 threads: SaSint,
6515 thread_state: &mut [ThreadState],
6516) {
6517 let d = partial_sorting_scan_left_to_right_32s_6k_omp(
6518 t,
6519 sa,
6520 n,
6521 buckets,
6522 left_suffixes_count,
6523 0,
6524 threads,
6525 thread_state,
6526 );
6527 partial_sorting_shift_markers_32s_6k_omp(sa, k, buckets, threads);
6528 partial_sorting_shift_buckets_32s_6k(k, buckets);
6529 let _ = partial_sorting_scan_right_to_left_32s_6k_omp(
6530 t,
6531 sa,
6532 n,
6533 buckets,
6534 first_lms_suffix,
6535 left_suffixes_count,
6536 d,
6537 threads,
6538 thread_state,
6539 );
6540}
6541
6542#[doc(hidden)]
6544pub fn induce_partial_order_32s_4k_omp(
6545 t: &[SaSint],
6546 sa: &mut [SaSint],
6547 n: SaSint,
6548 k: SaSint,
6549 buckets: &mut [SaSint],
6550 threads: SaSint,
6551 thread_state: &mut [ThreadState],
6552) {
6553 let zero_len = 2 * usize::try_from(k).expect("k must be non-negative");
6554 buckets[..zero_len].fill(0);
6555
6556 let d = partial_sorting_scan_left_to_right_32s_4k_omp(
6557 t,
6558 sa,
6559 n,
6560 k,
6561 buckets,
6562 0,
6563 threads,
6564 thread_state,
6565 );
6566 partial_sorting_shift_markers_32s_4k(sa, n);
6567 let _ = partial_sorting_scan_right_to_left_32s_4k_omp(
6568 t,
6569 sa,
6570 n,
6571 k,
6572 buckets,
6573 d,
6574 threads,
6575 thread_state,
6576 );
6577 partial_sorting_gather_lms_suffixes_32s_4k_omp(sa, n, threads, thread_state);
6578}
6579
6580#[doc(hidden)]
6582pub fn induce_partial_order_32s_2k_omp(
6583 t: &[SaSint],
6584 sa: &mut [SaSint],
6585 n: SaSint,
6586 k: SaSint,
6587 buckets: &mut [SaSint],
6588 threads: SaSint,
6589 thread_state: &mut [ThreadState],
6590) {
6591 let k_usize = usize::try_from(k).expect("k must be non-negative");
6592 let (left, right) = buckets.split_at_mut(k_usize);
6593 partial_sorting_scan_left_to_right_32s_1k_omp(t, sa, n, right, threads, thread_state);
6594 partial_sorting_scan_right_to_left_32s_1k_omp(t, sa, n, left, threads, thread_state);
6595 partial_sorting_gather_lms_suffixes_32s_1k_omp(sa, n, threads, thread_state);
6596}
6597
6598#[doc(hidden)]
6600pub fn induce_partial_order_32s_1k_omp(
6601 t: &[SaSint],
6602 sa: &mut [SaSint],
6603 n: SaSint,
6604 k: SaSint,
6605 buckets: &mut [SaSint],
6606 threads: SaSint,
6607 thread_state: &mut [ThreadState],
6608) {
6609 count_suffixes_32s(t, n, k, buckets);
6610 initialize_buckets_start_32s_1k(k, buckets);
6611 partial_sorting_scan_left_to_right_32s_1k_omp(t, sa, n, buckets, threads, thread_state);
6612
6613 count_suffixes_32s(t, n, k, buckets);
6614 initialize_buckets_end_32s_1k(k, buckets);
6615 partial_sorting_scan_right_to_left_32s_1k_omp(t, sa, n, buckets, threads, thread_state);
6616
6617 partial_sorting_gather_lms_suffixes_32s_1k_omp(sa, n, threads, thread_state);
6618}
6619
6620#[doc(hidden)]
6622pub fn renumber_lms_suffixes_8u(
6623 sa: &mut [SaSint],
6624 m: SaSint,
6625 mut name: SaSint,
6626 omp_block_start: FastSint,
6627 omp_block_size: FastSint,
6628) -> SaSint {
6629 if omp_block_size <= 0 {
6630 return name;
6631 }
6632
6633 let m_usize = usize::try_from(m).expect("m must be non-negative");
6634 let (sa_head, sam) = sa.split_at_mut(m_usize);
6635 let mut i = omp_block_start;
6636 let mut j = omp_block_start + omp_block_size - 64 - 3;
6637
6638 while i < j {
6639 let i0 = i as usize;
6640 let p0 = sa_head[i0];
6641 let d0 = ((p0 & SAINT_MAX) >> 1) as usize;
6642 sam[d0] = name | SAINT_MIN;
6643 name += SaSint::from(p0 < 0);
6644
6645 let p1 = sa_head[i0 + 1];
6646 let d1 = ((p1 & SAINT_MAX) >> 1) as usize;
6647 sam[d1] = name | SAINT_MIN;
6648 name += SaSint::from(p1 < 0);
6649
6650 let p2 = sa_head[i0 + 2];
6651 let d2 = ((p2 & SAINT_MAX) >> 1) as usize;
6652 sam[d2] = name | SAINT_MIN;
6653 name += SaSint::from(p2 < 0);
6654
6655 let p3 = sa_head[i0 + 3];
6656 let d3 = ((p3 & SAINT_MAX) >> 1) as usize;
6657 sam[d3] = name | SAINT_MIN;
6658 name += SaSint::from(p3 < 0);
6659
6660 i += 4;
6661 }
6662
6663 j += 64 + 3;
6664 while i < j {
6665 let p = sa_head[i as usize];
6666 let d = ((p & SAINT_MAX) >> 1) as usize;
6667 sam[d] = name | SAINT_MIN;
6668 name += SaSint::from(p < 0);
6669 i += 1;
6670 }
6671
6672 name
6673}
6674
6675#[doc(hidden)]
6677pub fn gather_marked_lms_suffixes(
6678 sa: &mut [SaSint],
6679 m: SaSint,
6680 l: FastSint,
6681 omp_block_start: FastSint,
6682 omp_block_size: FastSint,
6683) -> FastSint {
6684 if omp_block_size <= 0 {
6685 return l;
6686 }
6687
6688 let mut l = l - 1;
6689 let mut i = m as FastSint + omp_block_start + omp_block_size - 1;
6690 let mut j = m as FastSint + omp_block_start + 3;
6691
6692 while i >= j {
6693 let i0 = i as usize;
6694 let s0 = sa[i0];
6695 sa[l as usize] = s0 & SAINT_MAX;
6696 l -= FastSint::from(s0 < 0);
6697
6698 let s1 = sa[i0 - 1];
6699 sa[l as usize] = s1 & SAINT_MAX;
6700 l -= FastSint::from(s1 < 0);
6701
6702 let s2 = sa[i0 - 2];
6703 sa[l as usize] = s2 & SAINT_MAX;
6704 l -= FastSint::from(s2 < 0);
6705
6706 let s3 = sa[i0 - 3];
6707 sa[l as usize] = s3 & SAINT_MAX;
6708 l -= FastSint::from(s3 < 0);
6709
6710 i -= 4;
6711 }
6712
6713 j -= 3;
6714 while i >= j {
6715 let s = sa[i as usize];
6716 sa[l as usize] = s & SAINT_MAX;
6717 l -= FastSint::from(s < 0);
6718 i -= 1;
6719 }
6720
6721 l + 1
6722}
6723
6724#[doc(hidden)]
6726pub fn renumber_lms_suffixes_8u_omp(
6727 sa: &mut [SaSint],
6728 m: SaSint,
6729 threads: SaSint,
6730 thread_state: &mut [ThreadState],
6731) -> SaSint {
6732 let omp_num_threads = if threads > 1 && m >= 65_536 {
6733 usize::try_from(threads)
6734 .expect("threads must be non-negative")
6735 .min(thread_state.len())
6736 .max(1)
6737 } else {
6738 1
6739 };
6740 let omp_block_stride = (m as FastSint / omp_num_threads as FastSint) & !15;
6741
6742 let name = if omp_num_threads == 1 {
6743 renumber_lms_suffixes_8u(sa, m, 0, 0, m as FastSint)
6744 } else {
6745 {
6746 let sa_ro: &[SaSint] = sa;
6747 run_rayon_with_threads(omp_num_threads, || {
6748 thread_state[..omp_num_threads]
6749 .par_iter_mut()
6750 .enumerate()
6751 .for_each(|(omp_thread_num, state)| {
6752 let omp_block_start = omp_thread_num as FastSint * omp_block_stride;
6753 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
6754 omp_block_stride
6755 } else {
6756 m as FastSint - omp_block_start
6757 };
6758 state.count =
6759 count_negative_marked_suffixes(sa_ro, omp_block_start, omp_block_size)
6760 as FastSint;
6761 });
6762 });
6763 }
6764
6765 let counts: Vec<FastSint> = thread_state[..omp_num_threads]
6766 .iter()
6767 .map(|s| s.count)
6768 .collect();
6769 let name = counts.iter().sum::<FastSint>() as SaSint;
6770
6771 {
6772 let sa_ptr = SyncMutPtr::new(sa);
6773 let counts_ref: &[FastSint] = &counts;
6774 run_rayon_with_threads(omp_num_threads, || {
6775 (0..omp_num_threads)
6776 .into_par_iter()
6777 .for_each(|omp_thread_num| {
6778 let omp_block_start = omp_thread_num as FastSint * omp_block_stride;
6779 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
6780 omp_block_stride
6781 } else {
6782 m as FastSint - omp_block_start
6783 };
6784
6785 let mut count: FastSint = 0;
6786 for t in 0..omp_thread_num {
6787 count += counts_ref[t];
6788 }
6789
6790 let sa = unsafe { sa_ptr.as_slice() };
6792 let _ = renumber_lms_suffixes_8u(
6793 sa,
6794 m,
6795 count as SaSint,
6796 omp_block_start,
6797 omp_block_size,
6798 );
6799 });
6800 });
6801 }
6802 name
6803 };
6804
6805 name
6806}
6807
6808#[doc(hidden)]
6810pub fn gather_marked_lms_suffixes_omp(
6811 sa: &mut [SaSint],
6812 n: SaSint,
6813 m: SaSint,
6814 fs: SaSint,
6815 threads: SaSint,
6816 thread_state: &mut [ThreadState],
6817) {
6818 let n_fast = n as FastSint;
6819 let m_fast = m as FastSint;
6820 let omp_num_threads = if threads > 1 && n >= 131_072 {
6821 usize::try_from(threads)
6822 .expect("threads must be non-negative")
6823 .min(thread_state.len())
6824 .max(1)
6825 } else {
6826 1
6827 };
6828 let omp_block_stride = ((n_fast >> 1) / omp_num_threads as FastSint) & !15;
6829
6830 if omp_num_threads == 1 {
6831 let _ = gather_marked_lms_suffixes(sa, m, n_fast + fs as FastSint, 0, n_fast >> 1);
6832 } else {
6833 {
6834 let sa_ptr = SyncMutPtr::new(sa);
6835 run_rayon_with_threads(omp_num_threads, || {
6836 thread_state[..omp_num_threads]
6837 .par_iter_mut()
6838 .enumerate()
6839 .for_each(|(omp_thread_num, state)| {
6840 let omp_block_start = omp_thread_num as FastSint * omp_block_stride;
6841 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
6842 omp_block_stride
6843 } else {
6844 (n_fast >> 1) - omp_block_start
6845 };
6846
6847 let sa = unsafe { sa_ptr.as_slice() };
6849 if omp_thread_num < omp_num_threads - 1 {
6850 state.position = gather_marked_lms_suffixes(
6851 sa,
6852 m,
6853 m_fast + omp_block_start + omp_block_size,
6854 omp_block_start,
6855 omp_block_size,
6856 );
6857 state.count =
6858 m_fast + omp_block_start + omp_block_size - state.position;
6859 } else {
6860 state.position = gather_marked_lms_suffixes(
6861 sa,
6862 m,
6863 n_fast + fs as FastSint,
6864 omp_block_start,
6865 omp_block_size,
6866 );
6867 state.count = n_fast + fs as FastSint - state.position;
6868 }
6869 });
6870 });
6871 }
6872
6873 let mut position = n_fast + fs as FastSint;
6874 for t in (0..omp_num_threads).rev() {
6875 position -= thread_state[t].count;
6876 if t + 1 != omp_num_threads && thread_state[t].count > 0 {
6877 let src = usize::try_from(thread_state[t].position)
6878 .expect("position must be non-negative");
6879 let len =
6880 usize::try_from(thread_state[t].count).expect("count must be non-negative");
6881 let dst = usize::try_from(position).expect("position must be non-negative");
6882 sa.copy_within(src..src + len, dst);
6883 }
6884 }
6885 }
6886}
6887
6888#[doc(hidden)]
6890pub fn renumber_and_gather_lms_suffixes_omp(
6891 sa: &mut [SaSint],
6892 n: SaSint,
6893 m: SaSint,
6894 fs: SaSint,
6895 threads: SaSint,
6896 thread_state: &mut [ThreadState],
6897) -> SaSint {
6898 let m_usize = usize::try_from(m).expect("m must be non-negative");
6899 let half_n = usize::try_from(n >> 1).expect("n must be non-negative");
6900 sa[m_usize..m_usize + half_n].fill(0);
6901
6902 let name = renumber_lms_suffixes_8u_omp(sa, m, threads, thread_state);
6903 if name < m {
6904 gather_marked_lms_suffixes_omp(sa, n, m, fs, threads, thread_state);
6905 } else {
6906 let mut i = 0;
6907 while i < m_usize {
6908 sa[i] &= SAINT_MAX;
6909 i += 1;
6910 }
6911 }
6912
6913 name
6914}
6915
6916#[doc(hidden)]
6918pub fn renumber_distinct_lms_suffixes_32s_4k(
6919 sa: &mut [SaSint],
6920 m: SaSint,
6921 mut name: SaSint,
6922 omp_block_start: FastSint,
6923 omp_block_size: FastSint,
6924) -> SaSint {
6925 if omp_block_size <= 0 {
6926 return name;
6927 }
6928
6929 let prefetch_distance = 64usize;
6930 let m_usize = usize::try_from(m).expect("m must be non-negative");
6931 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
6932 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
6933 let (sa_head, sam) = sa.split_at_mut(m_usize);
6934 let mut i = start;
6935 let mut j = start
6936 .saturating_add(size)
6937 .saturating_sub(prefetch_distance + 3);
6938 let mut p0;
6939 let mut p1;
6940 let mut p2;
6941 let mut p3 = 0;
6942
6943 while i < j {
6944 p0 = sa_head[i];
6945 sa_head[i] = p0 & SAINT_MAX;
6946 sam[(sa_head[i] >> 1) as usize] = name | (p0 & p3 & SAINT_MIN);
6947 name += SaSint::from(p0 < 0);
6948
6949 p1 = sa_head[i + 1];
6950 sa_head[i + 1] = p1 & SAINT_MAX;
6951 sam[(sa_head[i + 1] >> 1) as usize] = name | (p1 & p0 & SAINT_MIN);
6952 name += SaSint::from(p1 < 0);
6953
6954 p2 = sa_head[i + 2];
6955 sa_head[i + 2] = p2 & SAINT_MAX;
6956 sam[(sa_head[i + 2] >> 1) as usize] = name | (p2 & p1 & SAINT_MIN);
6957 name += SaSint::from(p2 < 0);
6958
6959 p3 = sa_head[i + 3];
6960 sa_head[i + 3] = p3 & SAINT_MAX;
6961 sam[(sa_head[i + 3] >> 1) as usize] = name | (p3 & p2 & SAINT_MIN);
6962 name += SaSint::from(p3 < 0);
6963
6964 i += 4;
6965 }
6966
6967 j = start + size;
6968 while i < j {
6969 p2 = p3;
6970 p3 = sa_head[i];
6971 sa_head[i] = p3 & SAINT_MAX;
6972 sam[(sa_head[i] >> 1) as usize] = name | (p3 & p2 & SAINT_MIN);
6973 name += SaSint::from(p3 < 0);
6974 i += 1;
6975 }
6976
6977 name
6978}
6979
6980#[doc(hidden)]
6982pub fn mark_distinct_lms_suffixes_32s(
6983 sa: &mut [SaSint],
6984 m: SaSint,
6985 omp_block_start: FastSint,
6986 omp_block_size: FastSint,
6987) {
6988 if omp_block_size <= 0 {
6989 return;
6990 }
6991
6992 let m_usize = usize::try_from(m).expect("m must be non-negative");
6993 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
6994 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
6995 let mut i = m_usize + start;
6996 let mut j = m_usize + start + size.saturating_sub(3);
6997 let mut p3 = 0;
6998
6999 while i < j {
7000 let mut p0 = sa[i];
7001 sa[i] = p0 & (p3 | SAINT_MAX);
7002 p0 = if p0 == 0 { p3 } else { p0 };
7003
7004 let mut p1 = sa[i + 1];
7005 sa[i + 1] = p1 & (p0 | SAINT_MAX);
7006 p1 = if p1 == 0 { p0 } else { p1 };
7007
7008 let mut p2 = sa[i + 2];
7009 sa[i + 2] = p2 & (p1 | SAINT_MAX);
7010 p2 = if p2 == 0 { p1 } else { p2 };
7011
7012 p3 = sa[i + 3];
7013 sa[i + 3] = p3 & (p2 | SAINT_MAX);
7014 p3 = if p3 == 0 { p2 } else { p3 };
7015
7016 i += 4;
7017 }
7018
7019 j = m_usize + start + size;
7020 while i < j {
7021 let p2 = p3;
7022 p3 = sa[i];
7023 sa[i] = p3 & (p2 | SAINT_MAX);
7024 p3 = if p3 == 0 { p2 } else { p3 };
7025 i += 1;
7026 }
7027}
7028
7029#[doc(hidden)]
7031pub fn clamp_lms_suffixes_length_32s(
7032 sa: &mut [SaSint],
7033 m: SaSint,
7034 omp_block_start: FastSint,
7035 omp_block_size: FastSint,
7036) {
7037 if omp_block_size <= 0 {
7038 return;
7039 }
7040
7041 let m_usize = usize::try_from(m).expect("m must be non-negative");
7042 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
7043 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
7044 let mut i = m_usize + start;
7045 let mut j = m_usize + start + size.saturating_sub(3);
7046
7047 while i < j {
7048 let s0 = sa[i];
7049 sa[i] = if s0 < 0 { s0 } else { 0 } & SAINT_MAX;
7050
7051 let s1 = sa[i + 1];
7052 sa[i + 1] = if s1 < 0 { s1 } else { 0 } & SAINT_MAX;
7053
7054 let s2 = sa[i + 2];
7055 sa[i + 2] = if s2 < 0 { s2 } else { 0 } & SAINT_MAX;
7056
7057 let s3 = sa[i + 3];
7058 sa[i + 3] = if s3 < 0 { s3 } else { 0 } & SAINT_MAX;
7059
7060 i += 4;
7061 }
7062
7063 j = m_usize + start + size;
7064 while i < j {
7065 let s = sa[i];
7066 sa[i] = if s < 0 { s } else { 0 } & SAINT_MAX;
7067 i += 1;
7068 }
7069}
7070
7071#[doc(hidden)]
7073pub fn renumber_distinct_lms_suffixes_32s_4k_omp(
7074 sa: &mut [SaSint],
7075 m: SaSint,
7076 threads: SaSint,
7077 thread_state: &mut [ThreadState],
7078) -> SaSint {
7079 let m_usize = usize::try_from(m).expect("m must be non-negative");
7080 let omp_num_threads = if threads > 1 && m >= 65_536 {
7081 usize::try_from(threads)
7082 .expect("threads must be non-negative")
7083 .min(thread_state.len())
7084 .max(1)
7085 } else {
7086 1
7087 };
7088 let omp_block_stride = (m_usize / omp_num_threads) & !15usize;
7089
7090 let name = if omp_num_threads == 1 {
7091 let omp_block_start = 0usize;
7092 let omp_block_size = m_usize - omp_block_start;
7093 renumber_distinct_lms_suffixes_32s_4k(
7094 sa,
7095 m,
7096 1,
7097 omp_block_start as FastSint,
7098 omp_block_size as FastSint,
7099 )
7100 } else {
7101 {
7102 let sa_ro: &[SaSint] = sa;
7103 run_rayon_with_threads(omp_num_threads, || {
7104 thread_state[..omp_num_threads]
7105 .par_iter_mut()
7106 .enumerate()
7107 .for_each(|(omp_thread_num, state)| {
7108 let omp_block_start = omp_thread_num * omp_block_stride;
7109 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
7110 omp_block_stride
7111 } else {
7112 m_usize - omp_block_start
7113 };
7114 state.count = count_negative_marked_suffixes(
7115 sa_ro,
7116 omp_block_start as FastSint,
7117 omp_block_size as FastSint,
7118 ) as FastSint;
7119 });
7120 });
7121 }
7122
7123 let counts: Vec<FastSint> = thread_state[..omp_num_threads]
7124 .iter()
7125 .map(|s| s.count)
7126 .collect();
7127 let name = (1 + counts.iter().sum::<FastSint>()) as SaSint;
7128
7129 {
7130 let sa_ptr = SyncMutPtr::new(sa);
7131 let counts_ref: &[FastSint] = &counts;
7132 run_rayon_with_threads(omp_num_threads, || {
7133 (0..omp_num_threads)
7134 .into_par_iter()
7135 .for_each(|omp_thread_num| {
7136 let omp_block_start = omp_thread_num * omp_block_stride;
7137 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
7138 omp_block_stride
7139 } else {
7140 m_usize - omp_block_start
7141 };
7142
7143 let mut count: FastSint = 1;
7144 for t in 0..omp_thread_num {
7145 count += counts_ref[t];
7146 }
7147
7148 let sa = unsafe { sa_ptr.as_slice() };
7150 let _ = renumber_distinct_lms_suffixes_32s_4k(
7151 sa,
7152 m,
7153 count as SaSint,
7154 omp_block_start as FastSint,
7155 omp_block_size as FastSint,
7156 );
7157 });
7158 });
7159 }
7160 name
7161 };
7162
7163 name - 1
7164}
7165
7166#[doc(hidden)]
7168pub fn mark_distinct_lms_suffixes_32s_omp(
7169 sa: &mut [SaSint],
7170 n: SaSint,
7171 m: SaSint,
7172 threads: SaSint,
7173) {
7174 let half_n = usize::try_from(n >> 1).expect("n must be non-negative");
7175 let omp_num_threads = if threads > 1 && n >= 131_072 {
7176 usize::try_from(threads)
7177 .expect("threads must be non-negative")
7178 .max(1)
7179 } else {
7180 1
7181 };
7182 let omp_block_stride = (half_n / omp_num_threads) & !15usize;
7183
7184 {
7185 let sa_ptr = SyncMutPtr::new(sa);
7186 run_rayon_with_threads(omp_num_threads, || {
7187 (0..omp_num_threads)
7188 .into_par_iter()
7189 .for_each(|omp_thread_num| {
7190 let omp_block_start = omp_thread_num * omp_block_stride;
7191 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
7192 omp_block_stride
7193 } else {
7194 half_n - omp_block_start
7195 };
7196 let sa = unsafe { sa_ptr.as_slice() };
7198 mark_distinct_lms_suffixes_32s(
7199 sa,
7200 m,
7201 omp_block_start as FastSint,
7202 omp_block_size as FastSint,
7203 );
7204 });
7205 });
7206 }
7207}
7208
7209#[doc(hidden)]
7211pub fn clamp_lms_suffixes_length_32s_omp(sa: &mut [SaSint], n: SaSint, m: SaSint, threads: SaSint) {
7212 let half_n = usize::try_from(n >> 1).expect("n must be non-negative");
7213 let omp_num_threads = if threads > 1 && n >= 131_072 {
7214 usize::try_from(threads)
7215 .expect("threads must be non-negative")
7216 .max(1)
7217 } else {
7218 1
7219 };
7220 let omp_block_stride = (half_n / omp_num_threads) & !15usize;
7221
7222 {
7223 let sa_ptr = SyncMutPtr::new(sa);
7224 run_rayon_with_threads(omp_num_threads, || {
7225 (0..omp_num_threads)
7226 .into_par_iter()
7227 .for_each(|omp_thread_num| {
7228 let omp_block_start = omp_thread_num * omp_block_stride;
7229 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
7230 omp_block_stride
7231 } else {
7232 half_n - omp_block_start
7233 };
7234 let sa = unsafe { sa_ptr.as_slice() };
7236 clamp_lms_suffixes_length_32s(
7237 sa,
7238 m,
7239 omp_block_start as FastSint,
7240 omp_block_size as FastSint,
7241 );
7242 });
7243 });
7244 }
7245}
7246
7247#[doc(hidden)]
7249pub fn renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
7250 sa: &mut [SaSint],
7251 n: SaSint,
7252 m: SaSint,
7253 threads: SaSint,
7254 thread_state: &mut [ThreadState],
7255) -> SaSint {
7256 let m_usize = usize::try_from(m).expect("m must be non-negative");
7257 let half_n = usize::try_from(n >> 1).expect("n must be non-negative");
7258 sa[m_usize..m_usize + half_n].fill(0);
7259
7260 let name = renumber_distinct_lms_suffixes_32s_4k_omp(sa, m, threads, thread_state);
7261 if name < m {
7262 mark_distinct_lms_suffixes_32s_omp(sa, n, m, threads);
7263 }
7264
7265 name
7266}
7267
7268#[doc(hidden)]
7270pub fn renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(
7271 t: &[SaSint],
7272 sa: &mut [SaSint],
7273 n: SaSint,
7274 m: SaSint,
7275 threads: SaSint,
7276) -> SaSint {
7277 let m_usize = usize::try_from(m).expect("m must be non-negative");
7278 let n_usize = usize::try_from(n).expect("n must be non-negative");
7279
7280 let _ = gather_lms_suffixes_32s(t, sa, n);
7281
7282 let zero_len = n_usize
7283 .checked_sub(m_usize)
7284 .and_then(|v| v.checked_sub(m_usize))
7285 .expect("n must be at least 2*m");
7286 sa[m_usize..m_usize + zero_len].fill(0);
7287
7288 {
7289 let prefetch_distance: FastSint = 64;
7290 let mut i = n as FastSint - m as FastSint;
7291 let mut j = n as FastSint - 1 - prefetch_distance - 3;
7292
7293 while i < j {
7294 let iu = i as usize;
7295 let s0 = (sa[iu] as SaUint >> 1) as usize;
7296 let s1 = (sa[iu + 1] as SaUint >> 1) as usize;
7297 let s2 = (sa[iu + 2] as SaUint >> 1) as usize;
7298 let s3 = (sa[iu + 3] as SaUint >> 1) as usize;
7299
7300 sa[m_usize + s0] = sa[iu + 1] - sa[iu] + 1 + SAINT_MIN;
7301 sa[m_usize + s1] = sa[iu + 2] - sa[iu + 1] + 1 + SAINT_MIN;
7302 sa[m_usize + s2] = sa[iu + 3] - sa[iu + 2] + 1 + SAINT_MIN;
7303 sa[m_usize + s3] = sa[iu + 4] - sa[iu + 3] + 1 + SAINT_MIN;
7304 i += 4;
7305 }
7306
7307 j += prefetch_distance + 3;
7308 while i < j {
7309 let iu = i as usize;
7310 let s = (sa[iu] as SaUint >> 1) as usize;
7311 sa[m_usize + s] = sa[iu + 1] - sa[iu] + 1 + SAINT_MIN;
7312 i += 1;
7313 }
7314
7315 let tail = (sa[n_usize - 1] as SaUint >> 1) as usize;
7316 sa[m_usize + tail] = 1 + SAINT_MIN;
7317 }
7318
7319 clamp_lms_suffixes_length_32s_omp(sa, n, m, threads);
7320
7321 let mut name = 1;
7322 if m_usize > 0 {
7323 let (sa_head, sam) = sa.split_at_mut(m_usize);
7324 let mut i = 1usize;
7325 let prefetch_distance = 64usize;
7326 let mut j = m_usize.saturating_sub(prefetch_distance + 1);
7327 let mut p = usize::try_from(sa_head[0]).expect("suffix index must be non-negative");
7328 let mut plen = sam[p >> 1];
7329 let mut pdiff = SAINT_MIN;
7330
7331 while i < j {
7332 let q = usize::try_from(sa_head[i]).expect("suffix index must be non-negative");
7333 let qlen = sam[q >> 1];
7334 let mut qdiff = SAINT_MIN;
7335 if plen == qlen {
7336 let mut l = 0usize;
7337 while l < qlen as usize {
7338 if t[p + l] != t[q + l] {
7339 break;
7340 }
7341 l += 1;
7342 }
7343 qdiff = ((l as SaSint) - qlen) & SAINT_MIN;
7344 }
7345 sam[p >> 1] = name | (pdiff & qdiff);
7346 name += SaSint::from(qdiff < 0);
7347
7348 p = usize::try_from(sa_head[i + 1]).expect("suffix index must be non-negative");
7349 plen = sam[p >> 1];
7350 pdiff = SAINT_MIN;
7351 if qlen == plen {
7352 let mut l = 0usize;
7353 while l < plen as usize {
7354 if t[q + l] != t[p + l] {
7355 break;
7356 }
7357 l += 1;
7358 }
7359 pdiff = ((l as SaSint) - plen) & SAINT_MIN;
7360 }
7361 sam[q >> 1] = name | (qdiff & pdiff);
7362 name += SaSint::from(pdiff < 0);
7363 i += 2;
7364 }
7365
7366 j = m_usize;
7367 while i < j {
7368 let q = usize::try_from(sa_head[i]).expect("suffix index must be non-negative");
7369 let qlen = sam[q >> 1];
7370 let mut qdiff = SAINT_MIN;
7371 if plen == qlen {
7372 let mut l = 0usize;
7373 while l < plen as usize {
7374 if t[p + l] != t[q + l] {
7375 break;
7376 }
7377 l += 1;
7378 }
7379 qdiff = ((l as SaSint) - plen) & SAINT_MIN;
7380 }
7381 sam[p >> 1] = name | (pdiff & qdiff);
7382 name += SaSint::from(qdiff < 0);
7383
7384 p = q;
7385 plen = qlen;
7386 pdiff = qdiff;
7387 i += 1;
7388 }
7389
7390 sam[p >> 1] = name | pdiff;
7391 name += 1;
7392 }
7393
7394 if name <= m {
7395 mark_distinct_lms_suffixes_32s_omp(sa, n, m, threads);
7396 }
7397
7398 name - 1
7399}
7400
7401#[doc(hidden)]
7403pub fn reconstruct_lms_suffixes(
7404 sa: &mut [SaSint],
7405 n: SaSint,
7406 m: SaSint,
7407 omp_block_start: FastSint,
7408 omp_block_size: FastSint,
7409) {
7410 if omp_block_size <= 0 {
7411 return;
7412 }
7413
7414 let prefetch_distance: FastSint = 64;
7415 let base = (n - m) as usize;
7416 let mut i = omp_block_start;
7417 let mut j = omp_block_start + omp_block_size - prefetch_distance - 3;
7418
7419 while i < j {
7420 let iu = i as usize;
7421 let s0 = sa[iu] as usize;
7422 let s1 = sa[iu + 1] as usize;
7423 let s2 = sa[iu + 2] as usize;
7424 let s3 = sa[iu + 3] as usize;
7425 sa[iu] = sa[base + s0];
7426 sa[iu + 1] = sa[base + s1];
7427 sa[iu + 2] = sa[base + s2];
7428 sa[iu + 3] = sa[base + s3];
7429 i += 4;
7430 }
7431
7432 j += prefetch_distance + 3;
7433 while i < j {
7434 let iu = i as usize;
7435 let s = sa[iu] as usize;
7436 sa[iu] = sa[base + s];
7437 i += 1;
7438 }
7439}
7440
7441#[doc(hidden)]
7443pub fn reconstruct_lms_suffixes_omp(sa: &mut [SaSint], n: SaSint, m: SaSint, threads: SaSint) {
7444 let m_usize = usize::try_from(m).expect("m must be non-negative");
7445 let omp_num_threads = if threads > 1 && m >= 65_536 {
7446 usize::try_from(threads)
7447 .expect("threads must be non-negative")
7448 .max(1)
7449 } else {
7450 1
7451 };
7452 let omp_block_stride = (m_usize / omp_num_threads) & !15usize;
7453
7454 {
7455 let sa_ptr = SyncMutPtr::new(sa);
7456 run_rayon_with_threads(omp_num_threads, || {
7457 (0..omp_num_threads)
7458 .into_par_iter()
7459 .for_each(|omp_thread_num| {
7460 let omp_block_start = omp_thread_num * omp_block_stride;
7461 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
7462 omp_block_stride
7463 } else {
7464 m_usize - omp_block_start
7465 };
7466 let sa = unsafe { sa_ptr.as_slice() };
7468 reconstruct_lms_suffixes(
7469 sa,
7470 n,
7471 m,
7472 omp_block_start as FastSint,
7473 omp_block_size as FastSint,
7474 );
7475 });
7476 });
7477 }
7478}
7479
7480#[doc(hidden)]
7482pub fn place_lms_suffixes_interval_8u(
7483 sa: &mut [SaSint],
7484 n: SaSint,
7485 mut m: SaSint,
7486 flags: SaSint,
7487 buckets: &mut [SaSint],
7488) {
7489 let bucket_end_base = 7 * ALPHABET_SIZE;
7490 if (flags & LIBSAIS_FLAGS_GSA) != 0 {
7491 buckets[bucket_end_base] -= 1;
7492 }
7493
7494 let mut j = usize::try_from(n).expect("n must be non-negative");
7495 for c in (0..ALPHABET_SIZE - 1).rev() {
7496 let l = usize::try_from(
7497 buckets[buckets_index2(c, 1) + buckets_index2(1, 0)] - buckets[buckets_index2(c, 1)],
7498 )
7499 .expect("interval length must be non-negative");
7500 if l > 0 {
7501 let i = usize::try_from(buckets[bucket_end_base + c])
7502 .expect("bucket end must be non-negative");
7503 if j > i {
7504 sa[i..j].fill(0);
7505 }
7506
7507 let new_j = i - l;
7508 let src_end = usize::try_from(m).expect("m must be non-negative");
7509 let src_start = src_end - l;
7510 sa.copy_within(src_start..src_end, new_j);
7511 m -= l as SaSint;
7512 j = new_j;
7513 }
7514 }
7515
7516 sa[..j].fill(0);
7517
7518 if (flags & LIBSAIS_FLAGS_GSA) != 0 {
7519 buckets[bucket_end_base] += 1;
7520 }
7521}
7522
7523#[doc(hidden)]
7525pub fn place_lms_suffixes_interval_32s_4k(
7526 sa: &mut [SaSint],
7527 n: SaSint,
7528 k: SaSint,
7529 mut m: SaSint,
7530 buckets: &[SaSint],
7531) {
7532 let k_usize = usize::try_from(k).expect("k must be non-negative");
7533 let bucket_end = &buckets[3 * k_usize..4 * k_usize];
7534
7535 let mut j = usize::try_from(n).expect("n must be non-negative");
7536 for c in (0..k_usize - 1).rev() {
7537 let l = usize::try_from(
7538 buckets[buckets_index2(c, 1) + buckets_index2(1, 0)] - buckets[buckets_index2(c, 1)],
7539 )
7540 .expect("interval length must be non-negative");
7541 if l > 0 {
7542 let i = usize::try_from(bucket_end[c]).expect("bucket end must be non-negative");
7543 if j > i {
7544 sa[i..j].fill(0);
7545 }
7546
7547 let new_j = i - l;
7548 let src_end = usize::try_from(m).expect("m must be non-negative");
7549 let src_start = src_end - l;
7550 sa.copy_within(src_start..src_end, new_j);
7551 m -= l as SaSint;
7552 j = new_j;
7553 }
7554 }
7555
7556 sa[..j].fill(0);
7557}
7558
7559#[doc(hidden)]
7561pub fn place_lms_suffixes_interval_32s_2k(
7562 sa: &mut [SaSint],
7563 n: SaSint,
7564 k: SaSint,
7565 mut m: SaSint,
7566 buckets: &[SaSint],
7567) {
7568 let k_usize = usize::try_from(k).expect("k must be non-negative");
7569 let mut j = usize::try_from(n).expect("n must be non-negative");
7570
7571 if k_usize > 1 {
7572 let mut c = buckets_index2(k_usize - 2, 0) as isize;
7573 while c >= buckets_index2(0, 0) as isize {
7574 let c_usize = c as usize;
7575 let l = usize::try_from(
7576 buckets[c_usize + buckets_index2(1, 1)] - buckets[c_usize + buckets_index2(0, 1)],
7577 )
7578 .expect("interval length must be non-negative");
7579 if l > 0 {
7580 let i =
7581 usize::try_from(buckets[c_usize]).expect("bucket start must be non-negative");
7582 if j > i {
7583 sa[i..j].fill(0);
7584 }
7585
7586 let new_j = i - l;
7587 let src_end = usize::try_from(m).expect("m must be non-negative");
7588 let src_start = src_end - l;
7589 sa.copy_within(src_start..src_end, new_j);
7590 m -= l as SaSint;
7591 j = new_j;
7592 }
7593 c -= buckets_index2(1, 0) as isize;
7594 }
7595 }
7596
7597 sa[..j].fill(0);
7598}
7599
7600#[doc(hidden)]
7602pub fn place_lms_suffixes_interval_32s_1k(
7603 t: &[SaSint],
7604 sa: &mut [SaSint],
7605 k: SaSint,
7606 m: SaSint,
7607 buckets: &[SaSint],
7608) {
7609 let mut c = k - 1;
7610 let c_usize = usize::try_from(c).expect("k must be positive");
7611 let mut l = usize::try_from(buckets[c_usize]).expect("bucket end must be non-negative");
7612
7613 let m_usize = usize::try_from(m).expect("m must be non-negative");
7614 for i in (0..m_usize).rev() {
7615 let p = usize::try_from(sa[i]).expect("suffix index must be non-negative");
7616 let tp = t[p];
7617 if tp != c {
7618 c = tp;
7619 let bucket = usize::try_from(c).expect("bucket index must be non-negative");
7620 let bucket_pos =
7621 usize::try_from(buckets[bucket]).expect("bucket end must be non-negative");
7622 if l > bucket_pos {
7623 sa[bucket_pos..l].fill(0);
7624 }
7625 l = bucket_pos;
7626 }
7627 l -= 1;
7628 sa[l] = p as SaSint;
7629 }
7630
7631 sa[..l].fill(0);
7632}
7633
7634#[doc(hidden)]
7636pub fn place_lms_suffixes_histogram_32s_6k(
7637 sa: &mut [SaSint],
7638 n: SaSint,
7639 k: SaSint,
7640 mut m: SaSint,
7641 buckets: &[SaSint],
7642) {
7643 let k_usize = usize::try_from(k).expect("k must be non-negative");
7644 let bucket_end = &buckets[5 * k_usize..6 * k_usize];
7645
7646 let mut j = usize::try_from(n).expect("n must be non-negative");
7647 for c in (0..k_usize - 1).rev() {
7648 let l = usize::try_from(buckets[buckets_index4(c, 1)])
7649 .expect("histogram length must be non-negative");
7650 if l > 0 {
7651 let i = usize::try_from(bucket_end[c]).expect("bucket end must be non-negative");
7652 if j > i {
7653 sa[i..j].fill(0);
7654 }
7655
7656 let new_j = i - l;
7657 let src_end = usize::try_from(m).expect("m must be non-negative");
7658 let src_start = src_end - l;
7659 sa.copy_within(src_start..src_end, new_j);
7660 m -= l as SaSint;
7661 j = new_j;
7662 }
7663 }
7664
7665 sa[..j].fill(0);
7666}
7667
7668#[doc(hidden)]
7670pub fn place_lms_suffixes_histogram_32s_4k(
7671 sa: &mut [SaSint],
7672 n: SaSint,
7673 k: SaSint,
7674 mut m: SaSint,
7675 buckets: &[SaSint],
7676) {
7677 let k_usize = usize::try_from(k).expect("k must be non-negative");
7678 let bucket_end = &buckets[3 * k_usize..4 * k_usize];
7679
7680 let mut j = usize::try_from(n).expect("n must be non-negative");
7681 for c in (0..k_usize - 1).rev() {
7682 let l = usize::try_from(buckets[buckets_index2(c, 1)])
7683 .expect("histogram length must be non-negative");
7684 if l > 0 {
7685 let i = usize::try_from(bucket_end[c]).expect("bucket end must be non-negative");
7686 if j > i {
7687 sa[i..j].fill(0);
7688 }
7689
7690 let new_j = i - l;
7691 let src_end = usize::try_from(m).expect("m must be non-negative");
7692 let src_start = src_end - l;
7693 sa.copy_within(src_start..src_end, new_j);
7694 m -= l as SaSint;
7695 j = new_j;
7696 }
7697 }
7698
7699 sa[..j].fill(0);
7700}
7701
7702#[doc(hidden)]
7704pub fn place_lms_suffixes_histogram_32s_2k(
7705 sa: &mut [SaSint],
7706 n: SaSint,
7707 k: SaSint,
7708 mut m: SaSint,
7709 buckets: &[SaSint],
7710) {
7711 let k_usize = usize::try_from(k).expect("k must be non-negative");
7712 let mut j = usize::try_from(n).expect("n must be non-negative");
7713
7714 if k_usize > 1 {
7715 let mut c = buckets_index2(k_usize - 2, 0) as isize;
7716 while c >= buckets_index2(0, 0) as isize {
7717 let c_usize = c as usize;
7718 let l = usize::try_from(buckets[c_usize + buckets_index2(0, 1)])
7719 .expect("histogram length must be non-negative");
7720 if l > 0 {
7721 let i =
7722 usize::try_from(buckets[c_usize]).expect("bucket start must be non-negative");
7723 if j > i {
7724 sa[i..j].fill(0);
7725 }
7726
7727 let new_j = i - l;
7728 let src_end = usize::try_from(m).expect("m must be non-negative");
7729 let src_start = src_end - l;
7730 sa.copy_within(src_start..src_end, new_j);
7731 m -= l as SaSint;
7732 j = new_j;
7733 }
7734 c -= buckets_index2(1, 0) as isize;
7735 }
7736 }
7737
7738 sa[..j].fill(0);
7739}
7740
7741#[doc(hidden)]
7743pub fn final_bwt_scan_left_to_right_8u(
7744 t: &[u8],
7745 sa: &mut [SaSint],
7746 induction_bucket: &mut [SaSint],
7747 omp_block_start: FastSint,
7748 omp_block_size: FastSint,
7749) {
7750 if omp_block_size <= 0 {
7751 return;
7752 }
7753
7754 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
7755 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
7756 for i in start..start + size {
7757 let mut p = sa[i];
7758 sa[i] = p & SAINT_MAX;
7759 if p > 0 {
7760 p -= 1;
7761 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
7762 sa[i] = t[p_usize] as SaSint | SAINT_MIN;
7763 let bucket = t[p_usize] as usize;
7764 let slot = usize::try_from(induction_bucket[bucket])
7765 .expect("bucket slot must be non-negative");
7766 sa[slot] = p
7767 | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
7768 << (SAINT_BIT - 1));
7769 induction_bucket[bucket] += 1;
7770 }
7771 }
7772}
7773
7774#[doc(hidden)]
7776pub fn final_bwt_aux_scan_left_to_right_8u(
7777 t: &[u8],
7778 sa: &mut [SaSint],
7779 rm: SaSint,
7780 i_out: &mut [SaSint],
7781 induction_bucket: &mut [SaSint],
7782 omp_block_start: FastSint,
7783 omp_block_size: FastSint,
7784) {
7785 if omp_block_size <= 0 {
7786 return;
7787 }
7788
7789 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
7790 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
7791 for i in start..start + size {
7792 let mut p = sa[i];
7793 sa[i] = p & SAINT_MAX;
7794 if p > 0 {
7795 p -= 1;
7796 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
7797 sa[i] = t[p_usize] as SaSint | SAINT_MIN;
7798 let bucket = t[p_usize] as usize;
7799 let slot = usize::try_from(induction_bucket[bucket])
7800 .expect("bucket slot must be non-negative");
7801 sa[slot] = p
7802 | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
7803 << (SAINT_BIT - 1));
7804 induction_bucket[bucket] += 1;
7805 if (p & rm) == 0 {
7806 let out_idx =
7807 usize::try_from(p / (rm + 1)).expect("sample index must be non-negative");
7808 i_out[out_idx] = induction_bucket[bucket];
7809 }
7810 }
7811 }
7812}
7813
7814#[doc(hidden)]
7816pub fn final_sorting_scan_left_to_right_8u(
7817 t: &[u8],
7818 sa: &mut [SaSint],
7819 induction_bucket: &mut [SaSint],
7820 omp_block_start: FastSint,
7821 omp_block_size: FastSint,
7822) {
7823 if omp_block_size <= 0 {
7824 return;
7825 }
7826
7827 let prefetch_distance = 64usize;
7828 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
7829 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
7830
7831 let mut i = start;
7832 let mut j = if size > prefetch_distance + 1 {
7833 start + size - (prefetch_distance + 1)
7834 } else {
7835 start
7836 };
7837 let sa_ptr = sa.as_ptr();
7838 let t_ptr = t.as_ptr();
7839 while i < j {
7840 libsais_prefetchw(sa_ptr.wrapping_add(i + 2 * prefetch_distance));
7841 let s0 = sa[i + prefetch_distance];
7842 let ts0 = if s0 > 0 { s0 as usize } else { 2 };
7843 libsais_prefetchr(t_ptr.wrapping_add(ts0).wrapping_sub(1));
7844 libsais_prefetchr(t_ptr.wrapping_add(ts0).wrapping_sub(2));
7845 let s1 = sa[i + prefetch_distance + 1];
7846 let ts1 = if s1 > 0 { s1 as usize } else { 2 };
7847 libsais_prefetchr(t_ptr.wrapping_add(ts1).wrapping_sub(1));
7848 libsais_prefetchr(t_ptr.wrapping_add(ts1).wrapping_sub(2));
7849
7850 let mut p0 = sa[i];
7851 sa[i] = p0 ^ SAINT_MIN;
7852 if p0 > 0 {
7853 p0 -= 1;
7854 let p0_usize = p0 as usize;
7855 let bucket0 = t[p0_usize] as usize;
7856 let slot0 = induction_bucket[bucket0] as usize;
7857 sa[slot0] = p0
7858 | ((usize::from(t[p0_usize - usize::from(p0 > 0)] < t[p0_usize]) as SaSint)
7859 << (SAINT_BIT - 1));
7860 induction_bucket[bucket0] += 1;
7861 }
7862
7863 let mut p1 = sa[i + 1];
7864 sa[i + 1] = p1 ^ SAINT_MIN;
7865 if p1 > 0 {
7866 p1 -= 1;
7867 let p1_usize = p1 as usize;
7868 let bucket1 = t[p1_usize] as usize;
7869 let slot1 = induction_bucket[bucket1] as usize;
7870 sa[slot1] = p1
7871 | ((usize::from(t[p1_usize - usize::from(p1 > 0)] < t[p1_usize]) as SaSint)
7872 << (SAINT_BIT - 1));
7873 induction_bucket[bucket1] += 1;
7874 }
7875
7876 i += 2;
7877 }
7878
7879 j = start + size;
7880 while i < j {
7881 let mut p = sa[i];
7882 sa[i] = p ^ SAINT_MIN;
7883 if p > 0 {
7884 p -= 1;
7885 let p_usize = p as usize;
7886 let bucket = t[p_usize] as usize;
7887 let slot = induction_bucket[bucket] as usize;
7888 sa[slot] = p
7889 | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
7890 << (SAINT_BIT - 1));
7891 induction_bucket[bucket] += 1;
7892 }
7893 i += 1;
7894 }
7895}
7896
7897#[doc(hidden)]
7899pub fn final_sorting_scan_left_to_right_32s(
7900 t: &[SaSint],
7901 sa: &mut [SaSint],
7902 induction_bucket: &mut [SaSint],
7903 omp_block_start: FastSint,
7904 omp_block_size: FastSint,
7905) {
7906 if omp_block_size <= 0 {
7907 return;
7908 }
7909
7910 let prefetch_distance: FastSint = 64;
7911 let mut i = omp_block_start;
7912 let mut j = omp_block_start + omp_block_size - 2 * prefetch_distance - 1;
7913
7914 let sa_ptr = sa.as_ptr();
7915 let t_ptr = t.as_ptr();
7916 let prefetch_distance_us = prefetch_distance as usize;
7917 while i < j {
7918 let i_us = i as usize;
7919 libsais_prefetchw(sa_ptr.wrapping_add(i_us + 3 * prefetch_distance_us));
7920 let s0 = sa[i_us + 2 * prefetch_distance_us];
7921 let ts0 = if s0 > 0 { s0 as usize } else { 1 };
7922 libsais_prefetchr(t_ptr.wrapping_add(ts0).wrapping_sub(1));
7923 let s1 = sa[i_us + 2 * prefetch_distance_us + 1];
7924 let ts1 = if s1 > 0 { s1 as usize } else { 1 };
7925 libsais_prefetchr(t_ptr.wrapping_add(ts1).wrapping_sub(1));
7926 let s2 = sa[i_us + prefetch_distance_us];
7927 if s2 > 0 {
7928 let s2u = s2 as usize;
7929 libsais_prefetchw(induction_bucket.as_ptr().wrapping_add(t[s2u - 1] as usize));
7930 libsais_prefetchr(t_ptr.wrapping_add(s2u).wrapping_sub(2));
7931 }
7932 let s3 = sa[i_us + prefetch_distance_us + 1];
7933 if s3 > 0 {
7934 let s3u = s3 as usize;
7935 libsais_prefetchw(induction_bucket.as_ptr().wrapping_add(t[s3u - 1] as usize));
7936 libsais_prefetchr(t_ptr.wrapping_add(s3u).wrapping_sub(2));
7937 }
7938
7939 let i0 = i as usize;
7940 let mut p0 = sa[i0];
7941 sa[i0] = p0 ^ SAINT_MIN;
7942 if p0 > 0 {
7943 p0 -= 1;
7944 let p0u = p0 as usize;
7945 let bucket0 = t[p0u] as usize;
7946 let slot0 = induction_bucket[bucket0] as usize;
7947 sa[slot0] = p0
7948 | ((usize::from(t[p0u - usize::from(p0 > 0)] < t[p0u]) as SaSint)
7949 << (SAINT_BIT - 1));
7950 induction_bucket[bucket0] += 1;
7951 }
7952
7953 let i1 = (i + 1) as usize;
7954 let mut p1 = sa[i1];
7955 sa[i1] = p1 ^ SAINT_MIN;
7956 if p1 > 0 {
7957 p1 -= 1;
7958 let p1u = p1 as usize;
7959 let bucket1 = t[p1u] as usize;
7960 let slot1 = induction_bucket[bucket1] as usize;
7961 sa[slot1] = p1
7962 | ((usize::from(t[p1u - usize::from(p1 > 0)] < t[p1u]) as SaSint)
7963 << (SAINT_BIT - 1));
7964 induction_bucket[bucket1] += 1;
7965 }
7966 i += 2;
7967 }
7968
7969 j += 2 * prefetch_distance + 1;
7970 while i < j {
7971 let iu = i as usize;
7972 let mut p = sa[iu];
7973 sa[iu] = p ^ SAINT_MIN;
7974 if p > 0 {
7975 p -= 1;
7976 let pu = p as usize;
7977 let bucket = t[pu] as usize;
7978 let slot = induction_bucket[bucket] as usize;
7979 sa[slot] = p
7980 | ((usize::from(t[pu - usize::from(p > 0)] < t[pu]) as SaSint) << (SAINT_BIT - 1));
7981 induction_bucket[bucket] += 1;
7982 }
7983 i += 1;
7984 }
7985}
7986
7987#[doc(hidden)]
7989pub fn final_bwt_scan_left_to_right_8u_block_prepare(
7990 t: &[u8],
7991 sa: &mut [SaSint],
7992 k: SaSint,
7993 buckets: &mut [SaSint],
7994 cache: &mut [ThreadCache],
7995 omp_block_start: FastSint,
7996 omp_block_size: FastSint,
7997) -> FastSint {
7998 if omp_block_size <= 0 {
7999 return 0;
8000 }
8001
8002 let k_usize = usize::try_from(k).expect("k must be non-negative");
8003 buckets[..k_usize].fill(0);
8004
8005 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
8006 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
8007 let mut count = 0usize;
8008 for i in start..start + size {
8009 let mut p = sa[i];
8010 sa[i] = p & SAINT_MAX;
8011 if p > 0 {
8012 p -= 1;
8013 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
8014 let symbol = t[p_usize] as usize;
8015 sa[i] = t[p_usize] as SaSint | SAINT_MIN;
8016 buckets[symbol] += 1;
8017 cache[count].symbol = symbol as SaSint;
8018 cache[count].index = p
8019 | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
8020 << (SAINT_BIT - 1));
8021 count += 1;
8022 }
8023 }
8024
8025 count as FastSint
8026}
8027
8028#[doc(hidden)]
8030pub fn final_sorting_scan_left_to_right_8u_block_prepare(
8031 t: &[u8],
8032 sa: &mut [SaSint],
8033 k: SaSint,
8034 buckets: &mut [SaSint],
8035 cache: &mut [ThreadCache],
8036 omp_block_start: FastSint,
8037 omp_block_size: FastSint,
8038) -> FastSint {
8039 if omp_block_size <= 0 {
8040 return 0;
8041 }
8042
8043 let k_usize = usize::try_from(k).expect("k must be non-negative");
8044 buckets[..k_usize].fill(0);
8045
8046 let prefetch_distance = 64usize;
8047 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
8048 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
8049 let mut count = 0usize;
8050 let mut i = start;
8051 let end = start + size;
8052 let unroll_end = if size > prefetch_distance + 1 {
8053 end - (prefetch_distance + 1)
8054 } else {
8055 start
8056 };
8057 let sa_ptr = sa.as_ptr();
8058 let t_ptr = t.as_ptr();
8059 while i < unroll_end {
8060 libsais_prefetchw(sa_ptr.wrapping_add(i + 2 * prefetch_distance));
8061 let s0 = sa[i + prefetch_distance];
8062 let ts0 = if s0 > 0 { s0 as usize } else { 2 };
8063 libsais_prefetchr(t_ptr.wrapping_add(ts0).wrapping_sub(1));
8064 libsais_prefetchr(t_ptr.wrapping_add(ts0).wrapping_sub(2));
8065 let s1 = sa[i + prefetch_distance + 1];
8066 let ts1 = if s1 > 0 { s1 as usize } else { 2 };
8067 libsais_prefetchr(t_ptr.wrapping_add(ts1).wrapping_sub(1));
8068 libsais_prefetchr(t_ptr.wrapping_add(ts1).wrapping_sub(2));
8069
8070 let mut p0 = sa[i];
8071 sa[i] = p0 ^ SAINT_MIN;
8072 if p0 > 0 {
8073 p0 -= 1;
8074 let p0u = p0 as usize;
8075 let symbol = t[p0u] as usize;
8076 buckets[symbol] += 1;
8077 cache[count].symbol = symbol as SaSint;
8078 cache[count].index = p0
8079 | ((usize::from(t[p0u - usize::from(p0 > 0)] < t[p0u]) as SaSint)
8080 << (SAINT_BIT - 1));
8081 count += 1;
8082 }
8083 let mut p1 = sa[i + 1];
8084 sa[i + 1] = p1 ^ SAINT_MIN;
8085 if p1 > 0 {
8086 p1 -= 1;
8087 let p1u = p1 as usize;
8088 let symbol = t[p1u] as usize;
8089 buckets[symbol] += 1;
8090 cache[count].symbol = symbol as SaSint;
8091 cache[count].index = p1
8092 | ((usize::from(t[p1u - usize::from(p1 > 0)] < t[p1u]) as SaSint)
8093 << (SAINT_BIT - 1));
8094 count += 1;
8095 }
8096 i += 2;
8097 }
8098 while i < end {
8099 let mut p = sa[i];
8100 sa[i] = p ^ SAINT_MIN;
8101 if p > 0 {
8102 p -= 1;
8103 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
8104 let symbol = t[p_usize] as usize;
8105 buckets[symbol] += 1;
8106 cache[count].symbol = symbol as SaSint;
8107 cache[count].index = p
8108 | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
8109 << (SAINT_BIT - 1));
8110 count += 1;
8111 }
8112 i += 1;
8113 }
8114
8115 count as FastSint
8116}
8117
8118#[doc(hidden)]
8120pub fn final_order_scan_left_to_right_8u_block_place(
8121 sa: &mut [SaSint],
8122 buckets: &mut [SaSint],
8123 cache: &[ThreadCache],
8124 count: FastSint,
8125) {
8126 if count <= 0 {
8127 return;
8128 }
8129
8130 let count_usize = usize::try_from(count).expect("count must be non-negative");
8131 for entry in &cache[..count_usize] {
8132 let symbol = usize::try_from(entry.symbol).expect("cache symbol must be non-negative");
8133 let slot = usize::try_from(buckets[symbol]).expect("bucket slot must be non-negative");
8134 sa[slot] = entry.index;
8135 buckets[symbol] += 1;
8136 }
8137}
8138
8139#[doc(hidden)]
8141pub fn final_bwt_aux_scan_left_to_right_8u_block_place(
8142 sa: &mut [SaSint],
8143 rm: SaSint,
8144 i_out: &mut [SaSint],
8145 buckets: &mut [SaSint],
8146 cache: &[ThreadCache],
8147 count: FastSint,
8148) {
8149 if count <= 0 {
8150 return;
8151 }
8152
8153 let count_usize = usize::try_from(count).expect("count must be non-negative");
8154 for entry in &cache[..count_usize] {
8155 let symbol = usize::try_from(entry.symbol).expect("cache symbol must be non-negative");
8156 let slot = usize::try_from(buckets[symbol]).expect("bucket slot must be non-negative");
8157 sa[slot] = entry.index;
8158 buckets[symbol] += 1;
8159 if (entry.index & rm) == 0 {
8160 let sample_index = usize::try_from((entry.index & SAINT_MAX) / (rm + 1))
8161 .expect("sample index must be non-negative");
8162 i_out[sample_index] = buckets[symbol];
8163 }
8164 }
8165}
8166
8167#[doc(hidden)]
8169pub fn final_sorting_scan_left_to_right_32s_block_gather(
8170 t: &[SaSint],
8171 sa: &mut [SaSint],
8172 cache: &mut [ThreadCache],
8173 omp_block_start: FastSint,
8174 omp_block_size: FastSint,
8175) {
8176 if omp_block_size <= 0 {
8177 return;
8178 }
8179 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
8180 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
8181 for offset in 0..size {
8182 let i = start + offset;
8183 let mut symbol = SAINT_MIN;
8184 let mut p = sa[i];
8185 sa[i] = p ^ SAINT_MIN;
8186 if p > 0 {
8187 p -= 1;
8188 let p_usize = p as usize;
8189 cache[offset].index = p
8190 | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
8191 << (SAINT_BIT - 1));
8192 symbol = t[p_usize];
8193 }
8194 cache[offset].symbol = symbol;
8195 }
8196}
8197
8198#[doc(hidden)]
8200pub fn final_sorting_scan_left_to_right_32s_block_sort(
8201 t: &[SaSint],
8202 induction_bucket: &mut [SaSint],
8203 cache: &mut [ThreadCache],
8204 omp_block_start: FastSint,
8205 omp_block_size: FastSint,
8206) {
8207 if omp_block_size <= 0 {
8208 return;
8209 }
8210 let prefetch_distance = 64usize;
8211 let start = omp_block_start as usize;
8212 let block_end = start + omp_block_size as usize;
8213 let mut i = start;
8214 let mut j = start + (omp_block_size as usize).saturating_sub(prefetch_distance + 1);
8215
8216 while i < j {
8217 let ci = i - start;
8218 let v0 = cache[ci].symbol;
8219 if v0 >= 0 {
8220 let bucket_index0 = v0 as usize;
8221 cache[ci].symbol = induction_bucket[bucket_index0];
8222 induction_bucket[bucket_index0] += 1;
8223 if cache[ci].symbol < block_end as SaSint {
8224 let ni = cache[ci].symbol as usize;
8225 let cni = ni - start;
8226 let mut np = cache[ci].index;
8227 cache[ci].index = np ^ SAINT_MIN;
8228 if np > 0 {
8229 np -= 1;
8230 let np_usize = np as usize;
8231 cache[cni].index = np
8232 | ((usize::from(t[np_usize - usize::from(np > 0)] < t[np_usize])
8233 as SaSint)
8234 << (SAINT_BIT - 1));
8235 cache[cni].symbol = t[np_usize];
8236 }
8237 }
8238 }
8239
8240 let i1 = i + 1;
8241 let ci1 = i1 - start;
8242 let v1 = cache[ci1].symbol;
8243 if v1 >= 0 {
8244 let bucket_index1 = v1 as usize;
8245 cache[ci1].symbol = induction_bucket[bucket_index1];
8246 induction_bucket[bucket_index1] += 1;
8247 if cache[ci1].symbol < block_end as SaSint {
8248 let ni = cache[ci1].symbol as usize;
8249 let cni = ni - start;
8250 let mut np = cache[ci1].index;
8251 cache[ci1].index = np ^ SAINT_MIN;
8252 if np > 0 {
8253 np -= 1;
8254 let np_usize = np as usize;
8255 cache[cni].index = np
8256 | ((usize::from(t[np_usize - usize::from(np > 0)] < t[np_usize])
8257 as SaSint)
8258 << (SAINT_BIT - 1));
8259 cache[cni].symbol = t[np_usize];
8260 }
8261 }
8262 }
8263
8264 i += 2;
8265 }
8266
8267 j = block_end;
8268 while i < j {
8269 let ci = i - start;
8270 let v = cache[ci].symbol;
8271 if v >= 0 {
8272 let bucket_index = v as usize;
8273 cache[ci].symbol = induction_bucket[bucket_index];
8274 induction_bucket[bucket_index] += 1;
8275 if cache[ci].symbol < block_end as SaSint {
8276 let ni = cache[ci].symbol as usize;
8277 let cni = ni - start;
8278 let mut np = cache[ci].index;
8279 cache[ci].index = np ^ SAINT_MIN;
8280 if np > 0 {
8281 np -= 1;
8282 let np_usize = np as usize;
8283 cache[cni].index = np
8284 | ((usize::from(t[np_usize - usize::from(np > 0)] < t[np_usize])
8285 as SaSint)
8286 << (SAINT_BIT - 1));
8287 cache[cni].symbol = t[np_usize];
8288 }
8289 }
8290 }
8291 i += 1;
8292 }
8293}
8294
8295#[doc(hidden)]
8297pub fn final_bwt_scan_left_to_right_8u_block_omp(
8298 t: &[u8],
8299 sa: &mut [SaSint],
8300 k: SaSint,
8301 induction_bucket: &mut [SaSint],
8302 block_start: FastSint,
8303 block_size: FastSint,
8304 threads: SaSint,
8305 thread_state: &mut [ThreadState],
8306) {
8307 if block_size <= 0 {
8308 return;
8309 }
8310
8311 let k_usize = usize::try_from(k).expect("k must be non-negative");
8312 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
8313 let omp_num_threads = if threads > 1 && block_size_usize >= 64 * k_usize.max(256) {
8314 usize::try_from(threads)
8315 .expect("threads must be non-negative")
8316 .min(thread_state.len())
8317 .max(1)
8318 } else {
8319 1
8320 };
8321
8322 if omp_num_threads == 1 {
8323 final_bwt_scan_left_to_right_8u(t, sa, induction_bucket, block_start, block_size);
8324 return;
8325 }
8326
8327 let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
8328 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
8329 for (thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
8330 let relative_start = thread_num * omp_block_stride;
8331 let size = if thread_num + 1 < omp_num_threads {
8332 omp_block_stride
8333 } else {
8334 block_size_usize - relative_start
8335 };
8336 state.count = final_bwt_scan_left_to_right_8u_block_prepare(
8337 t,
8338 sa,
8339 k,
8340 &mut state.buckets,
8341 &mut state.cache,
8342 (block_start_usize + relative_start) as FastSint,
8343 size as FastSint,
8344 );
8345 }
8346
8347 for state in thread_state.iter_mut().take(omp_num_threads) {
8348 for (c, bucket) in induction_bucket.iter_mut().take(k_usize).enumerate() {
8349 let a = *bucket;
8350 let b = state.buckets[c];
8351 *bucket = a + b;
8352 state.buckets[c] = a;
8353 }
8354 }
8355
8356 for state in thread_state.iter_mut().take(omp_num_threads) {
8357 final_order_scan_left_to_right_8u_block_place(
8358 sa,
8359 &mut state.buckets,
8360 &state.cache,
8361 state.count,
8362 );
8363 }
8364}
8365
8366#[doc(hidden)]
8368pub fn final_bwt_aux_scan_left_to_right_8u_block_omp(
8369 t: &[u8],
8370 sa: &mut [SaSint],
8371 k: SaSint,
8372 rm: SaSint,
8373 i_out: &mut [SaSint],
8374 induction_bucket: &mut [SaSint],
8375 block_start: FastSint,
8376 block_size: FastSint,
8377 threads: SaSint,
8378 thread_state: &mut [ThreadState],
8379) {
8380 if block_size <= 0 {
8381 return;
8382 }
8383
8384 let k_usize = usize::try_from(k).expect("k must be non-negative");
8385 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
8386 let omp_num_threads = if threads > 1 && block_size_usize >= 64 * k_usize.max(256) {
8387 usize::try_from(threads)
8388 .expect("threads must be non-negative")
8389 .min(thread_state.len())
8390 .max(1)
8391 } else {
8392 1
8393 };
8394
8395 if omp_num_threads == 1 {
8396 final_bwt_aux_scan_left_to_right_8u(
8397 t,
8398 sa,
8399 rm,
8400 i_out,
8401 induction_bucket,
8402 block_start,
8403 block_size,
8404 );
8405 return;
8406 }
8407
8408 let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
8409 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
8410 for (thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
8411 let relative_start = thread_num * omp_block_stride;
8412 let size = if thread_num + 1 < omp_num_threads {
8413 omp_block_stride
8414 } else {
8415 block_size_usize - relative_start
8416 };
8417 state.count = final_bwt_scan_left_to_right_8u_block_prepare(
8418 t,
8419 sa,
8420 k,
8421 &mut state.buckets,
8422 &mut state.cache,
8423 (block_start_usize + relative_start) as FastSint,
8424 size as FastSint,
8425 );
8426 }
8427
8428 for state in thread_state.iter_mut().take(omp_num_threads) {
8429 for (c, bucket) in induction_bucket.iter_mut().take(k_usize).enumerate() {
8430 let a = *bucket;
8431 let b = state.buckets[c];
8432 *bucket = a + b;
8433 state.buckets[c] = a;
8434 }
8435 }
8436
8437 for state in thread_state.iter_mut().take(omp_num_threads) {
8438 final_bwt_aux_scan_left_to_right_8u_block_place(
8439 sa,
8440 rm,
8441 i_out,
8442 &mut state.buckets,
8443 &state.cache,
8444 state.count,
8445 );
8446 }
8447}
8448
8449#[doc(hidden)]
8451pub fn final_sorting_scan_left_to_right_8u_block_omp(
8452 t: &[u8],
8453 sa: &mut [SaSint],
8454 k: SaSint,
8455 induction_bucket: &mut [SaSint],
8456 block_start: FastSint,
8457 block_size: FastSint,
8458 threads: SaSint,
8459 thread_state: &mut [ThreadState],
8460) {
8461 if block_size <= 0 {
8462 return;
8463 }
8464
8465 let k_usize = usize::try_from(k).expect("k must be non-negative");
8466 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
8467 let omp_num_threads = if threads > 1 && block_size_usize >= 64 * k_usize.max(256) {
8468 usize::try_from(threads)
8469 .expect("threads must be non-negative")
8470 .min(thread_state.len())
8471 .max(1)
8472 } else {
8473 1
8474 };
8475
8476 if omp_num_threads == 1 {
8477 final_sorting_scan_left_to_right_8u(t, sa, induction_bucket, block_start, block_size);
8478 return;
8479 }
8480
8481 let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
8482 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
8483 for (thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
8484 let relative_start = thread_num * omp_block_stride;
8485 let size = if thread_num + 1 < omp_num_threads {
8486 omp_block_stride
8487 } else {
8488 block_size_usize - relative_start
8489 };
8490 state.count = final_sorting_scan_left_to_right_8u_block_prepare(
8491 t,
8492 sa,
8493 k,
8494 &mut state.buckets,
8495 &mut state.cache,
8496 (block_start_usize + relative_start) as FastSint,
8497 size as FastSint,
8498 );
8499 }
8500
8501 for state in thread_state.iter_mut().take(omp_num_threads) {
8502 for (c, bucket) in induction_bucket.iter_mut().take(k_usize).enumerate() {
8503 let a = *bucket;
8504 let b = state.buckets[c];
8505 *bucket = a + b;
8506 state.buckets[c] = a;
8507 }
8508 }
8509
8510 for state in thread_state.iter_mut().take(omp_num_threads) {
8511 final_order_scan_left_to_right_8u_block_place(
8512 sa,
8513 &mut state.buckets,
8514 &state.cache,
8515 state.count,
8516 );
8517 }
8518}
8519
8520#[doc(hidden)]
8522pub fn final_sorting_scan_left_to_right_32s_block_omp(
8523 t: &[SaSint],
8524 sa: &mut [SaSint],
8525 buckets: &mut [SaSint],
8526 cache: &mut [ThreadCache],
8527 block_start: FastSint,
8528 block_size: FastSint,
8529 threads: SaSint,
8530) {
8531 if threads <= 1 || block_size < 16_384 {
8532 final_sorting_scan_left_to_right_32s(t, sa, buckets, block_start, block_size);
8533 return;
8534 }
8535
8536 final_sorting_scan_left_to_right_32s_block_gather(t, sa, cache, block_start, block_size);
8537 final_sorting_scan_left_to_right_32s_block_sort(t, buckets, cache, block_start, block_size);
8538 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
8539 let threads_usize = usize::try_from(threads.max(1)).expect("threads must be positive");
8540 let omp_num_threads = threads_usize.min(block_size_usize);
8541 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
8542 {
8543 let sa_ptr = SyncMutPtr::new(sa);
8544 let cache_ptr = SyncMutPtr::new(cache);
8545 run_rayon_with_threads(omp_num_threads, || {
8546 (0..omp_num_threads)
8547 .into_par_iter()
8548 .for_each(|omp_thread_num| {
8549 let omp_block_start = omp_thread_num * omp_block_stride;
8550 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
8551 omp_block_stride
8552 } else {
8553 block_size_usize - omp_block_start
8554 };
8555 let sa = unsafe { sa_ptr.as_slice() };
8558 let cache = unsafe { cache_ptr.as_slice() };
8559 compact_and_place_cached_suffixes(
8560 sa,
8561 cache,
8562 omp_block_start as FastSint,
8563 omp_block_size as FastSint,
8564 );
8565 });
8566 });
8567 }
8568}
8569
8570#[doc(hidden)]
8572pub fn final_bwt_scan_left_to_right_8u_omp(
8573 t: &[u8],
8574 sa: &mut [SaSint],
8575 n: FastSint,
8576 k: SaSint,
8577 induction_bucket: &mut [SaSint],
8578 threads: SaSint,
8579 thread_state: &mut [ThreadState],
8580) {
8581 let n_usize = usize::try_from(n).expect("n must be non-negative");
8582 let last = n_usize - 1;
8583 let bucket = t[last] as usize;
8584 let slot = usize::try_from(induction_bucket[bucket]).expect("bucket slot must be non-negative");
8585 sa[slot] =
8586 (n as SaSint - 1) | ((usize::from(t[last - 1] < t[last]) as SaSint) << (SAINT_BIT - 1));
8587 induction_bucket[bucket] += 1;
8588
8589 if threads == 1 || n < 65_536 {
8590 final_bwt_scan_left_to_right_8u(t, sa, induction_bucket, 0, n);
8591 return;
8592 }
8593
8594 let mut block_start = 0usize;
8595 while block_start < n_usize {
8596 if sa[block_start] == 0 {
8597 block_start += 1;
8598 } else {
8599 let threads_usize = usize::try_from(threads)
8600 .expect("threads must be non-negative")
8601 .min(thread_state.len())
8602 .max(1);
8603 let max_span = threads_usize * (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * threads_usize);
8604 let block_max_end = (block_start + max_span).min(n_usize);
8605 let mut block_end = block_start + 1;
8606 while block_end < block_max_end && sa[block_end] != 0 {
8607 block_end += 1;
8608 }
8609 let size = block_end - block_start;
8610
8611 if size < 32 {
8612 final_bwt_scan_left_to_right_8u(
8613 t,
8614 sa,
8615 induction_bucket,
8616 block_start as FastSint,
8617 size as FastSint,
8618 );
8619 } else {
8620 final_bwt_scan_left_to_right_8u_block_omp(
8621 t,
8622 sa,
8623 k,
8624 induction_bucket,
8625 block_start as FastSint,
8626 size as FastSint,
8627 threads,
8628 thread_state,
8629 );
8630 }
8631 block_start = block_end;
8632 }
8633 }
8634}
8635
8636#[doc(hidden)]
8638pub fn final_bwt_aux_scan_left_to_right_8u_omp(
8639 t: &[u8],
8640 sa: &mut [SaSint],
8641 n: FastSint,
8642 k: SaSint,
8643 rm: SaSint,
8644 i_out: &mut [SaSint],
8645 induction_bucket: &mut [SaSint],
8646 threads: SaSint,
8647 thread_state: &mut [ThreadState],
8648) {
8649 let n_usize = usize::try_from(n).expect("n must be non-negative");
8650 let last = n_usize - 1;
8651 let bucket = t[last] as usize;
8652 let slot = usize::try_from(induction_bucket[bucket]).expect("bucket slot must be non-negative");
8653 sa[slot] =
8654 (n as SaSint - 1) | ((usize::from(t[last - 1] < t[last]) as SaSint) << (SAINT_BIT - 1));
8655 induction_bucket[bucket] += 1;
8656 if (((n as SaSint) - 1) & rm) == 0 {
8657 i_out[last / usize::try_from(rm + 1).expect("rm must allow positive step")] =
8658 induction_bucket[bucket];
8659 }
8660
8661 if threads == 1 || n < 65_536 {
8662 final_bwt_aux_scan_left_to_right_8u(t, sa, rm, i_out, induction_bucket, 0, n);
8663 return;
8664 }
8665
8666 let mut block_start = 0usize;
8667 while block_start < n_usize {
8668 if sa[block_start] == 0 {
8669 block_start += 1;
8670 } else {
8671 let threads_usize = usize::try_from(threads)
8672 .expect("threads must be non-negative")
8673 .min(thread_state.len())
8674 .max(1);
8675 let max_span = threads_usize * (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * threads_usize);
8676 let block_max_end = (block_start + max_span).min(n_usize);
8677 let mut block_end = block_start + 1;
8678 while block_end < block_max_end && sa[block_end] != 0 {
8679 block_end += 1;
8680 }
8681 let size = block_end - block_start;
8682
8683 if size < 32 {
8684 final_bwt_aux_scan_left_to_right_8u(
8685 t,
8686 sa,
8687 rm,
8688 i_out,
8689 induction_bucket,
8690 block_start as FastSint,
8691 size as FastSint,
8692 );
8693 } else {
8694 final_bwt_aux_scan_left_to_right_8u_block_omp(
8695 t,
8696 sa,
8697 k,
8698 rm,
8699 i_out,
8700 induction_bucket,
8701 block_start as FastSint,
8702 size as FastSint,
8703 threads,
8704 thread_state,
8705 );
8706 }
8707 block_start = block_end;
8708 }
8709 }
8710}
8711
8712#[doc(hidden)]
8714pub fn final_sorting_scan_left_to_right_8u_omp(
8715 t: &[u8],
8716 sa: &mut [SaSint],
8717 n: FastSint,
8718 k: SaSint,
8719 induction_bucket: &mut [SaSint],
8720 threads: SaSint,
8721 thread_state: &mut [ThreadState],
8722) {
8723 let n_usize = usize::try_from(n).expect("n must be non-negative");
8724 let last = n_usize - 1;
8725 let bucket = t[last] as usize;
8726 let slot = usize::try_from(induction_bucket[bucket]).expect("bucket slot must be non-negative");
8727 sa[slot] =
8728 (n as SaSint - 1) | ((usize::from(t[last - 1] < t[last]) as SaSint) << (SAINT_BIT - 1));
8729 induction_bucket[bucket] += 1;
8730
8731 if threads == 1 || n < 65_536 {
8732 final_sorting_scan_left_to_right_8u(t, sa, induction_bucket, 0, n);
8733 return;
8734 }
8735
8736 let mut block_start = 0usize;
8737 while block_start < n_usize {
8738 if sa[block_start] == 0 {
8739 block_start += 1;
8740 } else {
8741 let threads_usize = usize::try_from(threads)
8742 .expect("threads must be non-negative")
8743 .min(thread_state.len())
8744 .max(1);
8745 let max_span = threads_usize * (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * threads_usize);
8746 let block_max_end = (block_start + max_span).min(n_usize);
8747 let mut block_end = block_start + 1;
8748 while block_end < block_max_end && sa[block_end] != 0 {
8749 block_end += 1;
8750 }
8751 let size = block_end - block_start;
8752
8753 if size < 32 {
8754 final_sorting_scan_left_to_right_8u(
8755 t,
8756 sa,
8757 induction_bucket,
8758 block_start as FastSint,
8759 size as FastSint,
8760 );
8761 } else {
8762 final_sorting_scan_left_to_right_8u_block_omp(
8763 t,
8764 sa,
8765 k,
8766 induction_bucket,
8767 block_start as FastSint,
8768 size as FastSint,
8769 threads,
8770 thread_state,
8771 );
8772 }
8773 block_start = block_end;
8774 }
8775 }
8776}
8777
8778#[doc(hidden)]
8780pub fn final_sorting_scan_left_to_right_32s_omp(
8781 t: &[SaSint],
8782 sa: &mut [SaSint],
8783 n: SaSint,
8784 induction_bucket: &mut [SaSint],
8785 threads: SaSint,
8786 thread_state: &mut [ThreadState],
8787) {
8788 let n_usize = usize::try_from(n).expect("n must be non-negative");
8789 let last = n_usize - 1;
8790 let bucket = usize::try_from(t[last]).expect("bucket symbol must be non-negative");
8791 let slot = usize::try_from(induction_bucket[bucket]).expect("bucket slot must be non-negative");
8792 sa[slot] = (n - 1) | ((usize::from(t[last - 1] < t[last]) as SaSint) << (SAINT_BIT - 1));
8793 induction_bucket[bucket] += 1;
8794
8795 if threads == 1 || n < 65_536 {
8796 final_sorting_scan_left_to_right_32s(t, sa, induction_bucket, 0, n as FastSint);
8797 return;
8798 }
8799
8800 if thread_state.is_empty() {
8801 final_sorting_scan_left_to_right_32s(t, sa, induction_bucket, 0, n as FastSint);
8802 return;
8803 }
8804
8805 let threads_usize = usize::try_from(threads)
8806 .expect("threads must be non-negative")
8807 .max(1);
8808 let mut cache = vec![ThreadCache::default(); threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE];
8809 let mut block_start = 0usize;
8810 while block_start < n_usize {
8811 let block_end = (block_start + threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE).min(n_usize);
8812 final_sorting_scan_left_to_right_32s_block_omp(
8813 t,
8814 sa,
8815 induction_bucket,
8816 &mut cache,
8817 block_start as FastSint,
8818 (block_end - block_start) as FastSint,
8819 threads,
8820 );
8821 block_start = block_end;
8822 }
8823}
8824
8825#[doc(hidden)]
8827pub fn final_bwt_scan_right_to_left_8u(
8828 t: &[u8],
8829 sa: &mut [SaSint],
8830 induction_bucket: &mut [SaSint],
8831 omp_block_start: FastSint,
8832 omp_block_size: FastSint,
8833) -> SaSint {
8834 if omp_block_size <= 0 {
8835 return -1;
8836 }
8837
8838 let mut index = -1;
8839
8840 let start =
8841 usize::try_from(omp_block_start).expect("omp_block_start must be non-negative") as FastSint;
8842 let mut i = omp_block_start + omp_block_size - 1;
8843 let mut j = start + 1;
8844 while i >= j {
8845 let i0 = usize::try_from(i).expect("loop index must be non-negative");
8846 let i1 = usize::try_from(i - 1).expect("loop index must be non-negative");
8847
8848 let mut p0 = sa[i0];
8849 if p0 == 0 {
8850 index = i0 as SaSint;
8851 }
8852 sa[i0] = p0 & SAINT_MAX;
8853 if p0 > 0 {
8854 p0 -= 1;
8855 let p0_usize = usize::try_from(p0).expect("suffix index must be non-negative");
8856 let c0 = t[p0_usize - usize::from(p0 > 0)] as SaSint;
8857 let c1 = t[p0_usize] as SaSint;
8858 sa[i0] = c1;
8859 induction_bucket[c1 as usize] -= 1;
8860 let slot = usize::try_from(induction_bucket[c1 as usize])
8861 .expect("bucket slot must be non-negative");
8862 let marked = c0 | SAINT_MIN;
8863 sa[slot] = if c0 <= c1 { p0 } else { marked };
8864 }
8865
8866 let mut p1 = sa[i1];
8867 if p1 == 0 {
8868 index = i1 as SaSint;
8869 }
8870 sa[i1] = p1 & SAINT_MAX;
8871 if p1 > 0 {
8872 p1 -= 1;
8873 let p1_usize = usize::try_from(p1).expect("suffix index must be non-negative");
8874 let c0 = t[p1_usize - usize::from(p1 > 0)] as SaSint;
8875 let c1 = t[p1_usize] as SaSint;
8876 sa[i1] = c1;
8877 induction_bucket[c1 as usize] -= 1;
8878 let slot = usize::try_from(induction_bucket[c1 as usize])
8879 .expect("bucket slot must be non-negative");
8880 let marked = c0 | SAINT_MIN;
8881 sa[slot] = if c0 <= c1 { p1 } else { marked };
8882 }
8883
8884 i -= 2;
8885 }
8886
8887 j -= 1;
8888 while i >= j {
8889 let idx = usize::try_from(i).expect("loop index must be non-negative");
8890 let mut p = sa[idx];
8891 if p == 0 {
8892 index = idx as SaSint;
8893 }
8894 sa[idx] = p & SAINT_MAX;
8895 if p > 0 {
8896 p -= 1;
8897 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
8898 let c0 = t[p_usize - usize::from(p > 0)] as SaSint;
8899 let c1 = t[p_usize] as SaSint;
8900 sa[idx] = c1;
8901 induction_bucket[c1 as usize] -= 1;
8902 let slot = usize::try_from(induction_bucket[c1 as usize])
8903 .expect("bucket slot must be non-negative");
8904 let marked = c0 | SAINT_MIN;
8905 sa[slot] = if c0 <= c1 { p } else { marked };
8906 }
8907
8908 i -= 1;
8909 }
8910
8911 index
8912}
8913
8914#[doc(hidden)]
8916pub fn final_bwt_aux_scan_right_to_left_8u(
8917 t: &[u8],
8918 sa: &mut [SaSint],
8919 rm: SaSint,
8920 i_out: &mut [SaSint],
8921 induction_bucket: &mut [SaSint],
8922 omp_block_start: FastSint,
8923 omp_block_size: FastSint,
8924) {
8925 if omp_block_size <= 0 {
8926 return;
8927 }
8928
8929 let start =
8930 usize::try_from(omp_block_start).expect("omp_block_start must be non-negative") as FastSint;
8931 let mut i = omp_block_start + omp_block_size - 1;
8932 let mut j = start + 1;
8933 while i >= j {
8934 let i0 = usize::try_from(i).expect("loop index must be non-negative");
8935 let i1 = usize::try_from(i - 1).expect("loop index must be non-negative");
8936
8937 let mut p0 = sa[i0];
8938 sa[i0] = p0 & SAINT_MAX;
8939 if p0 > 0 {
8940 p0 -= 1;
8941 let p0_usize = usize::try_from(p0).expect("suffix index must be non-negative");
8942 let c0 = t[p0_usize - usize::from(p0 > 0)] as SaSint;
8943 let c1 = t[p0_usize] as SaSint;
8944 sa[i0] = c1;
8945 induction_bucket[c1 as usize] -= 1;
8946 let slot = usize::try_from(induction_bucket[c1 as usize])
8947 .expect("bucket slot must be non-negative");
8948 let marked = c0 | SAINT_MIN;
8949 sa[slot] = if c0 <= c1 { p0 } else { marked };
8950 if (p0 & rm) == 0 {
8951 let out_idx =
8952 usize::try_from(p0 / (rm + 1)).expect("sample index must be non-negative");
8953 i_out[out_idx] = induction_bucket[t[p0_usize] as usize] + 1;
8954 }
8955 }
8956
8957 let mut p1 = sa[i1];
8958 sa[i1] = p1 & SAINT_MAX;
8959 if p1 > 0 {
8960 p1 -= 1;
8961 let p1_usize = usize::try_from(p1).expect("suffix index must be non-negative");
8962 let c0 = t[p1_usize - usize::from(p1 > 0)] as SaSint;
8963 let c1 = t[p1_usize] as SaSint;
8964 sa[i1] = c1;
8965 induction_bucket[c1 as usize] -= 1;
8966 let slot = usize::try_from(induction_bucket[c1 as usize])
8967 .expect("bucket slot must be non-negative");
8968 let marked = c0 | SAINT_MIN;
8969 sa[slot] = if c0 <= c1 { p1 } else { marked };
8970 if (p1 & rm) == 0 {
8971 let out_idx =
8972 usize::try_from(p1 / (rm + 1)).expect("sample index must be non-negative");
8973 i_out[out_idx] = induction_bucket[t[p1_usize] as usize] + 1;
8974 }
8975 }
8976
8977 i -= 2;
8978 }
8979
8980 j -= 1;
8981 while i >= j {
8982 let idx = usize::try_from(i).expect("loop index must be non-negative");
8983 let mut p = sa[idx];
8984 sa[idx] = p & SAINT_MAX;
8985 if p > 0 {
8986 p -= 1;
8987 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
8988 let c0 = t[p_usize - usize::from(p > 0)] as SaSint;
8989 let c1 = t[p_usize] as SaSint;
8990 sa[idx] = c1;
8991 induction_bucket[c1 as usize] -= 1;
8992 let slot = usize::try_from(induction_bucket[c1 as usize])
8993 .expect("bucket slot must be non-negative");
8994 let marked = c0 | SAINT_MIN;
8995 sa[slot] = if c0 <= c1 { p } else { marked };
8996 if (p & rm) == 0 {
8997 let out_idx =
8998 usize::try_from(p / (rm + 1)).expect("sample index must be non-negative");
8999 i_out[out_idx] = induction_bucket[t[p_usize] as usize] + 1;
9000 }
9001 }
9002
9003 i -= 1;
9004 }
9005}
9006
9007#[doc(hidden)]
9009pub fn final_sorting_scan_right_to_left_8u(
9010 t: &[u8],
9011 sa: &mut [SaSint],
9012 induction_bucket: &mut [SaSint],
9013 omp_block_start: FastSint,
9014 omp_block_size: FastSint,
9015) {
9016 if omp_block_size <= 0 {
9017 return;
9018 }
9019
9020 let prefetch_distance = 64usize;
9021 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
9022 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
9023 let mut i = start + size - 1;
9024 let mut j = start + prefetch_distance + 1;
9025
9026 let sa_ptr = sa.as_ptr();
9027 let t_ptr = t.as_ptr();
9028 while i >= j {
9029 libsais_prefetchw(sa_ptr.wrapping_add(i.wrapping_sub(2 * prefetch_distance)));
9030 let s0 = sa[i - prefetch_distance];
9031 let ts0 = if s0 > 0 { s0 as usize } else { 2 };
9032 libsais_prefetchr(t_ptr.wrapping_add(ts0).wrapping_sub(1));
9033 libsais_prefetchr(t_ptr.wrapping_add(ts0).wrapping_sub(2));
9034 let s1 = sa[i - prefetch_distance - 1];
9035 let ts1 = if s1 > 0 { s1 as usize } else { 2 };
9036 libsais_prefetchr(t_ptr.wrapping_add(ts1).wrapping_sub(1));
9037 libsais_prefetchr(t_ptr.wrapping_add(ts1).wrapping_sub(2));
9038
9039 let mut p0 = sa[i];
9040 sa[i] = p0 & SAINT_MAX;
9041 if p0 > 0 {
9042 p0 -= 1;
9043 let p0_usize = p0 as usize;
9044 let bucket0 = t[p0_usize] as usize;
9045 induction_bucket[bucket0] -= 1;
9046 let slot0 = induction_bucket[bucket0] as usize;
9047 sa[slot0] = p0
9048 | ((usize::from(t[p0_usize - usize::from(p0 > 0)] > t[p0_usize]) as SaSint)
9049 << (SAINT_BIT - 1));
9050 }
9051
9052 let mut p1 = sa[i - 1];
9053 sa[i - 1] = p1 & SAINT_MAX;
9054 if p1 > 0 {
9055 p1 -= 1;
9056 let p1_usize = p1 as usize;
9057 let bucket1 = t[p1_usize] as usize;
9058 induction_bucket[bucket1] -= 1;
9059 let slot1 = induction_bucket[bucket1] as usize;
9060 sa[slot1] = p1
9061 | ((usize::from(t[p1_usize - usize::from(p1 > 0)] > t[p1_usize]) as SaSint)
9062 << (SAINT_BIT - 1));
9063 }
9064
9065 i -= 2;
9066 }
9067
9068 j -= prefetch_distance + 1;
9069 while i >= j {
9070 let mut p = sa[i];
9071 sa[i] = p & SAINT_MAX;
9072 if p > 0 {
9073 p -= 1;
9074 let p_usize = p as usize;
9075 let bucket = t[p_usize] as usize;
9076 induction_bucket[bucket] -= 1;
9077 let slot = induction_bucket[bucket] as usize;
9078 sa[slot] = p
9079 | ((usize::from(t[p_usize - usize::from(p > 0)] > t[p_usize]) as SaSint)
9080 << (SAINT_BIT - 1));
9081 }
9082
9083 if i == 0 {
9084 break;
9085 }
9086 i -= 1;
9087 }
9088}
9089
9090#[doc(hidden)]
9092pub fn final_gsa_scan_right_to_left_8u(
9093 t: &[u8],
9094 sa: &mut [SaSint],
9095 induction_bucket: &mut [SaSint],
9096 omp_block_start: FastSint,
9097 omp_block_size: FastSint,
9098) {
9099 if omp_block_size <= 0 {
9100 return;
9101 }
9102
9103 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
9104 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
9105 let mut i = start + size;
9106 while i > start {
9107 i -= 1;
9108 let mut p = sa[i];
9109 sa[i] = p & SAINT_MAX;
9110 if p > 0 {
9111 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
9112 if t[p_usize - 1] > 0 {
9113 p -= 1;
9114 let bucket =
9115 t[usize::try_from(p).expect("suffix index must be non-negative")] as usize;
9116 induction_bucket[bucket] -= 1;
9117 let slot = usize::try_from(induction_bucket[bucket])
9118 .expect("bucket slot must be non-negative");
9119 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
9120 sa[slot] = p
9121 | ((usize::from(t[p_usize - usize::from(p > 0)] > t[p_usize]) as SaSint)
9122 << (SAINT_BIT - 1));
9123 }
9124 }
9125 }
9126}
9127
9128#[doc(hidden)]
9130pub fn final_sorting_scan_right_to_left_32s(
9131 t: &[SaSint],
9132 sa: &mut [SaSint],
9133 induction_bucket: &mut [SaSint],
9134 omp_block_start: FastSint,
9135 omp_block_size: FastSint,
9136) {
9137 if omp_block_size <= 0 {
9138 return;
9139 }
9140
9141 let prefetch_distance: FastSint = 64;
9142 let mut i = omp_block_start + omp_block_size - 1;
9143 let mut j = omp_block_start + 2 * prefetch_distance + 1;
9144
9145 let sa_ptr = sa.as_ptr();
9146 let t_ptr = t.as_ptr();
9147 let prefetch_distance_us = prefetch_distance as usize;
9148 while i >= j {
9149 let i_us = i as usize;
9150 libsais_prefetchw(sa_ptr.wrapping_add(i_us.wrapping_sub(3 * prefetch_distance_us)));
9151 let s0 = sa[i_us - 2 * prefetch_distance_us];
9152 let ts0 = if s0 > 0 { s0 as usize } else { 1 };
9153 libsais_prefetchr(t_ptr.wrapping_add(ts0).wrapping_sub(1));
9154 let s1 = sa[i_us - 2 * prefetch_distance_us - 1];
9155 let ts1 = if s1 > 0 { s1 as usize } else { 1 };
9156 libsais_prefetchr(t_ptr.wrapping_add(ts1).wrapping_sub(1));
9157 let s2 = sa[i_us - prefetch_distance_us];
9158 if s2 > 0 {
9159 let s2u = s2 as usize;
9160 libsais_prefetchw(induction_bucket.as_ptr().wrapping_add(t[s2u - 1] as usize));
9161 libsais_prefetchr(t_ptr.wrapping_add(s2u).wrapping_sub(2));
9162 }
9163 let s3 = sa[i_us - prefetch_distance_us - 1];
9164 if s3 > 0 {
9165 let s3u = s3 as usize;
9166 libsais_prefetchw(induction_bucket.as_ptr().wrapping_add(t[s3u - 1] as usize));
9167 libsais_prefetchr(t_ptr.wrapping_add(s3u).wrapping_sub(2));
9168 }
9169
9170 let i0 = i as usize;
9171 let mut p0 = sa[i0];
9172 sa[i0] = p0 & SAINT_MAX;
9173 if p0 > 0 {
9174 p0 -= 1;
9175 let p0u = p0 as usize;
9176 let bucket0 = t[p0u] as usize;
9177 induction_bucket[bucket0] -= 1;
9178 let slot0 = induction_bucket[bucket0] as usize;
9179 sa[slot0] = p0
9180 | ((usize::from(t[p0u - usize::from(p0 > 0)] > t[p0u]) as SaSint)
9181 << (SAINT_BIT - 1));
9182 }
9183
9184 let i1 = (i - 1) as usize;
9185 let mut p1 = sa[i1];
9186 sa[i1] = p1 & SAINT_MAX;
9187 if p1 > 0 {
9188 p1 -= 1;
9189 let p1u = p1 as usize;
9190 let bucket1 = t[p1u] as usize;
9191 induction_bucket[bucket1] -= 1;
9192 let slot1 = induction_bucket[bucket1] as usize;
9193 sa[slot1] = p1
9194 | ((usize::from(t[p1u - usize::from(p1 > 0)] > t[p1u]) as SaSint)
9195 << (SAINT_BIT - 1));
9196 }
9197 i -= 2;
9198 }
9199
9200 j -= 2 * prefetch_distance + 1;
9201 while i >= j {
9202 let iu = i as usize;
9203 let mut p = sa[iu];
9204 sa[iu] = p & SAINT_MAX;
9205 if p > 0 {
9206 p -= 1;
9207 let pu = p as usize;
9208 let bucket = t[pu] as usize;
9209 induction_bucket[bucket] -= 1;
9210 let slot = induction_bucket[bucket] as usize;
9211 sa[slot] = p
9212 | ((usize::from(t[pu - usize::from(p > 0)] > t[pu]) as SaSint) << (SAINT_BIT - 1));
9213 }
9214 i -= 1;
9215 }
9216}
9217
9218#[doc(hidden)]
9220pub fn final_bwt_scan_right_to_left_8u_block_prepare(
9221 t: &[u8],
9222 sa: &mut [SaSint],
9223 k: SaSint,
9224 buckets: &mut [SaSint],
9225 cache: &mut [ThreadCache],
9226 omp_block_start: FastSint,
9227 omp_block_size: FastSint,
9228) -> FastSint {
9229 if omp_block_size <= 0 {
9230 return 0;
9231 }
9232 let k_usize = usize::try_from(k).expect("k must be non-negative");
9233 buckets[..k_usize].fill(0);
9234 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
9235 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
9236 let mut count = 0usize;
9237 let mut i = start + size;
9238 while i > start {
9239 i -= 1;
9240 let mut p = sa[i];
9241 sa[i] = p & SAINT_MAX;
9242 if p > 0 {
9243 p -= 1;
9244 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
9245 let c0 = t[p_usize - usize::from(p > 0)] as SaSint;
9246 let c1 = t[p_usize] as SaSint;
9247 sa[i] = c1;
9248 buckets[c1 as usize] += 1;
9249 cache[count].symbol = c1;
9250 cache[count].index = if c0 <= c1 { p } else { c0 | SAINT_MIN };
9251 count += 1;
9252 }
9253 }
9254 count as FastSint
9255}
9256
9257#[doc(hidden)]
9259pub fn final_bwt_aux_scan_right_to_left_8u_block_prepare(
9260 t: &[u8],
9261 sa: &mut [SaSint],
9262 k: SaSint,
9263 buckets: &mut [SaSint],
9264 cache: &mut [ThreadCache],
9265 omp_block_start: FastSint,
9266 omp_block_size: FastSint,
9267) -> FastSint {
9268 if omp_block_size <= 0 {
9269 return 0;
9270 }
9271 let k_usize = usize::try_from(k).expect("k must be non-negative");
9272 buckets[..k_usize].fill(0);
9273 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
9274 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
9275 let mut count = 0usize;
9276 let mut i = start + size;
9277 while i > start {
9278 i -= 1;
9279 let mut p = sa[i];
9280 sa[i] = p & SAINT_MAX;
9281 if p > 0 {
9282 p -= 1;
9283 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
9284 let c0 = t[p_usize - usize::from(p > 0)] as SaSint;
9285 let c1 = t[p_usize] as SaSint;
9286 sa[i] = c1;
9287 buckets[c1 as usize] += 1;
9288 cache[count].symbol = c1;
9289 cache[count].index = if c0 <= c1 { p } else { c0 | SAINT_MIN };
9290 cache[count + 1].index = p;
9291 count += 2;
9292 }
9293 }
9294 count as FastSint
9295}
9296
9297#[doc(hidden)]
9299pub fn final_sorting_scan_right_to_left_8u_block_prepare(
9300 t: &[u8],
9301 sa: &mut [SaSint],
9302 k: SaSint,
9303 buckets: &mut [SaSint],
9304 cache: &mut [ThreadCache],
9305 omp_block_start: FastSint,
9306 omp_block_size: FastSint,
9307) -> FastSint {
9308 if omp_block_size <= 0 {
9309 return 0;
9310 }
9311
9312 let k_usize = usize::try_from(k).expect("k must be non-negative");
9313 buckets[..k_usize].fill(0);
9314
9315 let prefetch_distance = 64usize;
9316 let start =
9317 usize::try_from(omp_block_start).expect("omp_block_start must be non-negative") as FastSint;
9318 let start_us = start as usize;
9319 let mut i = omp_block_start + omp_block_size - 1;
9320 let mut count = 0usize;
9321
9322 let sa_ptr = sa.as_ptr();
9323 let t_ptr = t.as_ptr();
9324 let j_pf = (start_us + prefetch_distance + 1) as FastSint;
9325 while i >= j_pf {
9326 let i0 = i as usize;
9327 let i1 = (i - 1) as usize;
9328
9329 libsais_prefetchw(sa_ptr.wrapping_add(i0.wrapping_sub(2 * prefetch_distance)));
9330 let s0 = sa[i0 - prefetch_distance];
9331 let ts0 = if s0 > 0 { s0 as usize } else { 2 };
9332 libsais_prefetchr(t_ptr.wrapping_add(ts0).wrapping_sub(1));
9333 libsais_prefetchr(t_ptr.wrapping_add(ts0).wrapping_sub(2));
9334 let s1 = sa[i0 - prefetch_distance - 1];
9335 let ts1 = if s1 > 0 { s1 as usize } else { 2 };
9336 libsais_prefetchr(t_ptr.wrapping_add(ts1).wrapping_sub(1));
9337 libsais_prefetchr(t_ptr.wrapping_add(ts1).wrapping_sub(2));
9338
9339 let mut p0 = sa[i0];
9340 sa[i0] = p0 & SAINT_MAX;
9341 if p0 > 0 {
9342 p0 -= 1;
9343 let p0_usize = p0 as usize;
9344 let c0 = t[p0_usize] as SaSint;
9345 buckets[c0 as usize] += 1;
9346 cache[count].symbol = c0;
9347 cache[count].index = p0
9348 | ((usize::from(t[p0_usize - usize::from(p0 > 0)] > t[p0_usize]) as SaSint)
9349 << (SAINT_BIT - 1));
9350 count += 1;
9351 }
9352
9353 let mut p1 = sa[i1];
9354 sa[i1] = p1 & SAINT_MAX;
9355 if p1 > 0 {
9356 p1 -= 1;
9357 let p1_usize = p1 as usize;
9358 let c1 = t[p1_usize] as SaSint;
9359 buckets[c1 as usize] += 1;
9360 cache[count].symbol = c1;
9361 cache[count].index = p1
9362 | ((usize::from(t[p1_usize - usize::from(p1 > 0)] > t[p1_usize]) as SaSint)
9363 << (SAINT_BIT - 1));
9364 count += 1;
9365 }
9366
9367 i -= 2;
9368 }
9369
9370 while i >= start {
9371 let idx = i as usize;
9372 let mut p = sa[idx];
9373 sa[idx] = p & SAINT_MAX;
9374 if p > 0 {
9375 p -= 1;
9376 let p_usize = p as usize;
9377 let c = t[p_usize] as SaSint;
9378 buckets[c as usize] += 1;
9379 cache[count].symbol = c;
9380 cache[count].index = p
9381 | ((usize::from(t[p_usize - usize::from(p > 0)] > t[p_usize]) as SaSint)
9382 << (SAINT_BIT - 1));
9383 count += 1;
9384 }
9385
9386 if i == 0 {
9387 break;
9388 }
9389 i -= 1;
9390 }
9391
9392 count as FastSint
9393}
9394
9395#[doc(hidden)]
9397pub fn final_order_scan_right_to_left_8u_block_place(
9398 sa: &mut [SaSint],
9399 buckets: &mut [SaSint],
9400 cache: &[ThreadCache],
9401 count: FastSint,
9402) {
9403 if count <= 0 {
9404 return;
9405 }
9406 let count_usize = usize::try_from(count).expect("count must be non-negative");
9407 for entry in &cache[..count_usize] {
9408 let symbol = usize::try_from(entry.symbol).expect("cache symbol must be non-negative");
9409 buckets[symbol] -= 1;
9410 let slot = usize::try_from(buckets[symbol]).expect("bucket slot must be non-negative");
9411 sa[slot] = entry.index;
9412 }
9413}
9414
9415#[doc(hidden)]
9417pub fn final_gsa_scan_right_to_left_8u_block_place(
9418 sa: &mut [SaSint],
9419 buckets: &mut [SaSint],
9420 cache: &[ThreadCache],
9421 count: FastSint,
9422) {
9423 if count <= 0 {
9424 return;
9425 }
9426 let count_usize = usize::try_from(count).expect("count must be non-negative");
9427 for entry in &cache[..count_usize] {
9428 if entry.symbol > 0 {
9429 let symbol = usize::try_from(entry.symbol).expect("cache symbol must be non-negative");
9430 buckets[symbol] -= 1;
9431 let slot = usize::try_from(buckets[symbol]).expect("bucket slot must be non-negative");
9432 sa[slot] = entry.index;
9433 }
9434 }
9435}
9436
9437#[doc(hidden)]
9439pub fn final_bwt_aux_scan_right_to_left_8u_block_place(
9440 sa: &mut [SaSint],
9441 rm: SaSint,
9442 i_out: &mut [SaSint],
9443 buckets: &mut [SaSint],
9444 cache: &[ThreadCache],
9445 count: FastSint,
9446) {
9447 if count <= 0 {
9448 return;
9449 }
9450 let count_usize = usize::try_from(count).expect("count must be non-negative");
9451 let mut i = 0usize;
9452 while i < count_usize {
9453 let symbol = usize::try_from(cache[i].symbol).expect("cache symbol must be non-negative");
9454 buckets[symbol] -= 1;
9455 let slot = usize::try_from(buckets[symbol]).expect("bucket slot must be non-negative");
9456 sa[slot] = cache[i].index;
9457 if (cache[i + 1].index & rm) == 0 {
9458 let sample_index = usize::try_from((cache[i + 1].index & SAINT_MAX) / (rm + 1))
9459 .expect("sample index must be non-negative");
9460 i_out[sample_index] = buckets[symbol] + 1;
9461 }
9462 i += 2;
9463 }
9464}
9465
9466#[doc(hidden)]
9468pub fn final_sorting_scan_right_to_left_32s_block_gather(
9469 t: &[SaSint],
9470 sa: &mut [SaSint],
9471 cache: &mut [ThreadCache],
9472 omp_block_start: FastSint,
9473 omp_block_size: FastSint,
9474) {
9475 if omp_block_size <= 0 {
9476 return;
9477 }
9478 let prefetch_distance = 64usize;
9479 let start = omp_block_start as usize;
9480 let block_end = start + omp_block_size as usize;
9481 let mut i = start;
9482 let mut j = block_end.saturating_sub(prefetch_distance + 1);
9483
9484 while i < j {
9485 let ci = i - start;
9486 let mut symbol0 = SAINT_MIN;
9487 let mut p0 = sa[i];
9488 sa[i] = p0 & SAINT_MAX;
9489 if p0 > 0 {
9490 p0 -= 1;
9491 let p0_usize = p0 as usize;
9492 cache[ci].index = p0
9493 | ((usize::from(t[p0_usize - usize::from(p0 > 0)] > t[p0_usize]) as SaSint)
9494 << (SAINT_BIT - 1));
9495 symbol0 = t[p0_usize];
9496 }
9497 cache[ci].symbol = symbol0;
9498
9499 let i1 = i + 1;
9500 let ci1 = i1 - start;
9501 let mut symbol1 = SAINT_MIN;
9502 let mut p1 = sa[i1];
9503 sa[i1] = p1 & SAINT_MAX;
9504 if p1 > 0 {
9505 p1 -= 1;
9506 let p1_usize = p1 as usize;
9507 cache[ci1].index = p1
9508 | ((usize::from(t[p1_usize - usize::from(p1 > 0)] > t[p1_usize]) as SaSint)
9509 << (SAINT_BIT - 1));
9510 symbol1 = t[p1_usize];
9511 }
9512 cache[ci1].symbol = symbol1;
9513
9514 i += 2;
9515 }
9516
9517 j = block_end;
9518 while i < j {
9519 let ci = i - start;
9520 let mut symbol = SAINT_MIN;
9521 let mut p = sa[i];
9522 sa[i] = p & SAINT_MAX;
9523 if p > 0 {
9524 p -= 1;
9525 let p_usize = p as usize;
9526 cache[ci].index = p
9527 | ((usize::from(t[p_usize - usize::from(p > 0)] > t[p_usize]) as SaSint)
9528 << (SAINT_BIT - 1));
9529 symbol = t[p_usize];
9530 }
9531 cache[ci].symbol = symbol;
9532 i += 1;
9533 }
9534}
9535
9536#[doc(hidden)]
9538pub fn final_sorting_scan_right_to_left_32s_block_sort(
9539 t: &[SaSint],
9540 induction_bucket: &mut [SaSint],
9541 cache: &mut [ThreadCache],
9542 omp_block_start: FastSint,
9543 omp_block_size: FastSint,
9544) {
9545 if omp_block_size <= 0 {
9546 return;
9547 }
9548 let prefetch_distance = 64usize;
9549 let start = omp_block_start as usize;
9550 let mut i = start + omp_block_size as usize - 1;
9551 let mut j = start + prefetch_distance + 1;
9552
9553 while i >= j {
9554 let ci = i - start;
9555 let v0 = cache[ci].symbol;
9556 if v0 >= 0 {
9557 let bucket_index0 = v0 as usize;
9558 induction_bucket[bucket_index0] -= 1;
9559 cache[ci].symbol = induction_bucket[bucket_index0];
9560 if cache[ci].symbol >= omp_block_start as SaSint {
9561 let ni = cache[ci].symbol as usize;
9562 let cni = ni - start;
9563 let mut np = cache[ci].index;
9564 cache[ci].index = np & SAINT_MAX;
9565 if np > 0 {
9566 np -= 1;
9567 let np_usize = np as usize;
9568 cache[cni].index = np
9569 | ((usize::from(t[np_usize - usize::from(np > 0)] > t[np_usize])
9570 as SaSint)
9571 << (SAINT_BIT - 1));
9572 cache[cni].symbol = t[np_usize];
9573 }
9574 }
9575 }
9576
9577 let i1 = i - 1;
9578 let ci1 = i1 - start;
9579 let v1 = cache[ci1].symbol;
9580 if v1 >= 0 {
9581 let bucket_index1 = v1 as usize;
9582 induction_bucket[bucket_index1] -= 1;
9583 cache[ci1].symbol = induction_bucket[bucket_index1];
9584 if cache[ci1].symbol >= omp_block_start as SaSint {
9585 let ni = cache[ci1].symbol as usize;
9586 let cni = ni - start;
9587 let mut np = cache[ci1].index;
9588 cache[ci1].index = np & SAINT_MAX;
9589 if np > 0 {
9590 np -= 1;
9591 let np_usize = np as usize;
9592 cache[cni].index = np
9593 | ((usize::from(t[np_usize - usize::from(np > 0)] > t[np_usize])
9594 as SaSint)
9595 << (SAINT_BIT - 1));
9596 cache[cni].symbol = t[np_usize];
9597 }
9598 }
9599 }
9600
9601 i -= 2;
9602 }
9603
9604 j -= prefetch_distance + 1;
9605 while i >= j {
9606 let ci = i - start;
9607 let v = cache[ci].symbol;
9608 if v >= 0 {
9609 let bucket_index = v as usize;
9610 induction_bucket[bucket_index] -= 1;
9611 cache[ci].symbol = induction_bucket[bucket_index];
9612 if cache[ci].symbol >= omp_block_start as SaSint {
9613 let ni = cache[ci].symbol as usize;
9614 let cni = ni - start;
9615 let mut np = cache[ci].index;
9616 cache[ci].index = np & SAINT_MAX;
9617 if np > 0 {
9618 np -= 1;
9619 let np_usize = np as usize;
9620 cache[cni].index = np
9621 | ((usize::from(t[np_usize - usize::from(np > 0)] > t[np_usize])
9622 as SaSint)
9623 << (SAINT_BIT - 1));
9624 cache[cni].symbol = t[np_usize];
9625 }
9626 }
9627 }
9628
9629 if i == 0 {
9630 break;
9631 }
9632 i -= 1;
9633 }
9634}
9635
9636#[doc(hidden)]
9638pub fn final_bwt_scan_right_to_left_8u_block_omp(
9639 t: &[u8],
9640 sa: &mut [SaSint],
9641 k: SaSint,
9642 induction_bucket: &mut [SaSint],
9643 block_start: FastSint,
9644 block_size: FastSint,
9645 threads: SaSint,
9646 thread_state: &mut [ThreadState],
9647) {
9648 if block_size <= 0 {
9649 return;
9650 }
9651 let k_usize = usize::try_from(k).expect("k must be non-negative");
9652 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
9653 let threads_usize = usize::try_from(threads.max(1)).expect("threads must be positive");
9654 let omp_num_threads = threads_usize.min(thread_state.len()).min(block_size_usize);
9655 if omp_num_threads <= 1 || block_size < 64 * k.max(256) as FastSint {
9656 let _ = final_bwt_scan_right_to_left_8u(t, sa, induction_bucket, block_start, block_size);
9657 return;
9658 }
9659
9660 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
9661 for (omp_thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
9662 let omp_block_start = omp_thread_num * omp_block_stride;
9663 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
9664 omp_block_stride
9665 } else {
9666 block_size_usize - omp_block_start
9667 };
9668 state.count = final_bwt_scan_right_to_left_8u_block_prepare(
9669 t,
9670 sa,
9671 k,
9672 &mut state.buckets,
9673 &mut state.cache,
9674 block_start + omp_block_start as FastSint,
9675 omp_block_size as FastSint,
9676 );
9677 }
9678 for state in thread_state.iter_mut().take(omp_num_threads).rev() {
9679 for c in 0..k_usize {
9680 let a = induction_bucket[c];
9681 let b = state.buckets[c];
9682 induction_bucket[c] = a - b;
9683 state.buckets[c] = a;
9684 }
9685 }
9686 for state in thread_state.iter_mut().take(omp_num_threads) {
9687 final_order_scan_right_to_left_8u_block_place(
9688 sa,
9689 &mut state.buckets,
9690 &state.cache,
9691 state.count,
9692 );
9693 }
9694}
9695
9696#[doc(hidden)]
9698pub fn final_bwt_aux_scan_right_to_left_8u_block_omp(
9699 t: &[u8],
9700 sa: &mut [SaSint],
9701 k: SaSint,
9702 rm: SaSint,
9703 i_out: &mut [SaSint],
9704 induction_bucket: &mut [SaSint],
9705 block_start: FastSint,
9706 block_size: FastSint,
9707 threads: SaSint,
9708 thread_state: &mut [ThreadState],
9709) {
9710 if block_size <= 0 {
9711 return;
9712 }
9713 let k_usize = usize::try_from(k).expect("k must be non-negative");
9714 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
9715 let threads_usize = usize::try_from(threads.max(1)).expect("threads must be positive");
9716 let omp_num_threads = threads_usize.min(thread_state.len()).min(block_size_usize);
9717 if omp_num_threads <= 1 || block_size < 64 * k.max(256) as FastSint {
9718 final_bwt_aux_scan_right_to_left_8u(
9719 t,
9720 sa,
9721 rm,
9722 i_out,
9723 induction_bucket,
9724 block_start,
9725 block_size,
9726 );
9727 return;
9728 }
9729
9730 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
9731 for (omp_thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
9732 let omp_block_start = omp_thread_num * omp_block_stride;
9733 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
9734 omp_block_stride
9735 } else {
9736 block_size_usize - omp_block_start
9737 };
9738 state.count = final_bwt_aux_scan_right_to_left_8u_block_prepare(
9739 t,
9740 sa,
9741 k,
9742 &mut state.buckets,
9743 &mut state.cache,
9744 block_start + omp_block_start as FastSint,
9745 omp_block_size as FastSint,
9746 );
9747 }
9748 for state in thread_state.iter_mut().take(omp_num_threads).rev() {
9749 for c in 0..k_usize {
9750 let a = induction_bucket[c];
9751 let b = state.buckets[c];
9752 induction_bucket[c] = a - b;
9753 state.buckets[c] = a;
9754 }
9755 }
9756 for state in thread_state.iter_mut().take(omp_num_threads) {
9757 final_bwt_aux_scan_right_to_left_8u_block_place(
9758 sa,
9759 rm,
9760 i_out,
9761 &mut state.buckets,
9762 &state.cache,
9763 state.count,
9764 );
9765 }
9766}
9767
9768#[doc(hidden)]
9770pub fn final_sorting_scan_right_to_left_8u_block_omp(
9771 t: &[u8],
9772 sa: &mut [SaSint],
9773 k: SaSint,
9774 induction_bucket: &mut [SaSint],
9775 block_start: FastSint,
9776 block_size: FastSint,
9777 threads: SaSint,
9778 thread_state: &mut [ThreadState],
9779) {
9780 if block_size <= 0 {
9781 return;
9782 }
9783 let k_usize = usize::try_from(k).expect("k must be non-negative");
9784 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
9785 let threads_usize = usize::try_from(threads.max(1)).expect("threads must be positive");
9786 let omp_num_threads = threads_usize.min(thread_state.len()).min(block_size_usize);
9787 if omp_num_threads <= 1 || block_size < 64 * k.max(256) as FastSint {
9788 final_sorting_scan_right_to_left_8u(t, sa, induction_bucket, block_start, block_size);
9789 return;
9790 }
9791
9792 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
9793 for (omp_thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
9794 let omp_block_start = omp_thread_num * omp_block_stride;
9795 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
9796 omp_block_stride
9797 } else {
9798 block_size_usize - omp_block_start
9799 };
9800 state.count = final_sorting_scan_right_to_left_8u_block_prepare(
9801 t,
9802 sa,
9803 k,
9804 &mut state.buckets,
9805 &mut state.cache,
9806 block_start + omp_block_start as FastSint,
9807 omp_block_size as FastSint,
9808 );
9809 }
9810 for state in thread_state.iter_mut().take(omp_num_threads).rev() {
9811 for c in 0..k_usize {
9812 let a = induction_bucket[c];
9813 let b = state.buckets[c];
9814 induction_bucket[c] = a - b;
9815 state.buckets[c] = a;
9816 }
9817 }
9818 for state in thread_state.iter_mut().take(omp_num_threads) {
9819 final_order_scan_right_to_left_8u_block_place(
9820 sa,
9821 &mut state.buckets,
9822 &state.cache,
9823 state.count,
9824 );
9825 }
9826}
9827
9828#[doc(hidden)]
9830pub fn final_gsa_scan_right_to_left_8u_block_omp(
9831 t: &[u8],
9832 sa: &mut [SaSint],
9833 k: SaSint,
9834 induction_bucket: &mut [SaSint],
9835 block_start: FastSint,
9836 block_size: FastSint,
9837 threads: SaSint,
9838 thread_state: &mut [ThreadState],
9839) {
9840 if block_size <= 0 {
9841 return;
9842 }
9843 let k_usize = usize::try_from(k).expect("k must be non-negative");
9844 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
9845 let threads_usize = usize::try_from(threads.max(1)).expect("threads must be positive");
9846 let omp_num_threads = threads_usize.min(thread_state.len()).min(block_size_usize);
9847 if omp_num_threads <= 1 || block_size < 64 * k.max(256) as FastSint {
9848 final_gsa_scan_right_to_left_8u(t, sa, induction_bucket, block_start, block_size);
9849 return;
9850 }
9851
9852 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
9853 for (omp_thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
9854 let omp_block_start = omp_thread_num * omp_block_stride;
9855 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
9856 omp_block_stride
9857 } else {
9858 block_size_usize - omp_block_start
9859 };
9860 state.count = final_sorting_scan_right_to_left_8u_block_prepare(
9861 t,
9862 sa,
9863 k,
9864 &mut state.buckets,
9865 &mut state.cache,
9866 block_start + omp_block_start as FastSint,
9867 omp_block_size as FastSint,
9868 );
9869 }
9870 for state in thread_state.iter_mut().take(omp_num_threads).rev() {
9871 for c in 0..k_usize {
9872 let a = induction_bucket[c];
9873 let b = state.buckets[c];
9874 induction_bucket[c] = a - b;
9875 state.buckets[c] = a;
9876 }
9877 }
9878 for state in thread_state.iter_mut().take(omp_num_threads) {
9879 final_gsa_scan_right_to_left_8u_block_place(
9880 sa,
9881 &mut state.buckets,
9882 &state.cache,
9883 state.count,
9884 );
9885 }
9886}
9887
9888#[doc(hidden)]
9890pub fn final_sorting_scan_right_to_left_32s_block_omp(
9891 t: &[SaSint],
9892 sa: &mut [SaSint],
9893 buckets: &mut [SaSint],
9894 cache: &mut [ThreadCache],
9895 block_start: FastSint,
9896 block_size: FastSint,
9897 threads: SaSint,
9898) {
9899 if threads <= 1 || block_size < 16_384 {
9900 final_sorting_scan_right_to_left_32s(t, sa, buckets, block_start, block_size);
9901 return;
9902 }
9903
9904 final_sorting_scan_right_to_left_32s_block_gather(t, sa, cache, block_start, block_size);
9905 final_sorting_scan_right_to_left_32s_block_sort(t, buckets, cache, block_start, block_size);
9906 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
9907 let threads_usize = usize::try_from(threads.max(1)).expect("threads must be positive");
9908 let omp_num_threads = threads_usize.min(block_size_usize);
9909 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
9910 {
9911 let sa_ptr = SyncMutPtr::new(sa);
9912 let cache_ptr = SyncMutPtr::new(cache);
9913 run_rayon_with_threads(omp_num_threads, || {
9914 (0..omp_num_threads)
9915 .into_par_iter()
9916 .for_each(|omp_thread_num| {
9917 let omp_block_start = omp_thread_num * omp_block_stride;
9918 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
9919 omp_block_stride
9920 } else {
9921 block_size_usize - omp_block_start
9922 };
9923 let sa = unsafe { sa_ptr.as_slice() };
9925 let cache = unsafe { cache_ptr.as_slice() };
9926 compact_and_place_cached_suffixes(
9927 sa,
9928 cache,
9929 omp_block_start as FastSint,
9930 omp_block_size as FastSint,
9931 );
9932 });
9933 });
9934 }
9935}
9936
9937#[doc(hidden)]
9939pub fn final_bwt_scan_right_to_left_8u_omp(
9940 t: &[u8],
9941 sa: &mut [SaSint],
9942 n: SaSint,
9943 k: SaSint,
9944 induction_bucket: &mut [SaSint],
9945 threads: SaSint,
9946 thread_state: &mut [ThreadState],
9947) -> SaSint {
9948 if threads == 1 || n < 65_536 {
9949 return final_bwt_scan_right_to_left_8u(t, sa, induction_bucket, 0, n as FastSint);
9950 }
9951 let mut index = -1;
9952 let mut block_start = usize::try_from(n).expect("n must be non-negative");
9953 while block_start > 0 {
9954 block_start -= 1;
9955 if sa[block_start] == 0 {
9956 index = block_start as SaSint;
9957 } else {
9958 let threads_usize = usize::try_from(threads)
9959 .expect("threads must be non-negative")
9960 .min(thread_state.len())
9961 .max(1);
9962 let max_back =
9963 threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE.saturating_sub(16 * threads_usize);
9964 let block_max_end = block_start.saturating_sub(max_back);
9965 let mut block_end = block_start;
9966 while block_end > block_max_end && sa[block_end - 1] != 0 {
9967 block_end -= 1;
9968 }
9969 let size = block_start - block_end + 1;
9970 if size < 32 {
9971 let res = final_bwt_scan_right_to_left_8u(
9972 t,
9973 sa,
9974 induction_bucket,
9975 block_end as FastSint,
9976 size as FastSint,
9977 );
9978 if res >= 0 {
9979 index = res;
9980 }
9981 } else {
9982 final_bwt_scan_right_to_left_8u_block_omp(
9983 t,
9984 sa,
9985 k,
9986 induction_bucket,
9987 block_end as FastSint,
9988 size as FastSint,
9989 threads,
9990 thread_state,
9991 );
9992 }
9993 block_start = block_end;
9994 }
9995 }
9996 index
9997}
9998
9999#[doc(hidden)]
10001pub fn final_bwt_aux_scan_right_to_left_8u_omp(
10002 t: &[u8],
10003 sa: &mut [SaSint],
10004 n: SaSint,
10005 k: SaSint,
10006 rm: SaSint,
10007 i_out: &mut [SaSint],
10008 induction_bucket: &mut [SaSint],
10009 threads: SaSint,
10010 thread_state: &mut [ThreadState],
10011) {
10012 if threads == 1 || n < 65_536 {
10013 final_bwt_aux_scan_right_to_left_8u(t, sa, rm, i_out, induction_bucket, 0, n as FastSint);
10014 return;
10015 }
10016 let mut block_start = usize::try_from(n).expect("n must be non-negative");
10017 while block_start > 0 {
10018 block_start -= 1;
10019 if sa[block_start] != 0 {
10020 let threads_usize = usize::try_from(threads)
10021 .expect("threads must be non-negative")
10022 .min(thread_state.len())
10023 .max(1);
10024 let max_back = threads_usize
10025 * (LIBSAIS_PER_THREAD_CACHE_SIZE.saturating_sub(16 * threads_usize) / 2);
10026 let block_max_end = block_start.saturating_sub(max_back);
10027 let mut block_end = block_start;
10028 while block_end > block_max_end && sa[block_end - 1] != 0 {
10029 block_end -= 1;
10030 }
10031 let size = block_start - block_end + 1;
10032 if size < 32 {
10033 final_bwt_aux_scan_right_to_left_8u(
10034 t,
10035 sa,
10036 rm,
10037 i_out,
10038 induction_bucket,
10039 block_end as FastSint,
10040 size as FastSint,
10041 );
10042 } else {
10043 final_bwt_aux_scan_right_to_left_8u_block_omp(
10044 t,
10045 sa,
10046 k,
10047 rm,
10048 i_out,
10049 induction_bucket,
10050 block_end as FastSint,
10051 size as FastSint,
10052 threads,
10053 thread_state,
10054 );
10055 }
10056 block_start = block_end;
10057 }
10058 }
10059}
10060
10061#[doc(hidden)]
10063pub fn final_sorting_scan_right_to_left_8u_omp(
10064 t: &[u8],
10065 sa: &mut [SaSint],
10066 omp_block_start: FastSint,
10067 omp_block_size: FastSint,
10068 k: SaSint,
10069 induction_bucket: &mut [SaSint],
10070 threads: SaSint,
10071 thread_state: &mut [ThreadState],
10072) {
10073 if threads == 1 || omp_block_size < 65_536 {
10074 final_sorting_scan_right_to_left_8u(
10075 t,
10076 sa,
10077 induction_bucket,
10078 omp_block_start,
10079 omp_block_size,
10080 );
10081 return;
10082 }
10083 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
10084 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
10085 let mut block_start = start + size;
10086 while block_start > start {
10087 block_start -= 1;
10088 if sa[block_start] != 0 {
10089 let threads_usize = usize::try_from(threads)
10090 .expect("threads must be non-negative")
10091 .min(thread_state.len())
10092 .max(1);
10093 let max_back =
10094 threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE.saturating_sub(16 * threads_usize);
10095 let block_max_end = block_start.saturating_sub(max_back).max(start);
10096 let mut block_end = block_start;
10097 while block_end > block_max_end && sa[block_end - 1] != 0 {
10098 block_end -= 1;
10099 }
10100 let span = block_start - block_end + 1;
10101 if span < 32 {
10102 final_sorting_scan_right_to_left_8u(
10103 t,
10104 sa,
10105 induction_bucket,
10106 block_end as FastSint,
10107 span as FastSint,
10108 );
10109 } else {
10110 final_sorting_scan_right_to_left_8u_block_omp(
10111 t,
10112 sa,
10113 k,
10114 induction_bucket,
10115 block_end as FastSint,
10116 span as FastSint,
10117 threads,
10118 thread_state,
10119 );
10120 }
10121 block_start = block_end;
10122 }
10123 }
10124}
10125
10126#[doc(hidden)]
10128pub fn final_gsa_scan_right_to_left_8u_omp(
10129 t: &[u8],
10130 sa: &mut [SaSint],
10131 omp_block_start: FastSint,
10132 omp_block_size: FastSint,
10133 k: SaSint,
10134 induction_bucket: &mut [SaSint],
10135 threads: SaSint,
10136 thread_state: &mut [ThreadState],
10137) {
10138 if threads == 1 || omp_block_size < 65_536 {
10139 final_gsa_scan_right_to_left_8u(t, sa, induction_bucket, omp_block_start, omp_block_size);
10140 return;
10141 }
10142 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
10143 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
10144 let mut block_start = start + size;
10145 while block_start > start {
10146 block_start -= 1;
10147 if sa[block_start] != 0 {
10148 let threads_usize = usize::try_from(threads)
10149 .expect("threads must be non-negative")
10150 .min(thread_state.len())
10151 .max(1);
10152 let max_back =
10153 threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE.saturating_sub(16 * threads_usize);
10154 let block_max_end = block_start.saturating_sub(max_back).max(start);
10155 let mut block_end = block_start;
10156 while block_end > block_max_end && sa[block_end - 1] != 0 {
10157 block_end -= 1;
10158 }
10159 let span = block_start - block_end + 1;
10160 if span < 32 {
10161 final_gsa_scan_right_to_left_8u(
10162 t,
10163 sa,
10164 induction_bucket,
10165 block_end as FastSint,
10166 span as FastSint,
10167 );
10168 } else {
10169 final_gsa_scan_right_to_left_8u_block_omp(
10170 t,
10171 sa,
10172 k,
10173 induction_bucket,
10174 block_end as FastSint,
10175 span as FastSint,
10176 threads,
10177 thread_state,
10178 );
10179 }
10180 block_start = block_end;
10181 }
10182 }
10183}
10184
10185#[doc(hidden)]
10187pub fn final_sorting_scan_right_to_left_32s_omp(
10188 t: &[SaSint],
10189 sa: &mut [SaSint],
10190 n: SaSint,
10191 induction_bucket: &mut [SaSint],
10192 threads: SaSint,
10193 thread_state: &mut [ThreadState],
10194) {
10195 if threads == 1 || n < 65_536 {
10196 final_sorting_scan_right_to_left_32s(t, sa, induction_bucket, 0, n as FastSint);
10197 return;
10198 }
10199 if thread_state.is_empty() {
10200 final_sorting_scan_right_to_left_32s(t, sa, induction_bucket, 0, n as FastSint);
10201 return;
10202 }
10203 let threads_usize = usize::try_from(threads)
10204 .expect("threads must be non-negative")
10205 .max(1);
10206 let mut cache = vec![ThreadCache::default(); threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE];
10207 let mut block_start = isize::try_from(n).expect("n must fit isize") - 1;
10208 while block_start >= 0 {
10209 let block_end = (block_start
10210 - isize::try_from(threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE)
10211 .expect("block span must fit isize"))
10212 .max(-1);
10213 final_sorting_scan_right_to_left_32s_block_omp(
10214 t,
10215 sa,
10216 induction_bucket,
10217 &mut cache,
10218 (block_end + 1) as FastSint,
10219 (block_start - block_end) as FastSint,
10220 threads,
10221 );
10222 block_start = block_end;
10223 }
10224}
10225
10226#[doc(hidden)]
10228pub fn clear_lms_suffixes_omp(
10229 sa: &mut [SaSint],
10230 n: SaSint,
10231 k: SaSint,
10232 bucket_start: &[SaSint],
10233 bucket_end: &[SaSint],
10234 threads: SaSint,
10235) {
10236 let k_usize = usize::try_from(k).expect("k must be non-negative");
10237 let thread_count = if threads > 1 && n >= 65536 {
10238 usize::try_from(threads).expect("threads must be positive")
10239 } else {
10240 1
10241 };
10242 {
10243 let sa_ptr = SyncMutPtr::new(sa);
10244 let bucket_start_ref: &[SaSint] = bucket_start;
10245 let bucket_end_ref: &[SaSint] = bucket_end;
10246 run_rayon_with_threads(thread_count, || {
10247 (0..thread_count).into_par_iter().for_each(|t| {
10248 let mut c = t;
10249 let sa = unsafe { sa_ptr.as_slice() };
10252 while c < k_usize {
10253 if bucket_end_ref[c] > bucket_start_ref[c] {
10254 let start = usize::try_from(bucket_start_ref[c])
10255 .expect("bucket start must be non-negative");
10256 let end = usize::try_from(bucket_end_ref[c])
10257 .expect("bucket end must be non-negative");
10258 sa[start..end].fill(0);
10259 }
10260 c += thread_count;
10261 }
10262 });
10263 });
10264 }
10265}
10266
10267#[doc(hidden)]
10269pub fn induce_final_order_8u_omp(
10270 t: &[u8],
10271 sa: &mut [SaSint],
10272 n: SaSint,
10273 k: SaSint,
10274 flags: SaSint,
10275 r: SaSint,
10276 i_out: Option<&mut [SaSint]>,
10277 buckets: &mut [SaSint],
10278 threads: SaSint,
10279 thread_state: &mut [ThreadState],
10280) -> SaSint {
10281 if (flags & LIBSAIS_FLAGS_BWT) == 0 {
10282 if (flags & LIBSAIS_FLAGS_GSA) != 0 {
10283 buckets[6 * ALPHABET_SIZE] = buckets[7 * ALPHABET_SIZE] - 1;
10284 }
10285
10286 let (left_buckets, right_tail) = buckets.split_at_mut(7 * ALPHABET_SIZE);
10287 let bucket_start = &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE];
10288 let bucket_end = &mut right_tail[..ALPHABET_SIZE];
10289
10290 final_sorting_scan_left_to_right_8u_omp(
10291 t,
10292 sa,
10293 n as FastSint,
10294 k,
10295 bucket_start,
10296 threads,
10297 thread_state,
10298 );
10299 if threads > 1 && n >= 65_536 {
10300 clear_lms_suffixes_omp(
10301 sa,
10302 n,
10303 ALPHABET_SIZE as SaSint,
10304 bucket_start,
10305 bucket_end,
10306 threads,
10307 );
10308 }
10309
10310 if (flags & LIBSAIS_FLAGS_GSA) != 0 {
10311 flip_suffix_markers_omp(sa, bucket_end[0], threads);
10312 final_gsa_scan_right_to_left_8u_omp(
10313 t,
10314 sa,
10315 bucket_end[0] as FastSint,
10316 n as FastSint - bucket_end[0] as FastSint,
10317 k,
10318 bucket_end,
10319 1,
10320 thread_state,
10321 );
10322 } else {
10323 final_sorting_scan_right_to_left_8u_omp(
10324 t,
10325 sa,
10326 0,
10327 n as FastSint,
10328 k,
10329 bucket_end,
10330 threads,
10331 thread_state,
10332 );
10333 }
10334
10335 0
10336 } else if let Some(i_out) = i_out {
10337 let (left_buckets, right_tail) = buckets.split_at_mut(7 * ALPHABET_SIZE);
10338 let bucket_start = &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE];
10339 let bucket_end = &mut right_tail[..ALPHABET_SIZE];
10340
10341 final_bwt_aux_scan_left_to_right_8u_omp(
10342 t,
10343 sa,
10344 n as FastSint,
10345 k,
10346 r - 1,
10347 i_out,
10348 bucket_start,
10349 threads,
10350 thread_state,
10351 );
10352 if threads > 1 && n >= 65_536 {
10353 clear_lms_suffixes_omp(
10354 sa,
10355 n,
10356 ALPHABET_SIZE as SaSint,
10357 bucket_start,
10358 bucket_end,
10359 threads,
10360 );
10361 }
10362 final_bwt_aux_scan_right_to_left_8u_omp(
10363 t,
10364 sa,
10365 n,
10366 k,
10367 r - 1,
10368 i_out,
10369 bucket_end,
10370 threads,
10371 thread_state,
10372 );
10373 0
10374 } else {
10375 let (left_buckets, right_tail) = buckets.split_at_mut(7 * ALPHABET_SIZE);
10376 let bucket_start = &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE];
10377 let bucket_end = &mut right_tail[..ALPHABET_SIZE];
10378
10379 final_bwt_scan_left_to_right_8u_omp(
10380 t,
10381 sa,
10382 n as FastSint,
10383 k,
10384 bucket_start,
10385 threads,
10386 thread_state,
10387 );
10388 if threads > 1 && n >= 65_536 {
10389 clear_lms_suffixes_omp(
10390 sa,
10391 n,
10392 ALPHABET_SIZE as SaSint,
10393 bucket_start,
10394 bucket_end,
10395 threads,
10396 );
10397 }
10398 final_bwt_scan_right_to_left_8u_omp(t, sa, n, k, bucket_end, threads, thread_state)
10399 }
10400}
10401
10402#[doc(hidden)]
10404pub fn induce_final_order_32s_6k(
10405 t: &[SaSint],
10406 sa: &mut [SaSint],
10407 n: SaSint,
10408 k: SaSint,
10409 buckets: &mut [SaSint],
10410 threads: SaSint,
10411 thread_state: &mut [ThreadState],
10412) {
10413 let k_usize = usize::try_from(k).expect("k must be non-negative");
10414 let (_head, tail) = buckets.split_at_mut(4 * k_usize);
10415 let (left, right) = tail.split_at_mut(k_usize);
10416 final_sorting_scan_left_to_right_32s_omp(t, sa, n, left, threads, thread_state);
10417 final_sorting_scan_right_to_left_32s_omp(t, sa, n, right, threads, thread_state);
10418}
10419
10420#[doc(hidden)]
10422pub fn induce_final_order_32s_4k(
10423 t: &[SaSint],
10424 sa: &mut [SaSint],
10425 n: SaSint,
10426 k: SaSint,
10427 buckets: &mut [SaSint],
10428 threads: SaSint,
10429 thread_state: &mut [ThreadState],
10430) {
10431 let k_usize = usize::try_from(k).expect("k must be non-negative");
10432 let (_head, tail) = buckets.split_at_mut(2 * k_usize);
10433 let (left, right) = tail.split_at_mut(k_usize);
10434 final_sorting_scan_left_to_right_32s_omp(t, sa, n, left, threads, thread_state);
10435 final_sorting_scan_right_to_left_32s_omp(t, sa, n, right, threads, thread_state);
10436}
10437
10438#[doc(hidden)]
10440pub fn induce_final_order_32s_2k(
10441 t: &[SaSint],
10442 sa: &mut [SaSint],
10443 n: SaSint,
10444 k: SaSint,
10445 buckets: &mut [SaSint],
10446 threads: SaSint,
10447 thread_state: &mut [ThreadState],
10448) {
10449 let k_usize = usize::try_from(k).expect("k must be non-negative");
10450 let (right, left) = buckets.split_at_mut(k_usize);
10451 final_sorting_scan_left_to_right_32s_omp(t, sa, n, left, threads, thread_state);
10452 final_sorting_scan_right_to_left_32s_omp(t, sa, n, right, threads, thread_state);
10453}
10454
10455#[doc(hidden)]
10457pub fn induce_final_order_32s_1k(
10458 t: &[SaSint],
10459 sa: &mut [SaSint],
10460 n: SaSint,
10461 k: SaSint,
10462 buckets: &mut [SaSint],
10463 threads: SaSint,
10464 thread_state: &mut [ThreadState],
10465) {
10466 count_suffixes_32s(t, n, k, buckets);
10467 initialize_buckets_start_32s_1k(k, buckets);
10468 final_sorting_scan_left_to_right_32s_omp(t, sa, n, buckets, threads, thread_state);
10469
10470 count_suffixes_32s(t, n, k, buckets);
10471 initialize_buckets_end_32s_1k(k, buckets);
10472 final_sorting_scan_right_to_left_32s_omp(t, sa, n, buckets, threads, thread_state);
10473}
10474
10475#[doc(hidden)]
10477pub fn renumber_unique_and_nonunique_lms_suffixes_32s(
10478 t: &mut [SaSint],
10479 sa: &mut [SaSint],
10480 m: SaSint,
10481 mut f: SaSint,
10482 omp_block_start: FastSint,
10483 omp_block_size: FastSint,
10484) -> SaSint {
10485 if omp_block_size <= 0 {
10486 return f;
10487 }
10488
10489 let prefetch_distance = 64 as SaSint;
10490 let m_usize = usize::try_from(m).expect("m must be non-negative");
10491 let (sa_head, sam) = sa.split_at_mut(m_usize);
10492 let mut i = omp_block_start as SaSint;
10493 let mut j = omp_block_start as SaSint + omp_block_size as SaSint - 2 * prefetch_distance - 3;
10494
10495 while i < j {
10496 let p0 = sa_head[i as usize] as SaUint;
10497 let p0_half = (p0 >> 1) as usize;
10498 let mut s0 = sam[p0_half];
10499 if s0 < 0 {
10500 t[p0 as usize] |= SAINT_MIN;
10501 f += 1;
10502 s0 = i + SAINT_MIN + f;
10503 }
10504 sam[p0_half] = s0 - f;
10505
10506 let p1 = sa_head[(i + 1) as usize] as SaUint;
10507 let p1_half = (p1 >> 1) as usize;
10508 let mut s1 = sam[p1_half];
10509 if s1 < 0 {
10510 t[p1 as usize] |= SAINT_MIN;
10511 f += 1;
10512 s1 = i + 1 + SAINT_MIN + f;
10513 }
10514 sam[p1_half] = s1 - f;
10515
10516 let p2 = sa_head[(i + 2) as usize] as SaUint;
10517 let p2_half = (p2 >> 1) as usize;
10518 let mut s2 = sam[p2_half];
10519 if s2 < 0 {
10520 t[p2 as usize] |= SAINT_MIN;
10521 f += 1;
10522 s2 = i + 2 + SAINT_MIN + f;
10523 }
10524 sam[p2_half] = s2 - f;
10525
10526 let p3 = sa_head[(i + 3) as usize] as SaUint;
10527 let p3_half = (p3 >> 1) as usize;
10528 let mut s3 = sam[p3_half];
10529 if s3 < 0 {
10530 t[p3 as usize] |= SAINT_MIN;
10531 f += 1;
10532 s3 = i + 3 + SAINT_MIN + f;
10533 }
10534 sam[p3_half] = s3 - f;
10535
10536 i += 4;
10537 }
10538
10539 j += 2 * prefetch_distance + 3;
10540 while i < j {
10541 let p = sa_head[i as usize] as SaUint;
10542 let p_half = (p >> 1) as usize;
10543 let mut s = sam[p_half];
10544 if s < 0 {
10545 t[p as usize] |= SAINT_MIN;
10546 f += 1;
10547 s = i + SAINT_MIN + f;
10548 }
10549 sam[p_half] = s - f;
10550 i += 1;
10551 }
10552
10553 f
10554}
10555
10556#[doc(hidden)]
10558pub fn compact_unique_and_nonunique_lms_suffixes_32s(
10559 sa: &mut [SaSint],
10560 m: SaSint,
10561 pl: &mut FastSint,
10562 pr: &mut FastSint,
10563 omp_block_start: FastSint,
10564 omp_block_size: FastSint,
10565) {
10566 if omp_block_size <= 0 {
10567 return;
10568 }
10569
10570 let m_usize = usize::try_from(m).expect("m must be non-negative");
10571 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
10572 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
10573
10574 let source: Vec<SaSint> = sa[m_usize + start..m_usize + start + size].to_vec();
10575 let mut l = usize::try_from(*pl - 1).expect("left position must be positive");
10576 let mut r = usize::try_from(*pr - 1).expect("right position must be positive");
10577
10578 for &p in source.iter().rev() {
10579 let pu = p as SaUint;
10580 sa[l] = (pu & SAINT_MAX as SaUint) as SaSint;
10581 l = l.saturating_sub(usize::from((pu as SaSint) < 0));
10582
10583 sa[r] = pu.wrapping_sub(1) as SaSint;
10584 r = r.saturating_sub(usize::from((pu as SaSint) > 0));
10585 }
10586
10587 *pl = l as FastSint + 1;
10588 *pr = r as FastSint + 1;
10589}
10590
10591#[doc(hidden)]
10593pub fn count_unique_suffixes(
10594 sa: &[SaSint],
10595 m: SaSint,
10596 omp_block_start: FastSint,
10597 omp_block_size: FastSint,
10598) -> SaSint {
10599 if omp_block_size <= 0 {
10600 return 0;
10601 }
10602
10603 let m_usize = usize::try_from(m).expect("m must be non-negative");
10604 let sam = &sa[m_usize..];
10605 let mut i = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
10606 let block_end =
10607 i + usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
10608 let j = block_end.saturating_sub(67);
10609 let mut f0 = 0;
10610 let mut f1 = 0;
10611 let mut f2 = 0;
10612 let mut f3 = 0;
10613
10614 while i < j {
10615 f0 += SaSint::from(
10616 sam[usize::try_from((sa[i] as SaUint) >> 1).expect("name slot must fit usize")] < 0,
10617 );
10618 f1 += SaSint::from(
10619 sam[usize::try_from((sa[i + 1] as SaUint) >> 1).expect("name slot must fit usize")] < 0,
10620 );
10621 f2 += SaSint::from(
10622 sam[usize::try_from((sa[i + 2] as SaUint) >> 1).expect("name slot must fit usize")] < 0,
10623 );
10624 f3 += SaSint::from(
10625 sam[usize::try_from((sa[i + 3] as SaUint) >> 1).expect("name slot must fit usize")] < 0,
10626 );
10627 i += 4;
10628 }
10629
10630 while i < block_end {
10631 f0 += SaSint::from(
10632 sam[usize::try_from((sa[i] as SaUint) >> 1).expect("name slot must fit usize")] < 0,
10633 );
10634 i += 1;
10635 }
10636
10637 f0 + f1 + f2 + f3
10638}
10639
10640#[doc(hidden)]
10642pub fn renumber_unique_and_nonunique_lms_suffixes_32s_omp(
10643 t: &mut [SaSint],
10644 sa: &mut [SaSint],
10645 m: SaSint,
10646 threads: SaSint,
10647 thread_state: &mut [ThreadState],
10648) -> SaSint {
10649 let f = if threads == 1 || m < 65_536 {
10650 renumber_unique_and_nonunique_lms_suffixes_32s(t, sa, m, 0, 0, m as FastSint)
10651 } else {
10652 let threads_usize = usize::try_from(threads)
10653 .expect("threads must be non-negative")
10654 .max(1);
10655 let m_usize = usize::try_from(m).expect("m must be non-negative");
10656 let omp_num_threads = threads_usize.min(m_usize.max(1));
10657 let omp_block_stride = (m_usize / omp_num_threads) & !15usize;
10658
10659 {
10660 let sa_ro: &[SaSint] = sa;
10661 run_rayon_with_threads(omp_num_threads, || {
10662 thread_state[..omp_num_threads]
10663 .par_iter_mut()
10664 .enumerate()
10665 .for_each(|(omp_thread_num, state)| {
10666 let omp_block_start = omp_thread_num * omp_block_stride;
10667 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
10668 omp_block_stride
10669 } else {
10670 m_usize - omp_block_start
10671 };
10672 state.count = count_unique_suffixes(
10673 sa_ro,
10674 m,
10675 omp_block_start as FastSint,
10676 omp_block_size as FastSint,
10677 ) as FastSint;
10678 });
10679 });
10680 }
10681
10682 let counts: Vec<FastSint> = thread_state[..omp_num_threads]
10683 .iter()
10684 .map(|s| s.count)
10685 .collect();
10686 let f = counts.iter().sum::<FastSint>() as SaSint;
10687
10688 {
10689 let sa_ptr = SyncMutPtr::new(sa);
10690 let t_ptr = SyncMutPtr::new(t);
10691 let counts_ref: &[FastSint] = &counts;
10692 run_rayon_with_threads(omp_num_threads, || {
10693 (0..omp_num_threads)
10694 .into_par_iter()
10695 .for_each(|omp_thread_num| {
10696 let omp_block_start = omp_thread_num * omp_block_stride;
10697 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
10698 omp_block_stride
10699 } else {
10700 m_usize - omp_block_start
10701 };
10702
10703 let mut count: FastSint = 0;
10704 for tt in 0..omp_thread_num {
10705 count += counts_ref[tt];
10706 }
10707
10708 let sa = unsafe { sa_ptr.as_slice() };
10710 let t = unsafe { t_ptr.as_slice() };
10711 renumber_unique_and_nonunique_lms_suffixes_32s(
10712 t,
10713 sa,
10714 m,
10715 count as SaSint,
10716 omp_block_start as FastSint,
10717 omp_block_size as FastSint,
10718 );
10719 });
10720 });
10721 }
10722 f
10723 };
10724
10725 f
10726}
10727
10728#[doc(hidden)]
10730pub fn compact_unique_and_nonunique_lms_suffixes_32s_omp(
10731 sa: &mut [SaSint],
10732 n: SaSint,
10733 m: SaSint,
10734 fs: SaSint,
10735 f: SaSint,
10736 threads: SaSint,
10737 thread_state: &mut [ThreadState],
10738) {
10739 let half_n = (n as FastSint) >> 1;
10740 if threads == 1 || n < 131_072 || m >= fs {
10741 let mut l = m as FastSint;
10742 let mut r = n as FastSint + fs as FastSint;
10743 compact_unique_and_nonunique_lms_suffixes_32s(sa, m, &mut l, &mut r, 0, half_n);
10744 } else {
10745 let threads_usize = usize::try_from(threads)
10746 .expect("threads must be non-negative")
10747 .max(1);
10748 let half_n_usize = usize::try_from(half_n).expect("half_n must be non-negative");
10749 let omp_num_threads = threads_usize.min(half_n_usize.max(1));
10750 let omp_block_stride = (half_n_usize / omp_num_threads) & !15usize;
10751
10752 {
10753 let sa_ptr = SyncMutPtr::new(sa);
10754 run_rayon_with_threads(omp_num_threads, || {
10755 thread_state[..omp_num_threads]
10756 .par_iter_mut()
10757 .enumerate()
10758 .for_each(|(omp_thread_num, state)| {
10759 let omp_block_start = omp_thread_num * omp_block_stride;
10760 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
10761 omp_block_stride
10762 } else {
10763 half_n_usize - omp_block_start
10764 };
10765
10766 let mut position = m as FastSint
10767 + half_n
10768 + omp_block_start as FastSint
10769 + omp_block_size as FastSint;
10770 let mut count = m as FastSint
10771 + omp_block_start as FastSint
10772 + omp_block_size as FastSint;
10773
10774 let sa = unsafe { sa_ptr.as_slice() };
10776 compact_unique_and_nonunique_lms_suffixes_32s(
10777 sa,
10778 m,
10779 &mut position,
10780 &mut count,
10781 omp_block_start as FastSint,
10782 omp_block_size as FastSint,
10783 );
10784 state.position = position;
10785 state.count = count;
10786 });
10787 });
10788 }
10789
10790 let mut position = m as FastSint;
10791 for t in (0..omp_num_threads).rev() {
10792 let omp_block_end = if t + 1 < omp_num_threads {
10793 omp_block_stride * (t + 1)
10794 } else {
10795 half_n_usize
10796 };
10797 let count =
10798 m as FastSint + half_n + omp_block_end as FastSint - thread_state[t].position;
10799 if count > 0 {
10800 position -= count;
10801 let dst = usize::try_from(position).expect("destination must be non-negative");
10802 let src =
10803 usize::try_from(thread_state[t].position).expect("source must be non-negative");
10804 let len = usize::try_from(count).expect("length must be non-negative");
10805 sa.copy_within(src..src + len, dst);
10806 }
10807 }
10808
10809 let mut position = n as FastSint + fs as FastSint;
10810 for t in (0..omp_num_threads).rev() {
10811 let omp_block_end = if t + 1 < omp_num_threads {
10812 omp_block_stride * (t + 1)
10813 } else {
10814 half_n_usize
10815 };
10816 let count = m as FastSint + omp_block_end as FastSint - thread_state[t].count;
10817 if count > 0 {
10818 position -= count;
10819 let dst = usize::try_from(position).expect("destination must be non-negative");
10820 let src =
10821 usize::try_from(thread_state[t].count).expect("source must be non-negative");
10822 let len = usize::try_from(count).expect("length must be non-negative");
10823 sa.copy_within(src..src + len, dst);
10824 }
10825 }
10826 }
10827
10828 let copy_dst = usize::try_from(n + fs - m).expect("copy destination must be non-negative");
10829 let copy_src = usize::try_from(m - f).expect("copy source must be non-negative");
10830 let copy_len = usize::try_from(f).expect("copy length must be non-negative");
10831 sa.copy_within(copy_src..copy_src + copy_len, copy_dst);
10832}
10833
10834#[doc(hidden)]
10836pub fn compact_lms_suffixes_32s_omp(
10837 t: &mut [SaSint],
10838 sa: &mut [SaSint],
10839 n: SaSint,
10840 m: SaSint,
10841 fs: SaSint,
10842 threads: SaSint,
10843 thread_state: &mut [ThreadState],
10844) -> SaSint {
10845 let f = renumber_unique_and_nonunique_lms_suffixes_32s_omp(t, sa, m, threads, thread_state);
10846 compact_unique_and_nonunique_lms_suffixes_32s_omp(sa, n, m, fs, f, threads, thread_state);
10847 f
10848}
10849
10850#[doc(hidden)]
10852pub fn merge_unique_lms_suffixes_32s(
10853 t: &mut [SaSint],
10854 sa: &mut [SaSint],
10855 n: SaSint,
10856 m: SaSint,
10857 l: FastSint,
10858 omp_block_start: FastSint,
10859 omp_block_size: FastSint,
10860) {
10861 if omp_block_size <= 0 {
10862 return;
10863 }
10864
10865 let n_usize = usize::try_from(n).expect("n must be non-negative");
10866 let m_usize = usize::try_from(m).expect("m must be non-negative");
10867 let mut src_index = n_usize - m_usize - 1 + usize::try_from(l).expect("l must be non-negative");
10868 let mut tmp = sa[src_index] as FastSint;
10869 src_index += 1;
10870
10871 let mut i = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
10872 let block_end =
10873 i + usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
10874 let j = block_end.saturating_sub(6);
10875 while i < j {
10876 let c0 = t[i];
10877 if c0 < 0 {
10878 t[i] = c0 & SAINT_MAX;
10879 sa[usize::try_from(tmp).expect("target slot must be non-negative")] = i as SaSint;
10880 i += 1;
10881 tmp = sa[src_index] as FastSint;
10882 src_index += 1;
10883 }
10884
10885 let c1 = t[i + 1];
10886 if c1 < 0 {
10887 t[i + 1] = c1 & SAINT_MAX;
10888 sa[usize::try_from(tmp).expect("target slot must be non-negative")] = i as SaSint + 1;
10889 i += 1;
10890 tmp = sa[src_index] as FastSint;
10891 src_index += 1;
10892 }
10893
10894 let c2 = t[i + 2];
10895 if c2 < 0 {
10896 t[i + 2] = c2 & SAINT_MAX;
10897 sa[usize::try_from(tmp).expect("target slot must be non-negative")] = i as SaSint + 2;
10898 i += 1;
10899 tmp = sa[src_index] as FastSint;
10900 src_index += 1;
10901 }
10902
10903 let c3 = t[i + 3];
10904 if c3 < 0 {
10905 t[i + 3] = c3 & SAINT_MAX;
10906 sa[usize::try_from(tmp).expect("target slot must be non-negative")] = i as SaSint + 3;
10907 i += 1;
10908 tmp = sa[src_index] as FastSint;
10909 src_index += 1;
10910 }
10911
10912 i += 4;
10913 }
10914
10915 while i < block_end {
10916 let c = t[i];
10917 if c < 0 {
10918 t[i] = c & SAINT_MAX;
10919 sa[usize::try_from(tmp).expect("target slot must be non-negative")] = i as SaSint;
10920 i += 1;
10921 tmp = sa[src_index] as FastSint;
10922 src_index += 1;
10923 }
10924 i += 1;
10925 }
10926}
10927
10928#[doc(hidden)]
10930pub fn merge_nonunique_lms_suffixes_32s(
10931 sa: &mut [SaSint],
10932 n: SaSint,
10933 m: SaSint,
10934 l: FastSint,
10935 omp_block_start: FastSint,
10936 omp_block_size: FastSint,
10937) {
10938 if omp_block_size <= 0 {
10939 return;
10940 }
10941
10942 let n_usize = usize::try_from(n).expect("n must be non-negative");
10943 let m_usize = usize::try_from(m).expect("m must be non-negative");
10944 let mut src_index = n_usize - m_usize - 1 + usize::try_from(l).expect("l must be non-negative");
10945 let mut tmp = sa[src_index];
10946 src_index += 1;
10947
10948 let mut i = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
10949 let block_end =
10950 i + usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
10951 let j = block_end.saturating_sub(3);
10952 while i < j {
10953 if sa[i] == 0 {
10954 sa[i] = tmp;
10955 tmp = sa[src_index];
10956 src_index += 1;
10957 }
10958 if sa[i + 1] == 0 {
10959 sa[i + 1] = tmp;
10960 tmp = sa[src_index];
10961 src_index += 1;
10962 }
10963 if sa[i + 2] == 0 {
10964 sa[i + 2] = tmp;
10965 tmp = sa[src_index];
10966 src_index += 1;
10967 }
10968 if sa[i + 3] == 0 {
10969 sa[i + 3] = tmp;
10970 tmp = sa[src_index];
10971 src_index += 1;
10972 }
10973 i += 4;
10974 }
10975
10976 while i < block_end {
10977 if sa[i] == 0 {
10978 sa[i] = tmp;
10979 tmp = sa[src_index];
10980 src_index += 1;
10981 }
10982 i += 1;
10983 }
10984}
10985
10986#[doc(hidden)]
10988pub fn merge_unique_lms_suffixes_32s_omp(
10989 t: &mut [SaSint],
10990 sa: &mut [SaSint],
10991 n: SaSint,
10992 m: SaSint,
10993 threads: SaSint,
10994 thread_state: &mut [ThreadState],
10995) {
10996 if threads == 1 || n < 65_536 {
10997 merge_unique_lms_suffixes_32s(t, sa, n, m, 0, 0, n as FastSint);
10998 return;
10999 }
11000
11001 let threads_usize = usize::try_from(threads)
11002 .expect("threads must be non-negative")
11003 .max(1);
11004 let n_usize = usize::try_from(n).expect("n must be non-negative");
11005 let omp_num_threads = threads_usize.min(n_usize.max(1));
11006 let omp_block_stride = (n_usize / omp_num_threads) & !15usize;
11007
11008 {
11009 let t_ro: &[SaSint] = t;
11010 run_rayon_with_threads(omp_num_threads, || {
11011 thread_state[..omp_num_threads]
11012 .par_iter_mut()
11013 .enumerate()
11014 .for_each(|(omp_thread_num, state)| {
11015 let omp_block_start = omp_thread_num * omp_block_stride;
11016 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
11017 omp_block_stride
11018 } else {
11019 n_usize - omp_block_start
11020 };
11021 state.count = count_negative_marked_suffixes(
11022 t_ro,
11023 omp_block_start as FastSint,
11024 omp_block_size as FastSint,
11025 ) as FastSint;
11026 });
11027 });
11028 }
11029
11030 let counts: Vec<FastSint> = thread_state[..omp_num_threads]
11031 .iter()
11032 .map(|s| s.count)
11033 .collect();
11034
11035 {
11036 let sa_ptr = SyncMutPtr::new(sa);
11037 let t_ptr = SyncMutPtr::new(t);
11038 let counts_ref: &[FastSint] = &counts;
11039 run_rayon_with_threads(omp_num_threads, || {
11040 (0..omp_num_threads)
11041 .into_par_iter()
11042 .for_each(|omp_thread_num| {
11043 let omp_block_start = omp_thread_num * omp_block_stride;
11044 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
11045 omp_block_stride
11046 } else {
11047 n_usize - omp_block_start
11048 };
11049
11050 let mut count: FastSint = 0;
11051 for tt in 0..omp_thread_num {
11052 count += counts_ref[tt];
11053 }
11054
11055 let sa = unsafe { sa_ptr.as_slice() };
11057 let t = unsafe { t_ptr.as_slice() };
11058 merge_unique_lms_suffixes_32s(
11059 t,
11060 sa,
11061 n,
11062 m,
11063 count,
11064 omp_block_start as FastSint,
11065 omp_block_size as FastSint,
11066 );
11067 });
11068 });
11069 }
11070}
11071
11072#[doc(hidden)]
11074pub fn merge_nonunique_lms_suffixes_32s_omp(
11075 sa: &mut [SaSint],
11076 n: SaSint,
11077 m: SaSint,
11078 f: SaSint,
11079 threads: SaSint,
11080 thread_state: &mut [ThreadState],
11081) {
11082 if threads == 1 || m < 65_536 {
11083 merge_nonunique_lms_suffixes_32s(sa, n, m, f as FastSint, 0, m as FastSint);
11084 return;
11085 }
11086
11087 let threads_usize = usize::try_from(threads)
11088 .expect("threads must be non-negative")
11089 .max(1);
11090 let m_usize = usize::try_from(m).expect("m must be non-negative");
11091 let omp_num_threads = threads_usize.min(m_usize.max(1));
11092 let omp_block_stride = (m_usize / omp_num_threads) & !15usize;
11093
11094 {
11095 let sa_ro: &[SaSint] = sa;
11096 run_rayon_with_threads(omp_num_threads, || {
11097 thread_state[..omp_num_threads]
11098 .par_iter_mut()
11099 .enumerate()
11100 .for_each(|(omp_thread_num, state)| {
11101 let omp_block_start = omp_thread_num * omp_block_stride;
11102 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
11103 omp_block_stride
11104 } else {
11105 m_usize - omp_block_start
11106 };
11107 state.count = count_zero_marked_suffixes(
11108 sa_ro,
11109 omp_block_start as FastSint,
11110 omp_block_size as FastSint,
11111 ) as FastSint;
11112 });
11113 });
11114 }
11115
11116 let counts: Vec<FastSint> = thread_state[..omp_num_threads]
11117 .iter()
11118 .map(|s| s.count)
11119 .collect();
11120
11121 {
11122 let sa_ptr = SyncMutPtr::new(sa);
11123 let counts_ref: &[FastSint] = &counts;
11124 run_rayon_with_threads(omp_num_threads, || {
11125 (0..omp_num_threads)
11126 .into_par_iter()
11127 .for_each(|omp_thread_num| {
11128 let omp_block_start = omp_thread_num * omp_block_stride;
11129 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
11130 omp_block_stride
11131 } else {
11132 m_usize - omp_block_start
11133 };
11134
11135 let mut count: FastSint = f as FastSint;
11136 for tt in 0..omp_thread_num {
11137 count += counts_ref[tt];
11138 }
11139
11140 let sa = unsafe { sa_ptr.as_slice() };
11142 merge_nonunique_lms_suffixes_32s(
11143 sa,
11144 n,
11145 m,
11146 count,
11147 omp_block_start as FastSint,
11148 omp_block_size as FastSint,
11149 );
11150 });
11151 });
11152 }
11153}
11154
11155#[doc(hidden)]
11157pub fn merge_compacted_lms_suffixes_32s_omp(
11158 t: &mut [SaSint],
11159 sa: &mut [SaSint],
11160 n: SaSint,
11161 m: SaSint,
11162 f: SaSint,
11163 threads: SaSint,
11164 thread_state: &mut [ThreadState],
11165) {
11166 merge_unique_lms_suffixes_32s_omp(t, sa, n, m, threads, thread_state);
11167 merge_nonunique_lms_suffixes_32s_omp(sa, n, m, f, threads, thread_state);
11168}
11169
11170#[doc(hidden)]
11172pub fn reconstruct_compacted_lms_suffixes_32s_2k_omp(
11173 t: &mut [SaSint],
11174 sa: &mut [SaSint],
11175 n: SaSint,
11176 k: SaSint,
11177 m: SaSint,
11178 fs: SaSint,
11179 f: SaSint,
11180 buckets: &mut [SaSint],
11181 local_buckets: SaSint,
11182 threads: SaSint,
11183 thread_state: &mut [ThreadState],
11184) {
11185 if f > 0 {
11186 let dst = usize::try_from(n - m - 1).expect("destination must be non-negative");
11187 let src = usize::try_from(n + fs - m).expect("source must be non-negative");
11188 let len = usize::try_from(f).expect("length must be non-negative");
11189 sa.copy_within(src..src + len, dst);
11190
11191 let _ = count_and_gather_compacted_lms_suffixes_32s_2k_omp(
11192 t,
11193 sa,
11194 n,
11195 k,
11196 buckets,
11197 local_buckets,
11198 threads,
11199 thread_state,
11200 );
11201 reconstruct_lms_suffixes_omp(sa, n, m - f, threads);
11202
11203 let src_copy = 0usize;
11204 let dst_copy = usize::try_from(n - m - 1 + f).expect("destination must be non-negative");
11205 let copy_len = usize::try_from(m - f).expect("copy length must be non-negative");
11206 sa.copy_within(src_copy..src_copy + copy_len, dst_copy);
11207 sa[..usize::try_from(m).expect("m must be non-negative")].fill(0);
11208
11209 merge_compacted_lms_suffixes_32s_omp(t, sa, n, m, f, threads, thread_state);
11210 } else {
11211 let _ = count_and_gather_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as FastSint);
11212 reconstruct_lms_suffixes_omp(sa, n, m, threads);
11213 }
11214}
11215
11216#[doc(hidden)]
11218pub fn reconstruct_compacted_lms_suffixes_32s_1k_omp(
11219 t: &mut [SaSint],
11220 sa: &mut [SaSint],
11221 n: SaSint,
11222 m: SaSint,
11223 fs: SaSint,
11224 f: SaSint,
11225 threads: SaSint,
11226 thread_state: &mut [ThreadState],
11227) {
11228 if f > 0 {
11229 let dst = usize::try_from(n - m - 1).expect("destination must be non-negative");
11230 let src = usize::try_from(n + fs - m).expect("source must be non-negative");
11231 let len = usize::try_from(f).expect("length must be non-negative");
11232 sa.copy_within(src..src + len, dst);
11233
11234 let _ = gather_compacted_lms_suffixes_32s(t, sa, n);
11235 reconstruct_lms_suffixes_omp(sa, n, m - f, threads);
11236
11237 let dst_copy = usize::try_from(n - m - 1 + f).expect("destination must be non-negative");
11238 let copy_len = usize::try_from(m - f).expect("copy length must be non-negative");
11239 sa.copy_within(0..copy_len, dst_copy);
11240 sa[..usize::try_from(m).expect("m must be non-negative")].fill(0);
11241
11242 merge_compacted_lms_suffixes_32s_omp(t, sa, n, m, f, threads, thread_state);
11243 } else {
11244 let _ = gather_lms_suffixes_32s(t, sa, n);
11245 reconstruct_lms_suffixes_omp(sa, n, m, threads);
11246 }
11247}
11248
11249fn normalize_omp_threads(threads: SaSint) -> SaSint {
11250 if threads > 0 {
11251 threads
11252 } else {
11253 std::thread::available_parallelism()
11254 .map(|value| value.get() as SaSint)
11255 .unwrap_or(1)
11256 .max(1)
11257 }
11258}
11259
11260fn libsais_main_32s_recursion(
11261 t_ptr: *mut SaSint,
11262 sa_ptr: *mut SaSint,
11263 sa_capacity: usize,
11264 n: SaSint,
11265 k: SaSint,
11266 fs: SaSint,
11267 threads: SaSint,
11268 thread_state: &mut [ThreadState],
11269 _local_buffer: &mut [SaSint],
11270) -> SaSint {
11271 let fs = fs.min(SAINT_MAX - n);
11272 let local_buffer_size = SaSint::try_from(LIBSAIS_LOCAL_BUFFER_SIZE).expect("fits");
11273 let n_usize = usize::try_from(n).expect("n must be non-negative");
11274 let fs_usize = usize::try_from(fs).expect("fs must be non-negative");
11275 let total_len = n_usize + fs_usize;
11276 assert!(total_len <= sa_capacity);
11277
11278 if k > 0 && ((fs / k) >= 6 || (local_buffer_size / k) >= 6) {
11279 let k_usize = usize::try_from(k).expect("k must be non-negative");
11280 let alignment = if fs >= 1024 && ((fs - 1024) / k) >= 6 {
11281 1024usize
11282 } else {
11283 16usize
11284 };
11285 let need = 6 * k_usize;
11286 let use_local_buffer = local_buffer_size > fs;
11287 let mut bucket_free_space = SaSint::from(use_local_buffer);
11288 let buckets_ptr = if use_local_buffer {
11289 _local_buffer.as_mut_ptr()
11290 } else {
11291 unsafe {
11292 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11293 let start =
11294 if fs_usize >= need + alignment && ((fs_usize - alignment) / k_usize) >= 6 {
11295 let byte_ptr = sa[total_len - need - alignment..].as_mut_ptr() as usize;
11296 let aligned = align_up(byte_ptr, alignment * mem::size_of::<SaSint>());
11297 (aligned - sa_ptr as usize) / mem::size_of::<SaSint>()
11298 } else {
11299 total_len - need
11300 };
11301 bucket_free_space =
11302 SaSint::try_from(start - n_usize).expect("bucket free space must fit SaSint");
11303 sa[start..].as_mut_ptr()
11304 }
11305 };
11306
11307 let m = unsafe {
11308 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11309 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11310 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11311 count_and_gather_lms_suffixes_32s_4k_omp(
11312 t,
11313 sa,
11314 n,
11315 k,
11316 buckets,
11317 bucket_free_space,
11318 threads,
11319 thread_state,
11320 )
11321 };
11322 if m > 1 {
11323 let m_usize = usize::try_from(m).expect("m must be non-negative");
11324 unsafe {
11325 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11326 sa[..n_usize - m_usize].fill(0);
11327 }
11328
11329 let first_lms_suffix = unsafe {
11330 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11331 sa[n_usize - m_usize]
11332 };
11333 let left_suffixes_count = unsafe {
11334 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11335 initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
11336 std::slice::from_raw_parts_mut(t_ptr, n_usize),
11337 k,
11338 buckets,
11339 first_lms_suffix,
11340 )
11341 };
11342
11343 unsafe {
11344 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11345 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11346 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11347 let (_, induction_bucket) = buckets.split_at_mut(4 * k_usize);
11348 radix_sort_lms_suffixes_32s_6k_omp(
11349 t,
11350 sa,
11351 n,
11352 m,
11353 induction_bucket,
11354 threads,
11355 thread_state,
11356 );
11357 if (n / 8192) < k {
11358 radix_sort_set_markers_32s_6k_omp(sa, k, induction_bucket, threads);
11359 }
11360 if threads > 1 && n >= 65_536 {
11361 sa[n_usize - m_usize..n_usize].fill(0);
11362 }
11363 initialize_buckets_for_partial_sorting_32s_6k(
11364 t,
11365 k,
11366 buckets,
11367 first_lms_suffix,
11368 left_suffixes_count,
11369 );
11370 induce_partial_order_32s_6k_omp(
11371 t,
11372 sa,
11373 n,
11374 k,
11375 buckets,
11376 first_lms_suffix,
11377 left_suffixes_count,
11378 threads,
11379 thread_state,
11380 );
11381 }
11382
11383 let names = unsafe {
11384 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11385 if (n / 8192) < k {
11386 renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
11387 sa,
11388 n,
11389 m,
11390 threads,
11391 thread_state,
11392 )
11393 } else {
11394 renumber_and_gather_lms_suffixes_omp(sa, n, m, fs, threads, thread_state)
11395 }
11396 };
11397
11398 if names < m {
11399 let f = if (n / 8192) < k {
11400 unsafe {
11401 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11402 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11403 compact_lms_suffixes_32s_omp(t, sa, n, m, fs, threads, thread_state)
11404 }
11405 } else {
11406 0
11407 };
11408
11409 let new_t_start =
11410 total_len - usize::try_from(m - f).expect("m - f must be non-negative");
11411 let recursive_n = m - f;
11412 let recursive_fs = fs + n - 2 * m + f;
11413 if libsais_main_32s_recursion(
11414 unsafe {
11415 std::slice::from_raw_parts_mut(sa_ptr, total_len)[new_t_start..]
11416 .as_mut_ptr()
11417 },
11418 sa_ptr,
11419 sa_capacity,
11420 recursive_n,
11421 names - f,
11422 recursive_fs,
11423 threads,
11424 thread_state,
11425 _local_buffer,
11426 ) != 0
11427 {
11428 return -2;
11429 }
11430
11431 unsafe {
11432 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11433 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11434 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11435 reconstruct_compacted_lms_suffixes_32s_2k_omp(
11436 t,
11437 sa,
11438 n,
11439 k,
11440 m,
11441 fs,
11442 f,
11443 buckets,
11444 SaSint::from(use_local_buffer),
11445 threads,
11446 thread_state,
11447 );
11448 }
11449 } else {
11450 unsafe {
11451 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11452 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11453 count_lms_suffixes_32s_2k(t, n, k, buckets);
11454 }
11455 }
11456
11457 unsafe {
11458 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11459 initialize_buckets_start_and_end_32s_4k(k, buckets);
11460 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11461 place_lms_suffixes_histogram_32s_4k(sa, n, k, m, buckets);
11462 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11463 induce_final_order_32s_4k(t, sa, n, k, buckets, threads, thread_state);
11464 }
11465 } else {
11466 unsafe {
11467 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11468 sa[0] = sa[n_usize - 1];
11469 }
11470
11471 unsafe {
11472 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11473 initialize_buckets_start_and_end_32s_6k(k, buckets);
11474 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11475 place_lms_suffixes_histogram_32s_6k(sa, n, k, m, buckets);
11476 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11477 induce_final_order_32s_6k(t, sa, n, k, buckets, threads, thread_state);
11478 }
11479 }
11480
11481 return 0;
11482 } else if k > 0 && n <= SAINT_MAX / 2 && ((fs / k) >= 4 || (local_buffer_size / k) >= 4) {
11483 let k_usize = usize::try_from(k).expect("k must be non-negative");
11484 let alignment = if fs >= 1024 && ((fs - 1024) / k) >= 4 {
11485 1024usize
11486 } else {
11487 16usize
11488 };
11489 let need = 4 * k_usize;
11490 let use_local_buffer = local_buffer_size > fs;
11491 let mut bucket_free_space = SaSint::from(use_local_buffer);
11492 let buckets_ptr = if use_local_buffer {
11493 _local_buffer.as_mut_ptr()
11494 } else {
11495 unsafe {
11496 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11497 let start =
11498 if fs_usize >= need + alignment && ((fs_usize - alignment) / k_usize) >= 4 {
11499 let byte_ptr = sa[total_len - need - alignment..].as_mut_ptr() as usize;
11500 let aligned = align_up(byte_ptr, alignment * mem::size_of::<SaSint>());
11501 (aligned - sa_ptr as usize) / mem::size_of::<SaSint>()
11502 } else {
11503 total_len - need
11504 };
11505 bucket_free_space =
11506 SaSint::try_from(start - n_usize).expect("bucket free space must fit SaSint");
11507 sa[start..].as_mut_ptr()
11508 }
11509 };
11510
11511 let m = unsafe {
11512 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11513 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11514 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11515 count_and_gather_lms_suffixes_32s_2k_omp(
11516 t,
11517 sa,
11518 n,
11519 k,
11520 buckets,
11521 bucket_free_space,
11522 threads,
11523 thread_state,
11524 )
11525 };
11526 if m > 1 {
11527 let m_usize = usize::try_from(m).expect("m must be non-negative");
11528 unsafe {
11529 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11530 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11531 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11532 initialize_buckets_for_radix_and_partial_sorting_32s_4k(
11533 t,
11534 k,
11535 buckets,
11536 sa[n_usize - m_usize],
11537 );
11538 let (_, induction_bucket) = buckets.split_at_mut(1);
11539 radix_sort_lms_suffixes_32s_2k_omp(
11540 t,
11541 sa,
11542 n,
11543 m,
11544 induction_bucket,
11545 threads,
11546 thread_state,
11547 );
11548 radix_sort_set_markers_32s_4k_omp(sa, k, induction_bucket, threads);
11549 place_lms_suffixes_interval_32s_4k(sa, n, k, m - 1, buckets);
11550 induce_partial_order_32s_4k_omp(t, sa, n, k, buckets, threads, thread_state);
11551 }
11552
11553 let names = unsafe {
11554 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11555 renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(sa, n, m, threads, thread_state)
11556 };
11557 if names < m {
11558 let f = unsafe {
11559 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11560 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11561 compact_lms_suffixes_32s_omp(t, sa, n, m, fs, threads, thread_state)
11562 };
11563
11564 let new_t_start =
11565 total_len - usize::try_from(m - f).expect("m - f must be non-negative");
11566 if libsais_main_32s_recursion(
11567 unsafe {
11568 std::slice::from_raw_parts_mut(sa_ptr, total_len)[new_t_start..]
11569 .as_mut_ptr()
11570 },
11571 sa_ptr,
11572 sa_capacity,
11573 m - f,
11574 names - f,
11575 fs + n - 2 * m + f,
11576 threads,
11577 thread_state,
11578 _local_buffer,
11579 ) != 0
11580 {
11581 return -2;
11582 }
11583
11584 unsafe {
11585 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11586 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11587 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11588 reconstruct_compacted_lms_suffixes_32s_2k_omp(
11589 t,
11590 sa,
11591 n,
11592 k,
11593 m,
11594 fs,
11595 f,
11596 buckets,
11597 SaSint::from(use_local_buffer),
11598 threads,
11599 thread_state,
11600 );
11601 }
11602 } else {
11603 unsafe {
11604 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11605 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11606 count_lms_suffixes_32s_2k(t, n, k, buckets);
11607 }
11608 }
11609 } else {
11610 unsafe {
11611 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11612 sa[0] = sa[n_usize - 1];
11613 }
11614 }
11615
11616 unsafe {
11617 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11618 initialize_buckets_start_and_end_32s_4k(k, buckets);
11619 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11620 place_lms_suffixes_histogram_32s_4k(sa, n, k, m, buckets);
11621 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11622 induce_final_order_32s_4k(t, sa, n, k, buckets, threads, thread_state);
11623 }
11624
11625 return 0;
11626 } else if k > 0 && ((fs / k) >= 2 || (local_buffer_size / k) >= 2) {
11627 let k_usize = usize::try_from(k).expect("k must be non-negative");
11628 let alignment = if fs >= 1024 && ((fs - 1024) / k) >= 2 {
11629 1024usize
11630 } else {
11631 16usize
11632 };
11633 let need = 2 * k_usize;
11634 let use_local_buffer = local_buffer_size > fs;
11635 let mut bucket_free_space = SaSint::from(use_local_buffer);
11636 let buckets_ptr = if use_local_buffer {
11637 _local_buffer.as_mut_ptr()
11638 } else {
11639 unsafe {
11640 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11641 let start =
11642 if fs_usize >= need + alignment && ((fs_usize - alignment) / k_usize) >= 2 {
11643 let byte_ptr = sa[total_len - need - alignment..].as_mut_ptr() as usize;
11644 let aligned = align_up(byte_ptr, alignment * mem::size_of::<SaSint>());
11645 (aligned - sa_ptr as usize) / mem::size_of::<SaSint>()
11646 } else {
11647 total_len - need
11648 };
11649 bucket_free_space =
11650 SaSint::try_from(start - n_usize).expect("bucket free space must fit SaSint");
11651 sa[start..].as_mut_ptr()
11652 }
11653 };
11654
11655 let m = unsafe {
11656 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11657 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11658 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11659 count_and_gather_lms_suffixes_32s_2k_omp(
11660 t,
11661 sa,
11662 n,
11663 k,
11664 buckets,
11665 bucket_free_space,
11666 threads,
11667 thread_state,
11668 )
11669 };
11670 if m > 1 {
11671 let m_usize = usize::try_from(m).expect("m must be non-negative");
11672 unsafe {
11673 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11674 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11675 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11676 initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
11677 t,
11678 k,
11679 buckets,
11680 sa[n_usize - m_usize],
11681 );
11682 let (_, induction_bucket) = buckets.split_at_mut(1);
11683 radix_sort_lms_suffixes_32s_2k_omp(
11684 t,
11685 sa,
11686 n,
11687 m,
11688 induction_bucket,
11689 threads,
11690 thread_state,
11691 );
11692 place_lms_suffixes_interval_32s_2k(sa, n, k, m - 1, buckets);
11693 }
11694
11695 unsafe {
11696 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11697 initialize_buckets_start_and_end_32s_2k(k, buckets);
11698 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11699 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11700 induce_partial_order_32s_2k_omp(t, sa, n, k, buckets, threads, thread_state);
11701 }
11702
11703 let names = unsafe {
11704 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11705 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11706 renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(t, sa, n, m, threads)
11707 };
11708 if names < m {
11709 let f = unsafe {
11710 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11711 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11712 compact_lms_suffixes_32s_omp(t, sa, n, m, fs, threads, thread_state)
11713 };
11714
11715 let new_t_start =
11716 total_len - usize::try_from(m - f).expect("m - f must be non-negative");
11717 if libsais_main_32s_recursion(
11718 unsafe {
11719 std::slice::from_raw_parts_mut(sa_ptr, total_len)[new_t_start..]
11720 .as_mut_ptr()
11721 },
11722 sa_ptr,
11723 sa_capacity,
11724 m - f,
11725 names - f,
11726 fs + n - 2 * m + f,
11727 threads,
11728 thread_state,
11729 _local_buffer,
11730 ) != 0
11731 {
11732 return -2;
11733 }
11734
11735 unsafe {
11736 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11737 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11738 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11739 reconstruct_compacted_lms_suffixes_32s_2k_omp(
11740 t,
11741 sa,
11742 n,
11743 k,
11744 m,
11745 fs,
11746 f,
11747 buckets,
11748 SaSint::from(use_local_buffer),
11749 threads,
11750 thread_state,
11751 );
11752 }
11753 } else {
11754 unsafe {
11755 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11756 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11757 count_lms_suffixes_32s_2k(t, n, k, buckets);
11758 }
11759 }
11760 } else {
11761 unsafe {
11762 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11763 sa[0] = sa[n_usize - 1];
11764 }
11765 }
11766
11767 unsafe {
11768 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11769 initialize_buckets_end_32s_2k(k, buckets);
11770 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11771 place_lms_suffixes_histogram_32s_2k(sa, n, k, m, buckets);
11772 }
11773
11774 unsafe {
11775 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11776 initialize_buckets_start_and_end_32s_2k(k, buckets);
11777 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11778 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11779 induce_final_order_32s_2k(t, sa, n, k, buckets, threads, thread_state);
11780 }
11781
11782 return 0;
11783 } else {
11784 let k_usize = usize::try_from(k).expect("k must be non-negative");
11785 let mut heap_buckets = if fs < k { Some(vec![0; k_usize]) } else { None };
11786 let alignment = if fs >= 1024 && (fs - 1024) >= k {
11787 1024usize
11788 } else {
11789 16usize
11790 };
11791 let mut buckets_ptr = if let Some(ref mut heap) = heap_buckets {
11792 heap.as_mut_ptr()
11793 } else {
11794 unsafe {
11795 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11796 let start = if fs_usize >= k_usize + alignment {
11797 let byte_ptr = sa[total_len - k_usize - alignment..].as_mut_ptr() as usize;
11798 let aligned = align_up(byte_ptr, alignment * mem::size_of::<SaSint>());
11799 (aligned - sa_ptr as usize) / mem::size_of::<SaSint>()
11800 } else {
11801 total_len - k_usize
11802 };
11803 sa[start..].as_mut_ptr()
11804 }
11805 };
11806
11807 if buckets_ptr.is_null() {
11808 return -2;
11809 }
11810
11811 unsafe {
11812 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11813 sa[..n_usize].fill(0);
11814 }
11815
11816 unsafe {
11817 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11818 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
11819 count_suffixes_32s(t, n, k, buckets);
11820 }
11821 unsafe {
11822 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
11823 initialize_buckets_end_32s_1k(k, buckets);
11824 }
11825
11826 let m = unsafe {
11827 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11828 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11829 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
11830 radix_sort_lms_suffixes_32s_1k(t, sa, n, buckets)
11831 };
11832 if m > 1 {
11833 unsafe {
11834 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11835 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11836 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
11837 induce_partial_order_32s_1k_omp(t, sa, n, k, buckets, threads, thread_state);
11838 }
11839
11840 let names = unsafe {
11841 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11842 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11843 renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(t, sa, n, m, threads)
11844 };
11845 if names < m {
11846 if heap_buckets.is_some() {
11847 let _ = heap_buckets.take();
11848 buckets_ptr = std::ptr::null_mut();
11849 }
11850
11851 let f = unsafe {
11852 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11853 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11854 compact_lms_suffixes_32s_omp(t, sa, n, m, fs, threads, thread_state)
11855 };
11856
11857 let new_t_start =
11858 total_len - usize::try_from(m - f).expect("m - f must be non-negative");
11859 if libsais_main_32s_recursion(
11860 unsafe {
11861 std::slice::from_raw_parts_mut(sa_ptr, total_len)[new_t_start..]
11862 .as_mut_ptr()
11863 },
11864 sa_ptr,
11865 sa_capacity,
11866 m - f,
11867 names - f,
11868 fs + n - 2 * m + f,
11869 threads,
11870 thread_state,
11871 _local_buffer,
11872 ) != 0
11873 {
11874 return -2;
11875 }
11876
11877 unsafe {
11878 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11879 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11880 reconstruct_compacted_lms_suffixes_32s_1k_omp(
11881 t,
11882 sa,
11883 n,
11884 m,
11885 fs,
11886 f,
11887 threads,
11888 thread_state,
11889 );
11890 }
11891
11892 if buckets_ptr.is_null() {
11893 heap_buckets = Some(vec![0; k_usize]);
11894 buckets_ptr = heap_buckets
11895 .as_mut()
11896 .expect("heap buckets must exist")
11897 .as_mut_ptr();
11898 if buckets_ptr.is_null() {
11899 return -2;
11900 }
11901 }
11902 }
11903
11904 unsafe {
11905 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11906 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
11907 count_suffixes_32s(t, n, k, buckets);
11908 }
11909 unsafe {
11910 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
11911 initialize_buckets_end_32s_1k(k, buckets);
11912 }
11913 unsafe {
11914 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11915 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11916 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
11917 place_lms_suffixes_interval_32s_1k(t, sa, k, m, buckets);
11918 }
11919 }
11920
11921 unsafe {
11922 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11923 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11924 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
11925 induce_final_order_32s_1k(t, sa, n, k, buckets, threads, thread_state);
11926 }
11927
11928 0
11929 }
11930}
11931
11932fn libsais_main_32s_entry(
11933 t: &mut [SaSint],
11934 sa: &mut [SaSint],
11935 n: SaSint,
11936 k: SaSint,
11937 fs: SaSint,
11938 threads: SaSint,
11939 thread_state: &mut [ThreadState],
11940) -> SaSint {
11941 let mut local_buffer = [0; 2 * LIBSAIS_LOCAL_BUFFER_SIZE];
11942 libsais_main_32s_recursion(
11943 t.as_mut_ptr(),
11944 sa.as_mut_ptr(),
11945 sa.len(),
11946 n,
11947 k,
11948 fs,
11949 threads,
11950 thread_state,
11951 &mut local_buffer[LIBSAIS_LOCAL_BUFFER_SIZE..],
11952 )
11953}
11954
11955fn libsais_main_8u(
11956 t: &[u8],
11957 sa: &mut [SaSint],
11958 buckets: &mut [SaSint],
11959 flags: SaSint,
11960 r: SaSint,
11961 i: Option<&mut [SaSint]>,
11962 fs: SaSint,
11963 freq: Option<&mut [SaSint]>,
11964 threads: SaSint,
11965 thread_state: &mut [ThreadState],
11966) -> SaSint {
11967 let n = SaSint::try_from(t.len()).expect("input length must fit SaSint");
11968 let n_usize = usize::try_from(n).expect("n must be non-negative");
11969 let fs = fs.min(SAINT_MAX - n);
11970
11971 let m = count_and_gather_lms_suffixes_8u_omp(t, sa, n, buckets, threads, thread_state);
11972 let k = initialize_buckets_start_and_end_8u(buckets, freq);
11973
11974 if (flags & LIBSAIS_FLAGS_GSA) != 0 && (buckets[0] != 0 || buckets[2] != 0 || buckets[3] != 1) {
11975 return -1;
11976 }
11977
11978 if m > 0 {
11979 let m_usize = usize::try_from(m).expect("m must be non-negative");
11980 let first_lms_suffix = sa[n_usize - m_usize];
11981 let left_suffixes_count =
11982 initialize_buckets_for_lms_suffixes_radix_sort_8u(t, buckets, first_lms_suffix);
11983
11984 if threads > 1 && n >= 65_536 {
11985 sa[..n_usize - m_usize].fill(0);
11986 }
11987 radix_sort_lms_suffixes_8u_omp(t, sa, n, m, flags, buckets, threads, thread_state);
11988 if threads > 1 && n >= 65_536 {
11989 sa[n_usize - m_usize..n_usize].fill(0);
11990 }
11991
11992 initialize_buckets_for_partial_sorting_8u(
11993 t,
11994 buckets,
11995 first_lms_suffix,
11996 left_suffixes_count,
11997 );
11998 induce_partial_order_8u_omp(
11999 t,
12000 sa,
12001 n,
12002 k,
12003 flags,
12004 buckets,
12005 first_lms_suffix,
12006 left_suffixes_count,
12007 threads,
12008 thread_state,
12009 );
12010
12011 let names = renumber_and_gather_lms_suffixes_omp(sa, n, m, fs, threads, thread_state);
12012 if names < m {
12013 let recursive_text_start =
12014 n_usize + usize::try_from(fs).expect("fs must be non-negative") - m_usize;
12015 let recursive_fs = fs + n - 2 * m;
12016
12017 let index = libsais_main_32s_entry(
12018 unsafe {
12019 std::slice::from_raw_parts_mut(sa[recursive_text_start..].as_mut_ptr(), m_usize)
12020 },
12021 sa,
12022 m,
12023 names,
12024 recursive_fs,
12025 threads,
12026 thread_state,
12027 );
12028
12029 if index != 0 {
12030 return -2;
12031 }
12032
12033 gather_lms_suffixes_8u_omp(t, sa, n, threads, thread_state);
12034 reconstruct_lms_suffixes_omp(sa, n, m, threads);
12035 }
12036
12037 place_lms_suffixes_interval_8u(sa, n, m, flags, buckets);
12038 } else {
12039 sa[..n_usize].fill(0);
12040 }
12041
12042 induce_final_order_8u_omp(t, sa, n, k, flags, r, i, buckets, threads, thread_state)
12043}
12044
12045fn libsais_main(
12046 t: &[u8],
12047 sa: &mut [SaSint],
12048 flags: SaSint,
12049 r: SaSint,
12050 i: Option<&mut [SaSint]>,
12051 fs: SaSint,
12052 freq: Option<&mut [SaSint]>,
12053 threads: SaSint,
12054) -> SaSint {
12055 let threads = normalize_omp_threads(threads);
12056 if threads > 1 {
12057 let mut thread_state = match alloc_thread_state(threads) {
12058 Some(thread_state) => thread_state,
12059 None => return -2,
12060 };
12061 let mut buckets = vec![0; 8 * ALPHABET_SIZE];
12062
12063 libsais_main_8u(
12064 t,
12065 sa,
12066 &mut buckets,
12067 flags,
12068 r,
12069 i,
12070 fs,
12071 freq,
12072 threads,
12073 &mut thread_state,
12074 )
12075 } else {
12076 let mut thread_state = [];
12077 let mut buckets = [0; 8 * ALPHABET_SIZE];
12078
12079 libsais_main_8u(
12080 t,
12081 sa,
12082 &mut buckets,
12083 flags,
12084 r,
12085 i,
12086 fs,
12087 freq,
12088 threads,
12089 &mut thread_state,
12090 )
12091 }
12092}
12093
12094fn libsais_main_int(
12095 t: &mut [SaSint],
12096 sa: &mut [SaSint],
12097 k: SaSint,
12098 fs: SaSint,
12099 threads: SaSint,
12100) -> SaSint {
12101 let threads = normalize_omp_threads(threads);
12102 let mut thread_state = if threads > 1 {
12103 match alloc_thread_state(threads) {
12104 Some(thread_state) => thread_state,
12105 None => return -2,
12106 }
12107 } else {
12108 Vec::new()
12109 };
12110
12111 libsais_main_32s_entry(
12112 t,
12113 sa,
12114 SaSint::try_from(t.len()).expect("input length must fit SaSint"),
12115 k,
12116 fs,
12117 threads,
12118 &mut thread_state,
12119 )
12120}
12121
12122fn libsais_main_ctx(
12123 ctx: &mut Context,
12124 t: &[u8],
12125 sa: &mut [SaSint],
12126 flags: SaSint,
12127 r: SaSint,
12128 i: Option<&mut [SaSint]>,
12129 fs: SaSint,
12130 freq: Option<&mut [SaSint]>,
12131) -> SaSint {
12132 if ctx.threads <= 0 || ctx.buckets.len() != 8 * ALPHABET_SIZE {
12133 return -2;
12134 }
12135
12136 let mut empty_thread_state = [];
12137 let thread_state = if ctx.threads > 1 {
12138 match ctx.thread_state.as_deref_mut() {
12139 Some(thread_state) if thread_state.len() >= ctx.threads as usize => thread_state,
12140 None => return -2,
12141 Some(_) => return -2,
12142 }
12143 } else {
12144 &mut empty_thread_state
12145 };
12146
12147 libsais_main_8u(
12148 t,
12149 sa,
12150 &mut ctx.buckets,
12151 flags,
12152 r,
12153 i,
12154 fs,
12155 freq,
12156 ctx.threads as SaSint,
12157 thread_state,
12158 )
12159}
12160
12161#[cfg(feature = "upstream-c")]
12162unsafe extern "C" {
12163 fn probe_public_libsais_freq(
12164 t: *const u8,
12165 sa: *mut SaSint,
12166 n: SaSint,
12167 fs: SaSint,
12168 freq: *mut SaSint,
12169 ) -> SaSint;
12170
12171 fn probe_public_libsais_omp_freq(
12172 t: *const u8,
12173 sa: *mut SaSint,
12174 n: SaSint,
12175 fs: SaSint,
12176 freq: *mut SaSint,
12177 threads: SaSint,
12178 ) -> SaSint;
12179}
12180
12181#[cfg(feature = "upstream-c")]
12192pub fn libsais_upstream_c(
12193 t: &[u8],
12194 sa: &mut [SaSint],
12195 fs: SaSint,
12196 freq: Option<&mut [SaSint]>,
12197) -> SaSint {
12198 if fs < 0
12199 || t.len() > SaSint::MAX as usize
12200 || sa.len()
12201 < t.len()
12202 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12203 {
12204 return -1;
12205 }
12206 if let Some(freq) = freq.as_ref() {
12207 if freq.len() < ALPHABET_SIZE {
12208 return -1;
12209 }
12210 }
12211
12212 let n = t.len() as SaSint;
12213 let freq_ptr = freq.map_or(std::ptr::null_mut(), |freq| freq.as_mut_ptr());
12214 unsafe { probe_public_libsais_freq(t.as_ptr(), sa.as_mut_ptr(), n, fs, freq_ptr) }
12215}
12216
12217#[cfg(feature = "upstream-c")]
12229pub fn libsais_upstream_c_omp(
12230 t: &[u8],
12231 sa: &mut [SaSint],
12232 fs: SaSint,
12233 freq: Option<&mut [SaSint]>,
12234 threads: SaSint,
12235) -> SaSint {
12236 if threads < 0 {
12237 return -1;
12238 }
12239 if fs < 0
12240 || t.len() > SaSint::MAX as usize
12241 || sa.len()
12242 < t.len()
12243 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12244 {
12245 return -1;
12246 }
12247 if let Some(freq) = freq.as_ref() {
12248 if freq.len() < ALPHABET_SIZE {
12249 return -1;
12250 }
12251 }
12252
12253 let n = t.len() as SaSint;
12254 let freq_ptr = freq.map_or(std::ptr::null_mut(), |freq| freq.as_mut_ptr());
12255 unsafe {
12256 probe_public_libsais_omp_freq(t.as_ptr(), sa.as_mut_ptr(), n, fs, freq_ptr, threads.max(1))
12257 }
12258}
12259
12260pub fn libsais(t: &[u8], sa: &mut [SaSint], fs: SaSint, freq: Option<&mut [SaSint]>) -> SaSint {
12272 if fs < 0
12273 || sa.len()
12274 < t.len()
12275 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12276 {
12277 return -1;
12278 }
12279 if let Some(freq) = freq.as_ref() {
12280 if freq.len() < ALPHABET_SIZE {
12281 return -1;
12282 }
12283 }
12284
12285 let n = t.len();
12286 if n <= 1 {
12287 if let Some(freq) = freq {
12288 freq[..ALPHABET_SIZE].fill(0);
12289 if n == 1 {
12290 freq[t[0] as usize] += 1;
12291 }
12292 }
12293 if n == 1 {
12294 sa[0] = 0;
12295 }
12296 return 0;
12297 }
12298
12299 libsais_main(t, sa, LIBSAIS_FLAGS_NONE, 0, None, fs, freq, 1)
12300}
12301
12302pub fn libsais_gsa(t: &[u8], sa: &mut [SaSint], fs: SaSint, freq: Option<&mut [SaSint]>) -> SaSint {
12314 if fs < 0
12315 || sa.len()
12316 < t.len()
12317 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12318 {
12319 return -1;
12320 }
12321 if let Some(freq) = freq.as_ref() {
12322 if freq.len() < ALPHABET_SIZE {
12323 return -1;
12324 }
12325 }
12326
12327 let n = t.len();
12328 if n > 0 && t[n - 1] != 0 {
12329 return -1;
12330 }
12331
12332 if n <= 1 {
12333 if let Some(freq) = freq {
12334 freq[..ALPHABET_SIZE].fill(0);
12335 if n == 1 {
12336 freq[t[0] as usize] += 1;
12337 }
12338 }
12339 if n == 1 {
12340 sa[0] = 0;
12341 }
12342 return 0;
12343 }
12344
12345 libsais_main(t, sa, LIBSAIS_FLAGS_GSA, 0, None, fs, freq, 1)
12346}
12347
12348pub fn libsais_int(t: &mut [SaSint], sa: &mut [SaSint], k: SaSint, fs: SaSint) -> SaSint {
12361 if fs < 0
12362 || sa.len()
12363 < t.len()
12364 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12365 {
12366 return -1;
12367 }
12368
12369 if t.len() <= 1 {
12370 if t.len() == 1 {
12371 sa[0] = 0;
12372 }
12373 return 0;
12374 }
12375
12376 libsais_main_int(t, sa, k, fs, 1)
12377}
12378
12379pub fn libsais_ctx(
12392 ctx: &mut Context,
12393 t: &[u8],
12394 sa: &mut [SaSint],
12395 fs: SaSint,
12396 freq: Option<&mut [SaSint]>,
12397) -> SaSint {
12398 if fs < 0
12399 || sa.len()
12400 < t.len()
12401 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12402 {
12403 return -1;
12404 }
12405 if let Some(freq) = freq.as_ref() {
12406 if freq.len() < ALPHABET_SIZE {
12407 return -1;
12408 }
12409 }
12410
12411 let n = t.len();
12412 if n <= 1 {
12413 if let Some(freq) = freq {
12414 freq[..ALPHABET_SIZE].fill(0);
12415 if n == 1 {
12416 freq[t[0] as usize] += 1;
12417 }
12418 }
12419 if n == 1 {
12420 sa[0] = 0;
12421 }
12422 return 0;
12423 }
12424
12425 libsais_main_ctx(ctx, t, sa, LIBSAIS_FLAGS_NONE, 0, None, fs, freq)
12426}
12427
12428pub fn libsais_gsa_ctx(
12441 ctx: &mut Context,
12442 t: &[u8],
12443 sa: &mut [SaSint],
12444 fs: SaSint,
12445 freq: Option<&mut [SaSint]>,
12446) -> SaSint {
12447 if fs < 0
12448 || sa.len()
12449 < t.len()
12450 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12451 {
12452 return -1;
12453 }
12454 if let Some(freq) = freq.as_ref() {
12455 if freq.len() < ALPHABET_SIZE {
12456 return -1;
12457 }
12458 }
12459
12460 let n = t.len();
12461 if n > 0 && t[n - 1] != 0 {
12462 return -1;
12463 }
12464
12465 if n <= 1 {
12466 if let Some(freq) = freq {
12467 freq[..ALPHABET_SIZE].fill(0);
12468 if n == 1 {
12469 freq[t[0] as usize] += 1;
12470 }
12471 }
12472 if n == 1 {
12473 sa[0] = 0;
12474 }
12475 return 0;
12476 }
12477
12478 libsais_main_ctx(ctx, t, sa, LIBSAIS_FLAGS_GSA, 0, None, fs, freq)
12479}
12480
12481pub fn libsais_bwt(
12494 t: &[u8],
12495 u: &mut [u8],
12496 a: &mut [SaSint],
12497 fs: SaSint,
12498 freq: Option<&mut [SaSint]>,
12499) -> SaSint {
12500 if fs < 0
12501 || u.len() < t.len()
12502 || a.len()
12503 < t.len()
12504 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12505 {
12506 return -1;
12507 }
12508 if let Some(freq) = freq.as_ref() {
12509 if freq.len() < ALPHABET_SIZE {
12510 return -1;
12511 }
12512 }
12513
12514 let n = t.len();
12515 if n <= 1 {
12516 if let Some(freq) = freq {
12517 freq[..ALPHABET_SIZE].fill(0);
12518 if n == 1 {
12519 u[0] = t[0];
12520 freq[t[0] as usize] += 1;
12521 }
12522 } else if n == 1 {
12523 u[0] = t[0];
12524 }
12525 return n as SaSint;
12526 }
12527
12528 let mut index = libsais_main(t, a, LIBSAIS_FLAGS_BWT, 0, None, fs, freq, 1);
12529 if index >= 0 {
12530 index += 1;
12531 let split = usize::try_from(index).expect("index must be non-negative");
12532 u[0] = t[n - 1];
12533 bwt_copy_8u_omp(&mut u[1..split], &a[..split - 1], index - 1, 1);
12534 bwt_copy_8u_omp(
12535 &mut u[split..n],
12536 &a[split..n],
12537 SaSint::try_from(n - split).expect("fits"),
12538 1,
12539 );
12540 }
12541 index
12542}
12543
12544pub fn libsais_bwt_aux(
12559 t: &[u8],
12560 u: &mut [u8],
12561 a: &mut [SaSint],
12562 fs: SaSint,
12563 freq: Option<&mut [SaSint]>,
12564 r: SaSint,
12565 i: &mut [SaSint],
12566) -> SaSint {
12567 let n = t.len();
12568 if fs < 0
12569 || r < 2
12570 || (r & (r - 1)) != 0
12571 || u.len() < n
12572 || a.len() < n.saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12573 || freq.as_ref().is_some_and(|freq| freq.len() < ALPHABET_SIZE)
12574 {
12575 return -1;
12576 }
12577 let sample_count = if n == 0 {
12578 1
12579 } else {
12580 usize::try_from((SaSint::try_from(n).expect("input length must fit SaSint") - 1) / r)
12581 .expect("sample count must be non-negative")
12582 + 1
12583 };
12584 if i.len() < sample_count {
12585 return -1;
12586 }
12587
12588 if n <= 1 {
12589 if let Some(freq) = freq {
12590 freq[..ALPHABET_SIZE].fill(0);
12591 if n == 1 {
12592 u[0] = t[0];
12593 freq[t[0] as usize] += 1;
12594 }
12595 } else if n == 1 {
12596 u[0] = t[0];
12597 }
12598 i[0] = n as SaSint;
12599 return 0;
12600 }
12601
12602 let index = libsais_main(t, a, LIBSAIS_FLAGS_BWT, r, Some(i), fs, freq, 1);
12603 if index == 0 {
12604 let split = usize::try_from(i[0]).expect("primary index must be non-negative");
12605 u[0] = t[n - 1];
12606 bwt_copy_8u_omp(&mut u[1..split], &a[..split - 1], i[0] - 1, 1);
12607 bwt_copy_8u_omp(
12608 &mut u[split..n],
12609 &a[split..n],
12610 SaSint::try_from(n - split).expect("fits"),
12611 1,
12612 );
12613 }
12614 index
12615}
12616
12617pub fn libsais_bwt_ctx(
12631 ctx: &mut Context,
12632 t: &[u8],
12633 u: &mut [u8],
12634 a: &mut [SaSint],
12635 fs: SaSint,
12636 freq: Option<&mut [SaSint]>,
12637) -> SaSint {
12638 if fs < 0
12639 || u.len() < t.len()
12640 || a.len()
12641 < t.len()
12642 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12643 {
12644 return -1;
12645 }
12646 if let Some(freq) = freq.as_ref() {
12647 if freq.len() < ALPHABET_SIZE {
12648 return -1;
12649 }
12650 }
12651
12652 let n = t.len();
12653 if n <= 1 {
12654 if let Some(freq) = freq {
12655 freq[..ALPHABET_SIZE].fill(0);
12656 if n == 1 {
12657 u[0] = t[0];
12658 freq[t[0] as usize] += 1;
12659 }
12660 } else if n == 1 {
12661 u[0] = t[0];
12662 }
12663 return n as SaSint;
12664 }
12665
12666 let mut index = libsais_main_ctx(ctx, t, a, LIBSAIS_FLAGS_BWT, 0, None, fs, freq);
12667 if index >= 0 {
12668 index += 1;
12669 let split = usize::try_from(index).expect("index must be non-negative");
12670 u[0] = t[n - 1];
12671 bwt_copy_8u_omp(
12672 &mut u[1..split],
12673 &a[..split - 1],
12674 index - 1,
12675 ctx.threads as SaSint,
12676 );
12677 bwt_copy_8u_omp(
12678 &mut u[split..n],
12679 &a[split..n],
12680 SaSint::try_from(n - split).expect("fits"),
12681 ctx.threads as SaSint,
12682 );
12683 }
12684 index
12685}
12686
12687pub fn libsais_bwt_aux_ctx(
12703 ctx: &mut Context,
12704 t: &[u8],
12705 u: &mut [u8],
12706 a: &mut [SaSint],
12707 fs: SaSint,
12708 freq: Option<&mut [SaSint]>,
12709 r: SaSint,
12710 i: &mut [SaSint],
12711) -> SaSint {
12712 let n = t.len();
12713 if fs < 0
12714 || r < 2
12715 || (r & (r - 1)) != 0
12716 || u.len() < n
12717 || a.len() < n.saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12718 {
12719 return -1;
12720 }
12721 if let Some(freq) = freq.as_ref() {
12722 if freq.len() < ALPHABET_SIZE {
12723 return -1;
12724 }
12725 }
12726 let sample_count = if n == 0 {
12727 1
12728 } else {
12729 usize::try_from((SaSint::try_from(n).expect("input length must fit SaSint") - 1) / r)
12730 .expect("sample count must be non-negative")
12731 + 1
12732 };
12733 if i.len() < sample_count {
12734 return -1;
12735 }
12736
12737 if n <= 1 {
12738 if let Some(freq) = freq {
12739 freq[..ALPHABET_SIZE].fill(0);
12740 if n == 1 {
12741 u[0] = t[0];
12742 freq[t[0] as usize] += 1;
12743 }
12744 } else if n == 1 {
12745 u[0] = t[0];
12746 }
12747 i[0] = n as SaSint;
12748 return 0;
12749 }
12750
12751 let index = libsais_main_ctx(ctx, t, a, LIBSAIS_FLAGS_BWT, r, Some(i), fs, freq);
12752 if index == 0 {
12753 let split = usize::try_from(i[0]).expect("primary index must be non-negative");
12754 u[0] = t[n - 1];
12755 bwt_copy_8u_omp(
12756 &mut u[1..split],
12757 &a[..split - 1],
12758 i[0] - 1,
12759 ctx.threads as SaSint,
12760 );
12761 bwt_copy_8u_omp(
12762 &mut u[split..n],
12763 &a[split..n],
12764 SaSint::try_from(n - split).expect("fits"),
12765 ctx.threads as SaSint,
12766 );
12767 }
12768 index
12769}
12770
12771pub fn create_ctx_omp(threads: SaSint) -> Option<Context> {
12780 if threads < 0 {
12781 return None;
12782 }
12783
12784 create_ctx_main(normalize_omp_threads(threads))
12785}
12786
12787pub fn libsais_omp(
12800 t: &[u8],
12801 sa: &mut [SaSint],
12802 fs: SaSint,
12803 freq: Option<&mut [SaSint]>,
12804 threads: SaSint,
12805) -> SaSint {
12806 if threads < 0 {
12807 return -1;
12808 }
12809 if let Some(freq) = freq.as_ref() {
12810 if freq.len() < ALPHABET_SIZE {
12811 return -1;
12812 }
12813 }
12814 let n = t.len();
12815 if n <= 1 {
12816 if let Some(freq) = freq {
12817 freq[..ALPHABET_SIZE].fill(0);
12818 if n == 1 {
12819 sa[0] = 0;
12820 freq[t[0] as usize] += 1;
12821 }
12822 } else if n == 1 {
12823 sa[0] = 0;
12824 }
12825 return 0;
12826 }
12827
12828 libsais_main(
12829 t,
12830 sa,
12831 LIBSAIS_FLAGS_NONE,
12832 0,
12833 None,
12834 fs,
12835 freq,
12836 normalize_omp_threads(threads),
12837 )
12838}
12839
12840pub fn libsais_gsa_omp(
12853 t: &[u8],
12854 sa: &mut [SaSint],
12855 fs: SaSint,
12856 freq: Option<&mut [SaSint]>,
12857 threads: SaSint,
12858) -> SaSint {
12859 if threads < 0 || t.last().copied().unwrap_or(0) != 0 {
12860 return -1;
12861 }
12862 if let Some(freq) = freq.as_ref() {
12863 if freq.len() < ALPHABET_SIZE {
12864 return -1;
12865 }
12866 }
12867 let n = t.len();
12868 if n <= 1 {
12869 if let Some(freq) = freq {
12870 freq[..ALPHABET_SIZE].fill(0);
12871 if n == 1 {
12872 sa[0] = 0;
12873 freq[t[0] as usize] += 1;
12874 }
12875 } else if n == 1 {
12876 sa[0] = 0;
12877 }
12878 return 0;
12879 }
12880
12881 libsais_main(
12882 t,
12883 sa,
12884 LIBSAIS_FLAGS_GSA,
12885 0,
12886 None,
12887 fs,
12888 freq,
12889 normalize_omp_threads(threads),
12890 )
12891}
12892
12893pub fn libsais_int_omp(
12907 t: &mut [SaSint],
12908 sa: &mut [SaSint],
12909 k: SaSint,
12910 fs: SaSint,
12911 threads: SaSint,
12912) -> SaSint {
12913 if threads < 0 {
12914 return -1;
12915 }
12916 if t.len() <= 1 {
12917 if t.len() == 1 {
12918 sa[0] = 0;
12919 }
12920 return 0;
12921 }
12922
12923 libsais_main_int(t, sa, k, fs, normalize_omp_threads(threads))
12924}
12925
12926pub fn libsais_bwt_omp(
12940 t: &[u8],
12941 u: &mut [u8],
12942 a: &mut [SaSint],
12943 fs: SaSint,
12944 freq: Option<&mut [SaSint]>,
12945 threads: SaSint,
12946) -> SaSint {
12947 let n = t.len();
12948 if threads < 0
12949 || fs < 0
12950 || u.len() < n
12951 || a.len() < n.saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12952 || freq.as_ref().is_some_and(|freq| freq.len() < ALPHABET_SIZE)
12953 {
12954 return -1;
12955 }
12956
12957 if n <= 1 {
12958 if let Some(freq) = freq {
12959 freq[..ALPHABET_SIZE].fill(0);
12960 if n == 1 {
12961 u[0] = t[0];
12962 freq[t[0] as usize] += 1;
12963 }
12964 } else if n == 1 {
12965 u[0] = t[0];
12966 }
12967 return n as SaSint;
12968 }
12969
12970 let threads = if threads > 0 { threads } else { 1 };
12971 let mut index = libsais_main(t, a, LIBSAIS_FLAGS_BWT, 0, None, fs, freq, threads);
12972 if index >= 0 {
12973 index += 1;
12974 let index_usize = usize::try_from(index).expect("index must be non-negative");
12975 u[0] = t[n - 1];
12976 bwt_copy_8u_omp(
12977 &mut u[1..index_usize],
12978 &a[..index_usize - 1],
12979 index - 1,
12980 threads,
12981 );
12982 bwt_copy_8u_omp(
12983 &mut u[index_usize..n],
12984 &a[index_usize..n],
12985 SaSint::try_from(n - index_usize).expect("fits"),
12986 threads,
12987 );
12988 }
12989 index
12990}
12991
12992pub fn libsais_bwt_aux_omp(
13008 t: &[u8],
13009 u: &mut [u8],
13010 a: &mut [SaSint],
13011 fs: SaSint,
13012 freq: Option<&mut [SaSint]>,
13013 r: SaSint,
13014 i: &mut [SaSint],
13015 threads: SaSint,
13016) -> SaSint {
13017 let n = t.len();
13018 if threads < 0
13019 || fs < 0
13020 || r < 2
13021 || (r & (r - 1)) != 0
13022 || u.len() < n
13023 || a.len() < n.saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
13024 {
13025 return -1;
13026 }
13027 if let Some(freq) = freq.as_ref() {
13028 if freq.len() < ALPHABET_SIZE {
13029 return -1;
13030 }
13031 }
13032 let sample_count = if n == 0 {
13033 1
13034 } else {
13035 usize::try_from((SaSint::try_from(n).expect("input length must fit SaSint") - 1) / r)
13036 .expect("sample count must be non-negative")
13037 + 1
13038 };
13039 if i.len() < sample_count {
13040 return -1;
13041 }
13042 if n <= 1 {
13043 if let Some(freq) = freq {
13044 freq[..ALPHABET_SIZE].fill(0);
13045 if n == 1 {
13046 u[0] = t[0];
13047 freq[t[0] as usize] += 1;
13048 }
13049 } else if n == 1 {
13050 u[0] = t[0];
13051 }
13052 i[0] = n as SaSint;
13053 return 0;
13054 }
13055
13056 let threads = normalize_omp_threads(threads);
13057 let index = libsais_main(t, a, LIBSAIS_FLAGS_BWT, r, Some(i), fs, freq, threads);
13058 if index == 0 {
13059 let split = usize::try_from(i[0]).expect("primary index must be non-negative");
13060 u[0] = t[n - 1];
13061 bwt_copy_8u_omp(&mut u[1..split], &a[..split - 1], i[0] - 1, threads);
13062 bwt_copy_8u_omp(
13063 &mut u[split..n],
13064 &a[split..n],
13065 SaSint::try_from(n - split).expect("fits"),
13066 threads,
13067 );
13068 }
13069 index
13070}
13071
13072#[doc(hidden)]
13074pub fn compute_phi(
13075 sa: &[SaSint],
13076 plcp: &mut [SaSint],
13077 n: SaSint,
13078 omp_block_start: FastSint,
13079 omp_block_size: FastSint,
13080) {
13081 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
13082 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
13083 let end = start + size;
13084 let n_usize = usize::try_from(n).expect("n must be non-negative");
13085 let mut i = start;
13086 let mut k = if omp_block_start > 0 {
13087 sa[start - 1]
13088 } else {
13089 n
13090 };
13091
13092 let fast_end = omp_block_start + omp_block_size - 64 - 3;
13093 while (i as FastSint) < fast_end {
13094 plcp[usize::try_from(sa[i]).expect("suffix index must be non-negative")] = k;
13095 k = sa[i];
13096 plcp[usize::try_from(sa[i + 1]).expect("suffix index must be non-negative")] = k;
13097 k = sa[i + 1];
13098 plcp[usize::try_from(sa[i + 2]).expect("suffix index must be non-negative")] = k;
13099 k = sa[i + 2];
13100 plcp[usize::try_from(sa[i + 3]).expect("suffix index must be non-negative")] = k;
13101 k = sa[i + 3];
13102 i += 4;
13103 }
13104
13105 while i < end.min(n_usize) {
13106 plcp[usize::try_from(sa[i]).expect("suffix index must be non-negative")] = k;
13107 k = sa[i];
13108 i += 1;
13109 }
13110}
13111
13112#[doc(hidden)]
13114pub fn compute_phi_omp(sa: &[SaSint], plcp: &mut [SaSint], n: SaSint, threads: SaSint) {
13115 if threads == 1 || n < 65_536 {
13116 compute_phi(sa, plcp, n, 0, n as FastSint);
13117 return;
13118 }
13119
13120 let threads_usize = usize::try_from(threads).expect("threads must be non-negative");
13121 let block_stride = ((n as FastSint) / (threads as FastSint)) & !15;
13122 let plcp_addr = plcp.as_mut_ptr() as usize;
13123 let n_usize = usize::try_from(n).expect("n must be non-negative");
13124
13125 run_rayon_with_threads(threads_usize, || {
13126 (0..threads_usize).into_par_iter().for_each(|thread| {
13127 let block_start = thread as FastSint * block_stride;
13128 let block_size = if thread + 1 < threads_usize {
13129 block_stride
13130 } else {
13131 n as FastSint - block_start
13132 };
13133 let start = usize::try_from(block_start).expect("omp_block_start must be non-negative");
13134 let size = usize::try_from(block_size).expect("omp_block_size must be non-negative");
13135 let end = start + size;
13136 let mut i = start;
13137 let mut k = if block_start > 0 { sa[start - 1] } else { n };
13138 let plcp_ptr = plcp_addr as *mut SaSint;
13139
13140 let fast_end = block_start + block_size - 64 - 3;
13141 while (i as FastSint) < fast_end {
13142 unsafe {
13143 *plcp_ptr
13145 .add(usize::try_from(sa[i]).expect("suffix index must be non-negative")) =
13146 k;
13147 k = sa[i];
13148 *plcp_ptr.add(
13149 usize::try_from(sa[i + 1]).expect("suffix index must be non-negative"),
13150 ) = k;
13151 k = sa[i + 1];
13152 *plcp_ptr.add(
13153 usize::try_from(sa[i + 2]).expect("suffix index must be non-negative"),
13154 ) = k;
13155 k = sa[i + 2];
13156 *plcp_ptr.add(
13157 usize::try_from(sa[i + 3]).expect("suffix index must be non-negative"),
13158 ) = k;
13159 k = sa[i + 3];
13160 }
13161 i += 4;
13162 }
13163
13164 while i < end.min(n_usize) {
13165 unsafe {
13166 *plcp_ptr
13168 .add(usize::try_from(sa[i]).expect("suffix index must be non-negative")) =
13169 k;
13170 }
13171 k = sa[i];
13172 i += 1;
13173 }
13174 });
13175 });
13176}
13177
13178#[doc(hidden)]
13180pub fn compute_plcp(
13181 t: &[u8],
13182 plcp: &mut [SaSint],
13183 n: FastSint,
13184 omp_block_start: FastSint,
13185 omp_block_size: FastSint,
13186) {
13187 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
13188 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
13189 let end = start + size;
13190 let n_usize = usize::try_from(n).expect("n must be non-negative");
13191 let mut l = 0usize;
13192
13193 for i in start..end.min(n_usize) {
13194 let k = usize::try_from(plcp[i]).expect("phi entry must be non-negative");
13195 let m = n_usize - i.max(k);
13196 while l < m && t[i + l] == t[k + l] {
13197 l += 1;
13198 }
13199 plcp[i] = SaSint::try_from(l).expect("LCP length must fit SaSint");
13200 l = l.saturating_sub(1);
13201 }
13202}
13203
13204#[doc(hidden)]
13206pub fn compute_plcp_omp(t: &[u8], plcp: &mut [SaSint], n: SaSint, threads: SaSint) {
13207 if threads == 1 || n < 65_536 {
13208 compute_plcp(t, plcp, n as FastSint, 0, n as FastSint);
13209 return;
13210 }
13211
13212 let n_usize = usize::try_from(n).expect("n must be non-negative");
13213 let threads_usize = usize::try_from(threads).expect("threads must be non-negative");
13214 let chunk_size = ((n_usize / threads_usize) & !15usize).max(16);
13215 run_rayon_with_threads(threads_usize, || {
13216 plcp[..n_usize]
13217 .par_chunks_mut(chunk_size)
13218 .enumerate()
13219 .for_each(|(chunk_index, chunk)| {
13220 let start = chunk_index * chunk_size;
13221 let mut l = 0usize;
13222 for (offset, value) in chunk.iter_mut().enumerate() {
13223 let i = start + offset;
13224 let k = usize::try_from(*value).expect("phi entry must be non-negative");
13225 let m = n_usize - i.max(k);
13226 while l < m && t[i + l] == t[k + l] {
13227 l += 1;
13228 }
13229 *value = SaSint::try_from(l).expect("LCP length must fit SaSint");
13230 l = l.saturating_sub(1);
13231 }
13232 });
13233 });
13234}
13235
13236#[doc(hidden)]
13238pub fn compute_plcp_gsa(
13239 t: &[u8],
13240 plcp: &mut [SaSint],
13241 omp_block_start: FastSint,
13242 omp_block_size: FastSint,
13243) {
13244 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
13245 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
13246 let end = start + size;
13247 let mut l = 0usize;
13248
13249 for i in start..end.min(t.len()) {
13250 let k = usize::try_from(plcp[i]).expect("phi entry must be non-negative");
13251 while t[i + l] > 0 && t[i + l] == t[k + l] {
13252 l += 1;
13253 }
13254 plcp[i] = SaSint::try_from(l).expect("LCP length must fit SaSint");
13255 l = l.saturating_sub(1);
13256 }
13257}
13258
13259#[doc(hidden)]
13261pub fn compute_plcp_gsa_omp(t: &[u8], plcp: &mut [SaSint], n: SaSint, threads: SaSint) {
13262 if threads == 1 || n < 65_536 {
13263 compute_plcp_gsa(t, plcp, 0, n as FastSint);
13264 return;
13265 }
13266
13267 let n_usize = usize::try_from(n).expect("n must be non-negative");
13268 let threads_usize = usize::try_from(threads).expect("threads must be non-negative");
13269 let chunk_size = ((n_usize / threads_usize) & !15usize).max(16);
13270 run_rayon_with_threads(threads_usize, || {
13271 plcp[..n_usize]
13272 .par_chunks_mut(chunk_size)
13273 .enumerate()
13274 .for_each(|(chunk_index, chunk)| {
13275 let start = chunk_index * chunk_size;
13276 let mut l = 0usize;
13277 for (offset, value) in chunk.iter_mut().enumerate() {
13278 let i = start + offset;
13279 let k = usize::try_from(*value).expect("phi entry must be non-negative");
13280 while t[i + l] > 0 && t[i + l] == t[k + l] {
13281 l += 1;
13282 }
13283 *value = SaSint::try_from(l).expect("LCP length must fit SaSint");
13284 l = l.saturating_sub(1);
13285 }
13286 });
13287 });
13288}
13289
13290#[doc(hidden)]
13292pub fn compute_plcp_int(
13293 t: &[SaSint],
13294 plcp: &mut [SaSint],
13295 n: FastSint,
13296 omp_block_start: FastSint,
13297 omp_block_size: FastSint,
13298) {
13299 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
13300 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
13301 let end = start + size;
13302 let n_usize = usize::try_from(n).expect("n must be non-negative");
13303 let mut l = 0usize;
13304
13305 for i in start..end.min(n_usize) {
13306 let k = usize::try_from(plcp[i]).expect("phi entry must be non-negative");
13307 let m = n_usize - i.max(k);
13308 while l < m && t[i + l] == t[k + l] {
13309 l += 1;
13310 }
13311 plcp[i] = SaSint::try_from(l).expect("LCP length must fit SaSint");
13312 l = l.saturating_sub(1);
13313 }
13314}
13315
13316#[doc(hidden)]
13318pub fn compute_plcp_int_omp(t: &[SaSint], plcp: &mut [SaSint], n: SaSint, threads: SaSint) {
13319 if threads == 1 || n < 65_536 {
13320 compute_plcp_int(t, plcp, n as FastSint, 0, n as FastSint);
13321 return;
13322 }
13323
13324 let n_usize = usize::try_from(n).expect("n must be non-negative");
13325 let threads_usize = usize::try_from(threads).expect("threads must be non-negative");
13326 let chunk_size = ((n_usize / threads_usize) & !15usize).max(16);
13327 run_rayon_with_threads(threads_usize, || {
13328 plcp[..n_usize]
13329 .par_chunks_mut(chunk_size)
13330 .enumerate()
13331 .for_each(|(chunk_index, chunk)| {
13332 let start = chunk_index * chunk_size;
13333 let mut l = 0usize;
13334 for (offset, value) in chunk.iter_mut().enumerate() {
13335 let i = start + offset;
13336 let k = usize::try_from(*value).expect("phi entry must be non-negative");
13337 let m = n_usize - i.max(k);
13338 while l < m && t[i + l] == t[k + l] {
13339 l += 1;
13340 }
13341 *value = SaSint::try_from(l).expect("LCP length must fit SaSint");
13342 l = l.saturating_sub(1);
13343 }
13344 });
13345 });
13346}
13347
13348#[doc(hidden)]
13350pub fn compute_lcp(
13351 plcp: &[SaSint],
13352 sa: &[SaSint],
13353 lcp: &mut [SaSint],
13354 omp_block_start: FastSint,
13355 omp_block_size: FastSint,
13356) {
13357 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
13358 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
13359 let end = start + size;
13360
13361 for i in start..end.min(sa.len()) {
13362 lcp[i] = plcp[usize::try_from(sa[i]).expect("suffix index must be non-negative")];
13363 }
13364}
13365
13366#[doc(hidden)]
13368pub fn compute_lcp_omp(
13369 plcp: &[SaSint],
13370 sa: &[SaSint],
13371 lcp: &mut [SaSint],
13372 n: SaSint,
13373 threads: SaSint,
13374) {
13375 if threads == 1 || n < 65_536 {
13376 compute_lcp(plcp, sa, lcp, 0, n as FastSint);
13377 return;
13378 }
13379
13380 let n_usize = usize::try_from(n).expect("n must be non-negative");
13381 assert!(plcp.len() >= n_usize);
13382 assert!(sa.len() >= n_usize);
13383 assert!(lcp.len() >= n_usize);
13384 let threads_usize = usize::try_from(threads).expect("threads must be non-negative");
13385 let chunk_size = ((n_usize / threads_usize) & !15usize).max(16);
13386 let plcp_ptr = plcp.as_ptr() as usize;
13387 let sa_ptr = sa.as_ptr() as usize;
13388 run_rayon_with_threads(threads_usize, || {
13389 lcp[..n_usize]
13390 .par_chunks_mut(chunk_size)
13391 .enumerate()
13392 .for_each(|(chunk_index, chunk)| {
13393 let start = chunk_index * chunk_size;
13394 let dst_ptr = chunk.as_mut_ptr();
13395 let sa_ptr = sa_ptr as *const SaSint;
13396 let plcp_ptr = plcp_ptr as *const SaSint;
13397 for offset in 0..chunk.len() {
13398 let i = start + offset;
13399 let suffix = unsafe { *sa_ptr.add(i) };
13400 let suffix =
13401 usize::try_from(suffix).expect("suffix index must be non-negative");
13402 assert!(suffix < plcp.len());
13403 unsafe {
13404 *dst_ptr.add(offset) = *plcp_ptr.add(suffix);
13405 }
13406 }
13407 });
13408 });
13409}
13410
13411pub fn libsais_plcp(t: &[u8], sa: &[SaSint], plcp: &mut [SaSint]) -> SaSint {
13422 if sa.len() != t.len() || plcp.len() != t.len() {
13423 return -1;
13424 }
13425 if t.len() <= 1 {
13426 if t.len() == 1 {
13427 plcp[0] = 0;
13428 }
13429 return 0;
13430 }
13431
13432 let n = SaSint::try_from(t.len()).expect("input length must fit SaSint");
13433 compute_phi_omp(sa, plcp, n, 1);
13434 compute_plcp_omp(t, plcp, n, 1);
13435 0
13436}
13437
13438pub fn libsais_plcp_gsa(t: &[u8], sa: &[SaSint], plcp: &mut [SaSint]) -> SaSint {
13449 if t.last().copied().unwrap_or(0) != 0 {
13450 return -1;
13451 }
13452 if sa.len() != t.len() || plcp.len() != t.len() {
13453 return -1;
13454 }
13455 if t.len() <= 1 {
13456 if t.len() == 1 {
13457 plcp[0] = 0;
13458 }
13459 return 0;
13460 }
13461
13462 let n = SaSint::try_from(t.len()).expect("input length must fit SaSint");
13463 compute_phi_omp(sa, plcp, n, 1);
13464 compute_plcp_gsa_omp(t, plcp, n, 1);
13465 0
13466}
13467
13468pub fn libsais_plcp_int(t: &[SaSint], sa: &[SaSint], plcp: &mut [SaSint]) -> SaSint {
13479 if sa.len() != t.len() || plcp.len() != t.len() {
13480 return -1;
13481 }
13482 if t.len() <= 1 {
13483 if t.len() == 1 {
13484 plcp[0] = 0;
13485 }
13486 return 0;
13487 }
13488
13489 let n = SaSint::try_from(t.len()).expect("input length must fit SaSint");
13490 compute_phi_omp(sa, plcp, n, 1);
13491 compute_plcp_int_omp(t, plcp, n, 1);
13492 0
13493}
13494
13495pub fn libsais_lcp(plcp: &[SaSint], sa: &[SaSint], lcp: &mut [SaSint]) -> SaSint {
13506 if plcp.len() != sa.len() || lcp.len() != sa.len() {
13507 return -1;
13508 }
13509 if sa.len() <= 1 {
13510 if sa.len() == 1 {
13511 lcp[0] = plcp[usize::try_from(sa[0]).expect("suffix index must be non-negative")];
13512 }
13513 return 0;
13514 }
13515
13516 compute_lcp_omp(
13517 plcp,
13518 sa,
13519 lcp,
13520 SaSint::try_from(sa.len()).expect("suffix array length must fit SaSint"),
13521 1,
13522 );
13523 0
13524}
13525
13526pub fn libsais_plcp_omp(t: &[u8], sa: &[SaSint], plcp: &mut [SaSint], threads: SaSint) -> SaSint {
13538 if threads < 0 {
13539 return -1;
13540 }
13541 if sa.len() != t.len() || plcp.len() != t.len() {
13542 return -1;
13543 }
13544 if t.len() <= 1 {
13545 if t.len() == 1 {
13546 plcp[0] = 0;
13547 }
13548 return 0;
13549 }
13550
13551 let n = SaSint::try_from(t.len()).expect("input length must fit SaSint");
13552 let threads = normalize_omp_threads(threads);
13553 compute_phi_omp(sa, plcp, n, threads);
13554 compute_plcp_omp(t, plcp, n, threads);
13555 0
13556}
13557
13558pub fn libsais_plcp_gsa_omp(
13570 t: &[u8],
13571 sa: &[SaSint],
13572 plcp: &mut [SaSint],
13573 threads: SaSint,
13574) -> SaSint {
13575 if threads < 0 || t.last().copied().unwrap_or(0) != 0 {
13576 return -1;
13577 }
13578 if sa.len() != t.len() || plcp.len() != t.len() {
13579 return -1;
13580 }
13581 if t.len() <= 1 {
13582 if t.len() == 1 {
13583 plcp[0] = 0;
13584 }
13585 return 0;
13586 }
13587
13588 let n = SaSint::try_from(t.len()).expect("input length must fit SaSint");
13589 let threads = normalize_omp_threads(threads);
13590 compute_phi_omp(sa, plcp, n, threads);
13591 compute_plcp_gsa_omp(t, plcp, n, threads);
13592 0
13593}
13594
13595pub fn libsais_plcp_int_omp(
13607 t: &[SaSint],
13608 sa: &[SaSint],
13609 plcp: &mut [SaSint],
13610 threads: SaSint,
13611) -> SaSint {
13612 if threads < 0 {
13613 return -1;
13614 }
13615 if sa.len() != t.len() || plcp.len() != t.len() {
13616 return -1;
13617 }
13618 if t.len() <= 1 {
13619 if t.len() == 1 {
13620 plcp[0] = 0;
13621 }
13622 return 0;
13623 }
13624
13625 let n = SaSint::try_from(t.len()).expect("input length must fit SaSint");
13626 let threads = normalize_omp_threads(threads);
13627 compute_phi_omp(sa, plcp, n, threads);
13628 compute_plcp_int_omp(t, plcp, n, threads);
13629 0
13630}
13631
13632pub fn libsais_lcp_omp(
13644 plcp: &[SaSint],
13645 sa: &[SaSint],
13646 lcp: &mut [SaSint],
13647 threads: SaSint,
13648) -> SaSint {
13649 if threads < 0 {
13650 return -1;
13651 }
13652 if plcp.len() != sa.len() || lcp.len() != sa.len() {
13653 return -1;
13654 }
13655 if sa.len() <= 1 {
13656 if sa.len() == 1 {
13657 lcp[0] = plcp[usize::try_from(sa[0]).expect("suffix index must be non-negative")];
13658 }
13659 return 0;
13660 }
13661
13662 compute_lcp_omp(
13663 plcp,
13664 sa,
13665 lcp,
13666 SaSint::try_from(sa.len()).expect("suffix array length must fit SaSint"),
13667 normalize_omp_threads(threads),
13668 );
13669 0
13670}
13671
13672#[doc(hidden)]
13674pub fn unbwt_compute_histogram(t: &[u8], n: FastSint, count: &mut [SaUint]) {
13675 let n = usize::try_from(n).expect("n must be non-negative");
13676 assert!(count.len() >= ALPHABET_SIZE);
13677 for &byte in &t[..n] {
13678 count[byte as usize] += 1;
13679 }
13680}
13681
13682#[doc(hidden)]
13684pub fn unbwt_transpose_bucket2(bucket2: &mut [SaUint]) {
13685 assert!(bucket2.len() >= ALPHABET_SIZE * ALPHABET_SIZE);
13686 for x in 0..ALPHABET_SIZE {
13687 for y in x + 1..ALPHABET_SIZE {
13688 bucket2.swap((y << 8) + x, (x << 8) + y);
13689 }
13690 }
13691}
13692
13693#[doc(hidden)]
13695pub fn unbwt_compute_bigram_histogram_single(
13696 t: &[u8],
13697 bucket1: &mut [SaUint],
13698 bucket2: &mut [SaUint],
13699 index: FastUint,
13700) {
13701 let mut sum = 1usize;
13702 for c in 0..ALPHABET_SIZE {
13703 let prev = sum;
13704 sum += bucket1[c] as usize;
13705 bucket1[c] = prev as SaUint;
13706 if prev != sum {
13707 let bucket2_p = &mut bucket2[c << 8..(c + 1) << 8];
13708
13709 let hi = sum.min(index);
13710 if hi > prev {
13711 unbwt_compute_histogram(&t[prev..], (hi - prev) as FastSint, bucket2_p);
13712 }
13713
13714 let lo = prev.max(index + 1);
13715 if sum > lo {
13716 unbwt_compute_histogram(&t[lo - 1..], (sum - lo) as FastSint, bucket2_p);
13717 }
13718 }
13719 }
13720
13721 unbwt_transpose_bucket2(bucket2);
13722}
13723
13724#[doc(hidden)]
13726pub fn unbwt_calculate_fastbits(
13727 bucket2: &mut [SaUint],
13728 fastbits: &mut [u16],
13729 lastc: FastUint,
13730 shift: FastUint,
13731) {
13732 let mut v = 0usize;
13733 let mut w = 0usize;
13734 let mut sum = 1usize;
13735
13736 for c in 0..ALPHABET_SIZE {
13737 if c == lastc {
13738 sum += 1;
13739 }
13740
13741 for _d in 0..ALPHABET_SIZE {
13742 let prev = sum;
13743 sum += bucket2[w] as usize;
13744 bucket2[w] = prev as SaUint;
13745 if prev != sum {
13746 while v <= ((sum - 1) >> shift) {
13747 fastbits[v] = w as u16;
13748 v += 1;
13749 }
13750 }
13751 w += 1;
13752 }
13753 }
13754}
13755
13756#[doc(hidden)]
13758pub fn unbwt_calculate_bi_psi(
13759 t: &[u8],
13760 p: &mut [SaUint],
13761 bucket1: &mut [SaUint],
13762 bucket2: &mut [SaUint],
13763 index: FastUint,
13764 omp_block_start: FastSint,
13765 omp_block_end: FastSint,
13766) {
13767 let mut i = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
13768 let mut j = index;
13769 let block_end = usize::try_from(omp_block_end).expect("omp_block_end must be non-negative");
13770 if block_end < j {
13771 j = block_end;
13772 }
13773 while i < j {
13774 let c = t[i] as usize;
13775 let pidx = bucket1[c] as usize;
13776 bucket1[c] += 1;
13777 let tidx = index as isize - pidx as isize;
13778 if tidx != 0 {
13779 let src =
13780 pidx.wrapping_add((tidx >> ((std::mem::size_of::<FastSint>() * 8) - 1)) as usize);
13781 let w = ((t[src] as usize) << 8) + c;
13782 let dst = bucket2[w] as usize;
13783 p[dst] = i as SaUint;
13784 bucket2[w] += 1;
13785 }
13786 i += 1;
13787 }
13788
13789 let mut i = index;
13790 if usize::try_from(omp_block_start).expect("omp_block_start must be non-negative") > i {
13791 i = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
13792 }
13793 i += 1;
13794 while i <= block_end {
13795 let c = t[i - 1] as usize;
13796 let pidx = bucket1[c] as usize;
13797 bucket1[c] += 1;
13798 let tidx = index as isize - pidx as isize;
13799 if tidx != 0 {
13800 let src =
13801 pidx.wrapping_add((tidx >> ((std::mem::size_of::<FastSint>() * 8) - 1)) as usize);
13802 let w = ((t[src] as usize) << 8) + c;
13803 let dst = bucket2[w] as usize;
13804 p[dst] = i as SaUint;
13805 bucket2[w] += 1;
13806 }
13807 i += 1;
13808 }
13809}
13810
13811#[doc(hidden)]
13813#[allow(dead_code, non_snake_case)]
13814pub fn unbwt_calculate_biPSI(
13815 t: &[u8],
13816 p: &mut [SaUint],
13817 bucket1: &mut [SaUint],
13818 bucket2: &mut [SaUint],
13819 index: FastUint,
13820 omp_block_start: FastSint,
13821 omp_block_end: FastSint,
13822) {
13823 unbwt_calculate_bi_psi(
13824 t,
13825 p,
13826 bucket1,
13827 bucket2,
13828 index,
13829 omp_block_start,
13830 omp_block_end,
13831 );
13832}
13833
13834#[doc(hidden)]
13836pub fn unbwt_init_single(
13837 t: &[u8],
13838 p: &mut [SaUint],
13839 n: SaSint,
13840 freq: Option<&[SaSint]>,
13841 i: &[SaUint],
13842 bucket2: &mut [SaUint],
13843 fastbits: &mut [u16],
13844) {
13845 let mut bucket1 = vec![0u32; ALPHABET_SIZE];
13846 let index = i[0] as usize;
13847 let lastc = t[0] as usize;
13848 let mut shift = 0usize;
13849 while (usize::try_from(n).expect("n must be non-negative") >> shift)
13850 > (1usize << UNBWT_FASTBITS)
13851 {
13852 shift += 1;
13853 }
13854
13855 if let Some(freq) = freq {
13856 for c in 0..ALPHABET_SIZE {
13857 bucket1[c] = freq[c] as SaUint;
13858 }
13859 } else {
13860 unbwt_compute_histogram(t, n as FastSint, &mut bucket1);
13861 }
13862
13863 bucket2.fill(0);
13864 unbwt_compute_bigram_histogram_single(t, &mut bucket1, bucket2, index);
13865 unbwt_calculate_fastbits(bucket2, fastbits, lastc, shift);
13866 unbwt_calculate_bi_psi(t, p, &mut bucket1, bucket2, index, 0, n as FastSint);
13867}
13868
13869#[doc(hidden)]
13871pub fn unbwt_compute_bigram_histogram_parallel(
13872 t: &[u8],
13873 index: FastUint,
13874 bucket1: &mut [SaUint],
13875 bucket2: &mut [SaUint],
13876 omp_block_start: FastSint,
13877 omp_block_size: FastSint,
13878) {
13879 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
13880 let end = start + usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
13881 for &c_u8 in &t[start..end] {
13882 let c = c_u8 as usize;
13883 let p = bucket1[c] as usize;
13884 bucket1[c] += 1;
13885 let tidx = index as isize - p as isize;
13886 if tidx != 0 {
13887 let src =
13888 p.wrapping_add((tidx >> ((std::mem::size_of::<FastSint>() * 8) - 1)) as usize);
13889 let w = ((t[src] as usize) << 8) + c;
13890 bucket2[w] += 1;
13891 }
13892 }
13893}
13894
13895#[doc(hidden)]
13897pub fn unbwt_init_parallel(
13898 t: &[u8],
13899 p: &mut [SaUint],
13900 n: SaSint,
13901 freq: Option<&[SaSint]>,
13902 i: &[SaUint],
13903 bucket2: &mut [SaUint],
13904 fastbits: &mut [u16],
13905 buckets: Option<&mut [SaUint]>,
13906 threads: SaSint,
13907) {
13908 let num_threads = usize::try_from(threads.max(1)).expect("threads must be non-negative");
13909 if num_threads <= 1 || usize::try_from(n).expect("n must be non-negative") < 65_536 {
13910 unbwt_init_single(t, p, n, freq, i, bucket2, fastbits);
13911 return;
13912 }
13913
13914 let buckets = match buckets {
13915 Some(buckets) => buckets,
13916 None => {
13917 unbwt_init_single(t, p, n, freq, i, bucket2, fastbits);
13918 return;
13919 }
13920 };
13921
13922 let segment_len = ALPHABET_SIZE + ALPHABET_SIZE * ALPHABET_SIZE;
13923 assert!(buckets.len() >= num_threads * segment_len);
13924
13925 let index = i[0] as usize;
13926 let lastc = t[0] as usize;
13927 let mut shift = 0usize;
13928 while (usize::try_from(n).expect("n must be non-negative") >> shift)
13929 > (1usize << UNBWT_FASTBITS)
13930 {
13931 shift += 1;
13932 }
13933
13934 let mut bucket1 = vec![0u32; ALPHABET_SIZE];
13935 bucket2.fill(0);
13936
13937 let n_fast = n as FastSint;
13938 let block_stride = (n_fast / num_threads as FastSint) & (-16);
13939 let mut block_starts = vec![0usize; num_threads];
13940 let mut block_sizes = vec![0usize; num_threads];
13941
13942 for thread in 0..num_threads {
13943 let start = usize::try_from(thread as FastSint * block_stride)
13944 .expect("block start must be non-negative");
13945 let size = if thread + 1 < num_threads {
13946 usize::try_from(block_stride).expect("block stride must be non-negative")
13947 } else {
13948 usize::try_from(n_fast - thread as FastSint * block_stride)
13949 .expect("block size must be non-negative")
13950 };
13951 block_starts[thread] = start;
13952 block_sizes[thread] = size;
13953
13954 let segment = &mut buckets[thread * segment_len..(thread + 1) * segment_len];
13955 let (bucket1_local, _) = segment.split_at_mut(ALPHABET_SIZE);
13956 bucket1_local.fill(0);
13957 unbwt_compute_histogram(&t[start..], size as FastSint, bucket1_local);
13958 }
13959
13960 for thread in 0..num_threads {
13961 let segment = &mut buckets[thread * segment_len..(thread + 1) * segment_len];
13962 let (bucket1_temp, _) = segment.split_at_mut(ALPHABET_SIZE);
13963 for c in 0..ALPHABET_SIZE {
13964 let a = bucket1[c];
13965 let b = bucket1_temp[c];
13966 bucket1[c] = a + b;
13967 bucket1_temp[c] = a;
13968 }
13969 }
13970
13971 let mut sum = 1usize;
13972 for c in 0..ALPHABET_SIZE {
13973 let prev = sum;
13974 sum += bucket1[c] as usize;
13975 bucket1[c] = prev as SaUint;
13976 }
13977
13978 for thread in 0..num_threads {
13979 let start = block_starts[thread];
13980 let size = block_sizes[thread];
13981 let segment = &mut buckets[thread * segment_len..(thread + 1) * segment_len];
13982 let (bucket1_local, bucket2_local) = segment.split_at_mut(ALPHABET_SIZE);
13983 for c in 0..ALPHABET_SIZE {
13984 bucket1_local[c] += bucket1[c];
13985 }
13986 bucket2_local.fill(0);
13987 unbwt_compute_bigram_histogram_parallel(
13988 t,
13989 index,
13990 bucket1_local,
13991 bucket2_local,
13992 start as FastSint,
13993 size as FastSint,
13994 );
13995 }
13996
13997 for thread in 0..num_threads {
13998 let segment = &mut buckets[thread * segment_len..(thread + 1) * segment_len];
13999 let (_, bucket2_temp) = segment.split_at_mut(ALPHABET_SIZE);
14000 for c in 0..ALPHABET_SIZE * ALPHABET_SIZE {
14001 let a = bucket2[c];
14002 let b = bucket2_temp[c];
14003 bucket2[c] = a + b;
14004 bucket2_temp[c] = a;
14005 }
14006 }
14007
14008 unbwt_calculate_fastbits(bucket2, fastbits, lastc, shift);
14009
14010 for thread in (1..num_threads).rev() {
14011 let src_start = (thread - 1) * segment_len;
14012 let dst_start = thread * segment_len;
14013 let (head, tail) = buckets.split_at_mut(dst_start);
14014 let src = &head[src_start..src_start + ALPHABET_SIZE];
14015 let dst = &mut tail[..ALPHABET_SIZE];
14016 dst.copy_from_slice(src);
14017 }
14018 buckets[..ALPHABET_SIZE].copy_from_slice(&bucket1);
14019
14020 for thread in 0..num_threads {
14021 let start = block_starts[thread];
14022 let size = block_sizes[thread];
14023 let segment = &mut buckets[thread * segment_len..(thread + 1) * segment_len];
14024 let (bucket1_local, bucket2_local) = segment.split_at_mut(ALPHABET_SIZE);
14025 for c in 0..ALPHABET_SIZE * ALPHABET_SIZE {
14026 bucket2_local[c] += bucket2[c];
14027 }
14028 unbwt_calculate_bi_psi(
14029 t,
14030 p,
14031 bucket1_local,
14032 bucket2_local,
14033 index,
14034 start as FastSint,
14035 (start + size) as FastSint,
14036 );
14037 }
14038
14039 let last_segment = &buckets[(num_threads - 1) * segment_len..num_threads * segment_len];
14040 let (_, last_bucket2) = last_segment.split_at(ALPHABET_SIZE);
14041 bucket2.copy_from_slice(last_bucket2);
14042}
14043
14044fn bswap16(value: u16) -> u16 {
14045 value.swap_bytes()
14046}
14047
14048fn unbwt_resolve_symbol(bucket2: &[SaUint], fastbits: &[u16], shift: FastUint, p: SaUint) -> u16 {
14049 let mut c = fastbits[(p as usize) >> shift];
14050 while bucket2[c as usize] <= p {
14051 c += 1;
14052 }
14053 c
14054}
14055
14056#[doc(hidden)]
14058pub fn unbwt_decode_1(
14059 u: &mut [u8],
14060 p: &[SaUint],
14061 bucket2: &[SaUint],
14062 fastbits: &[u16],
14063 shift: FastUint,
14064 i0: &mut FastUint,
14065 k: FastUint,
14066) {
14067 let words = &mut u[..2 * k];
14068 let mut p0 = *i0 as SaUint;
14069
14070 for i in 0..k {
14071 let c0 = unbwt_resolve_symbol(bucket2, fastbits, shift, p0);
14072 p0 = p[p0 as usize];
14073 let bytes = bswap16(c0).to_ne_bytes();
14074 words[2 * i] = bytes[0];
14075 words[2 * i + 1] = bytes[1];
14076 }
14077
14078 *i0 = p0 as FastUint;
14079}
14080
14081#[doc(hidden)]
14083pub fn unbwt_decode_2(
14084 u: &mut [u8],
14085 p: &[SaUint],
14086 bucket2: &[SaUint],
14087 fastbits: &[u16],
14088 shift: FastUint,
14089 r: FastUint,
14090 i0: &mut FastUint,
14091 i1: &mut FastUint,
14092 k: FastUint,
14093) {
14094 let width = 2 * k;
14095 unbwt_decode_1(&mut u[0..width], p, bucket2, fastbits, shift, i0, k);
14096 unbwt_decode_1(&mut u[r..r + width], p, bucket2, fastbits, shift, i1, k);
14097}
14098
14099#[doc(hidden)]
14101pub fn unbwt_decode_3(
14102 u: &mut [u8],
14103 p: &[SaUint],
14104 bucket2: &[SaUint],
14105 fastbits: &[u16],
14106 shift: FastUint,
14107 r: FastUint,
14108 i0: &mut FastUint,
14109 i1: &mut FastUint,
14110 i2: &mut FastUint,
14111 k: FastUint,
14112) {
14113 let width = 2 * k;
14114 unbwt_decode_1(&mut u[0..width], p, bucket2, fastbits, shift, i0, k);
14115 unbwt_decode_1(&mut u[r..r + width], p, bucket2, fastbits, shift, i1, k);
14116 unbwt_decode_1(
14117 &mut u[2 * r..2 * r + width],
14118 p,
14119 bucket2,
14120 fastbits,
14121 shift,
14122 i2,
14123 k,
14124 );
14125}
14126
14127#[doc(hidden)]
14129pub fn unbwt_decode_4(
14130 u: &mut [u8],
14131 p: &[SaUint],
14132 bucket2: &[SaUint],
14133 fastbits: &[u16],
14134 shift: FastUint,
14135 r: FastUint,
14136 i0: &mut FastUint,
14137 i1: &mut FastUint,
14138 i2: &mut FastUint,
14139 i3: &mut FastUint,
14140 k: FastUint,
14141) {
14142 let width = 2 * k;
14143 unbwt_decode_1(&mut u[0..width], p, bucket2, fastbits, shift, i0, k);
14144 unbwt_decode_1(&mut u[r..r + width], p, bucket2, fastbits, shift, i1, k);
14145 unbwt_decode_1(
14146 &mut u[2 * r..2 * r + width],
14147 p,
14148 bucket2,
14149 fastbits,
14150 shift,
14151 i2,
14152 k,
14153 );
14154 unbwt_decode_1(
14155 &mut u[3 * r..3 * r + width],
14156 p,
14157 bucket2,
14158 fastbits,
14159 shift,
14160 i3,
14161 k,
14162 );
14163}
14164
14165#[doc(hidden)]
14167pub fn unbwt_decode_5(
14168 u: &mut [u8],
14169 p: &[SaUint],
14170 bucket2: &[SaUint],
14171 fastbits: &[u16],
14172 shift: FastUint,
14173 r: FastUint,
14174 i0: &mut FastUint,
14175 i1: &mut FastUint,
14176 i2: &mut FastUint,
14177 i3: &mut FastUint,
14178 i4: &mut FastUint,
14179 k: FastUint,
14180) {
14181 let width = 2 * k;
14182 unbwt_decode_1(&mut u[0..width], p, bucket2, fastbits, shift, i0, k);
14183 unbwt_decode_1(&mut u[r..r + width], p, bucket2, fastbits, shift, i1, k);
14184 unbwt_decode_1(
14185 &mut u[2 * r..2 * r + width],
14186 p,
14187 bucket2,
14188 fastbits,
14189 shift,
14190 i2,
14191 k,
14192 );
14193 unbwt_decode_1(
14194 &mut u[3 * r..3 * r + width],
14195 p,
14196 bucket2,
14197 fastbits,
14198 shift,
14199 i3,
14200 k,
14201 );
14202 unbwt_decode_1(
14203 &mut u[4 * r..4 * r + width],
14204 p,
14205 bucket2,
14206 fastbits,
14207 shift,
14208 i4,
14209 k,
14210 );
14211}
14212
14213#[doc(hidden)]
14215pub fn unbwt_decode_6(
14216 u: &mut [u8],
14217 p: &[SaUint],
14218 bucket2: &[SaUint],
14219 fastbits: &[u16],
14220 shift: FastUint,
14221 r: FastUint,
14222 i0: &mut FastUint,
14223 i1: &mut FastUint,
14224 i2: &mut FastUint,
14225 i3: &mut FastUint,
14226 i4: &mut FastUint,
14227 i5: &mut FastUint,
14228 k: FastUint,
14229) {
14230 let width = 2 * k;
14231 unbwt_decode_1(&mut u[0..width], p, bucket2, fastbits, shift, i0, k);
14232 unbwt_decode_1(&mut u[r..r + width], p, bucket2, fastbits, shift, i1, k);
14233 unbwt_decode_1(
14234 &mut u[2 * r..2 * r + width],
14235 p,
14236 bucket2,
14237 fastbits,
14238 shift,
14239 i2,
14240 k,
14241 );
14242 unbwt_decode_1(
14243 &mut u[3 * r..3 * r + width],
14244 p,
14245 bucket2,
14246 fastbits,
14247 shift,
14248 i3,
14249 k,
14250 );
14251 unbwt_decode_1(
14252 &mut u[4 * r..4 * r + width],
14253 p,
14254 bucket2,
14255 fastbits,
14256 shift,
14257 i4,
14258 k,
14259 );
14260 unbwt_decode_1(
14261 &mut u[5 * r..5 * r + width],
14262 p,
14263 bucket2,
14264 fastbits,
14265 shift,
14266 i5,
14267 k,
14268 );
14269}
14270
14271#[doc(hidden)]
14273pub fn unbwt_decode_7(
14274 u: &mut [u8],
14275 p: &[SaUint],
14276 bucket2: &[SaUint],
14277 fastbits: &[u16],
14278 shift: FastUint,
14279 r: FastUint,
14280 i0: &mut FastUint,
14281 i1: &mut FastUint,
14282 i2: &mut FastUint,
14283 i3: &mut FastUint,
14284 i4: &mut FastUint,
14285 i5: &mut FastUint,
14286 i6: &mut FastUint,
14287 k: FastUint,
14288) {
14289 let width = 2 * k;
14290 unbwt_decode_1(&mut u[0..width], p, bucket2, fastbits, shift, i0, k);
14291 unbwt_decode_1(&mut u[r..r + width], p, bucket2, fastbits, shift, i1, k);
14292 unbwt_decode_1(
14293 &mut u[2 * r..2 * r + width],
14294 p,
14295 bucket2,
14296 fastbits,
14297 shift,
14298 i2,
14299 k,
14300 );
14301 unbwt_decode_1(
14302 &mut u[3 * r..3 * r + width],
14303 p,
14304 bucket2,
14305 fastbits,
14306 shift,
14307 i3,
14308 k,
14309 );
14310 unbwt_decode_1(
14311 &mut u[4 * r..4 * r + width],
14312 p,
14313 bucket2,
14314 fastbits,
14315 shift,
14316 i4,
14317 k,
14318 );
14319 unbwt_decode_1(
14320 &mut u[5 * r..5 * r + width],
14321 p,
14322 bucket2,
14323 fastbits,
14324 shift,
14325 i5,
14326 k,
14327 );
14328 unbwt_decode_1(
14329 &mut u[6 * r..6 * r + width],
14330 p,
14331 bucket2,
14332 fastbits,
14333 shift,
14334 i6,
14335 k,
14336 );
14337}
14338
14339#[doc(hidden)]
14341pub fn unbwt_decode_8(
14342 u: &mut [u8],
14343 p: &[SaUint],
14344 bucket2: &[SaUint],
14345 fastbits: &[u16],
14346 shift: FastUint,
14347 r: FastUint,
14348 i0: &mut FastUint,
14349 i1: &mut FastUint,
14350 i2: &mut FastUint,
14351 i3: &mut FastUint,
14352 i4: &mut FastUint,
14353 i5: &mut FastUint,
14354 i6: &mut FastUint,
14355 i7: &mut FastUint,
14356 k: FastUint,
14357) {
14358 let width = 2 * k;
14359 unbwt_decode_1(&mut u[0..width], p, bucket2, fastbits, shift, i0, k);
14360 unbwt_decode_1(&mut u[r..r + width], p, bucket2, fastbits, shift, i1, k);
14361 unbwt_decode_1(
14362 &mut u[2 * r..2 * r + width],
14363 p,
14364 bucket2,
14365 fastbits,
14366 shift,
14367 i2,
14368 k,
14369 );
14370 unbwt_decode_1(
14371 &mut u[3 * r..3 * r + width],
14372 p,
14373 bucket2,
14374 fastbits,
14375 shift,
14376 i3,
14377 k,
14378 );
14379 unbwt_decode_1(
14380 &mut u[4 * r..4 * r + width],
14381 p,
14382 bucket2,
14383 fastbits,
14384 shift,
14385 i4,
14386 k,
14387 );
14388 unbwt_decode_1(
14389 &mut u[5 * r..5 * r + width],
14390 p,
14391 bucket2,
14392 fastbits,
14393 shift,
14394 i5,
14395 k,
14396 );
14397 unbwt_decode_1(
14398 &mut u[6 * r..6 * r + width],
14399 p,
14400 bucket2,
14401 fastbits,
14402 shift,
14403 i6,
14404 k,
14405 );
14406 unbwt_decode_1(
14407 &mut u[7 * r..7 * r + width],
14408 p,
14409 bucket2,
14410 fastbits,
14411 shift,
14412 i7,
14413 k,
14414 );
14415}
14416
14417#[doc(hidden)]
14419pub fn unbwt_decode(
14420 u: &mut [u8],
14421 p: &[SaUint],
14422 n: SaSint,
14423 r: SaSint,
14424 i: &[SaUint],
14425 bucket2: &[SaUint],
14426 fastbits: &[u16],
14427 mut blocks: FastSint,
14428 remainder: FastUint,
14429) {
14430 let mut shift = 0usize;
14431 while (usize::try_from(n).expect("n must be non-negative") >> shift)
14432 > (1usize << UNBWT_FASTBITS)
14433 {
14434 shift += 1;
14435 }
14436 let mut offset = 0usize;
14437 let mut i_index = 0usize;
14438 let r_usize = usize::try_from(r).expect("r must be non-negative");
14439
14440 while blocks > 8 {
14441 let mut i0 = i[i_index] as FastUint;
14442 let mut i1 = i[i_index + 1] as FastUint;
14443 let mut i2 = i[i_index + 2] as FastUint;
14444 let mut i3 = i[i_index + 3] as FastUint;
14445 let mut i4 = i[i_index + 4] as FastUint;
14446 let mut i5 = i[i_index + 5] as FastUint;
14447 let mut i6 = i[i_index + 6] as FastUint;
14448 let mut i7 = i[i_index + 7] as FastUint;
14449 unbwt_decode_8(
14450 &mut u[offset..],
14451 p,
14452 bucket2,
14453 fastbits,
14454 shift,
14455 r_usize,
14456 &mut i0,
14457 &mut i1,
14458 &mut i2,
14459 &mut i3,
14460 &mut i4,
14461 &mut i5,
14462 &mut i6,
14463 &mut i7,
14464 r_usize >> 1,
14465 );
14466 i_index += 8;
14467 blocks -= 8;
14468 offset += 8 * r_usize;
14469 }
14470
14471 match blocks {
14472 1 => {
14473 let mut i0 = i[i_index] as FastUint;
14474 unbwt_decode_1(
14475 &mut u[offset..],
14476 p,
14477 bucket2,
14478 fastbits,
14479 shift,
14480 &mut i0,
14481 remainder >> 1,
14482 );
14483 }
14484 2 => {
14485 let mut i0 = i[i_index] as FastUint;
14486 let mut i1 = i[i_index + 1] as FastUint;
14487 unbwt_decode_2(
14488 &mut u[offset..],
14489 p,
14490 bucket2,
14491 fastbits,
14492 shift,
14493 r_usize,
14494 &mut i0,
14495 &mut i1,
14496 remainder >> 1,
14497 );
14498 unbwt_decode_1(
14499 &mut u[offset + 2 * (remainder >> 1)..],
14500 p,
14501 bucket2,
14502 fastbits,
14503 shift,
14504 &mut i0,
14505 (r_usize >> 1) - (remainder >> 1),
14506 );
14507 }
14508 3 => {
14509 let mut i0 = i[i_index] as FastUint;
14510 let mut i1 = i[i_index + 1] as FastUint;
14511 let mut i2 = i[i_index + 2] as FastUint;
14512 unbwt_decode_3(
14513 &mut u[offset..],
14514 p,
14515 bucket2,
14516 fastbits,
14517 shift,
14518 r_usize,
14519 &mut i0,
14520 &mut i1,
14521 &mut i2,
14522 remainder >> 1,
14523 );
14524 unbwt_decode_2(
14525 &mut u[offset + 2 * (remainder >> 1)..],
14526 p,
14527 bucket2,
14528 fastbits,
14529 shift,
14530 r_usize,
14531 &mut i0,
14532 &mut i1,
14533 (r_usize >> 1) - (remainder >> 1),
14534 );
14535 }
14536 4 => {
14537 let mut i0 = i[i_index] as FastUint;
14538 let mut i1 = i[i_index + 1] as FastUint;
14539 let mut i2 = i[i_index + 2] as FastUint;
14540 let mut i3 = i[i_index + 3] as FastUint;
14541 unbwt_decode_4(
14542 &mut u[offset..],
14543 p,
14544 bucket2,
14545 fastbits,
14546 shift,
14547 r_usize,
14548 &mut i0,
14549 &mut i1,
14550 &mut i2,
14551 &mut i3,
14552 remainder >> 1,
14553 );
14554 unbwt_decode_3(
14555 &mut u[offset + 2 * (remainder >> 1)..],
14556 p,
14557 bucket2,
14558 fastbits,
14559 shift,
14560 r_usize,
14561 &mut i0,
14562 &mut i1,
14563 &mut i2,
14564 (r_usize >> 1) - (remainder >> 1),
14565 );
14566 }
14567 5 => {
14568 let mut i0 = i[i_index] as FastUint;
14569 let mut i1 = i[i_index + 1] as FastUint;
14570 let mut i2 = i[i_index + 2] as FastUint;
14571 let mut i3 = i[i_index + 3] as FastUint;
14572 let mut i4 = i[i_index + 4] as FastUint;
14573 unbwt_decode_5(
14574 &mut u[offset..],
14575 p,
14576 bucket2,
14577 fastbits,
14578 shift,
14579 r_usize,
14580 &mut i0,
14581 &mut i1,
14582 &mut i2,
14583 &mut i3,
14584 &mut i4,
14585 remainder >> 1,
14586 );
14587 unbwt_decode_4(
14588 &mut u[offset + 2 * (remainder >> 1)..],
14589 p,
14590 bucket2,
14591 fastbits,
14592 shift,
14593 r_usize,
14594 &mut i0,
14595 &mut i1,
14596 &mut i2,
14597 &mut i3,
14598 (r_usize >> 1) - (remainder >> 1),
14599 );
14600 }
14601 6 => {
14602 let mut i0 = i[i_index] as FastUint;
14603 let mut i1 = i[i_index + 1] as FastUint;
14604 let mut i2 = i[i_index + 2] as FastUint;
14605 let mut i3 = i[i_index + 3] as FastUint;
14606 let mut i4 = i[i_index + 4] as FastUint;
14607 let mut i5 = i[i_index + 5] as FastUint;
14608 unbwt_decode_6(
14609 &mut u[offset..],
14610 p,
14611 bucket2,
14612 fastbits,
14613 shift,
14614 r_usize,
14615 &mut i0,
14616 &mut i1,
14617 &mut i2,
14618 &mut i3,
14619 &mut i4,
14620 &mut i5,
14621 remainder >> 1,
14622 );
14623 unbwt_decode_5(
14624 &mut u[offset + 2 * (remainder >> 1)..],
14625 p,
14626 bucket2,
14627 fastbits,
14628 shift,
14629 r_usize,
14630 &mut i0,
14631 &mut i1,
14632 &mut i2,
14633 &mut i3,
14634 &mut i4,
14635 (r_usize >> 1) - (remainder >> 1),
14636 );
14637 }
14638 7 => {
14639 let mut i0 = i[i_index] as FastUint;
14640 let mut i1 = i[i_index + 1] as FastUint;
14641 let mut i2 = i[i_index + 2] as FastUint;
14642 let mut i3 = i[i_index + 3] as FastUint;
14643 let mut i4 = i[i_index + 4] as FastUint;
14644 let mut i5 = i[i_index + 5] as FastUint;
14645 let mut i6 = i[i_index + 6] as FastUint;
14646 unbwt_decode_7(
14647 &mut u[offset..],
14648 p,
14649 bucket2,
14650 fastbits,
14651 shift,
14652 r_usize,
14653 &mut i0,
14654 &mut i1,
14655 &mut i2,
14656 &mut i3,
14657 &mut i4,
14658 &mut i5,
14659 &mut i6,
14660 remainder >> 1,
14661 );
14662 unbwt_decode_6(
14663 &mut u[offset + 2 * (remainder >> 1)..],
14664 p,
14665 bucket2,
14666 fastbits,
14667 shift,
14668 r_usize,
14669 &mut i0,
14670 &mut i1,
14671 &mut i2,
14672 &mut i3,
14673 &mut i4,
14674 &mut i5,
14675 (r_usize >> 1) - (remainder >> 1),
14676 );
14677 }
14678 8 => {
14679 let mut i0 = i[i_index] as FastUint;
14680 let mut i1 = i[i_index + 1] as FastUint;
14681 let mut i2 = i[i_index + 2] as FastUint;
14682 let mut i3 = i[i_index + 3] as FastUint;
14683 let mut i4 = i[i_index + 4] as FastUint;
14684 let mut i5 = i[i_index + 5] as FastUint;
14685 let mut i6 = i[i_index + 6] as FastUint;
14686 let mut i7 = i[i_index + 7] as FastUint;
14687 unbwt_decode_8(
14688 &mut u[offset..],
14689 p,
14690 bucket2,
14691 fastbits,
14692 shift,
14693 r_usize,
14694 &mut i0,
14695 &mut i1,
14696 &mut i2,
14697 &mut i3,
14698 &mut i4,
14699 &mut i5,
14700 &mut i6,
14701 &mut i7,
14702 remainder >> 1,
14703 );
14704 unbwt_decode_7(
14705 &mut u[offset + 2 * (remainder >> 1)..],
14706 p,
14707 bucket2,
14708 fastbits,
14709 shift,
14710 r_usize,
14711 &mut i0,
14712 &mut i1,
14713 &mut i2,
14714 &mut i3,
14715 &mut i4,
14716 &mut i5,
14717 &mut i6,
14718 (r_usize >> 1) - (remainder >> 1),
14719 );
14720 }
14721 _ => {}
14722 }
14723}
14724
14725#[doc(hidden)]
14727pub fn unbwt_decode_omp(
14728 t: &[u8],
14729 u: &mut [u8],
14730 p: &[SaUint],
14731 n: SaSint,
14732 r: SaSint,
14733 i: &[SaUint],
14734 bucket2: &[SaUint],
14735 fastbits: &[u16],
14736 threads: SaSint,
14737) {
14738 let lastc = t[0];
14739 let blocks = 1 + ((n as FastSint - 1) / r as FastSint);
14740 let remainder = usize::try_from(n).expect("n must be non-negative")
14741 - usize::try_from(r).expect("r must be non-negative")
14742 * (usize::try_from(blocks).expect("blocks") - 1);
14743 let max_threads = usize::try_from(blocks.min(threads.max(1) as FastSint))
14744 .expect("thread count must fit usize");
14745 let block_stride = usize::try_from(blocks).expect("blocks must be non-negative") / max_threads;
14746 let block_remainder =
14747 usize::try_from(blocks).expect("blocks must be non-negative") % max_threads;
14748 let r_usize = usize::try_from(r).expect("r must be non-negative");
14749
14750 let u_ptr = SyncMutPtr::new(u);
14751 run_rayon_with_threads(max_threads, || {
14752 (0..max_threads).into_par_iter().for_each(|thread| {
14753 let block_size = block_stride + usize::from(thread < block_remainder);
14754 let block_start = block_stride * thread + thread.min(block_remainder);
14755 let u = unsafe { u_ptr.as_slice() };
14756 unbwt_decode(
14757 &mut u[r_usize * block_start..],
14758 p,
14759 n,
14760 r,
14761 &i[block_start..],
14762 bucket2,
14763 fastbits,
14764 block_size as FastSint,
14765 if thread + 1 < max_threads {
14766 r_usize
14767 } else {
14768 remainder
14769 },
14770 );
14771 });
14772 });
14773 u[usize::try_from(n).expect("n must be non-negative") - 1] = lastc;
14774}
14775
14776#[doc(hidden)]
14778pub fn unbwt_core(
14779 t: &[u8],
14780 u: &mut [u8],
14781 p: &mut [SaUint],
14782 n: SaSint,
14783 freq: Option<&[SaSint]>,
14784 r: SaSint,
14785 i: &[SaUint],
14786 bucket2: &mut [SaUint],
14787 fastbits: &mut [u16],
14788 buckets: Option<&mut [SaUint]>,
14789 threads: SaSint,
14790) -> SaSint {
14791 if threads > 1 && n >= 262_144 {
14792 unbwt_init_parallel(t, p, n, freq, i, bucket2, fastbits, buckets, threads);
14793 } else {
14794 unbwt_init_single(t, p, n, freq, i, bucket2, fastbits);
14795 }
14796
14797 unbwt_decode_omp(t, u, p, n, r, i, bucket2, fastbits, threads);
14798 0
14799}
14800
14801#[doc(hidden)]
14803pub fn unbwt_main(
14804 t: &[u8],
14805 u: &mut [u8],
14806 p: &mut [SaUint],
14807 n: SaSint,
14808 freq: Option<&[SaSint]>,
14809 r: SaSint,
14810 i: &[SaUint],
14811 threads: SaSint,
14812) -> SaSint {
14813 let mut shift = 0usize;
14814 while (usize::try_from(n).expect("n must be non-negative") >> shift)
14815 > (1usize << UNBWT_FASTBITS)
14816 {
14817 shift += 1;
14818 }
14819
14820 let mut bucket2 = vec![0u32; ALPHABET_SIZE * ALPHABET_SIZE];
14821 let mut fastbits =
14822 vec![0u16; 1 + (usize::try_from(n).expect("n must be non-negative") >> shift)];
14823 let mut buckets = if threads > 1 && n >= 262_144 {
14824 Some(vec![
14825 0u32;
14826 usize::try_from(threads)
14827 .expect("threads must be non-negative")
14828 * (ALPHABET_SIZE + ALPHABET_SIZE * ALPHABET_SIZE)
14829 ])
14830 } else {
14831 None
14832 };
14833
14834 unbwt_core(
14835 t,
14836 u,
14837 p,
14838 n,
14839 freq,
14840 r,
14841 i,
14842 &mut bucket2,
14843 &mut fastbits,
14844 buckets.as_deref_mut(),
14845 threads,
14846 )
14847}
14848
14849#[doc(hidden)]
14851pub fn unbwt_main_ctx(
14852 ctx: &mut UnbwtContext,
14853 t: &[u8],
14854 u: &mut [u8],
14855 p: &mut [SaUint],
14856 n: SaSint,
14857 freq: Option<&[SaSint]>,
14858 r: SaSint,
14859 i: &[SaUint],
14860) -> SaSint {
14861 if ctx.threads <= 0 {
14862 return -2;
14863 }
14864 let mut shift = 0usize;
14865 while (usize::try_from(n).expect("n must be non-negative") >> shift)
14866 > (1usize << UNBWT_FASTBITS)
14867 {
14868 shift += 1;
14869 }
14870 let required_fastbits = 1 + (usize::try_from(n).expect("n must be non-negative") >> shift);
14871 if ctx.bucket2.len() < ALPHABET_SIZE * ALPHABET_SIZE
14872 || ctx.fastbits.len() < required_fastbits
14873 || (ctx.threads > 1 && ctx.buckets.is_none())
14874 {
14875 return -2;
14876 }
14877
14878 unbwt_core(
14879 t,
14880 u,
14881 p,
14882 n,
14883 freq,
14884 r,
14885 i,
14886 &mut ctx.bucket2,
14887 &mut ctx.fastbits,
14888 ctx.buckets.as_deref_mut(),
14889 ctx.threads as SaSint,
14890 )
14891}
14892
14893pub fn libsais_unbwt(
14906 t: &[u8],
14907 u: &mut [u8],
14908 a: &mut [SaSint],
14909 freq: Option<&[SaSint]>,
14910 i: SaSint,
14911) -> SaSint {
14912 libsais_unbwt_aux(
14913 t,
14914 u,
14915 a,
14916 freq,
14917 SaSint::try_from(t.len()).expect("input length must fit SaSint"),
14918 &[i],
14919 )
14920}
14921
14922pub fn libsais_unbwt_ctx(
14936 ctx: &mut UnbwtContext,
14937 t: &[u8],
14938 u: &mut [u8],
14939 a: &mut [SaSint],
14940 freq: Option<&[SaSint]>,
14941 i: SaSint,
14942) -> SaSint {
14943 libsais_unbwt_aux_ctx(
14944 ctx,
14945 t,
14946 u,
14947 a,
14948 freq,
14949 SaSint::try_from(t.len()).expect("input length must fit SaSint"),
14950 &[i],
14951 )
14952}
14953
14954pub fn libsais_unbwt_aux(
14968 t: &[u8],
14969 u: &mut [u8],
14970 a: &mut [SaSint],
14971 freq: Option<&[SaSint]>,
14972 r: SaSint,
14973 i: &[SaSint],
14974) -> SaSint {
14975 let t_len = t.len();
14976 let n = SaSint::try_from(t_len).expect("input length must fit SaSint");
14977 if u.len() < t_len
14978 || a.len() < t_len
14979 || freq.is_some_and(|freq| freq.len() < ALPHABET_SIZE)
14980 || (r != n && (r < 2 || (r & (r - 1)) != 0))
14981 {
14982 return -1;
14983 }
14984 let sample_count = if n == 0 {
14985 1
14986 } else {
14987 ((n - 1) / r + 1) as usize
14988 };
14989 if i.len() < sample_count {
14990 return -1;
14991 }
14992
14993 if n <= 1 {
14994 if i[0] != n {
14995 return -1;
14996 }
14997 if n == 1 {
14998 u[0] = t[0];
14999 }
15000 return 0;
15001 }
15002
15003 for t in 0..sample_count {
15004 let sample = i[t];
15005 if sample <= 0 || sample > n {
15006 return -1;
15007 }
15008 }
15009
15010 let i_u32: Vec<SaUint> = i
15011 .iter()
15012 .take(sample_count)
15013 .map(|&sample| SaUint::try_from(sample).expect("sample was validated positive"))
15014 .collect();
15015 let mut p = vec![0u32; t_len + 1];
15016 let result = unbwt_main(t, u, &mut p, n, freq, r, &i_u32, 1);
15017 for t in 0..t_len {
15018 a[t] = p[t] as SaSint;
15019 }
15020 result
15021}
15022
15023pub fn libsais_unbwt_aux_ctx(
15038 ctx: &mut UnbwtContext,
15039 t: &[u8],
15040 u: &mut [u8],
15041 a: &mut [SaSint],
15042 freq: Option<&[SaSint]>,
15043 r: SaSint,
15044 i: &[SaSint],
15045) -> SaSint {
15046 let t_len = t.len();
15047 let n = SaSint::try_from(t_len).expect("input length must fit SaSint");
15048 if u.len() < t_len
15049 || a.len() < t_len
15050 || freq.is_some_and(|freq| freq.len() < ALPHABET_SIZE)
15051 || (r != n && (r < 2 || (r & (r - 1)) != 0))
15052 {
15053 return -1;
15054 }
15055 let sample_count = if n == 0 {
15056 1
15057 } else {
15058 ((n - 1) / r + 1) as usize
15059 };
15060 if i.len() < sample_count {
15061 return -1;
15062 }
15063
15064 if n <= 1 {
15065 if i[0] != n {
15066 return -1;
15067 }
15068 if n == 1 {
15069 u[0] = t[0];
15070 }
15071 return 0;
15072 }
15073
15074 for t in 0..sample_count {
15075 let sample = i[t];
15076 if sample <= 0 || sample > n {
15077 return -1;
15078 }
15079 }
15080
15081 let i_u32: Vec<SaUint> = i
15082 .iter()
15083 .take(sample_count)
15084 .map(|&sample| SaUint::try_from(sample).expect("sample was validated positive"))
15085 .collect();
15086 let mut p = vec![0u32; t_len + 1];
15087 let result = unbwt_main_ctx(ctx, t, u, &mut p, n, freq, r, &i_u32);
15088 for t in 0..t_len {
15089 a[t] = p[t] as SaSint;
15090 }
15091 result
15092}
15093
15094pub fn unbwt_create_ctx_omp(threads: SaSint) -> Option<UnbwtContext> {
15103 if threads < 0 {
15104 return None;
15105 }
15106 unbwt_create_ctx_main(normalize_omp_threads(threads))
15107}
15108
15109pub fn libsais_unbwt_omp(
15123 t: &[u8],
15124 u: &mut [u8],
15125 a: &mut [SaSint],
15126 freq: Option<&[SaSint]>,
15127 i: SaSint,
15128 threads: SaSint,
15129) -> SaSint {
15130 libsais_unbwt_aux_omp(
15131 t,
15132 u,
15133 a,
15134 freq,
15135 SaSint::try_from(t.len()).expect("input length must fit SaSint"),
15136 &[i],
15137 threads,
15138 )
15139}
15140
15141pub fn libsais_unbwt_aux_omp(
15156 t: &[u8],
15157 u: &mut [u8],
15158 a: &mut [SaSint],
15159 freq: Option<&[SaSint]>,
15160 r: SaSint,
15161 i: &[SaSint],
15162 threads: SaSint,
15163) -> SaSint {
15164 let t_len = t.len();
15165 let n = SaSint::try_from(t_len).expect("input length must fit SaSint");
15166 if threads < 0
15167 || u.len() < t_len
15168 || a.len() < t_len
15169 || freq.is_some_and(|freq| freq.len() < ALPHABET_SIZE)
15170 || (r != n && (r < 2 || (r & (r - 1)) != 0))
15171 {
15172 return -1;
15173 }
15174 let sample_count = if n == 0 {
15175 1
15176 } else {
15177 ((n - 1) / r + 1) as usize
15178 };
15179 if i.len() < sample_count {
15180 return -1;
15181 }
15182
15183 if n <= 1 {
15184 if i[0] != n {
15185 return -1;
15186 }
15187 if n == 1 {
15188 u[0] = t[0];
15189 }
15190 return 0;
15191 }
15192
15193 for sample in i.iter().take(sample_count) {
15194 let sample = *sample;
15195 if sample <= 0 || sample > n {
15196 return -1;
15197 }
15198 }
15199
15200 let threads = if threads > 0 { threads } else { 1 };
15201 let i_u32: Vec<SaUint> = i
15202 .iter()
15203 .take(sample_count)
15204 .map(|&sample| SaUint::try_from(sample).expect("sample was validated positive"))
15205 .collect();
15206 let mut p = vec![0u32; t_len + 1];
15207 let result = unbwt_main(t, u, &mut p, n, freq, r, &i_u32, threads);
15208 for idx in 0..t_len {
15209 a[idx] = p[idx] as SaSint;
15210 }
15211 result
15212}
15213
15214#[doc(hidden)]
15216pub fn bwt_copy_8u(u: &mut [u8], a: &[SaSint], n: SaSint) {
15217 if n <= 0 {
15218 return;
15219 }
15220
15221 let n_usize = usize::try_from(n).expect("n must be non-negative");
15222 for i in 0..n_usize {
15223 u[i] = a[i] as u8;
15224 }
15225}
15226
15227#[doc(hidden)]
15229pub fn bwt_copy_8u_omp(u: &mut [u8], a: &[SaSint], n: SaSint, threads: SaSint) {
15230 if threads == 1 || n < 65_536 {
15231 bwt_copy_8u(u, a, n);
15232 return;
15233 }
15234
15235 let n_usize = usize::try_from(n).expect("n must be non-negative");
15236 assert!(u.len() >= n_usize);
15237 assert!(a.len() >= n_usize);
15238 let threads_usize = usize::try_from(threads).expect("threads must be non-negative");
15239 let chunk_size = ((n_usize / threads_usize) & !15usize).max(16);
15240 let a_ptr = a.as_ptr() as usize;
15241 run_rayon_with_threads(threads_usize, || {
15242 u[..n_usize]
15243 .par_chunks_mut(chunk_size)
15244 .enumerate()
15245 .for_each(|(chunk_index, chunk)| {
15246 let start = chunk_index * chunk_size;
15247 let dst_ptr = chunk.as_mut_ptr();
15248 let src_ptr = unsafe { (a_ptr as *const SaSint).add(start) };
15249 for offset in 0..chunk.len() {
15250 unsafe {
15251 *dst_ptr.add(offset) = *src_ptr.add(offset) as u8;
15252 }
15253 }
15254 });
15255 });
15256}
15257
15258#[doc(hidden)]
15260pub fn accumulate_counts_s32_2(bucket00: &mut [SaSint], bucket01: &[SaSint]) {
15261 assert_eq!(bucket00.len(), bucket01.len());
15262 for (dst, src) in bucket00.iter_mut().zip(bucket01.iter()) {
15263 *dst += *src;
15264 }
15265}
15266
15267#[doc(hidden)]
15269pub fn accumulate_counts_s32_3(bucket00: &mut [SaSint], bucket01: &[SaSint], bucket02: &[SaSint]) {
15270 assert_eq!(bucket00.len(), bucket01.len());
15271 assert_eq!(bucket00.len(), bucket02.len());
15272 for ((dst, src1), src2) in bucket00
15273 .iter_mut()
15274 .zip(bucket01.iter())
15275 .zip(bucket02.iter())
15276 {
15277 *dst += *src1 + *src2;
15278 }
15279}
15280
15281#[doc(hidden)]
15283pub fn accumulate_counts_s32_4(
15284 bucket00: &mut [SaSint],
15285 bucket01: &[SaSint],
15286 bucket02: &[SaSint],
15287 bucket03: &[SaSint],
15288) {
15289 assert_eq!(bucket00.len(), bucket01.len());
15290 assert_eq!(bucket00.len(), bucket02.len());
15291 assert_eq!(bucket00.len(), bucket03.len());
15292 for (((dst, src1), src2), src3) in bucket00
15293 .iter_mut()
15294 .zip(bucket01.iter())
15295 .zip(bucket02.iter())
15296 .zip(bucket03.iter())
15297 {
15298 *dst += *src1 + *src2 + *src3;
15299 }
15300}
15301
15302#[doc(hidden)]
15304pub fn accumulate_counts_s32_5(
15305 bucket00: &mut [SaSint],
15306 bucket01: &[SaSint],
15307 bucket02: &[SaSint],
15308 bucket03: &[SaSint],
15309 bucket04: &[SaSint],
15310) {
15311 assert_eq!(bucket00.len(), bucket01.len());
15312 assert_eq!(bucket00.len(), bucket02.len());
15313 assert_eq!(bucket00.len(), bucket03.len());
15314 assert_eq!(bucket00.len(), bucket04.len());
15315 for ((((dst, src1), src2), src3), src4) in bucket00
15316 .iter_mut()
15317 .zip(bucket01.iter())
15318 .zip(bucket02.iter())
15319 .zip(bucket03.iter())
15320 .zip(bucket04.iter())
15321 {
15322 *dst += *src1 + *src2 + *src3 + *src4;
15323 }
15324}
15325
15326#[doc(hidden)]
15328pub fn accumulate_counts_s32_6(
15329 bucket00: &mut [SaSint],
15330 bucket01: &[SaSint],
15331 bucket02: &[SaSint],
15332 bucket03: &[SaSint],
15333 bucket04: &[SaSint],
15334 bucket05: &[SaSint],
15335) {
15336 assert_eq!(bucket00.len(), bucket01.len());
15337 assert_eq!(bucket00.len(), bucket02.len());
15338 assert_eq!(bucket00.len(), bucket03.len());
15339 assert_eq!(bucket00.len(), bucket04.len());
15340 assert_eq!(bucket00.len(), bucket05.len());
15341 for (((((dst, src1), src2), src3), src4), src5) in bucket00
15342 .iter_mut()
15343 .zip(bucket01.iter())
15344 .zip(bucket02.iter())
15345 .zip(bucket03.iter())
15346 .zip(bucket04.iter())
15347 .zip(bucket05.iter())
15348 {
15349 *dst += *src1 + *src2 + *src3 + *src4 + *src5;
15350 }
15351}
15352
15353#[doc(hidden)]
15355pub fn accumulate_counts_s32_7(
15356 bucket00: &mut [SaSint],
15357 bucket01: &[SaSint],
15358 bucket02: &[SaSint],
15359 bucket03: &[SaSint],
15360 bucket04: &[SaSint],
15361 bucket05: &[SaSint],
15362 bucket06: &[SaSint],
15363) {
15364 assert_eq!(bucket00.len(), bucket01.len());
15365 assert_eq!(bucket00.len(), bucket02.len());
15366 assert_eq!(bucket00.len(), bucket03.len());
15367 assert_eq!(bucket00.len(), bucket04.len());
15368 assert_eq!(bucket00.len(), bucket05.len());
15369 assert_eq!(bucket00.len(), bucket06.len());
15370 for ((((((dst, src1), src2), src3), src4), src5), src6) in bucket00
15371 .iter_mut()
15372 .zip(bucket01.iter())
15373 .zip(bucket02.iter())
15374 .zip(bucket03.iter())
15375 .zip(bucket04.iter())
15376 .zip(bucket05.iter())
15377 .zip(bucket06.iter())
15378 {
15379 *dst += *src1 + *src2 + *src3 + *src4 + *src5 + *src6;
15380 }
15381}
15382
15383#[doc(hidden)]
15385pub fn accumulate_counts_s32_8(
15386 bucket00: &mut [SaSint],
15387 bucket01: &[SaSint],
15388 bucket02: &[SaSint],
15389 bucket03: &[SaSint],
15390 bucket04: &[SaSint],
15391 bucket05: &[SaSint],
15392 bucket06: &[SaSint],
15393 bucket07: &[SaSint],
15394) {
15395 assert_eq!(bucket00.len(), bucket01.len());
15396 assert_eq!(bucket00.len(), bucket02.len());
15397 assert_eq!(bucket00.len(), bucket03.len());
15398 assert_eq!(bucket00.len(), bucket04.len());
15399 assert_eq!(bucket00.len(), bucket05.len());
15400 assert_eq!(bucket00.len(), bucket06.len());
15401 assert_eq!(bucket00.len(), bucket07.len());
15402 for (((((((dst, src1), src2), src3), src4), src5), src6), src7) in bucket00
15403 .iter_mut()
15404 .zip(bucket01.iter())
15405 .zip(bucket02.iter())
15406 .zip(bucket03.iter())
15407 .zip(bucket04.iter())
15408 .zip(bucket05.iter())
15409 .zip(bucket06.iter())
15410 .zip(bucket07.iter())
15411 {
15412 *dst += *src1 + *src2 + *src3 + *src4 + *src5 + *src6 + *src7;
15413 }
15414}
15415
15416#[doc(hidden)]
15418pub fn accumulate_counts_s32_9(
15419 bucket00: &mut [SaSint],
15420 bucket01: &[SaSint],
15421 bucket02: &[SaSint],
15422 bucket03: &[SaSint],
15423 bucket04: &[SaSint],
15424 bucket05: &[SaSint],
15425 bucket06: &[SaSint],
15426 bucket07: &[SaSint],
15427 bucket08: &[SaSint],
15428) {
15429 assert_eq!(bucket00.len(), bucket01.len());
15430 assert_eq!(bucket00.len(), bucket02.len());
15431 assert_eq!(bucket00.len(), bucket03.len());
15432 assert_eq!(bucket00.len(), bucket04.len());
15433 assert_eq!(bucket00.len(), bucket05.len());
15434 assert_eq!(bucket00.len(), bucket06.len());
15435 assert_eq!(bucket00.len(), bucket07.len());
15436 assert_eq!(bucket00.len(), bucket08.len());
15437 for ((((((((dst, src1), src2), src3), src4), src5), src6), src7), src8) in bucket00
15438 .iter_mut()
15439 .zip(bucket01.iter())
15440 .zip(bucket02.iter())
15441 .zip(bucket03.iter())
15442 .zip(bucket04.iter())
15443 .zip(bucket05.iter())
15444 .zip(bucket06.iter())
15445 .zip(bucket07.iter())
15446 .zip(bucket08.iter())
15447 {
15448 *dst += *src1 + *src2 + *src3 + *src4 + *src5 + *src6 + *src7 + *src8;
15449 }
15450}
15451
15452#[doc(hidden)]
15454pub fn accumulate_counts_s32(
15455 buckets: &mut [SaSint],
15456 bucket_size: FastSint,
15457 bucket_stride: FastSint,
15458 mut num_buckets: FastSint,
15459) {
15460 if num_buckets <= 1 {
15461 return;
15462 }
15463
15464 let bucket_size = usize::try_from(bucket_size).expect("bucket_size must be non-negative");
15465 let bucket_stride = usize::try_from(bucket_stride).expect("bucket_stride must be non-negative");
15466 let num_buckets_usize = usize::try_from(num_buckets).expect("num_buckets must be non-negative");
15467 assert!(buckets.len() >= bucket_size + (num_buckets_usize - 1) * bucket_stride);
15468 let bucket00_start = (num_buckets_usize - 1) * bucket_stride;
15469
15470 while num_buckets >= 9 {
15471 let start = bucket00_start
15472 - usize::try_from(num_buckets - 9).expect("non-negative") * bucket_stride;
15473 accumulate_counts_at(buckets, start, bucket_size, bucket_stride, 9);
15474 num_buckets -= 8;
15475 }
15476
15477 match num_buckets {
15478 1 => {}
15479 2..=8 => accumulate_counts_at(
15480 buckets,
15481 bucket00_start,
15482 bucket_size,
15483 bucket_stride,
15484 usize::try_from(num_buckets).expect("non-negative"),
15485 ),
15486 _ => {}
15487 }
15488}
15489
15490fn block_slice<T>(slice: &[T], block_start: FastSint, block_size: FastSint) -> &[T] {
15491 let start = usize::try_from(block_start).expect("block_start must be non-negative");
15492 let len = usize::try_from(block_size).expect("block_size must be non-negative");
15493 &slice[start..start + len]
15494}
15495
15496#[allow(dead_code)]
15497struct SharedMutArray<'a> {
15498 ptr: *mut SaSint,
15499 len: usize,
15500 _marker: PhantomData<&'a mut [SaSint]>,
15501}
15502
15503#[allow(dead_code)]
15504impl<'a> SharedMutArray<'a> {
15505 fn new(slice: &'a mut [SaSint]) -> Self {
15506 Self {
15507 ptr: slice.as_mut_ptr(),
15508 len: slice.len(),
15509 _marker: PhantomData,
15510 }
15511 }
15512
15513 fn len(&self) -> usize {
15514 self.len
15515 }
15516
15517 fn slice_mut(&mut self, start: usize, len: usize) -> &mut [SaSint] {
15518 assert!(start <= self.len);
15519 assert!(len <= self.len - start);
15520 unsafe {
15521 std::slice::from_raw_parts_mut(self.ptr.add(start), len)
15525 }
15526 }
15527}
15528
15529fn accumulate_counts_at(
15530 buckets: &mut [SaSint],
15531 bucket00_start: usize,
15532 bucket_size: usize,
15533 bucket_stride: usize,
15534 count: usize,
15535) {
15536 assert!((2..=9).contains(&count));
15537 assert!(bucket00_start >= (count - 1) * bucket_stride);
15538
15539 let dst_end = bucket00_start + bucket_size;
15540 let mut sums = vec![0; bucket_size];
15541
15542 for i in 0..count {
15543 let start = bucket00_start - i * bucket_stride;
15544 let end = start + bucket_size;
15545 for (sum, value) in sums.iter_mut().zip(buckets[start..end].iter()) {
15546 *sum += *value;
15547 }
15548 }
15549
15550 buckets[bucket00_start..dst_end].copy_from_slice(&sums);
15551}
15552
15553#[doc(hidden)]
15555pub fn thread_state_size() -> usize {
15556 mem::size_of::<ThreadState>()
15557}
15558
15559#[cfg(all(test, feature = "upstream-c"))]
15560mod tests {
15561 use super::*;
15562
15563 unsafe extern "C" {
15564 fn probe_renumber_lms_suffixes_8u(
15565 sa: *mut SaSint,
15566 m: SaSint,
15567 name: SaSint,
15568 omp_block_start: FastSint,
15569 omp_block_size: FastSint,
15570 ) -> SaSint;
15571
15572 fn probe_gather_marked_lms_suffixes(
15573 sa: *mut SaSint,
15574 m: SaSint,
15575 l: FastSint,
15576 omp_block_start: FastSint,
15577 omp_block_size: FastSint,
15578 ) -> FastSint;
15579
15580 fn probe_renumber_distinct_lms_suffixes_32s_4k(
15581 sa: *mut SaSint,
15582 m: SaSint,
15583 name: SaSint,
15584 omp_block_start: FastSint,
15585 omp_block_size: FastSint,
15586 ) -> SaSint;
15587
15588 fn probe_renumber_unique_and_nonunique_lms_suffixes_32s(
15589 t: *mut SaSint,
15590 sa: *mut SaSint,
15591 m: SaSint,
15592 f: SaSint,
15593 omp_block_start: FastSint,
15594 omp_block_size: FastSint,
15595 ) -> SaSint;
15596
15597 fn probe_renumber_unique_and_nonunique_lms_suffixes_32s_omp(
15598 t: *mut SaSint,
15599 sa: *mut SaSint,
15600 m: SaSint,
15601 threads: SaSint,
15602 ) -> SaSint;
15603
15604 fn probe_renumber_and_gather_lms_suffixes_omp(
15605 sa: *mut SaSint,
15606 n: SaSint,
15607 m: SaSint,
15608 fs: SaSint,
15609 threads: SaSint,
15610 ) -> SaSint;
15611
15612 fn probe_renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
15613 sa: *mut SaSint,
15614 n: SaSint,
15615 m: SaSint,
15616 threads: SaSint,
15617 ) -> SaSint;
15618
15619 fn probe_main_32s_entry(
15620 t: *mut SaSint,
15621 sa: *mut SaSint,
15622 n: SaSint,
15623 k: SaSint,
15624 fs: SaSint,
15625 threads: SaSint,
15626 ) -> SaSint;
15627
15628 fn probe_public_libsais_freq(
15629 t: *const u8,
15630 sa: *mut SaSint,
15631 n: SaSint,
15632 fs: SaSint,
15633 freq: *mut SaSint,
15634 ) -> SaSint;
15635
15636 fn probe_public_libsais_gsa_freq(
15637 t: *const u8,
15638 sa: *mut SaSint,
15639 n: SaSint,
15640 fs: SaSint,
15641 freq: *mut SaSint,
15642 ) -> SaSint;
15643
15644 fn probe_public_libsais_bwt_freq(
15645 t: *const u8,
15646 u: *mut u8,
15647 a: *mut SaSint,
15648 n: SaSint,
15649 fs: SaSint,
15650 freq: *mut SaSint,
15651 ) -> SaSint;
15652
15653 fn probe_public_libsais_bwt_aux_freq(
15654 t: *const u8,
15655 u: *mut u8,
15656 a: *mut SaSint,
15657 n: SaSint,
15658 fs: SaSint,
15659 freq: *mut SaSint,
15660 r: SaSint,
15661 i: *mut SaSint,
15662 ) -> SaSint;
15663
15664 fn probe_public_libsais_unbwt_freq(
15665 t: *const u8,
15666 u: *mut u8,
15667 a: *mut SaSint,
15668 n: SaSint,
15669 freq: *const SaSint,
15670 i: SaSint,
15671 ) -> SaSint;
15672
15673 fn probe_public_libsais_unbwt_aux_freq(
15674 t: *const u8,
15675 u: *mut u8,
15676 a: *mut SaSint,
15677 n: SaSint,
15678 freq: *const SaSint,
15679 r: SaSint,
15680 i: *const SaSint,
15681 ) -> SaSint;
15682 }
15683
15684 fn make_recursive_main_32s_text(repeats: usize) -> Vec<SaSint> {
15685 let motif = [9, 4, 9, 2, 9, 4, 9, 1];
15686 let mut t = Vec::with_capacity(repeats * motif.len() + 1);
15687 for _ in 0..repeats {
15688 t.extend_from_slice(&motif);
15689 }
15690 t.push(0);
15691 t
15692 }
15693
15694 fn make_large_main_32s_stress_text(len: usize, alphabet: SaSint) -> Vec<SaSint> {
15695 let mut state: u32 = 0x1357_9bdf;
15696 let mut t = Vec::with_capacity(len + 1);
15697
15698 for i in 0..len {
15699 state = state.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
15700 let mut value = ((state >> 16) % (alphabet as u32 - 1)) as SaSint + 1;
15701
15702 if i % 17 < 8 {
15703 value = ((i / 17) as SaSint % 11) + 1;
15704 }
15705 if i % 29 < 10 {
15706 value = (((i / 29) as SaSint * 3) % 19) + 1;
15707 }
15708 if i % 64 >= 48 {
15709 value = t[i - 48];
15710 }
15711
15712 t.push(value);
15713 }
15714
15715 t.push(0);
15716 t
15717 }
15718
15719 fn assert_main_32s_entry_matches_upstream_c(
15720 t: Vec<SaSint>,
15721 k: SaSint,
15722 fs: SaSint,
15723 compare_full_sa: bool,
15724 ) {
15725 let mut t = t;
15726 let n = t.len() as SaSint;
15727 let n_usize = t.len();
15728 let threads = 1;
15729 let extra = usize::try_from(fs).expect("fs must be non-negative");
15730 let mut sa = vec![0; t.len() + extra];
15731
15732 let initial_t = t.clone();
15733 let initial_sa = sa.clone();
15734
15735 let c_result =
15736 unsafe { probe_main_32s_entry(t.as_mut_ptr(), sa.as_mut_ptr(), n, k, fs, threads) };
15737 let c_t = t.clone();
15738 let c_sa = sa.clone();
15739
15740 t.copy_from_slice(&initial_t);
15741 sa.copy_from_slice(&initial_sa);
15742
15743 let mut thread_state = alloc_thread_state(threads).expect("thread state");
15744 let rust_result =
15745 libsais_main_32s_entry(&mut t, &mut sa, n, k, fs, threads, &mut thread_state);
15746
15747 assert_eq!(rust_result, c_result);
15748 assert_slice_eq_with_first_diff("T", &t, &c_t);
15749 if compare_full_sa {
15750 assert_slice_eq_with_first_diff("SA", &sa, &c_sa);
15751 } else {
15752 assert_slice_eq_with_first_diff("SA", &sa[..n_usize], &c_sa[..n_usize]);
15753 }
15754 }
15755
15756 fn assert_main_32s_entry_matches_upstream_c_for_branch(k: SaSint) {
15757 assert_main_32s_entry_matches_upstream_c(
15758 vec![17, 3, 17, 9, 5, 9, 2, 11, 2, 7, 1, 7, 0],
15759 k,
15760 0,
15761 true,
15762 );
15763 }
15764
15765 fn assert_slice_eq_with_first_diff(label: &str, left: &[SaSint], right: &[SaSint]) {
15766 assert_eq!(left.len(), right.len(), "{label} length mismatch");
15767 if let Some((idx, (l, r))) = left
15768 .iter()
15769 .zip(right.iter())
15770 .enumerate()
15771 .find(|(_, (l, r))| l != r)
15772 {
15773 panic!("{label} first diff at index {idx}: rust={l}, c={r}");
15774 }
15775 }
15776
15777 #[test]
15778 fn align_up_matches_power_of_two_alignment() {
15779 assert_eq!(align_up(0, 4096), 0);
15780 assert_eq!(align_up(1, 4096), 4096);
15781 assert_eq!(align_up(4095, 4096), 4096);
15782 assert_eq!(align_up(4096, 4096), 4096);
15783 assert_eq!(align_up(4097, 4096), 8192);
15784 assert_eq!(align_up(65, 64), 128);
15785 }
15786
15787 #[test]
15788 fn shared_mut_array_projects_mutable_spans_from_one_backing_buffer() {
15789 let mut backing = vec![1, 2, 3, 4, 5, 6];
15790 let len;
15791 {
15792 let mut shared = SharedMutArray::new(&mut backing);
15793 shared.slice_mut(1, 3).copy_from_slice(&[20, 30, 40]);
15794 shared.slice_mut(4, 2).copy_from_slice(&[50, 60]);
15795 len = shared.len();
15796 }
15797 assert_eq!(backing, vec![1, 20, 30, 40, 50, 60]);
15798 assert_eq!(len, 6);
15799 }
15800
15801 #[test]
15802 fn create_ctx_main_matches_single_thread_layout() {
15803 let ctx = create_ctx_main(1).expect("context");
15804 assert_eq!(ctx.buckets.len(), 8 * ALPHABET_SIZE);
15805 assert_eq!(ctx.threads, 1);
15806 assert!(ctx.thread_state.is_none());
15807 }
15808
15809 #[test]
15810 fn create_ctx_main_allocates_thread_state_for_multi_threaded_mode() {
15811 let ctx = create_ctx_main(3).expect("context");
15812 let states = ctx.thread_state.expect("thread state");
15813 assert_eq!(states.len(), 3);
15814 assert!(states
15815 .iter()
15816 .all(|state| state.buckets.len() == 4 * ALPHABET_SIZE));
15817 assert!(states
15818 .iter()
15819 .all(|state| state.cache.len() == LIBSAIS_PER_THREAD_CACHE_SIZE));
15820 }
15821
15822 #[test]
15823 fn create_ctx_wraps_single_thread_main_context() {
15824 let ctx = create_ctx().expect("context");
15825 assert_eq!(ctx.threads, 1);
15826 assert_eq!(ctx.buckets.len(), 8 * ALPHABET_SIZE);
15827 assert!(ctx.thread_state.is_none());
15828 }
15829
15830 #[test]
15831 fn free_ctx_accepts_context_value() {
15832 let ctx = create_ctx().expect("context");
15833 free_ctx(ctx);
15834 }
15835
15836 fn brute_force_suffix_array_u8(t: &[u8]) -> Vec<SaSint> {
15837 let mut sa: Vec<SaSint> = (0..t.len())
15838 .map(|index| SaSint::try_from(index).expect("index must fit SaSint"))
15839 .collect();
15840 sa.sort_by(|&lhs, &rhs| {
15841 t[usize::try_from(lhs).expect("non-negative")..]
15842 .cmp(&t[usize::try_from(rhs).expect("non-negative")..])
15843 });
15844 sa
15845 }
15846
15847 fn brute_force_plcp_u8(t: &[u8], sa: &[SaSint]) -> Vec<SaSint> {
15848 let mut rank = vec![0usize; t.len()];
15849 for (i, &suffix) in sa.iter().enumerate() {
15850 rank[usize::try_from(suffix).expect("suffix index must be non-negative")] = i;
15851 }
15852
15853 let mut plcp = vec![0; t.len()];
15854 for i in 0..t.len() {
15855 let r = rank[i];
15856 let prev = if r == 0 {
15857 t.len()
15858 } else {
15859 usize::try_from(sa[r - 1]).expect("suffix index must be non-negative")
15860 };
15861 if prev == t.len() {
15862 plcp[i] = 0;
15863 continue;
15864 }
15865
15866 let mut l = 0usize;
15867 while i + l < t.len() && prev + l < t.len() && t[i + l] == t[prev + l] {
15868 l += 1;
15869 }
15870 plcp[i] = l as SaSint;
15871 }
15872 plcp
15873 }
15874
15875 fn brute_force_lcp_from_sa_u8(t: &[u8], sa: &[SaSint]) -> Vec<SaSint> {
15876 let mut lcp = vec![0; sa.len()];
15877 for i in 0..sa.len() {
15878 let lhs = usize::try_from(sa[i]).expect("suffix index must be non-negative");
15879 let rhs = if i == 0 {
15880 sa.len()
15881 } else {
15882 usize::try_from(sa[i - 1]).expect("suffix index must be non-negative")
15883 };
15884 if rhs == sa.len() {
15885 lcp[i] = 0;
15886 continue;
15887 }
15888
15889 let mut l = 0usize;
15890 while lhs + l < t.len() && rhs + l < t.len() && t[lhs + l] == t[rhs + l] {
15891 l += 1;
15892 }
15893 lcp[i] = l as SaSint;
15894 }
15895 lcp
15896 }
15897
15898 #[test]
15899 fn libsais_matches_bruteforce_suffix_array_for_small_text() {
15900 let t = b"banana";
15901 let mut sa = vec![0; t.len()];
15902 let mut freq = vec![0; ALPHABET_SIZE];
15903
15904 let result = libsais(t, &mut sa, 0, Some(&mut freq));
15905
15906 assert_eq!(result, 0);
15907 assert_eq!(sa, brute_force_suffix_array_u8(t));
15908 assert_eq!(freq[b'a' as usize], 3);
15909 assert_eq!(freq[b'b' as usize], 1);
15910 assert_eq!(freq[b'n' as usize], 2);
15911 }
15912
15913 #[test]
15914 fn public_libsais_frequency_outputs_match_upstream_c() {
15915 let text = b"banana";
15916 let gsa_text = b"ban\0ana\0";
15917 let mut rust_sa = vec![0; text.len()];
15918 let mut c_sa = vec![0; text.len()];
15919 let mut rust_freq = vec![-1; ALPHABET_SIZE];
15920 let mut c_freq = vec![-1; ALPHABET_SIZE];
15921
15922 let rust_rc = libsais(text, &mut rust_sa, 0, Some(&mut rust_freq));
15923 let c_rc = unsafe {
15924 probe_public_libsais_freq(
15925 text.as_ptr(),
15926 c_sa.as_mut_ptr(),
15927 text.len() as SaSint,
15928 0,
15929 c_freq.as_mut_ptr(),
15930 )
15931 };
15932 assert_eq!(rust_rc, c_rc);
15933 assert_eq!(rust_sa, c_sa);
15934 assert_eq!(rust_freq, c_freq);
15935
15936 let mut rust_gsa = vec![0; gsa_text.len()];
15937 let mut c_gsa = vec![0; gsa_text.len()];
15938 rust_freq.fill(-1);
15939 c_freq.fill(-1);
15940 let rust_rc = libsais_gsa(gsa_text, &mut rust_gsa, 0, Some(&mut rust_freq));
15941 let c_rc = unsafe {
15942 probe_public_libsais_gsa_freq(
15943 gsa_text.as_ptr(),
15944 c_gsa.as_mut_ptr(),
15945 gsa_text.len() as SaSint,
15946 0,
15947 c_freq.as_mut_ptr(),
15948 )
15949 };
15950 assert_eq!(rust_rc, c_rc);
15951 assert_eq!(rust_gsa, c_gsa);
15952 assert_eq!(rust_freq, c_freq);
15953
15954 let mut rust_u = vec![0; text.len()];
15955 let mut rust_a = vec![0; text.len()];
15956 let mut c_u = vec![0; text.len()];
15957 let mut c_a = vec![0; text.len()];
15958 rust_freq.fill(-1);
15959 c_freq.fill(-1);
15960 let rust_rc = libsais_bwt(text, &mut rust_u, &mut rust_a, 0, Some(&mut rust_freq));
15961 let c_rc = unsafe {
15962 probe_public_libsais_bwt_freq(
15963 text.as_ptr(),
15964 c_u.as_mut_ptr(),
15965 c_a.as_mut_ptr(),
15966 text.len() as SaSint,
15967 0,
15968 c_freq.as_mut_ptr(),
15969 )
15970 };
15971 assert_eq!(rust_rc, c_rc);
15972 assert_eq!(rust_u, c_u);
15973 assert_eq!(rust_freq, c_freq);
15974
15975 let r = 4;
15976 let mut rust_i = vec![0; (text.len() - 1) / r as usize + 1];
15977 let mut c_i = vec![0; rust_i.len()];
15978 rust_freq.fill(-1);
15979 c_freq.fill(-1);
15980 let rust_rc = libsais_bwt_aux(
15981 text,
15982 &mut rust_u,
15983 &mut rust_a,
15984 0,
15985 Some(&mut rust_freq),
15986 r,
15987 &mut rust_i,
15988 );
15989 let c_rc = unsafe {
15990 probe_public_libsais_bwt_aux_freq(
15991 text.as_ptr(),
15992 c_u.as_mut_ptr(),
15993 c_a.as_mut_ptr(),
15994 text.len() as SaSint,
15995 0,
15996 c_freq.as_mut_ptr(),
15997 r,
15998 c_i.as_mut_ptr(),
15999 )
16000 };
16001 assert_eq!(rust_rc, c_rc);
16002 assert_eq!(rust_u, c_u);
16003 assert_eq!(rust_i, c_i);
16004 assert_eq!(rust_freq, c_freq);
16005 }
16006
16007 #[test]
16008 fn public_libsais_unbwt_with_frequency_matches_upstream_c() {
16009 let text = b"abracadabra";
16010 let mut freq = vec![0; ALPHABET_SIZE];
16011 let mut bwt = vec![0; text.len()];
16012 let mut work = vec![0; text.len()];
16013 let primary = libsais_bwt(text, &mut bwt, &mut work, 0, Some(&mut freq));
16014 assert!(primary >= 0);
16015
16016 let mut rust_u = vec![0; text.len()];
16017 let mut rust_a = vec![0; text.len() + 1];
16018 let mut c_u = vec![0; text.len()];
16019 let mut c_a = vec![0; text.len() + 1];
16020 let rust_rc = libsais_unbwt(&bwt, &mut rust_u, &mut rust_a, Some(&freq), primary);
16021 let c_rc = unsafe {
16022 probe_public_libsais_unbwt_freq(
16023 bwt.as_ptr(),
16024 c_u.as_mut_ptr(),
16025 c_a.as_mut_ptr(),
16026 bwt.len() as SaSint,
16027 freq.as_ptr(),
16028 primary,
16029 )
16030 };
16031 assert_eq!(rust_rc, c_rc);
16032 assert_eq!(rust_u, c_u);
16033 assert_eq!(rust_u, text);
16034
16035 let r = 4;
16036 let mut aux = vec![0; (text.len() - 1) / r as usize + 1];
16037 let bwt_rc = libsais_bwt_aux(text, &mut bwt, &mut work, 0, Some(&mut freq), r, &mut aux);
16038 assert_eq!(bwt_rc, 0);
16039
16040 rust_u.fill(0);
16041 rust_a.fill(0);
16042 c_u.fill(0);
16043 c_a.fill(0);
16044 let rust_rc = libsais_unbwt_aux(&bwt, &mut rust_u, &mut rust_a, Some(&freq), r, &aux);
16045 let c_rc = unsafe {
16046 probe_public_libsais_unbwt_aux_freq(
16047 bwt.as_ptr(),
16048 c_u.as_mut_ptr(),
16049 c_a.as_mut_ptr(),
16050 bwt.len() as SaSint,
16051 freq.as_ptr(),
16052 r,
16053 aux.as_ptr(),
16054 )
16055 };
16056 assert_eq!(rust_rc, c_rc);
16057 assert_eq!(rust_u, c_u);
16058 assert_eq!(rust_u, text);
16059 }
16060
16061 #[test]
16062 fn libsais_omp_frequency_wrappers_match_direct_calls() {
16063 let text = b"banana";
16064 let gsa_text = b"ban\0ana\0";
16065
16066 let mut direct_sa = vec![0; text.len()];
16067 let mut omp_sa = vec![0; text.len()];
16068 let mut direct_freq = vec![-1; ALPHABET_SIZE];
16069 let mut omp_freq = vec![-1; ALPHABET_SIZE];
16070 assert_eq!(libsais(text, &mut direct_sa, 0, Some(&mut direct_freq)), 0);
16071 assert_eq!(libsais_omp(text, &mut omp_sa, 0, Some(&mut omp_freq), 2), 0);
16072 assert_eq!(omp_sa, direct_sa);
16073 assert_eq!(omp_freq, direct_freq);
16074
16075 let mut direct_gsa = vec![0; gsa_text.len()];
16076 let mut omp_gsa = vec![0; gsa_text.len()];
16077 direct_freq.fill(-1);
16078 omp_freq.fill(-1);
16079 assert_eq!(
16080 libsais_gsa(gsa_text, &mut direct_gsa, 0, Some(&mut direct_freq)),
16081 0
16082 );
16083 assert_eq!(
16084 libsais_gsa_omp(gsa_text, &mut omp_gsa, 0, Some(&mut omp_freq), 2),
16085 0
16086 );
16087 assert_eq!(omp_gsa, direct_gsa);
16088 assert_eq!(omp_freq, direct_freq);
16089
16090 let mut direct_bwt = vec![0; text.len()];
16091 let mut direct_work = vec![0; text.len()];
16092 let mut omp_bwt = vec![0; text.len()];
16093 let mut omp_work = vec![0; text.len()];
16094 direct_freq.fill(-1);
16095 omp_freq.fill(-1);
16096 assert_eq!(
16097 libsais_bwt(
16098 text,
16099 &mut direct_bwt,
16100 &mut direct_work,
16101 0,
16102 Some(&mut direct_freq)
16103 ),
16104 libsais_bwt_omp(text, &mut omp_bwt, &mut omp_work, 0, Some(&mut omp_freq), 2)
16105 );
16106 assert_eq!(omp_bwt, direct_bwt);
16107 assert_eq!(omp_freq, direct_freq);
16108
16109 let mut direct_aux = vec![0; 2];
16110 let mut omp_aux = vec![0; 2];
16111 direct_freq.fill(-1);
16112 omp_freq.fill(-1);
16113 assert_eq!(
16114 libsais_bwt_aux(
16115 text,
16116 &mut direct_bwt,
16117 &mut direct_work,
16118 0,
16119 Some(&mut direct_freq),
16120 4,
16121 &mut direct_aux
16122 ),
16123 libsais_bwt_aux_omp(
16124 text,
16125 &mut omp_bwt,
16126 &mut omp_work,
16127 0,
16128 Some(&mut omp_freq),
16129 4,
16130 &mut omp_aux,
16131 2
16132 )
16133 );
16134 assert_eq!(omp_bwt, direct_bwt);
16135 assert_eq!(omp_aux, direct_aux);
16136 assert_eq!(omp_freq, direct_freq);
16137 }
16138
16139 #[test]
16140 #[ignore = "large real-data regression; requires local minibwa yeast fixture"]
16141 fn public_libsais_omp_handles_minibwa_yeast_two_strand_index_input() {
16142 let l2b_path =
16143 "/data/henriksson/github/claude/minibwa/.tmp/compare-yeast-now/ref.split.rust.l2b";
16144 let fasta_path =
16145 "/data/henriksson/github/claude/minibwa/.tmp/large-real/yeast/ref.sanitized.fa";
16146 let forward = if let Ok(bytes) = std::fs::read(l2b_path) {
16147 assert!(bytes.len() >= 64, "short l2b fixture: {l2b_path}");
16148 assert_eq!(&bytes[..4], b"L2B\x01", "bad l2b magic in {l2b_path}");
16149 let n_ctg = u64::from_le_bytes(bytes[8..16].try_into().unwrap()) as usize;
16150 let tot_len = u64::from_le_bytes(bytes[16..24].try_into().unwrap()) as usize;
16151 let n_ambi = u64::from_le_bytes(bytes[24..32].try_into().unwrap()) as usize;
16152 let n_mask = u64::from_le_bytes(bytes[32..40].try_into().unwrap()) as usize;
16153 let n_pac = u64::from_le_bytes(bytes[56..64].try_into().unwrap()) as usize;
16154 let pac_start = 64 + 8 * n_ctg + 16 * n_ambi + 16 * n_mask;
16155 assert!(
16156 bytes.len() >= pac_start + 8 * n_pac,
16157 "truncated l2b pac in {l2b_path}"
16158 );
16159 let mut pac = Vec::with_capacity(n_pac);
16160 for chunk in bytes[pac_start..pac_start + 8 * n_pac].chunks_exact(8) {
16161 pac.push(u64::from_le_bytes(chunk.try_into().unwrap()));
16162 }
16163 (0..tot_len)
16164 .map(|i| ((pac[i >> 5] >> ((i & 31) << 1)) & 3) as u8)
16165 .collect::<Vec<_>>()
16166 } else if let Ok(fasta) = std::fs::read_to_string(fasta_path) {
16167 let mut rng = 11u64;
16168 let mut forward = Vec::new();
16169 for line in fasta.lines() {
16170 if line.starts_with('>') {
16171 continue;
16172 }
16173 forward.extend(line.bytes().map(|b| {
16174 let mut c = match b {
16175 b'A' | b'a' => 0,
16176 b'C' | b'c' => 1,
16177 b'G' | b'g' => 2,
16178 b'T' | b't' | b'U' | b'u' => 3,
16179 _ => {
16180 rng = rng.wrapping_add(0x9e3779b97f4a7c15);
16181 let mut z = rng;
16182 z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
16183 z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
16184 4 | ((z ^ (z >> 31)) & 3) as u8
16185 }
16186 };
16187 if b < b'A' || b > b'Z' {
16188 c |= 1 << 3;
16189 }
16190 c & 3
16191 }));
16192 }
16193 forward
16194 } else {
16195 eprintln!("skipping missing fixtures: {l2b_path} and {fasta_path}");
16196 return;
16197 };
16198 assert!(
16199 forward.len() > 12_000_000,
16200 "fixture should exercise the minibwa yeast index workload"
16201 );
16202
16203 let mut text = Vec::with_capacity(forward.len() * 2);
16204 text.extend_from_slice(&forward);
16205 text.extend(forward.iter().rev().map(|&c| 3 - c));
16206
16207 const FS: SaSint = 10_000;
16208 let mut sa = vec![0; text.len() + FS as usize + 1];
16209 assert_eq!(libsais_omp(&text, &mut sa[1..], FS, None, 4), 0);
16210 if let Some((i, &value)) = sa[1..1 + text.len()]
16211 .iter()
16212 .enumerate()
16213 .find(|&(_, &value)| value < 0 || value as usize >= text.len())
16214 {
16215 panic!("invalid suffix-array entry at {i}: {value}");
16216 }
16217 }
16218
16219 #[test]
16220 #[ignore = "large real-data regression; requires local minibwa yeast fixture"]
16221 fn public_libsais_omp_matches_plain_on_minibwa_yeast_two_strand_index_input() {
16222 let l2b_path =
16223 "/data/henriksson/github/claude/minibwa/.tmp/compare-yeast-now/ref.split.rust.l2b";
16224 let fasta_path =
16225 "/data/henriksson/github/claude/minibwa/.tmp/large-real/yeast/ref.sanitized.fa";
16226 let forward = if let Ok(bytes) = std::fs::read(l2b_path) {
16227 assert!(bytes.len() >= 64, "short l2b fixture: {l2b_path}");
16228 assert_eq!(&bytes[..4], b"L2B\x01", "bad l2b magic in {l2b_path}");
16229 let n_ctg = u64::from_le_bytes(bytes[8..16].try_into().unwrap()) as usize;
16230 let tot_len = u64::from_le_bytes(bytes[16..24].try_into().unwrap()) as usize;
16231 let n_ambi = u64::from_le_bytes(bytes[24..32].try_into().unwrap()) as usize;
16232 let n_mask = u64::from_le_bytes(bytes[32..40].try_into().unwrap()) as usize;
16233 let n_pac = u64::from_le_bytes(bytes[56..64].try_into().unwrap()) as usize;
16234 let pac_start = 64 + 8 * n_ctg + 16 * n_ambi + 16 * n_mask;
16235 assert!(
16236 bytes.len() >= pac_start + 8 * n_pac,
16237 "truncated l2b pac in {l2b_path}"
16238 );
16239 let mut pac = Vec::with_capacity(n_pac);
16240 for chunk in bytes[pac_start..pac_start + 8 * n_pac].chunks_exact(8) {
16241 pac.push(u64::from_le_bytes(chunk.try_into().unwrap()));
16242 }
16243 (0..tot_len)
16244 .map(|i| ((pac[i >> 5] >> ((i & 31) << 1)) & 3) as u8)
16245 .collect::<Vec<_>>()
16246 } else if let Ok(fasta) = std::fs::read_to_string(fasta_path) {
16247 let mut rng = 11u64;
16248 let mut forward = Vec::new();
16249 for line in fasta.lines() {
16250 if line.starts_with('>') {
16251 continue;
16252 }
16253 forward.extend(line.bytes().map(|b| {
16254 let mut c = match b {
16255 b'A' | b'a' => 0,
16256 b'C' | b'c' => 1,
16257 b'G' | b'g' => 2,
16258 b'T' | b't' | b'U' | b'u' => 3,
16259 _ => {
16260 rng = rng.wrapping_add(0x9e3779b97f4a7c15);
16261 let mut z = rng;
16262 z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
16263 z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
16264 4 | ((z ^ (z >> 31)) & 3) as u8
16265 }
16266 };
16267 if b < b'A' || b > b'Z' {
16268 c |= 1 << 3;
16269 }
16270 c & 3
16271 }));
16272 }
16273 forward
16274 } else {
16275 eprintln!("skipping missing fixtures: {l2b_path} and {fasta_path}");
16276 return;
16277 };
16278 assert!(
16279 forward.len() > 12_000_000,
16280 "fixture should exercise the minibwa yeast index workload"
16281 );
16282
16283 let mut text = Vec::with_capacity(forward.len() * 2);
16284 text.extend_from_slice(&forward);
16285 text.extend(forward.iter().rev().map(|&c| 3 - c));
16286
16287 const FS: SaSint = 10_000;
16288 let mut plain_sa = vec![0; text.len() + FS as usize + 1];
16289 let mut omp_sa = vec![0; text.len() + FS as usize + 1];
16290 assert_eq!(libsais(&text, &mut plain_sa[1..], FS, None), 0);
16291 assert_eq!(libsais_omp(&text, &mut omp_sa[1..], FS, None, 4), 0);
16292 plain_sa[0] = text.len() as SaSint;
16293 omp_sa[0] = text.len() as SaSint;
16294 if let Some(i) = plain_sa[..=text.len()]
16295 .iter()
16296 .zip(&omp_sa[..=text.len()])
16297 .position(|(plain, omp)| plain != omp)
16298 {
16299 panic!(
16300 "first suffix-array diff at {i}: plain={} omp={}",
16301 plain_sa[i], omp_sa[i]
16302 );
16303 }
16304 }
16305
16306 #[test]
16307 fn libsais_unbwt_omp_frequency_wrappers_match_direct_calls() {
16308 let text = b"abracadabra";
16309 let mut freq = vec![0; ALPHABET_SIZE];
16310 let mut bwt = vec![0; text.len()];
16311 let mut work = vec![0; text.len()];
16312 let primary = libsais_bwt(text, &mut bwt, &mut work, 0, Some(&mut freq));
16313 assert!(primary >= 0);
16314
16315 let mut direct = vec![0; text.len()];
16316 let mut direct_work = vec![0; text.len() + 1];
16317 let mut omp = vec![0; text.len()];
16318 let mut omp_work = vec![0; text.len() + 1];
16319 assert_eq!(
16320 libsais_unbwt(&bwt, &mut direct, &mut direct_work, Some(&freq), primary),
16321 libsais_unbwt_omp(&bwt, &mut omp, &mut omp_work, Some(&freq), primary, 2)
16322 );
16323 assert_eq!(omp, direct);
16324 assert_eq!(omp, text);
16325
16326 let mut aux = vec![0; (text.len() - 1) / 4 + 1];
16327 assert_eq!(
16328 libsais_bwt_aux(text, &mut bwt, &mut work, 0, Some(&mut freq), 4, &mut aux),
16329 0
16330 );
16331 direct.fill(0);
16332 direct_work.fill(0);
16333 omp.fill(0);
16334 omp_work.fill(0);
16335 assert_eq!(
16336 libsais_unbwt_aux(&bwt, &mut direct, &mut direct_work, Some(&freq), 4, &aux),
16337 libsais_unbwt_aux_omp(&bwt, &mut omp, &mut omp_work, Some(&freq), 4, &aux, 2)
16338 );
16339 assert_eq!(omp, direct);
16340 assert_eq!(omp, text);
16341 }
16342
16343 #[test]
16344 fn libsais_ctx_matches_plain_entry_point_for_small_text() {
16345 let t = b"mississippi";
16346 let mut sa_plain = vec![0; t.len()];
16347 let mut sa_ctx = vec![0; t.len()];
16348 let plain = libsais(t, &mut sa_plain, 0, None);
16349
16350 let mut ctx = create_ctx().expect("context");
16351 let with_ctx = libsais_ctx(&mut ctx, t, &mut sa_ctx, 0, None);
16352
16353 assert_eq!(plain, 0);
16354 assert_eq!(with_ctx, 0);
16355 assert_eq!(sa_ctx, sa_plain);
16356 }
16357
16358 #[test]
16359 fn libsais_ctx_frequency_wrappers_match_direct_calls() {
16360 let text = b"banana";
16361 let gsa_text = b"ban\0ana\0";
16362 let mut ctx = create_ctx().expect("context");
16363
16364 let mut direct_sa = vec![0; text.len()];
16365 let mut ctx_sa = vec![0; text.len()];
16366 let mut direct_freq = vec![-1; ALPHABET_SIZE];
16367 let mut ctx_freq = vec![-1; ALPHABET_SIZE];
16368 assert_eq!(libsais(text, &mut direct_sa, 0, Some(&mut direct_freq)), 0);
16369 assert_eq!(
16370 libsais_ctx(&mut ctx, text, &mut ctx_sa, 0, Some(&mut ctx_freq)),
16371 0
16372 );
16373 assert_eq!(ctx_sa, direct_sa);
16374 assert_eq!(ctx_freq, direct_freq);
16375
16376 let mut direct_gsa = vec![0; gsa_text.len()];
16377 let mut ctx_gsa = vec![0; gsa_text.len()];
16378 direct_freq.fill(-1);
16379 ctx_freq.fill(-1);
16380 assert_eq!(
16381 libsais_gsa(gsa_text, &mut direct_gsa, 0, Some(&mut direct_freq)),
16382 0
16383 );
16384 assert_eq!(
16385 libsais_gsa_ctx(&mut ctx, gsa_text, &mut ctx_gsa, 0, Some(&mut ctx_freq)),
16386 0
16387 );
16388 assert_eq!(ctx_gsa, direct_gsa);
16389 assert_eq!(ctx_freq, direct_freq);
16390
16391 let mut direct_bwt = vec![0; text.len()];
16392 let mut direct_work = vec![0; text.len()];
16393 let mut ctx_bwt = vec![0; text.len()];
16394 let mut ctx_work = vec![0; text.len()];
16395 direct_freq.fill(-1);
16396 ctx_freq.fill(-1);
16397 assert_eq!(
16398 libsais_bwt(
16399 text,
16400 &mut direct_bwt,
16401 &mut direct_work,
16402 0,
16403 Some(&mut direct_freq)
16404 ),
16405 libsais_bwt_ctx(
16406 &mut ctx,
16407 text,
16408 &mut ctx_bwt,
16409 &mut ctx_work,
16410 0,
16411 Some(&mut ctx_freq)
16412 )
16413 );
16414 assert_eq!(ctx_bwt, direct_bwt);
16415 assert_eq!(ctx_freq, direct_freq);
16416
16417 let mut direct_aux = vec![0; 2];
16418 let mut ctx_aux = vec![0; 2];
16419 direct_freq.fill(-1);
16420 ctx_freq.fill(-1);
16421 assert_eq!(
16422 libsais_bwt_aux(
16423 text,
16424 &mut direct_bwt,
16425 &mut direct_work,
16426 0,
16427 Some(&mut direct_freq),
16428 4,
16429 &mut direct_aux
16430 ),
16431 libsais_bwt_aux_ctx(
16432 &mut ctx,
16433 text,
16434 &mut ctx_bwt,
16435 &mut ctx_work,
16436 0,
16437 Some(&mut ctx_freq),
16438 4,
16439 &mut ctx_aux
16440 )
16441 );
16442 assert_eq!(ctx_bwt, direct_bwt);
16443 assert_eq!(ctx_aux, direct_aux);
16444 assert_eq!(ctx_freq, direct_freq);
16445 }
16446
16447 #[test]
16448 fn libsais_unbwt_ctx_frequency_wrappers_match_direct_calls() {
16449 let text = b"abracadabra";
16450 let mut freq = vec![0; ALPHABET_SIZE];
16451 let mut bwt = vec![0; text.len()];
16452 let mut work = vec![0; text.len()];
16453 let primary = libsais_bwt(text, &mut bwt, &mut work, 0, Some(&mut freq));
16454 assert!(primary >= 0);
16455
16456 let mut ctx = unbwt_create_ctx().expect("unbwt context");
16457 let mut direct = vec![0; text.len()];
16458 let mut direct_work = vec![0; text.len() + 1];
16459 let mut via_ctx = vec![0; text.len()];
16460 let mut ctx_work = vec![0; text.len() + 1];
16461 assert_eq!(
16462 libsais_unbwt(&bwt, &mut direct, &mut direct_work, Some(&freq), primary),
16463 libsais_unbwt_ctx(
16464 &mut ctx,
16465 &bwt,
16466 &mut via_ctx,
16467 &mut ctx_work,
16468 Some(&freq),
16469 primary
16470 )
16471 );
16472 assert_eq!(via_ctx, direct);
16473 assert_eq!(via_ctx, text);
16474
16475 let mut aux = vec![0; (text.len() - 1) / 4 + 1];
16476 assert_eq!(
16477 libsais_bwt_aux(text, &mut bwt, &mut work, 0, Some(&mut freq), 4, &mut aux),
16478 0
16479 );
16480 direct.fill(0);
16481 direct_work.fill(0);
16482 via_ctx.fill(0);
16483 ctx_work.fill(0);
16484 assert_eq!(
16485 libsais_unbwt_aux(&bwt, &mut direct, &mut direct_work, Some(&freq), 4, &aux),
16486 libsais_unbwt_aux_ctx(
16487 &mut ctx,
16488 &bwt,
16489 &mut via_ctx,
16490 &mut ctx_work,
16491 Some(&freq),
16492 4,
16493 &aux
16494 )
16495 );
16496 assert_eq!(via_ctx, direct);
16497 assert_eq!(via_ctx, text);
16498 }
16499
16500 #[test]
16501 fn libsais_int_matches_bruteforce_suffix_array_for_small_integer_text() {
16502 let mut t = vec![2, 1, 3, 1, 0];
16503 let expected = {
16504 let mut sa: Vec<SaSint> = (0..t.len())
16505 .map(|index| SaSint::try_from(index).expect("index must fit SaSint"))
16506 .collect();
16507 sa.sort_by(|&lhs, &rhs| {
16508 t[usize::try_from(lhs).expect("non-negative")..]
16509 .cmp(&t[usize::try_from(rhs).expect("non-negative")..])
16510 });
16511 sa
16512 };
16513 let mut sa = vec![0; t.len()];
16514
16515 let result = libsais_int(&mut t, &mut sa, 4, 0);
16516
16517 assert_eq!(result, 0);
16518 assert_eq!(sa, expected);
16519 }
16520
16521 #[test]
16522 fn libsais_plcp_matches_bruteforce_for_small_text() {
16523 let t = b"banana";
16524 let sa = brute_force_suffix_array_u8(t);
16525 let expected = brute_force_plcp_u8(t, &sa);
16526 let mut plcp = vec![0; t.len()];
16527
16528 let result = libsais_plcp(t, &sa, &mut plcp);
16529
16530 assert_eq!(result, 0);
16531 assert_eq!(plcp, expected);
16532 }
16533
16534 #[test]
16535 fn libsais_plcp_gsa_stops_at_separator() {
16536 let t = b"ab\0b\0";
16537 let sa = brute_force_suffix_array_u8(t);
16538 let mut plcp = vec![0; t.len()];
16539
16540 let result = libsais_plcp_gsa(t, &sa, &mut plcp);
16541
16542 assert_eq!(result, 0);
16543 assert_eq!(plcp[2], 0);
16544 assert_eq!(plcp[4], 0);
16545 }
16546
16547 #[test]
16548 fn libsais_lcp_matches_bruteforce_for_small_text() {
16549 let t = b"banana";
16550 let sa = brute_force_suffix_array_u8(t);
16551 let plcp = brute_force_plcp_u8(t, &sa);
16552 let expected = brute_force_lcp_from_sa_u8(t, &sa);
16553 let mut lcp = vec![0; t.len()];
16554
16555 let result = libsais_lcp(&plcp, &sa, &mut lcp);
16556
16557 assert_eq!(result, 0);
16558 assert_eq!(lcp, expected);
16559 }
16560
16561 #[test]
16562 fn libsais_ctx_rejects_invalid_public_arguments() {
16563 let text = b"banana";
16564 let mut ctx = create_ctx().expect("context");
16565 let mut short_sa = vec![0; text.len() - 1];
16566 let mut full_sa = vec![0; text.len()];
16567 let mut short_freq = vec![0; ALPHABET_SIZE - 1];
16568 let mut short_u = vec![0; text.len() - 1];
16569 let mut full_u = vec![0; text.len()];
16570 let mut short_a = vec![0; text.len() - 1];
16571 let mut full_a = vec![0; text.len()];
16572 let mut aux = vec![0; 2];
16573
16574 assert_eq!(libsais_ctx(&mut ctx, text, &mut short_sa, 0, None), -1);
16575 assert_eq!(
16576 libsais_ctx(&mut ctx, text, &mut full_sa, 0, Some(&mut short_freq)),
16577 -1
16578 );
16579 assert_eq!(
16580 libsais_gsa_ctx(&mut ctx, b"banana", &mut full_sa, 0, None),
16581 -1
16582 );
16583 assert_eq!(
16584 libsais_gsa_ctx(&mut ctx, b"banana\0", &mut short_sa, 0, None),
16585 -1
16586 );
16587 assert_eq!(
16588 libsais_bwt_ctx(&mut ctx, text, &mut short_u, &mut full_a, 0, None),
16589 -1
16590 );
16591 assert_eq!(
16592 libsais_bwt_ctx(&mut ctx, text, &mut full_u, &mut short_a, 0, None),
16593 -1
16594 );
16595 assert_eq!(
16596 libsais_bwt_ctx(
16597 &mut ctx,
16598 text,
16599 &mut full_u,
16600 &mut full_a,
16601 0,
16602 Some(&mut short_freq)
16603 ),
16604 -1
16605 );
16606 assert_eq!(
16607 libsais_bwt_aux_ctx(
16608 &mut ctx,
16609 text,
16610 &mut full_u,
16611 &mut full_a,
16612 0,
16613 None,
16614 0,
16615 &mut aux
16616 ),
16617 -1
16618 );
16619 assert_eq!(
16620 libsais_bwt_aux_ctx(
16621 &mut ctx,
16622 text,
16623 &mut full_u,
16624 &mut full_a,
16625 0,
16626 None,
16627 3,
16628 &mut aux
16629 ),
16630 -1
16631 );
16632 assert_eq!(
16633 libsais_bwt_aux_ctx(
16634 &mut ctx,
16635 text,
16636 &mut full_u,
16637 &mut full_a,
16638 0,
16639 None,
16640 4,
16641 &mut []
16642 ),
16643 -1
16644 );
16645
16646 let mut missing_thread_state_ctx = Context {
16647 buckets: vec![0; 8 * ALPHABET_SIZE],
16648 thread_state: None,
16649 threads: 2,
16650 };
16651 assert_eq!(
16652 libsais_ctx(&mut missing_thread_state_ctx, text, &mut full_sa, 0, None),
16653 -2
16654 );
16655
16656 let mut zero_thread_ctx = Context {
16657 buckets: vec![0; 8 * ALPHABET_SIZE],
16658 thread_state: None,
16659 threads: 0,
16660 };
16661 assert_eq!(
16662 libsais_ctx(&mut zero_thread_ctx, text, &mut full_sa, 0, None),
16663 -2
16664 );
16665
16666 let mut short_thread_state_ctx = create_ctx_main(2).expect("context");
16667 short_thread_state_ctx
16668 .thread_state
16669 .as_mut()
16670 .expect("thread state")
16671 .truncate(1);
16672 assert_eq!(
16673 libsais_ctx(&mut short_thread_state_ctx, text, &mut full_sa, 0, None),
16674 -2
16675 );
16676 }
16677
16678 #[test]
16679 fn libsais_unbwt_ctx_rejects_invalid_public_arguments() {
16680 let text = b"banana";
16681 let mut bwt = vec![0; text.len()];
16682 let mut work = vec![0; text.len()];
16683 let primary = libsais_bwt(text, &mut bwt, &mut work, 0, None);
16684 let mut ctx = unbwt_create_ctx().expect("context");
16685
16686 let mut short_u = vec![0; text.len() - 1];
16687 let mut full_u = vec![0; text.len()];
16688 let mut short_a = vec![0; text.len() - 1];
16689 let mut full_a = vec![0; text.len()];
16690 let short_freq = vec![0; ALPHABET_SIZE - 1];
16691 let good_aux = vec![primary, 4];
16692
16693 assert_eq!(
16694 libsais_unbwt_ctx(&mut ctx, &bwt, &mut short_u, &mut full_a, None, primary),
16695 -1
16696 );
16697 assert_eq!(
16698 libsais_unbwt_ctx(&mut ctx, &bwt, &mut full_u, &mut short_a, None, primary),
16699 -1
16700 );
16701 assert_eq!(
16702 libsais_unbwt_ctx(
16703 &mut ctx,
16704 &bwt,
16705 &mut full_u,
16706 &mut full_a,
16707 Some(&short_freq),
16708 primary
16709 ),
16710 -1
16711 );
16712 assert_eq!(
16713 libsais_unbwt_ctx(&mut ctx, &bwt, &mut full_u, &mut full_a, None, 0),
16714 -1
16715 );
16716 assert_eq!(
16717 libsais_unbwt_aux_ctx(&mut ctx, &bwt, &mut full_u, &mut full_a, None, 3, &good_aux),
16718 -1
16719 );
16720 assert_eq!(
16721 libsais_unbwt_aux_ctx(
16722 &mut ctx,
16723 &bwt,
16724 &mut full_u,
16725 &mut full_a,
16726 None,
16727 4,
16728 &[primary]
16729 ),
16730 -1
16731 );
16732
16733 let mut malformed_ctx = UnbwtContext {
16734 bucket2: Vec::new(),
16735 fastbits: Vec::new(),
16736 buckets: None,
16737 threads: 1,
16738 };
16739 assert_eq!(
16740 libsais_unbwt_ctx(
16741 &mut malformed_ctx,
16742 &bwt,
16743 &mut full_u,
16744 &mut full_a,
16745 None,
16746 primary
16747 ),
16748 -2
16749 );
16750
16751 let mut missing_parallel_buckets_ctx = UnbwtContext {
16752 bucket2: vec![0; ALPHABET_SIZE * ALPHABET_SIZE],
16753 fastbits: vec![0; 1 + (1 << UNBWT_FASTBITS)],
16754 buckets: None,
16755 threads: 2,
16756 };
16757 assert_eq!(
16758 libsais_unbwt_ctx(
16759 &mut missing_parallel_buckets_ctx,
16760 &bwt,
16761 &mut full_u,
16762 &mut full_a,
16763 None,
16764 primary
16765 ),
16766 -2
16767 );
16768 }
16769
16770 #[test]
16771 fn unbwt_create_ctx_main_allocates_expected_buffers() {
16772 let ctx = unbwt_create_ctx_main(3).expect("context");
16773 assert_eq!(ctx.bucket2.len(), ALPHABET_SIZE * ALPHABET_SIZE);
16774 assert_eq!(ctx.fastbits.len(), 1 + (1 << UNBWT_FASTBITS));
16775 assert_eq!(
16776 ctx.buckets.as_ref().expect("parallel buckets").len(),
16777 3 * (ALPHABET_SIZE + ALPHABET_SIZE * ALPHABET_SIZE)
16778 );
16779 assert_eq!(ctx.threads, 3);
16780 }
16781
16782 #[test]
16783 fn unbwt_compute_histogram_counts_bytes() {
16784 let t = b"banana";
16785 let mut count = vec![0u32; ALPHABET_SIZE];
16786 unbwt_compute_histogram(t, t.len() as FastSint, &mut count);
16787 assert_eq!(count[b'a' as usize], 3);
16788 assert_eq!(count[b'b' as usize], 1);
16789 assert_eq!(count[b'n' as usize], 2);
16790 }
16791
16792 #[test]
16793 fn unbwt_transpose_bucket2_swaps_matrix_entries() {
16794 let mut bucket2 = vec![0u32; ALPHABET_SIZE * ALPHABET_SIZE];
16795 bucket2[(2 << 8) + 1] = 7;
16796 bucket2[(1 << 8) + 2] = 9;
16797 unbwt_transpose_bucket2(&mut bucket2);
16798 assert_eq!(bucket2[(1 << 8) + 2], 7);
16799 assert_eq!(bucket2[(2 << 8) + 1], 9);
16800 }
16801
16802 #[test]
16803 fn unbwt_init_single_builds_monotone_fastbits_and_writes_psi() {
16804 let t = b"annb\x00aa";
16805 let mut p = vec![0u32; t.len() + 1];
16806 let mut bucket2 = vec![0u32; ALPHABET_SIZE * ALPHABET_SIZE];
16807 let mut fastbits = vec![0u16; 1 + (1 << UNBWT_FASTBITS)];
16808 let i = vec![4u32];
16809
16810 unbwt_init_single(
16811 t,
16812 &mut p,
16813 t.len() as SaSint,
16814 None,
16815 &i,
16816 &mut bucket2,
16817 &mut fastbits,
16818 );
16819
16820 assert!(fastbits
16821 .iter()
16822 .all(|&value| usize::from(value) < ALPHABET_SIZE * ALPHABET_SIZE));
16823 assert!(fastbits.iter().any(|&value| value != 0));
16824 assert!(p.iter().any(|&value| value != 0));
16825 }
16826
16827 #[test]
16828 fn unbwt_init_parallel_currently_matches_single_initializer() {
16829 let t = b"annb\x00aa";
16830 let mut p_single = vec![0u32; t.len() + 1];
16831 let mut p_parallel = vec![0u32; t.len() + 1];
16832 let mut bucket2_single = vec![0u32; ALPHABET_SIZE * ALPHABET_SIZE];
16833 let mut bucket2_parallel = vec![0u32; ALPHABET_SIZE * ALPHABET_SIZE];
16834 let mut fastbits_single = vec![0u16; 1 + (1 << UNBWT_FASTBITS)];
16835 let mut fastbits_parallel = vec![0u16; 1 + (1 << UNBWT_FASTBITS)];
16836 let i = vec![4u32];
16837 let mut scratch = vec![0u32; 2 * (ALPHABET_SIZE + ALPHABET_SIZE * ALPHABET_SIZE)];
16838
16839 unbwt_init_single(
16840 t,
16841 &mut p_single,
16842 t.len() as SaSint,
16843 None,
16844 &i,
16845 &mut bucket2_single,
16846 &mut fastbits_single,
16847 );
16848 unbwt_init_parallel(
16849 t,
16850 &mut p_parallel,
16851 t.len() as SaSint,
16852 None,
16853 &i,
16854 &mut bucket2_parallel,
16855 &mut fastbits_parallel,
16856 Some(&mut scratch),
16857 2,
16858 );
16859
16860 assert_eq!(p_parallel, p_single);
16861 assert_eq!(bucket2_parallel, bucket2_single);
16862 assert_eq!(fastbits_parallel, fastbits_single);
16863 }
16864
16865 #[test]
16866 fn unbwt_init_parallel_uses_block_partition_for_large_inputs() {
16867 let n = 70_003usize;
16868 let t: Vec<u8> = (0..n)
16869 .map(|i| i.wrapping_mul(37).wrapping_add(i >> 3) as u8)
16870 .collect();
16871 let i = [12_345u32];
16872
16873 let mut single_p = vec![0u32; n + 1];
16874 let mut threaded_p = vec![0u32; n + 1];
16875 let mut single_bucket2 = vec![0u32; ALPHABET_SIZE * ALPHABET_SIZE];
16876 let mut threaded_bucket2 = vec![0u32; ALPHABET_SIZE * ALPHABET_SIZE];
16877 let mut single_fastbits = vec![0u16; 1 + (1 << UNBWT_FASTBITS)];
16878 let mut threaded_fastbits = vec![0u16; 1 + (1 << UNBWT_FASTBITS)];
16879 let mut buckets = vec![0u32; 4 * (ALPHABET_SIZE + ALPHABET_SIZE * ALPHABET_SIZE)];
16880
16881 unbwt_init_single(
16882 &t,
16883 &mut single_p,
16884 n as SaSint,
16885 None,
16886 &i,
16887 &mut single_bucket2,
16888 &mut single_fastbits,
16889 );
16890 unbwt_init_parallel(
16891 &t,
16892 &mut threaded_p,
16893 n as SaSint,
16894 None,
16895 &i,
16896 &mut threaded_bucket2,
16897 &mut threaded_fastbits,
16898 Some(&mut buckets),
16899 4,
16900 );
16901
16902 assert_eq!(threaded_p, single_p);
16903 assert_eq!(threaded_bucket2, single_bucket2);
16904 assert_eq!(threaded_fastbits, single_fastbits);
16905 }
16906
16907 #[test]
16908 fn unbwt_decode_1_writes_big_endian_symbol_words() {
16909 let mut u = vec![0u8; 4];
16910 let p = vec![1u32, 0u32];
16911 let mut bucket2 = vec![0u32; ALPHABET_SIZE * ALPHABET_SIZE];
16912 bucket2[0x1234] = 0;
16913 bucket2[0x1235] = 2;
16914 let mut fastbits = vec![0u16; 1 + (1 << UNBWT_FASTBITS)];
16915 fastbits[0] = 0x1234;
16916 let mut i0 = 0usize;
16917
16918 unbwt_decode_1(&mut u, &p, &bucket2, &fastbits, 0, &mut i0, 2);
16919
16920 assert_eq!(u, vec![0x12, 0x35, 0x12, 0x35]);
16921 assert_eq!(i0, 0);
16922 }
16923
16924 #[test]
16925 fn unbwt_decode_dispatches_two_block_tail_shape() {
16926 let mut u = vec![0u8; 8];
16927 let p = vec![1u32, 0u32];
16928 let mut bucket2 = vec![0u32; ALPHABET_SIZE * ALPHABET_SIZE];
16929 bucket2[0x1234] = 0;
16930 bucket2[0x1235] = 2;
16931 let mut fastbits = vec![0u16; 1 + (1 << UNBWT_FASTBITS)];
16932 fastbits[0] = 0x1234;
16933 let i = vec![0u32, 0u32];
16934
16935 unbwt_decode(&mut u, &p, 4, 2, &i, &bucket2, &fastbits, 2, 2);
16936
16937 assert_eq!(u, vec![0x12, 0x35, 0x12, 0x35, 0x00, 0x00, 0x00, 0x00]);
16938 }
16939
16940 #[test]
16941 fn libsais_unbwt_aux_rejects_invalid_sampling_range() {
16942 let t = b"abc";
16943 let mut u = vec![0u8; t.len()];
16944 let mut a = vec![0i32; t.len()];
16945
16946 let result = libsais_unbwt_aux(t, &mut u, &mut a, None, 2, &[0, 4]);
16947
16948 assert_eq!(result, -1);
16949
16950 assert_eq!(libsais_unbwt_aux(t, &mut u, &mut a, None, 0, &[1]), -1);
16951
16952 let mut ctx = unbwt_create_ctx().expect("context");
16953 assert_eq!(
16954 libsais_unbwt_aux_ctx(&mut ctx, t, &mut u, &mut a, None, 0, &[1]),
16955 -1
16956 );
16957 assert_eq!(
16958 libsais_unbwt_aux_omp(t, &mut u, &mut a, None, 0, &[1], 2),
16959 -1
16960 );
16961 }
16962
16963 #[test]
16964 fn libsais_bwt_and_unbwt_round_trip_small_text() {
16965 let t = b"banana";
16966 let mut bwt = vec![0u8; t.len()];
16967 let mut a = vec![0i32; t.len()];
16968
16969 let primary = libsais_bwt(t, &mut bwt, &mut a, 0, None);
16970 assert!(primary > 0);
16971
16972 let mut restored = vec![0u8; t.len()];
16973 let result = libsais_unbwt(&bwt, &mut restored, &mut a, None, primary);
16974
16975 assert_eq!(result, 0);
16976 assert_eq!(restored, t);
16977 }
16978
16979 #[test]
16980 fn libsais_bwt_aux_and_unbwt_aux_round_trip_small_text() {
16981 let t = b"mississippi";
16982 let mut bwt = vec![0u8; t.len()];
16983 let mut a = vec![0i32; t.len()];
16984 let mut samples = vec![0i32; 4];
16985
16986 let result = libsais_bwt_aux(t, &mut bwt, &mut a, 0, None, 4, &mut samples);
16987 assert_eq!(result, 0);
16988
16989 let mut restored = vec![0u8; t.len()];
16990 let result = libsais_unbwt_aux(&bwt, &mut restored, &mut a, None, 4, &samples);
16991
16992 assert_eq!(result, 0);
16993 assert_eq!(restored, t);
16994 }
16995
16996 #[test]
16997 fn libsais_bwt_aux_and_unbwt_aux_omp_round_trip_small_text() {
16998 let t = b"mississippi";
16999 let mut bwt = vec![0u8; t.len()];
17000 let mut a = vec![0i32; t.len()];
17001 let mut samples = vec![0i32; 4];
17002
17003 let result = libsais_bwt_aux(t, &mut bwt, &mut a, 0, None, 4, &mut samples);
17004 assert_eq!(result, 0);
17005
17006 let mut restored = vec![0u8; t.len()];
17007 let result = libsais_unbwt_aux_omp(&bwt, &mut restored, &mut a, None, 4, &samples, 2);
17008
17009 assert_eq!(result, 0);
17010 assert_eq!(restored, t);
17011 }
17012
17013 #[test]
17014 fn real_world_round_trip_on_upstream_readme() {
17015 let t = include_bytes!("../libsais/README.md");
17016 let mut bwt = vec![0u8; t.len()];
17017 let mut a = vec![0i32; t.len()];
17018
17019 let primary = libsais_bwt(t, &mut bwt, &mut a, 0, None);
17020 assert!(primary > 0);
17021
17022 let mut restored = vec![0u8; t.len()];
17023 let result = libsais_unbwt(&bwt, &mut restored, &mut a, None, primary);
17024
17025 assert_eq!(result, 0);
17026 assert_eq!(restored, t);
17027 }
17028
17029 #[test]
17030 fn real_world_aux_omp_round_trip_on_upstream_c_source() {
17031 let t = include_bytes!("../libsais/src/libsais.c");
17032 let mut bwt = vec![0u8; t.len()];
17033 let mut a = vec![0i32; t.len()];
17034 let r = 128i32;
17035 let mut samples = vec![0i32; (t.len() - 1) / usize::try_from(r).expect("fits") + 1];
17036
17037 let result = libsais_bwt_aux(t, &mut bwt, &mut a, 0, None, r, &mut samples);
17038 assert_eq!(result, 0);
17039
17040 let mut restored = vec![0u8; t.len()];
17041 let result = libsais_unbwt_aux_omp(&bwt, &mut restored, &mut a, None, r, &samples, 2);
17042
17043 assert_eq!(result, 0);
17044 assert_eq!(restored, t);
17045 }
17046
17047 #[test]
17048 fn libsais_bwt_aux_rejects_undersized_sampling_array() {
17049 let t = b"upstream source text";
17050 let mut bwt = vec![0u8; t.len()];
17051 let mut a = vec![0i32; t.len()];
17052 let mut samples = vec![0i32; 1];
17053
17054 let result = libsais_bwt_aux(t, &mut bwt, &mut a, 0, None, 2, &mut samples);
17055
17056 assert_eq!(result, -1);
17057
17058 let result = libsais_bwt_aux(t, &mut bwt, &mut a, 0, None, 0, &mut samples);
17059
17060 assert_eq!(result, -1);
17061 }
17062
17063 #[test]
17064 fn libsais_bwt_aux_omp_rejects_invalid_sampling_rate_without_panicking() {
17065 let t = b"upstream source text";
17066 let mut bwt = vec![0u8; t.len()];
17067 let mut a = vec![0i32; t.len()];
17068 let mut samples = vec![0i32; 4];
17069
17070 let result = libsais_bwt_aux_omp(t, &mut bwt, &mut a, 0, None, 0, &mut samples, 2);
17071
17072 assert_eq!(result, -1);
17073 }
17074
17075 #[test]
17076 fn count_helpers_match_c_predicates() {
17077 let sa = [1, -1, 0, -3, 4, 0, -9];
17078 assert_eq!(
17079 count_negative_marked_suffixes(&sa, 0, sa.len() as FastSint),
17080 3
17081 );
17082 assert_eq!(count_zero_marked_suffixes(&sa, 0, sa.len() as FastSint), 2);
17083 assert_eq!(count_negative_marked_suffixes(&sa, 2, 3), 1);
17084 assert_eq!(count_zero_marked_suffixes(&sa, 2, 3), 1);
17085 }
17086
17087 #[test]
17088 fn flip_suffix_markers_omp_toggles_saint_min_bits() {
17089 let mut sa = vec![1, -2, 3, -4];
17090 flip_suffix_markers_omp(&mut sa, 4, 1);
17091 assert_eq!(
17092 sa,
17093 vec![1 ^ SAINT_MIN, -2 ^ SAINT_MIN, 3 ^ SAINT_MIN, -4 ^ SAINT_MIN]
17094 );
17095 }
17096
17097 #[test]
17098 fn flip_suffix_markers_omp_uses_block_partition_for_large_inputs() {
17099 let n = 65_600usize;
17100 let mut single: Vec<SaSint> = (0..n).map(|i| (i as SaSint) ^ SAINT_MIN).collect();
17101 let mut threaded = single.clone();
17102
17103 flip_suffix_markers_omp(&mut single, n as SaSint, 1);
17104 flip_suffix_markers_omp(&mut threaded, n as SaSint, 4);
17105
17106 assert_eq!(threaded, single);
17107 }
17108
17109 #[test]
17110 fn place_cached_suffixes_writes_indices_to_symbol_slots() {
17111 let mut sa = vec![0; 8];
17112 let cache = vec![
17113 ThreadCache {
17114 symbol: 2,
17115 index: 10,
17116 },
17117 ThreadCache {
17118 symbol: 5,
17119 index: 20,
17120 },
17121 ThreadCache {
17122 symbol: 1,
17123 index: 30,
17124 },
17125 ];
17126
17127 place_cached_suffixes(&mut sa, &cache, 0, cache.len() as FastSint);
17128
17129 assert_eq!(sa[2], 10);
17130 assert_eq!(sa[5], 20);
17131 assert_eq!(sa[1], 30);
17132 }
17133
17134 #[test]
17135 fn compact_and_place_cached_suffixes_discards_negative_symbols() {
17136 let mut sa = vec![0; 8];
17137 let mut cache = vec![
17138 ThreadCache {
17139 symbol: 2,
17140 index: 10,
17141 },
17142 ThreadCache {
17143 symbol: -1,
17144 index: 99,
17145 },
17146 ThreadCache {
17147 symbol: 5,
17148 index: 20,
17149 },
17150 ThreadCache {
17151 symbol: -4,
17152 index: 77,
17153 },
17154 ThreadCache {
17155 symbol: 1,
17156 index: 30,
17157 },
17158 ];
17159 let cache_len = cache.len() as FastSint;
17160
17161 compact_and_place_cached_suffixes(&mut sa, &mut cache, 0, cache_len);
17162
17163 assert_eq!(sa[2], 10);
17164 assert_eq!(sa[5], 20);
17165 assert_eq!(sa[1], 30);
17166 assert_eq!(
17167 cache[0],
17168 ThreadCache {
17169 symbol: 2,
17170 index: 10
17171 }
17172 );
17173 assert_eq!(
17174 cache[1],
17175 ThreadCache {
17176 symbol: 5,
17177 index: 20
17178 }
17179 );
17180 assert_eq!(
17181 cache[2],
17182 ThreadCache {
17183 symbol: 1,
17184 index: 30
17185 }
17186 );
17187 }
17188
17189 #[test]
17190 fn gather_lms_suffixes_32s_collects_expected_suffix_starts() {
17191 let t = vec![2, 1, 3, 1, 0];
17192 let mut sa = vec![0; t.len()];
17193 let m = gather_lms_suffixes_32s(&t, &mut sa, t.len() as SaSint);
17194 assert!(m >= 0);
17195 assert!(sa
17196 .iter()
17197 .all(|&value| value >= 0 && value <= t.len() as SaSint));
17198 assert!(sa[t.len() - 1] >= 1 && sa[t.len() - 1] <= t.len() as SaSint - 1);
17199 }
17200
17201 #[test]
17202 fn gather_compacted_lms_suffixes_32s_skips_negative_marked_symbols() {
17203 let t = vec![2, -1, 3, 1, 0];
17204 let mut sa = vec![0; t.len()];
17205 let m = gather_compacted_lms_suffixes_32s(&t, &mut sa, t.len() as SaSint);
17206 assert!(m >= 0);
17207 assert!(sa
17208 .iter()
17209 .all(|&value| value >= 0 && value <= t.len() as SaSint));
17210 }
17211
17212 #[test]
17213 fn count_lms_suffixes_32s_2k_counts_two_bucket_categories() {
17214 let t = vec![2, 1, 3, 1, 0];
17215 let mut buckets = vec![0; 2 * 4];
17216 count_lms_suffixes_32s_2k(&t, t.len() as SaSint, 4, &mut buckets);
17217 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
17218 }
17219
17220 #[test]
17221 fn count_lms_suffixes_32s_4k_counts_four_bucket_categories() {
17222 let t = vec![2, 1, 3, 1, 0];
17223 let mut buckets = vec![0; 4 * 4];
17224 count_lms_suffixes_32s_4k(&t, t.len() as SaSint, 4, &mut buckets);
17225 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
17226 }
17227
17228 #[test]
17229 fn count_compacted_lms_suffixes_32s_2k_masks_saint_bits() {
17230 let t = vec![2, SAINT_MIN | 1, 3, 1, 0];
17231 let mut buckets = vec![0; 2 * 4];
17232 count_compacted_lms_suffixes_32s_2k(&t, t.len() as SaSint, 4, &mut buckets);
17233 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
17234 }
17235
17236 #[test]
17237 fn count_and_gather_lms_suffixes_8u_updates_sa_and_buckets() {
17238 let t = vec![2_u8, 1, 3, 1, 0];
17239 let mut sa = vec![0; t.len()];
17240 let mut buckets = vec![0; 4 * ALPHABET_SIZE];
17241 let m = count_and_gather_lms_suffixes_8u(
17242 &t,
17243 &mut sa,
17244 t.len() as SaSint,
17245 &mut buckets,
17246 0,
17247 t.len() as FastSint,
17248 );
17249 assert_eq!(m, 1);
17250 assert_eq!(sa[t.len() - 1], 1);
17251 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
17252 }
17253
17254 #[test]
17255 fn get_bucket_stride_prefers_aligned_sizes_when_space_allows() {
17256 assert_eq!(get_bucket_stride(8192, 1000, 2), 1024);
17257 assert_eq!(get_bucket_stride(256, 17, 2), 32);
17258 assert_eq!(get_bucket_stride(8, 17, 2), 17);
17259 }
17260
17261 #[test]
17262 fn count_suffixes_32s_counts_symbol_histogram() {
17263 let t = vec![2, 1, 2, 3, 1, 0, 2];
17264 let mut buckets = vec![0; 4];
17265 count_suffixes_32s(&t, t.len() as SaSint, 4, &mut buckets);
17266 assert_eq!(buckets, vec![1, 2, 3, 1]);
17267 }
17268
17269 #[test]
17270 fn initialize_buckets_start_and_end_8u_sets_ranges_and_freq() {
17271 let mut buckets = vec![0; 8 * ALPHABET_SIZE];
17272 buckets[buckets_index4(0, 0)] = 1;
17273 buckets[buckets_index4(1, 1)] = 2;
17274 buckets[buckets_index4(2, 3)] = 3;
17275 let mut freq = vec![0; ALPHABET_SIZE];
17276 let k = initialize_buckets_start_and_end_8u(&mut buckets, Some(&mut freq));
17277 assert_eq!(k, 3);
17278 assert_eq!(freq[0], 1);
17279 assert_eq!(freq[1], 2);
17280 assert_eq!(freq[2], 3);
17281 assert_eq!(buckets[6 * ALPHABET_SIZE], 0);
17282 assert_eq!(buckets[7 * ALPHABET_SIZE], 1);
17283 assert_eq!(buckets[6 * ALPHABET_SIZE + 1], 1);
17284 assert_eq!(buckets[7 * ALPHABET_SIZE + 1], 3);
17285 }
17286
17287 #[test]
17288 fn initialize_buckets_start_and_end_32s_6k_sets_prefix_ranges() {
17289 let k = 3;
17290 let mut buckets = vec![0; 6 * k];
17291 buckets[buckets_index4(0, 0)] = 1;
17292 buckets[buckets_index4(0, 1)] = 2;
17293 buckets[buckets_index4(1, 2)] = 3;
17294 buckets[buckets_index4(2, 3)] = 4;
17295 initialize_buckets_start_and_end_32s_6k(k as SaSint, &mut buckets);
17296 assert_eq!(&buckets[4 * k..5 * k], &[0, 3, 6]);
17297 assert_eq!(&buckets[5 * k..6 * k], &[3, 6, 10]);
17298 }
17299
17300 #[test]
17301 fn initialize_buckets_start_and_end_32s_4k_sets_prefix_ranges() {
17302 let k = 3;
17303 let mut buckets = vec![0; 4 * k];
17304 buckets[buckets_index2(0, 0)] = 1;
17305 buckets[buckets_index2(0, 1)] = 2;
17306 buckets[buckets_index2(1, 0)] = 3;
17307 buckets[buckets_index2(2, 1)] = 4;
17308 initialize_buckets_start_and_end_32s_4k(k as SaSint, &mut buckets);
17309 assert_eq!(&buckets[2 * k..3 * k], &[0, 3, 6]);
17310 assert_eq!(&buckets[3 * k..4 * k], &[3, 6, 10]);
17311 }
17312
17313 #[test]
17314 fn initialize_buckets_end_32s_2k_rewrites_first_lanes_to_end_positions() {
17315 let k = 3;
17316 let mut buckets = vec![1, 2, 3, 4, 5, 6];
17317 initialize_buckets_end_32s_2k(k as SaSint, &mut buckets);
17318 assert_eq!(buckets[0], 3);
17319 assert_eq!(buckets[2], 10);
17320 assert_eq!(buckets[4], 21);
17321 }
17322
17323 #[test]
17324 fn initialize_buckets_start_and_end_32s_2k_copies_start_positions() {
17325 let k = 3;
17326 let mut buckets = vec![3, 2, 10, 4, 21, 6];
17327 initialize_buckets_start_and_end_32s_2k(k as SaSint, &mut buckets);
17328 assert_eq!(&buckets[..k], &[3, 10, 21]);
17329 assert_eq!(&buckets[k..2 * k], &[0, 3, 10]);
17330 }
17331
17332 #[test]
17333 fn initialize_buckets_start_32s_1k_builds_prefix_starts() {
17334 let mut buckets = vec![1, 2, 3];
17335 initialize_buckets_start_32s_1k(3, &mut buckets);
17336 assert_eq!(buckets, vec![0, 1, 3]);
17337 }
17338
17339 #[test]
17340 fn initialize_buckets_end_32s_1k_builds_prefix_ends() {
17341 let mut buckets = vec![1, 2, 3];
17342 initialize_buckets_end_32s_1k(3, &mut buckets);
17343 assert_eq!(buckets, vec![1, 3, 6]);
17344 }
17345
17346 #[test]
17347 fn initialize_buckets_for_lms_suffixes_radix_sort_8u_returns_total_lms_slots() {
17348 let t = vec![2_u8, 1, 3, 1, 0];
17349 let mut buckets = vec![0; 6 * ALPHABET_SIZE];
17350 buckets[buckets_index4(0, 1)] = 1;
17351 buckets[buckets_index4(1, 3)] = 2;
17352 let sum = initialize_buckets_for_lms_suffixes_radix_sort_8u(&t, &mut buckets, 4);
17353 assert!(sum >= 0);
17354 }
17355
17356 #[test]
17357 fn initialize_buckets_for_lms_suffixes_radix_sort_32s_2k_rewrites_two_lane_prefixes() {
17358 let t = vec![2, 1, 3, 1, 0];
17359 let mut buckets = vec![0; 2 * 4];
17360 initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(&t, 4, &mut buckets, 4);
17361 assert!(buckets.iter().any(|&v| v != 0));
17362 }
17363
17364 #[test]
17365 fn initialize_buckets_for_lms_suffixes_radix_sort_32s_6k_returns_total_lms_slots() {
17366 let t = vec![2, 1, 3, 1, 0];
17367 let mut buckets = vec![0; 6 * 4];
17368 buckets[buckets_index4(0, 1)] = 1;
17369 buckets[buckets_index4(1, 3)] = 2;
17370 let sum = initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(&t, 4, &mut buckets, 4);
17371 assert!(sum >= 0);
17372 }
17373
17374 #[test]
17375 fn initialize_buckets_for_radix_and_partial_sorting_32s_4k_sets_start_end_views() {
17376 let t = vec![2, 1, 3, 1, 0];
17377 let k = 4usize;
17378 let mut buckets = vec![0; 4 * k];
17379 buckets[buckets_index2(0, 0)] = 1;
17380 buckets[buckets_index2(0, 1)] = 2;
17381 buckets[buckets_index2(1, 0)] = 3;
17382 initialize_buckets_for_radix_and_partial_sorting_32s_4k(&t, k as SaSint, &mut buckets, 4);
17383 assert_eq!(buckets[2 * k], 0);
17384 assert!(buckets[3 * k] >= buckets[2 * k]);
17385 }
17386
17387 #[test]
17388 fn radix_sort_lms_suffixes_8u_places_suffixes_by_bucket() {
17389 let t = vec![1_u8, 0, 1, 0];
17390 let mut sa = vec![9, 9, 9, 9, 0, 1, 2, 3];
17391 let mut induction_bucket = vec![0; 2 * ALPHABET_SIZE];
17392 induction_bucket[buckets_index2(0, 0)] = 2;
17393 induction_bucket[buckets_index2(1, 0)] = 4;
17394 radix_sort_lms_suffixes_8u(&t, &mut sa, &mut induction_bucket, 4, 4);
17395 assert_eq!(&sa[..4], &[1, 3, 0, 2]);
17396 }
17397
17398 #[test]
17399 fn radix_sort_lms_suffixes_8u_omp_wraps_sequential_version() {
17400 let t = vec![9_u8, 1, 0, 1, 0];
17401 let mut sa = vec![9, 9, 9, 9, 9, 1, 2, 3, 4];
17402 let mut buckets = vec![0; 6 * ALPHABET_SIZE];
17403 buckets[4 * ALPHABET_SIZE + buckets_index2(0, 0)] = 2;
17404 buckets[4 * ALPHABET_SIZE + buckets_index2(1, 0)] = 4;
17405 let mut thread_state = alloc_thread_state(2).unwrap();
17406 radix_sort_lms_suffixes_8u_omp(&t, &mut sa, 9, 5, 0, &mut buckets, 2, &mut thread_state);
17407 assert_eq!(&sa[..4], &[2, 4, 1, 3]);
17408 }
17409
17410 #[test]
17411 fn radix_sort_lms_suffixes_8u_omp_uses_thread_state_for_large_inputs() {
17412 let m = 65_600usize;
17413 let n = 2 * m + 16;
17414 let start = n - m + 1;
17415 let t: Vec<u8> = (0..n).map(|i| (i % 4) as u8).collect();
17416 let suffixes: Vec<SaSint> = (0..m - 1).map(|i| i as SaSint).collect();
17417
17418 let mut buckets = vec![0; 6 * ALPHABET_SIZE];
17419 for &suffix in &suffixes {
17420 buckets[4 * ALPHABET_SIZE + buckets_index2(t[suffix as usize] as usize, 0)] += 1;
17421 }
17422 let mut sum = 0;
17423 for symbol in 0..ALPHABET_SIZE {
17424 let bucket = 4 * ALPHABET_SIZE + buckets_index2(symbol, 0);
17425 sum += buckets[bucket];
17426 buckets[bucket] = sum;
17427 }
17428
17429 let mut sa_single = vec![0; n];
17430 sa_single[start..start + suffixes.len()].copy_from_slice(&suffixes);
17431 let mut sa_threaded = sa_single.clone();
17432 let mut buckets_single = buckets.clone();
17433 let mut buckets_threaded = buckets;
17434 let mut thread_state = alloc_thread_state(4).unwrap();
17435 thread_state[3].m = m as FastSint;
17436
17437 radix_sort_lms_suffixes_8u_omp(
17438 &t,
17439 &mut sa_single,
17440 n as SaSint,
17441 m as SaSint,
17442 0,
17443 &mut buckets_single,
17444 1,
17445 &mut [],
17446 );
17447 radix_sort_lms_suffixes_8u_omp(
17448 &t,
17449 &mut sa_threaded,
17450 n as SaSint,
17451 m as SaSint,
17452 0,
17453 &mut buckets_threaded,
17454 4,
17455 &mut thread_state,
17456 );
17457
17458 assert_eq!(sa_threaded, sa_single);
17459 }
17460
17461 #[test]
17462 fn radix_sort_lms_suffixes_32s_6k_places_suffixes_by_bucket() {
17463 let t = vec![1, 0, 1, 0];
17464 let mut sa = vec![9, 9, 9, 9, 0, 1, 2, 3];
17465 let mut induction_bucket = vec![2, 4];
17466 radix_sort_lms_suffixes_32s_6k(&t, &mut sa, &mut induction_bucket, 4, 4);
17467 assert_eq!(&sa[..4], &[1, 3, 0, 2]);
17468 }
17469
17470 #[test]
17471 fn radix_sort_lms_suffixes_32s_2k_places_suffixes_by_bucket() {
17472 let t = vec![1, 0, 1, 0];
17473 let mut sa = vec![9, 9, 9, 9, 0, 1, 2, 3];
17474 let mut induction_bucket = vec![2, 0, 4, 0];
17475 radix_sort_lms_suffixes_32s_2k(&t, &mut sa, &mut induction_bucket, 4, 4);
17476 assert_eq!(&sa[..4], &[1, 3, 0, 2]);
17477 }
17478
17479 #[test]
17480 fn radix_sort_lms_suffixes_32s_6k_omp_wraps_sequential_version() {
17481 let t = vec![9, 1, 0, 1, 0];
17482 let mut sa = vec![9, 9, 9, 9, 9, 1, 2, 3, 4];
17483 let mut induction_bucket = vec![2, 4];
17484 let mut thread_state = alloc_thread_state(2).unwrap();
17485 radix_sort_lms_suffixes_32s_6k_omp(
17486 &t,
17487 &mut sa,
17488 9,
17489 5,
17490 &mut induction_bucket,
17491 2,
17492 &mut thread_state,
17493 );
17494 assert_eq!(&sa[..4], &[2, 4, 1, 3]);
17495 }
17496
17497 #[test]
17498 fn radix_sort_lms_suffixes_32s_2k_omp_wraps_sequential_version() {
17499 let t = vec![9, 1, 0, 1, 0];
17500 let mut sa = vec![9, 9, 9, 9, 9, 1, 2, 3, 4];
17501 let mut induction_bucket = vec![2, 0, 4, 0];
17502 let mut thread_state = alloc_thread_state(2).unwrap();
17503 radix_sort_lms_suffixes_32s_2k_omp(
17504 &t,
17505 &mut sa,
17506 9,
17507 5,
17508 &mut induction_bucket,
17509 2,
17510 &mut thread_state,
17511 );
17512 assert_eq!(&sa[..4], &[2, 4, 1, 3]);
17513 }
17514
17515 #[test]
17516 fn radix_sort_lms_suffixes_32s_block_omp_runs_cache_pipeline() {
17517 let t = vec![9, 1, 0, 1, 0];
17518 let mut sa_6k = vec![9, 9, 9, 9, 9, 1, 2, 3, 4];
17519 let mut bucket_6k = vec![2, 4];
17520 let mut cache = vec![ThreadCache::default(); 9];
17521 radix_sort_lms_suffixes_32s_6k_block_omp(
17522 &t,
17523 &mut sa_6k,
17524 &mut bucket_6k,
17525 &mut cache,
17526 5,
17527 4,
17528 2,
17529 );
17530 assert_eq!(&sa_6k[..4], &[2, 4, 1, 3]);
17531
17532 let mut sa_2k = vec![9, 9, 9, 9, 9, 1, 2, 3, 4];
17533 let mut bucket_2k = vec![2, 0, 4, 0];
17534 cache.fill(ThreadCache::default());
17535 radix_sort_lms_suffixes_32s_2k_block_omp(
17536 &t,
17537 &mut sa_2k,
17538 &mut bucket_2k,
17539 &mut cache,
17540 5,
17541 4,
17542 2,
17543 );
17544 assert_eq!(&sa_2k[..4], &[2, 4, 1, 3]);
17545 }
17546
17547 #[test]
17548 fn radix_sort_lms_suffixes_32s_omp_uses_block_pipeline_for_large_inputs() {
17549 let m = 65_600usize;
17550 let n = 2 * m + 16;
17551 let start = n - m + 1;
17552 let t: Vec<SaSint> = (0..n).map(|i| (i % 4) as SaSint).collect();
17553 let suffixes: Vec<SaSint> = (0..m - 1).map(|i| i as SaSint).collect();
17554
17555 let mut bucket_ends = vec![0; 4];
17556 for &suffix in &suffixes {
17557 bucket_ends[t[suffix as usize] as usize] += 1;
17558 }
17559 let mut sum = 0;
17560 for bucket in &mut bucket_ends {
17561 sum += *bucket;
17562 *bucket = sum;
17563 }
17564
17565 let mut sa_single = vec![0; n];
17566 sa_single[start..start + suffixes.len()].copy_from_slice(&suffixes);
17567 let mut sa_threaded = sa_single.clone();
17568 let mut bucket_single = bucket_ends.clone();
17569 let mut bucket_threaded = bucket_ends.clone();
17570 let mut thread_state = alloc_thread_state(4).unwrap();
17571
17572 radix_sort_lms_suffixes_32s_6k_omp(
17573 &t,
17574 &mut sa_single,
17575 n as SaSint,
17576 m as SaSint,
17577 &mut bucket_single,
17578 1,
17579 &mut [],
17580 );
17581 radix_sort_lms_suffixes_32s_6k_omp(
17582 &t,
17583 &mut sa_threaded,
17584 n as SaSint,
17585 m as SaSint,
17586 &mut bucket_threaded,
17587 4,
17588 &mut thread_state,
17589 );
17590 assert_eq!(sa_threaded, sa_single);
17591 assert_eq!(bucket_threaded, bucket_single);
17592
17593 let mut bucket_2k = vec![0; 8];
17594 for (symbol, &end) in bucket_ends.iter().enumerate() {
17595 bucket_2k[buckets_index2(symbol, 0)] = end;
17596 }
17597 let mut sa_single = vec![0; n];
17598 sa_single[start..start + suffixes.len()].copy_from_slice(&suffixes);
17599 let mut sa_threaded = sa_single.clone();
17600 let mut bucket_single = bucket_2k.clone();
17601 let mut bucket_threaded = bucket_2k;
17602
17603 radix_sort_lms_suffixes_32s_2k_omp(
17604 &t,
17605 &mut sa_single,
17606 n as SaSint,
17607 m as SaSint,
17608 &mut bucket_single,
17609 1,
17610 &mut [],
17611 );
17612 radix_sort_lms_suffixes_32s_2k_omp(
17613 &t,
17614 &mut sa_threaded,
17615 n as SaSint,
17616 m as SaSint,
17617 &mut bucket_threaded,
17618 4,
17619 &mut thread_state,
17620 );
17621 assert_eq!(sa_threaded, sa_single);
17622 assert_eq!(bucket_threaded, bucket_single);
17623 }
17624
17625 #[test]
17626 fn radix_sort_lms_suffixes_32s_1k_collects_lms_suffixes() {
17627 let t = vec![2, 1, 3, 1, 0];
17628 let mut sa = vec![0; t.len()];
17629 let mut buckets = vec![0, 2, 4, 5];
17630 let m = radix_sort_lms_suffixes_32s_1k(&t, &mut sa, t.len() as SaSint, &mut buckets);
17631 assert!(m >= 0);
17632 }
17633
17634 #[test]
17635 fn radix_sort_set_markers_32s_6k_marks_target_suffixes() {
17636 let mut sa = vec![0; 6];
17637 let induction_bucket = vec![1, 3, 5];
17638 radix_sort_set_markers_32s_6k(&mut sa, &induction_bucket, 0, 3);
17639 assert_eq!(sa[1], SAINT_MIN);
17640 assert_eq!(sa[3], SAINT_MIN);
17641 assert_eq!(sa[5], SAINT_MIN);
17642 }
17643
17644 #[test]
17645 fn radix_sort_set_markers_32s_4k_marks_target_suffixes() {
17646 let mut sa = vec![0; 6];
17647 let induction_bucket = vec![1, 0, 3, 0, 5, 0];
17648 radix_sort_set_markers_32s_4k(&mut sa, &induction_bucket, 0, 3);
17649 assert_eq!(sa[1], SUFFIX_GROUP_MARKER);
17650 assert_eq!(sa[3], SUFFIX_GROUP_MARKER);
17651 assert_eq!(sa[5], SUFFIX_GROUP_MARKER);
17652 }
17653
17654 #[test]
17655 fn radix_sort_set_markers_32s_6k_omp_wraps_sequential_version() {
17656 let mut sa = vec![0; 6];
17657 let induction_bucket = vec![1, 3, 5];
17658 radix_sort_set_markers_32s_6k_omp(&mut sa, 4, &induction_bucket, 2);
17659 assert_eq!(sa[1], SAINT_MIN);
17660 assert_eq!(sa[3], SAINT_MIN);
17661 assert_eq!(sa[5], SAINT_MIN);
17662 }
17663
17664 #[test]
17665 fn radix_sort_set_markers_32s_4k_omp_wraps_sequential_version() {
17666 let mut sa = vec![0; 6];
17667 let induction_bucket = vec![1, 0, 3, 0, 5, 0];
17668 radix_sort_set_markers_32s_4k_omp(&mut sa, 4, &induction_bucket, 2);
17669 assert_eq!(sa[1], SUFFIX_GROUP_MARKER);
17670 assert_eq!(sa[3], SUFFIX_GROUP_MARKER);
17671 assert_eq!(sa[5], SUFFIX_GROUP_MARKER);
17672 }
17673
17674 #[test]
17675 fn radix_sort_set_markers_32s_omp_partitions_large_inputs() {
17676 let k = 65_600usize;
17677 let induction_bucket_6k: Vec<SaSint> = (0..k).map(|i| i as SaSint).collect();
17678 let mut sa_single = vec![0; k];
17679 let mut sa_threaded = vec![0; k];
17680 radix_sort_set_markers_32s_6k_omp(&mut sa_single, k as SaSint, &induction_bucket_6k, 1);
17681 radix_sort_set_markers_32s_6k_omp(&mut sa_threaded, k as SaSint, &induction_bucket_6k, 4);
17682 assert_eq!(sa_threaded, sa_single);
17683
17684 let mut induction_bucket_4k = vec![0; 2 * k];
17685 for i in 0..k {
17686 induction_bucket_4k[buckets_index2(i, 0)] = i as SaSint;
17687 }
17688 let mut sa_single = vec![0; k];
17689 let mut sa_threaded = vec![0; k];
17690 radix_sort_set_markers_32s_4k_omp(&mut sa_single, k as SaSint, &induction_bucket_4k, 1);
17691 radix_sort_set_markers_32s_4k_omp(&mut sa_threaded, k as SaSint, &induction_bucket_4k, 4);
17692 assert_eq!(sa_threaded, sa_single);
17693 }
17694
17695 #[test]
17696 fn initialize_buckets_for_partial_sorting_8u_sets_start_and_distinct_views() {
17697 let t = vec![2_u8, 1, 3, 1, 0];
17698 let mut buckets = vec![0; 6 * ALPHABET_SIZE];
17699 buckets[buckets_index4(0, 0)] = 1;
17700 buckets[buckets_index4(0, 2)] = 2;
17701 initialize_buckets_for_partial_sorting_8u(&t, &mut buckets, 4, 3);
17702 assert!(buckets[0] >= 4);
17703 assert!(buckets[1] >= 0);
17704 assert!(buckets[4 * ALPHABET_SIZE] >= 4);
17705 }
17706
17707 #[test]
17708 fn initialize_buckets_for_partial_sorting_32s_6k_rewrites_bucket_views() {
17709 let t = vec![2, 1, 3, 1, 0];
17710 let k = 4usize;
17711 let mut buckets = vec![0; 6 * k];
17712 buckets[buckets_index4(0, 0)] = 1;
17713 buckets[buckets_index4(0, 1)] = 2;
17714 buckets[buckets_index4(1, 2)] = 3;
17715 initialize_buckets_for_partial_sorting_32s_6k(&t, k as SaSint, &mut buckets, 4, 3);
17716 assert!(buckets[0] >= 4);
17717 assert!(buckets[4 * k] >= 4);
17718 }
17719
17720 #[test]
17721 fn partial_sorting_scan_left_to_right_8u_emits_induced_suffixes() {
17722 let t = vec![2_u8, 1, 3, 1, 0];
17723 let mut sa = vec![2 | SAINT_MIN, 4, 0, 0, 0, 0];
17724 let mut buckets = vec![0; 6 * ALPHABET_SIZE];
17725 buckets[4 * ALPHABET_SIZE + buckets_index2(1, 0)] = 2;
17726 let d = partial_sorting_scan_left_to_right_8u(&t, &mut sa, &mut buckets, 0, 0, 2);
17727 assert!(d >= 0);
17728 assert!(sa.iter().any(|&v| v != 0));
17729 }
17730
17731 #[test]
17732 fn partial_sorting_scan_left_to_right_8u_omp_wraps_sequential_version() {
17733 let t = vec![2_u8, 1, 3, 1, 0];
17734 let mut sa = vec![0; 8];
17735 let mut buckets = vec![0; 6 * ALPHABET_SIZE];
17736 buckets[4 * ALPHABET_SIZE + buckets_index2(0, 0)] = 1;
17737 let mut thread_state = alloc_thread_state(2).unwrap();
17738 let d = partial_sorting_scan_left_to_right_8u_omp(
17739 &t,
17740 &mut sa,
17741 5,
17742 4,
17743 &mut buckets,
17744 0,
17745 0,
17746 2,
17747 &mut thread_state,
17748 );
17749 assert!(d >= 1);
17750 }
17751
17752 #[test]
17753 fn partial_sorting_scan_left_to_right_32s_6k_emits_induced_suffixes() {
17754 let t = vec![2, 1, 3, 1, 0];
17755 let mut sa = vec![2 | SAINT_MIN, 4, 0, 0, 0, 0];
17756 let mut buckets = vec![0; 4 * 4];
17757 buckets[buckets_index4(1, 0)] = 2;
17758 let d = partial_sorting_scan_left_to_right_32s_6k(&t, &mut sa, &mut buckets, 0, 0, 2);
17759 assert!(d >= 0);
17760 assert!(sa.iter().any(|&v| v != 0));
17761 }
17762
17763 #[test]
17764 fn partial_sorting_scan_left_to_right_32s_4k_emits_induced_suffixes() {
17765 let t = vec![2, 1, 3, 1, 0];
17766 let k = 4usize;
17767 let mut sa = vec![2 | SUFFIX_GROUP_MARKER, 4, 0, 0, 0, 0];
17768 let mut buckets = vec![0; 4 * k];
17769 buckets[2 * k + 1] = 2;
17770 let d = partial_sorting_scan_left_to_right_32s_4k(
17771 &t,
17772 &mut sa,
17773 k as SaSint,
17774 &mut buckets,
17775 0,
17776 0,
17777 2,
17778 );
17779 assert!(d >= 0);
17780 assert!(sa.iter().any(|&v| v != 0));
17781 }
17782
17783 #[test]
17784 fn partial_sorting_scan_left_to_right_32s_1k_emits_induced_suffixes() {
17785 let t = vec![2, 1, 3, 1, 0];
17786 let mut sa = vec![2, 4, 0, 0, 0, 0];
17787 let mut buckets = vec![0; 4];
17788 buckets[1] = 2;
17789 partial_sorting_scan_left_to_right_32s_1k(&t, &mut sa, &mut buckets, 0, 2);
17790 assert!(sa.iter().any(|&v| v != 0));
17791 }
17792
17793 #[test]
17794 fn partial_sorting_scan_left_to_right_32s_6k_omp_wraps_sequential_version() {
17795 let t = vec![2, 1, 3, 1, 0];
17796 let mut sa = vec![0; 8];
17797 let mut buckets = vec![0; 4 * 4];
17798 let mut thread_state = alloc_thread_state(2).unwrap();
17799 let d = partial_sorting_scan_left_to_right_32s_6k_omp(
17800 &t,
17801 &mut sa,
17802 5,
17803 &mut buckets,
17804 0,
17805 0,
17806 2,
17807 &mut thread_state,
17808 );
17809 assert!(d >= 1);
17810 }
17811
17812 #[test]
17813 fn partial_sorting_scan_left_to_right_32s_4k_omp_wraps_sequential_version() {
17814 let t = vec![2, 1, 3, 1, 0];
17815 let k = 4usize;
17816 let mut sa = vec![0; 8];
17817 let mut buckets = vec![0; 4 * k];
17818 let mut thread_state = alloc_thread_state(2).unwrap();
17819 let d = partial_sorting_scan_left_to_right_32s_4k_omp(
17820 &t,
17821 &mut sa,
17822 5,
17823 k as SaSint,
17824 &mut buckets,
17825 0,
17826 2,
17827 &mut thread_state,
17828 );
17829 assert!(d >= 1);
17830 }
17831
17832 #[test]
17833 fn partial_sorting_scan_left_to_right_32s_1k_omp_wraps_sequential_version() {
17834 let t = vec![2, 1, 3, 1, 0];
17835 let mut sa = vec![0; 8];
17836 let mut buckets = vec![0; 4];
17837 let mut thread_state = alloc_thread_state(2).unwrap();
17838 partial_sorting_scan_left_to_right_32s_1k_omp(
17839 &t,
17840 &mut sa,
17841 5,
17842 &mut buckets,
17843 2,
17844 &mut thread_state,
17845 );
17846 assert!(sa.iter().any(|&v| v != 0));
17847 }
17848
17849 #[test]
17850 fn partial_sorting_scan_left_to_right_32s_6k_block_gather_records_bucket_symbols() {
17851 let t = vec![3, 1, 2, 0];
17852 let mut sa = vec![2 | SAINT_MIN, 0, 0, 0];
17853 let mut cache = vec![ThreadCache::default(); 1];
17854
17855 partial_sorting_scan_left_to_right_32s_6k_block_gather(&t, &mut sa, &mut cache, 0, 1);
17856
17857 assert_eq!(cache[0].index, 2 | SAINT_MIN);
17858 assert_eq!(cache[0].symbol, buckets_index4(1, 1) as SaSint);
17859 }
17860
17861 #[test]
17862 fn partial_sorting_scan_left_to_right_32s_1k_block_gather_zeroes_positive_entries() {
17863 let t = vec![3, 1, 2, 0];
17864 let mut sa = vec![2, 0, 0, 0];
17865 let mut cache = vec![ThreadCache::default(); 1];
17866
17867 partial_sorting_scan_left_to_right_32s_1k_block_gather(&t, &mut sa, &mut cache, 0, 1);
17868
17869 assert_eq!(cache[0].symbol, 1);
17870 assert_eq!(cache[0].index, 1);
17871 assert_eq!(sa[0], 0);
17872 }
17873
17874 #[test]
17875 fn partial_sorting_scan_left_to_right_32s_1k_block_omp_uses_relative_cache() {
17876 let block_start = 20_000usize;
17877 let block_size = 16_384usize;
17878 let n = block_start + block_size + 8;
17879 let t = vec![1; n];
17880 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
17881
17882 let mut sa_single = vec![0; n];
17883 sa_single[block_start..block_start + block_size].copy_from_slice(&suffixes);
17884 let mut sa_threaded = sa_single.clone();
17885 let mut bucket_single = vec![0, 0];
17886 let mut bucket_threaded = bucket_single.clone();
17887 let mut cache = vec![ThreadCache::default(); 4 * LIBSAIS_PER_THREAD_CACHE_SIZE];
17888
17889 partial_sorting_scan_left_to_right_32s_1k(
17890 &t,
17891 &mut sa_single,
17892 &mut bucket_single,
17893 block_start as FastSint,
17894 block_size as FastSint,
17895 );
17896 partial_sorting_scan_left_to_right_32s_1k_block_omp(
17897 &t,
17898 &mut sa_threaded,
17899 &mut bucket_threaded,
17900 &mut cache,
17901 block_start as FastSint,
17902 block_size as FastSint,
17903 4,
17904 );
17905
17906 assert_eq!(sa_threaded, sa_single);
17907 assert_eq!(bucket_threaded, bucket_single);
17908 }
17909
17910 #[test]
17911 fn partial_sorting_scan_left_to_right_8u_block_prepare_records_cache_and_counts() {
17912 let t = vec![2_u8, 1, 3, 1, 0];
17913 let sa = vec![2 | SAINT_MIN, 4, 0, 0, 0, 0];
17914 let k = 4;
17915 let mut buckets = vec![0; 4 * k];
17916 let mut cache = vec![ThreadCache::default(); 8];
17917 let mut state = ThreadState::new();
17918 let (position, count) = partial_sorting_scan_left_to_right_8u_block_prepare(
17919 &t,
17920 &sa,
17921 k as SaSint,
17922 &mut buckets,
17923 &mut cache,
17924 0,
17925 2,
17926 );
17927 state.position = position;
17928 state.count = count;
17929 assert!(state.count >= 1);
17930 assert!(cache
17931 .iter()
17932 .take(state.count as usize)
17933 .any(|entry| entry.symbol >= 0));
17934 }
17935
17936 #[test]
17937 fn partial_sorting_scan_left_to_right_8u_block_place_writes_induced_values() {
17938 let mut sa = vec![0; 8];
17939 let mut buckets = vec![0; 8];
17940 buckets[0] = 0;
17941 buckets[1] = 1;
17942 let cache = vec![
17943 ThreadCache {
17944 index: 3 | SAINT_MIN,
17945 symbol: 0,
17946 },
17947 ThreadCache {
17948 index: 5,
17949 symbol: 1,
17950 },
17951 ];
17952 partial_sorting_scan_left_to_right_8u_block_place(&mut sa, &mut buckets, 2, &cache, 2, 0);
17953 assert!(sa[0] != 0 || sa[1] != 0);
17954 }
17955
17956 #[test]
17957 fn partial_sorting_scan_left_to_right_8u_block_omp_wraps_sequential_version() {
17958 let t = vec![2_u8, 1, 3, 1, 0];
17959 let mut sa = vec![2 | SAINT_MIN, 4, 0, 0, 0, 0];
17960 let mut buckets = vec![0; 6 * ALPHABET_SIZE];
17961 let mut thread_state = alloc_thread_state(2).unwrap();
17962 let d = partial_sorting_scan_left_to_right_8u_block_omp(
17963 &t,
17964 &mut sa,
17965 4,
17966 &mut buckets,
17967 0,
17968 0,
17969 2,
17970 2,
17971 &mut thread_state,
17972 );
17973 assert!(d >= 0);
17974 }
17975
17976 #[test]
17977 fn partial_sorting_shift_markers_8u_omp_toggles_segment_markers() {
17978 let mut sa = vec![1 | SAINT_MIN, 2 | SAINT_MIN, 3, 4 | SAINT_MIN, 5];
17979 let mut buckets = vec![0; 6 * ALPHABET_SIZE];
17980 buckets[4 * ALPHABET_SIZE + buckets_index2(1, 0)] = 5;
17981 buckets[buckets_index2(0, 0)] = 0;
17982 let len = sa.len() as SaSint;
17983 partial_sorting_shift_markers_8u_omp(&mut sa, len, &buckets, 1);
17984 assert!(sa.iter().any(|&v| (v & SAINT_MIN) == 0));
17985 }
17986
17987 #[test]
17988 fn partial_sorting_shift_markers_32s_6k_omp_toggles_segment_markers() {
17989 let mut sa = vec![1 | SAINT_MIN, 2 | SAINT_MIN, 3, 4 | SAINT_MIN, 5];
17990 let k = 3usize;
17991 let mut buckets = vec![0; 6 * k];
17992 buckets[buckets_index4(1, 0)] = 5;
17993 buckets[4 * k + buckets_index2(0, 0)] = 0;
17994 partial_sorting_shift_markers_32s_6k_omp(&mut sa, k as SaSint, &buckets, 1);
17995 assert!(sa.iter().any(|&v| (v & SAINT_MIN) == 0));
17996 }
17997
17998 #[test]
17999 fn partial_sorting_shift_markers_32s_4k_toggles_group_markers() {
18000 let mut sa = vec![
18001 1 | SUFFIX_GROUP_MARKER,
18002 2 | SUFFIX_GROUP_MARKER,
18003 3,
18004 4 | SUFFIX_GROUP_MARKER,
18005 ];
18006 let len = sa.len() as SaSint;
18007 partial_sorting_shift_markers_32s_4k(&mut sa, len);
18008 assert!(sa.iter().any(|&v| (v & SUFFIX_GROUP_MARKER) == 0));
18009 }
18010
18011 #[test]
18012 fn partial_sorting_shift_buckets_32s_6k_moves_temp_bucket_view_into_main_slots() {
18013 let k = 3usize;
18014 let mut buckets = vec![0; 6 * k];
18015 buckets[4 * k + 0] = 10;
18016 buckets[4 * k + 1] = 11;
18017 buckets[4 * k + 2] = 12;
18018 buckets[4 * k + 3] = 13;
18019 partial_sorting_shift_buckets_32s_6k(k as SaSint, &mut buckets);
18020 assert_eq!(buckets[0], 10);
18021 assert_eq!(buckets[1], 11);
18022 assert_eq!(buckets[4], 12);
18023 assert_eq!(buckets[5], 13);
18024 }
18025
18026 #[test]
18027 fn partial_sorting_scan_right_to_left_8u_emits_induced_suffixes() {
18028 let t = vec![0_u8, 1, 2, 1, 0];
18029 let mut sa = vec![0, 0, 4 | SAINT_MIN];
18030 let mut buckets = vec![0; 4 * ALPHABET_SIZE];
18031 buckets[buckets_index2(1, 1)] = 2;
18032
18033 let d = partial_sorting_scan_right_to_left_8u(&t, &mut sa, &mut buckets, 0, 2, 1);
18034
18035 assert_eq!(d, 1);
18036 assert_eq!(sa[1], 3 | SAINT_MIN);
18037 assert_eq!(buckets[buckets_index2(1, 1)], 1);
18038 assert_eq!(buckets[2 * ALPHABET_SIZE + buckets_index2(1, 1)], 1);
18039 }
18040
18041 #[test]
18042 fn partial_gsa_scan_right_to_left_8u_skips_separator_bucket() {
18043 let t = vec![1_u8, 0, 0];
18044 let mut sa = vec![0, 2 | SAINT_MIN];
18045 let mut buckets = vec![0; 4 * ALPHABET_SIZE];
18046 buckets[buckets_index2(0, 1)] = 2;
18047
18048 let d = partial_gsa_scan_right_to_left_8u(&t, &mut sa, &mut buckets, 0, 1, 1);
18049
18050 assert_eq!(d, 1);
18051 assert_eq!(sa, vec![0, 2 | SAINT_MIN]);
18052 assert_eq!(buckets[buckets_index2(0, 1)], 2);
18053 }
18054
18055 #[test]
18056 fn partial_sorting_scan_right_to_left_32s_6k_emits_induced_suffixes() {
18057 let t = vec![0, 1, 2, 1, 0];
18058 let mut sa = vec![0, 0, 4 | SAINT_MIN];
18059 let mut buckets = vec![0; 4 * 3];
18060 buckets[buckets_index4(1, 1)] = 2;
18061
18062 let d = partial_sorting_scan_right_to_left_32s_6k(&t, &mut sa, &mut buckets, 0, 2, 1);
18063
18064 assert_eq!(d, 1);
18065 assert_eq!(sa[1], 3 | SAINT_MIN);
18066 assert_eq!(buckets[buckets_index4(1, 1)], 1);
18067 assert_eq!(buckets[buckets_index4(1, 1) + 2], 1);
18068 }
18069
18070 #[test]
18071 fn partial_sorting_scan_right_to_left_32s_1k_omp_wraps_sequential_version() {
18072 let t = vec![0, 1, 2, 1, 0];
18073 let mut sa = vec![0, 0, 4];
18074 let mut buckets = vec![0; 3];
18075 buckets[1] = 2;
18076 let mut thread_state = alloc_thread_state(2).unwrap();
18077
18078 partial_sorting_scan_right_to_left_32s_1k_omp(
18079 &t,
18080 &mut sa,
18081 3,
18082 &mut buckets,
18083 2,
18084 &mut thread_state,
18085 );
18086
18087 assert_eq!(sa[1], 3 | SAINT_MIN);
18088 assert_eq!(buckets[1], 1);
18089 }
18090
18091 #[test]
18092 fn partial_sorting_scan_right_to_left_32s_6k_block_gather_records_symbols() {
18093 let t = vec![0, 1, 2, 1, 0];
18094 let sa = vec![0, 4 | SAINT_MIN, 0];
18095 let mut cache = vec![ThreadCache::default(); sa.len()];
18096
18097 partial_sorting_scan_right_to_left_32s_6k_block_gather(&t, &sa, &mut cache, 1, 1);
18098
18099 assert_eq!(cache[0].index, 4 | SAINT_MIN);
18100 assert_eq!(cache[0].symbol, buckets_index4(1, 1) as SaSint);
18101 }
18102
18103 #[test]
18104 fn partial_sorting_scan_right_to_left_32s_4k_block_gather_zeroes_positive_entries() {
18105 let t = vec![0, 1, 2, 1, 0];
18106 let mut sa = vec![0, 4 | SUFFIX_GROUP_MARKER, 0];
18107 let mut cache = vec![ThreadCache::default(); sa.len()];
18108
18109 partial_sorting_scan_right_to_left_32s_4k_block_gather(&t, &mut sa, &mut cache, 1, 1);
18110
18111 assert_eq!(sa[1], 0);
18112 assert_eq!(cache[0].index, 4 | SUFFIX_GROUP_MARKER);
18113 assert_eq!(cache[0].symbol, buckets_index2(1, 1) as SaSint);
18114 }
18115
18116 #[test]
18117 fn partial_sorting_scan_right_to_left_32s_1k_block_gather_stores_preinduced_entries() {
18118 let t = vec![0, 1, 2, 1, 0];
18119 let mut sa = vec![0, 4, 0];
18120 let mut cache = vec![ThreadCache::default(); sa.len()];
18121
18122 partial_sorting_scan_right_to_left_32s_1k_block_gather(&t, &mut sa, &mut cache, 1, 1);
18123
18124 assert_eq!(sa[1], 0);
18125 assert_eq!(cache[0].index, 3 | SAINT_MIN);
18126 assert_eq!(cache[0].symbol, 1);
18127 }
18128
18129 #[test]
18130 fn partial_sorting_scan_right_to_left_32s_6k_block_sort_updates_bucket_and_marker_state() {
18131 let t = vec![0, 1, 2, 1, 0];
18132 let mut cache = vec![ThreadCache::default(); 3];
18133 cache[0].index = 4 | SAINT_MIN;
18134 cache[0].symbol = buckets_index4(1, 1) as SaSint;
18135 let mut buckets = vec![0; 4 * 3];
18136 buckets[buckets_index4(1, 1)] = 2;
18137
18138 let d = partial_sorting_scan_right_to_left_32s_6k_block_sort(
18139 &t,
18140 &mut buckets,
18141 0,
18142 &mut cache,
18143 1,
18144 1,
18145 );
18146
18147 assert_eq!(d, 1);
18148 assert_eq!(cache[0].index, 3 | SAINT_MIN);
18149 assert_eq!(buckets[buckets_index4(1, 1)], 1);
18150 assert_eq!(buckets[buckets_index4(1, 1) + 2], 1);
18151 }
18152
18153 #[test]
18154 fn partial_sorting_scan_right_to_left_32s_1k_block_omp_places_cached_suffixes() {
18155 let t = vec![0, 1, 2, 1, 0];
18156 let mut sa = vec![0, 4, 0];
18157 let mut buckets = vec![0; 3];
18158 buckets[1] = 2;
18159 let mut cache = vec![ThreadCache::default(); sa.len()];
18160
18161 partial_sorting_scan_right_to_left_32s_1k_block_omp(
18162 &t,
18163 &mut sa,
18164 &mut buckets,
18165 &mut cache,
18166 1,
18167 1,
18168 2,
18169 );
18170
18171 assert_eq!(sa[1], 3 | SAINT_MIN);
18172 assert_eq!(buckets[1], 1);
18173 }
18174
18175 #[test]
18176 fn partial_sorting_scan_right_to_left_32s_1k_block_omp_uses_relative_cache() {
18177 let block_start = 20_000usize;
18178 let block_size = 16_384usize;
18179 let n = block_start + block_size + 8;
18180 let t = vec![1; n];
18181 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
18182
18183 let mut sa_single = vec![0; n];
18184 sa_single[block_start..block_start + block_size].copy_from_slice(&suffixes);
18185 let mut sa_threaded = sa_single.clone();
18186 let mut bucket_single = vec![0, block_size as SaSint];
18187 let mut bucket_threaded = bucket_single.clone();
18188 let mut cache = vec![ThreadCache::default(); 4 * LIBSAIS_PER_THREAD_CACHE_SIZE];
18189
18190 partial_sorting_scan_right_to_left_32s_1k(
18191 &t,
18192 &mut sa_single,
18193 &mut bucket_single,
18194 block_start as FastSint,
18195 block_size as FastSint,
18196 );
18197 partial_sorting_scan_right_to_left_32s_1k_block_omp(
18198 &t,
18199 &mut sa_threaded,
18200 &mut bucket_threaded,
18201 &mut cache,
18202 block_start as FastSint,
18203 block_size as FastSint,
18204 4,
18205 );
18206
18207 assert_eq!(sa_threaded, sa_single);
18208 assert_eq!(bucket_threaded, bucket_single);
18209 }
18210
18211 #[test]
18212 fn partial_sorting_gather_lms_suffixes_32s_4k_compacts_negative_marked_entries() {
18213 let mut sa = vec![1 | SUFFIX_GROUP_MARKER, -3, 5 | SUFFIX_GROUP_MARKER, -7];
18214 let n = sa.len() as FastSint;
18215
18216 let l = partial_sorting_gather_lms_suffixes_32s_4k(&mut sa, 0, n);
18217
18218 assert_eq!(l, 2);
18219 assert_eq!(sa[0], -1073741827);
18220 assert_eq!(sa[1], -1073741831);
18221 }
18222
18223 #[test]
18224 fn partial_sorting_gather_lms_suffixes_32s_1k_compacts_negative_marked_entries() {
18225 let mut sa = vec![1, -3, 5, -7];
18226 let n = sa.len() as FastSint;
18227
18228 let l = partial_sorting_gather_lms_suffixes_32s_1k(&mut sa, 0, n);
18229
18230 assert_eq!(l, 2);
18231 assert_eq!(sa[0], SAINT_MAX - 2);
18232 assert_eq!(sa[1], SAINT_MAX - 6);
18233 }
18234
18235 #[test]
18236 fn partial_sorting_gather_lms_suffixes_32s_4k_omp_wraps_sequential_version() {
18237 let mut sa = vec![1 | SUFFIX_GROUP_MARKER, -3, 5 | SUFFIX_GROUP_MARKER, -7];
18238 let mut thread_state = alloc_thread_state(2).unwrap();
18239
18240 partial_sorting_gather_lms_suffixes_32s_4k_omp(&mut sa, 4, 2, &mut thread_state);
18241
18242 assert_eq!(sa[0], -1073741827);
18243 assert_eq!(sa[1], -1073741831);
18244 }
18245
18246 #[test]
18247 fn partial_sorting_gather_lms_suffixes_32s_1k_omp_wraps_sequential_version() {
18248 let mut sa = vec![1, -3, 5, -7];
18249 let mut thread_state = alloc_thread_state(2).unwrap();
18250
18251 partial_sorting_gather_lms_suffixes_32s_1k_omp(&mut sa, 4, 2, &mut thread_state);
18252
18253 assert_eq!(sa[0], SAINT_MAX - 2);
18254 assert_eq!(sa[1], SAINT_MAX - 6);
18255 }
18256
18257 #[test]
18258 fn partial_sorting_gather_lms_suffixes_32s_omp_uses_block_partition() {
18259 let n = 65_600usize;
18260 let input_4k: Vec<SaSint> = (0..n)
18261 .map(|i| {
18262 let value = (i as SaSint) | SUFFIX_GROUP_MARKER;
18263 if i % 5 == 0 {
18264 value | SAINT_MIN
18265 } else {
18266 value
18267 }
18268 })
18269 .collect();
18270 let count_4k = input_4k.iter().filter(|&&value| value < 0).count();
18271
18272 let mut single = input_4k.clone();
18273 let mut threaded = input_4k;
18274 let mut thread_state = alloc_thread_state(4).unwrap();
18275 partial_sorting_gather_lms_suffixes_32s_4k_omp(&mut single, n as SaSint, 1, &mut []);
18276 partial_sorting_gather_lms_suffixes_32s_4k_omp(
18277 &mut threaded,
18278 n as SaSint,
18279 4,
18280 &mut thread_state,
18281 );
18282 assert_eq!(&threaded[..count_4k], &single[..count_4k]);
18283
18284 let input_1k: Vec<SaSint> = (0..n)
18285 .map(|i| {
18286 let value = i as SaSint;
18287 if i % 7 == 0 {
18288 value | SAINT_MIN
18289 } else {
18290 value
18291 }
18292 })
18293 .collect();
18294 let count_1k = input_1k.iter().filter(|&&value| value < 0).count();
18295
18296 let mut single = input_1k.clone();
18297 let mut threaded = input_1k;
18298 partial_sorting_gather_lms_suffixes_32s_1k_omp(&mut single, n as SaSint, 1, &mut []);
18299 partial_sorting_gather_lms_suffixes_32s_1k_omp(
18300 &mut threaded,
18301 n as SaSint,
18302 4,
18303 &mut thread_state,
18304 );
18305 assert_eq!(&threaded[..count_1k], &single[..count_1k]);
18306 }
18307
18308 #[test]
18309 fn renumber_lms_suffixes_8u_writes_names_into_second_half() {
18310 let mut sa = vec![1 | SAINT_MIN, 3, 0, 0];
18311
18312 let name = renumber_lms_suffixes_8u(&mut sa, 2, 0, 0, 2);
18313
18314 assert_eq!(name, 1);
18315 assert_eq!(sa[2], SAINT_MIN);
18316 assert_eq!(sa[3], SAINT_MIN | 1);
18317 }
18318
18319 #[test]
18320 fn renumber_lms_suffixes_8u_matches_upstream_c_helper() {
18321 let mut sa_rust = vec![1 | SAINT_MIN, 3, 0, 0];
18322 let mut sa_c = sa_rust.clone();
18323
18324 let rust_name = renumber_lms_suffixes_8u(&mut sa_rust, 2, 0, 0, 2);
18325 let c_name = unsafe { probe_renumber_lms_suffixes_8u(sa_c.as_mut_ptr(), 2, 0, 0, 2) };
18326
18327 assert_eq!(rust_name, c_name);
18328 assert_eq!(sa_rust, sa_c);
18329 }
18330
18331 #[test]
18332 fn gather_marked_lms_suffixes_moves_negative_marked_entries_to_tail() {
18333 let mut sa = vec![0, 0, 1 | SAINT_MIN, 3];
18334
18335 let l = gather_marked_lms_suffixes(&mut sa, 2, 4, 0, 2);
18336
18337 assert_eq!(l, 3);
18338 assert_eq!(sa[3], 1);
18339 }
18340
18341 #[test]
18342 fn gather_marked_lms_suffixes_matches_upstream_c_helper() {
18343 let mut sa_rust = vec![0, 0, 1 | SAINT_MIN, 3];
18344 let mut sa_c = sa_rust.clone();
18345
18346 let rust_l = gather_marked_lms_suffixes(&mut sa_rust, 2, 4, 0, 2);
18347 let c_l = unsafe { probe_gather_marked_lms_suffixes(sa_c.as_mut_ptr(), 2, 4, 0, 2) };
18348
18349 assert_eq!(rust_l, c_l);
18350 assert_eq!(sa_rust, sa_c);
18351 }
18352
18353 #[test]
18354 fn renumber_lms_suffixes_8u_omp_wraps_sequential_version() {
18355 let mut sa = vec![1 | SAINT_MIN, 3, 0, 0];
18356 let mut thread_state = alloc_thread_state(2).unwrap();
18357
18358 let name = renumber_lms_suffixes_8u_omp(&mut sa, 2, 2, &mut thread_state);
18359
18360 assert_eq!(name, 1);
18361 assert_eq!(sa[2], SAINT_MIN);
18362 }
18363
18364 #[test]
18365 fn renumber_lms_suffixes_8u_omp_uses_block_partition_for_large_inputs() {
18366 let m = 65_600usize;
18367 let mut input = vec![0; 2 * m];
18368 for (i, slot) in input[..m].iter_mut().enumerate() {
18369 let suffix = (2 * i + 1) as SaSint;
18370 *slot = if i % 5 == 0 {
18371 suffix | SAINT_MIN
18372 } else {
18373 suffix
18374 };
18375 }
18376
18377 let mut single = input.clone();
18378 let mut threaded = input;
18379 let mut thread_state = alloc_thread_state(4).unwrap();
18380 let single_name = renumber_lms_suffixes_8u(&mut single, m as SaSint, 0, 0, m as FastSint);
18381 let threaded_name =
18382 renumber_lms_suffixes_8u_omp(&mut threaded, m as SaSint, 4, &mut thread_state);
18383
18384 assert_eq!(threaded_name, single_name);
18385 assert_eq!(threaded, single);
18386 }
18387
18388 #[test]
18389 fn gather_marked_lms_suffixes_omp_uses_block_partition_for_large_inputs() {
18390 let n = 131_200usize;
18391 let half_n = n >> 1;
18392 let mut input = vec![-77; n];
18393 for (i, slot) in input[..half_n].iter_mut().enumerate() {
18394 let suffix = (3 * i + 1) as SaSint;
18395 *slot = if i % 7 == 0 {
18396 suffix | SAINT_MIN
18397 } else {
18398 suffix
18399 };
18400 }
18401 let marked_count = input[..half_n].iter().filter(|&&value| value < 0).count();
18402
18403 let mut single = input.clone();
18404 let mut threaded = input;
18405 let mut thread_state = alloc_thread_state(4).unwrap();
18406 let _ = gather_marked_lms_suffixes(&mut single, 0, n as FastSint, 0, half_n as FastSint);
18407 gather_marked_lms_suffixes_omp(&mut threaded, n as SaSint, 0, 0, 4, &mut thread_state);
18408
18409 assert_eq!(&threaded[n - marked_count..], &single[n - marked_count..]);
18410 }
18411
18412 #[test]
18413 fn renumber_and_gather_lms_suffixes_omp_uses_large_input_paths() {
18414 let m = 65_600usize;
18415 let n = 2 * m;
18416 let mut input = vec![0; n];
18417 for (i, slot) in input[..m].iter_mut().enumerate() {
18418 let suffix = (2 * i + 1) as SaSint;
18419 *slot = if i % 5 == 0 {
18420 suffix | SAINT_MIN
18421 } else {
18422 suffix
18423 };
18424 }
18425
18426 let mut single = input.clone();
18427 let mut threaded = input;
18428 let mut single_state = alloc_thread_state(1).unwrap();
18429 let mut threaded_state = alloc_thread_state(4).unwrap();
18430 let single_name = renumber_and_gather_lms_suffixes_omp(
18431 &mut single,
18432 n as SaSint,
18433 m as SaSint,
18434 0,
18435 1,
18436 &mut single_state,
18437 );
18438 let threaded_name = renumber_and_gather_lms_suffixes_omp(
18439 &mut threaded,
18440 n as SaSint,
18441 m as SaSint,
18442 0,
18443 4,
18444 &mut threaded_state,
18445 );
18446
18447 assert_eq!(threaded_name, single_name);
18448 assert_eq!(threaded, single);
18449 }
18450
18451 #[test]
18452 fn renumber_and_gather_lms_suffixes_omp_gathers_when_names_are_not_distinct() {
18453 let mut sa = vec![1 | SAINT_MIN, 3, 0, 0];
18454 let mut thread_state = alloc_thread_state(2).unwrap();
18455
18456 let name = renumber_and_gather_lms_suffixes_omp(&mut sa, 4, 2, 0, 2, &mut thread_state);
18457
18458 assert_eq!(name, 1);
18459 assert_eq!(sa[3], 1);
18460 }
18461
18462 #[test]
18463 fn renumber_and_gather_lms_suffixes_omp_matches_upstream_c_helper() {
18464 let mut sa_rust = vec![1 | SAINT_MIN, 3, 0, 0];
18465 let mut sa_c = sa_rust.clone();
18466 let mut thread_state = alloc_thread_state(2).unwrap();
18467
18468 let rust_name =
18469 renumber_and_gather_lms_suffixes_omp(&mut sa_rust, 4, 2, 0, 2, &mut thread_state);
18470 let c_name =
18471 unsafe { probe_renumber_and_gather_lms_suffixes_omp(sa_c.as_mut_ptr(), 4, 2, 0, 2) };
18472
18473 assert_eq!(rust_name, c_name);
18474 assert_eq!(sa_rust, sa_c);
18475 }
18476
18477 #[test]
18478 fn renumber_distinct_lms_suffixes_32s_4k_masks_sources_and_writes_second_half() {
18479 let mut sa = vec![1 | SAINT_MIN, 3 | SAINT_MIN, 0, 0];
18480
18481 let name = renumber_distinct_lms_suffixes_32s_4k(&mut sa, 2, 1, 0, 2);
18482
18483 assert_eq!(name, 3);
18484 assert_eq!(sa[0], 1);
18485 assert_eq!(sa[1], 3);
18486 assert_eq!(sa[2], 1);
18487 assert_eq!(sa[3], 2 | SAINT_MIN);
18488 }
18489
18490 #[test]
18491 fn renumber_distinct_lms_suffixes_32s_4k_matches_upstream_c_helper() {
18492 let mut sa_rust = vec![1 | SAINT_MIN, 3 | SAINT_MIN, 0, 0];
18493 let mut sa_c = sa_rust.clone();
18494
18495 let rust_name = renumber_distinct_lms_suffixes_32s_4k(&mut sa_rust, 2, 1, 0, 2);
18496 let c_name =
18497 unsafe { probe_renumber_distinct_lms_suffixes_32s_4k(sa_c.as_mut_ptr(), 2, 1, 0, 2) };
18498
18499 assert_eq!(rust_name, c_name);
18500 assert_eq!(sa_rust, sa_c);
18501 }
18502
18503 #[test]
18504 fn mark_distinct_lms_suffixes_32s_propagates_previous_nonzero_marker() {
18505 let mut sa = vec![0, 0, SAINT_MIN | 5, 0, SAINT_MIN | 7];
18506
18507 mark_distinct_lms_suffixes_32s(&mut sa, 2, 0, 3);
18508
18509 assert_eq!(sa[2], 5);
18510 assert_eq!(sa[3], 0);
18511 assert_eq!(sa[4], SAINT_MIN | 7);
18512 }
18513
18514 #[test]
18515 fn clamp_lms_suffixes_length_32s_keeps_only_negative_lengths() {
18516 let mut sa = vec![0, 0, SAINT_MIN | 5, 7, SAINT_MIN | 3];
18517
18518 clamp_lms_suffixes_length_32s(&mut sa, 2, 0, 3);
18519
18520 assert_eq!(sa[2], 5);
18521 assert_eq!(sa[3], 0);
18522 assert_eq!(sa[4], 3);
18523 }
18524
18525 #[test]
18526 fn renumber_and_mark_distinct_lms_suffixes_32s_4k_omp_marks_second_half_when_names_repeat() {
18527 let mut sa = vec![1 | SAINT_MIN, 3 | SAINT_MIN, 0, 0];
18528 let mut thread_state = alloc_thread_state(2).unwrap();
18529
18530 let name =
18531 renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(&mut sa, 4, 2, 2, &mut thread_state);
18532
18533 assert_eq!(name, 2);
18534 assert_eq!(sa[2], 1);
18535 assert_eq!(sa[3], SAINT_MIN | 2);
18536 }
18537
18538 #[test]
18539 fn renumber_and_mark_distinct_lms_suffixes_32s_4k_omp_matches_upstream_c_helper() {
18540 let mut sa_rust = vec![1 | SAINT_MIN, 3 | SAINT_MIN, 0, 0];
18541 let mut sa_c = sa_rust.clone();
18542 let mut thread_state = alloc_thread_state(2).unwrap();
18543
18544 let rust_name = renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
18545 &mut sa_rust,
18546 4,
18547 2,
18548 2,
18549 &mut thread_state,
18550 );
18551 let c_name = unsafe {
18552 probe_renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(sa_c.as_mut_ptr(), 4, 2, 2)
18553 };
18554
18555 assert_eq!(rust_name, c_name);
18556 assert_eq!(sa_rust, sa_c);
18557 }
18558
18559 #[test]
18560 fn reconstruct_lms_suffixes_maps_indices_from_tail_interval() {
18561 let mut sa = vec![0, 1, 2, 7, 11, 13];
18562
18563 reconstruct_lms_suffixes(&mut sa, 6, 3, 0, 3);
18564
18565 assert_eq!(&sa[..3], &[7, 11, 13]);
18566 }
18567
18568 #[test]
18569 fn reconstruct_lms_suffixes_omp_wraps_sequential_version() {
18570 let mut sa = vec![0, 1, 2, 7, 11, 13];
18571
18572 reconstruct_lms_suffixes_omp(&mut sa, 6, 3, 2);
18573
18574 assert_eq!(&sa[..3], &[7, 11, 13]);
18575 }
18576
18577 #[test]
18578 fn reconstruct_lms_suffixes_omp_uses_block_partition_for_large_inputs() {
18579 let m = 65_600usize;
18580 let n = 2 * m;
18581 let mut input = vec![0; n];
18582 for (i, slot) in input[..m].iter_mut().enumerate() {
18583 *slot = (m - 1 - i) as SaSint;
18584 }
18585 for (i, slot) in input[m..].iter_mut().enumerate() {
18586 *slot = (i * 17 + 3) as SaSint;
18587 }
18588
18589 let mut single = input.clone();
18590 let mut threaded = input;
18591 reconstruct_lms_suffixes(&mut single, n as SaSint, m as SaSint, 0, m as FastSint);
18592 reconstruct_lms_suffixes_omp(&mut threaded, n as SaSint, m as SaSint, 4);
18593
18594 assert_eq!(threaded, single);
18595 }
18596
18597 #[test]
18598 fn renumber_and_mark_distinct_lms_suffixes_32s_1k_omp_handles_single_lms_suffix() {
18599 let t = vec![2, 1, 0];
18600 let mut sa = vec![0; t.len()];
18601
18602 let name = renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(&t, &mut sa, 3, 1, 1);
18603
18604 assert_eq!(name, 1);
18605 assert_eq!(sa[1], SAINT_MIN | 1);
18606 }
18607
18608 #[test]
18609 fn libsais_main_32s_entry_matches_upstream_c_on_6k_branch() {
18610 assert_main_32s_entry_matches_upstream_c_for_branch(300);
18611 }
18612
18613 #[test]
18614 fn libsais_main_32s_entry_matches_upstream_c_on_4k_branch() {
18615 assert_main_32s_entry_matches_upstream_c_for_branch(400);
18616 }
18617
18618 #[test]
18619 fn libsais_main_32s_entry_matches_upstream_c_on_2k_branch() {
18620 assert_main_32s_entry_matches_upstream_c_for_branch(700);
18621 }
18622
18623 #[test]
18624 fn libsais_main_32s_entry_matches_upstream_c_on_1k_branch() {
18625 assert_main_32s_entry_matches_upstream_c_for_branch(1501);
18626 }
18627
18628 #[test]
18629 fn libsais_main_32s_entry_matches_upstream_c_on_recursive_repetitive_6k_case() {
18630 assert_main_32s_entry_matches_upstream_c(make_recursive_main_32s_text(24), 300, 0, true);
18631 }
18632
18633 #[test]
18634 fn libsais_main_32s_entry_matches_upstream_c_on_recursive_repetitive_1k_case() {
18635 assert_main_32s_entry_matches_upstream_c(make_recursive_main_32s_text(24), 1501, 0, true);
18636 }
18637
18638 #[test]
18639 fn libsais_main_32s_entry_matches_upstream_c_on_large_generated_6k_case() {
18640 assert_main_32s_entry_matches_upstream_c(
18641 make_large_main_32s_stress_text(1024, 300),
18642 300,
18643 0,
18644 true,
18645 );
18646 }
18647
18648 #[test]
18649 fn libsais_main_32s_entry_matches_upstream_c_on_large_generated_6k_case_with_fs() {
18650 assert_main_32s_entry_matches_upstream_c(
18651 make_large_main_32s_stress_text(1024, 300),
18652 300,
18653 2048,
18654 false,
18655 );
18656 }
18657
18658 #[test]
18659 fn libsais_main_32s_entry_matches_upstream_c_on_large_generated_4k_case() {
18660 assert_main_32s_entry_matches_upstream_c(
18661 make_large_main_32s_stress_text(1024, 400),
18662 400,
18663 0,
18664 true,
18665 );
18666 }
18667
18668 #[test]
18669 fn libsais_main_32s_entry_matches_upstream_c_on_large_generated_4k_case_with_fs() {
18670 assert_main_32s_entry_matches_upstream_c(
18671 make_large_main_32s_stress_text(1024, 400),
18672 400,
18673 2048,
18674 false,
18675 );
18676 }
18677
18678 #[test]
18679 fn libsais_main_32s_entry_matches_upstream_c_on_large_generated_2k_case() {
18680 assert_main_32s_entry_matches_upstream_c(
18681 make_large_main_32s_stress_text(1024, 700),
18682 700,
18683 0,
18684 true,
18685 );
18686 }
18687
18688 #[test]
18689 fn libsais_main_32s_entry_matches_upstream_c_on_large_generated_2k_case_with_fs() {
18690 assert_main_32s_entry_matches_upstream_c(
18691 make_large_main_32s_stress_text(1024, 700),
18692 700,
18693 2048,
18694 false,
18695 );
18696 }
18697
18698 #[test]
18699 fn libsais_main_32s_entry_matches_upstream_c_on_large_generated_1k_case_with_fs() {
18700 assert_main_32s_entry_matches_upstream_c(
18701 make_large_main_32s_stress_text(1024, 1501),
18702 1501,
18703 2048,
18704 false,
18705 );
18706 }
18707
18708 #[test]
18709 fn place_lms_suffixes_interval_32s_4k_moves_suffixes_into_bucket_intervals() {
18710 let mut sa = vec![10, 11, 12, 13, 14];
18711 let k = 3usize;
18712 let mut buckets = vec![0; 4 * k];
18713 buckets[buckets_index2(0, 1)] = 0;
18714 buckets[buckets_index2(1, 1)] = 2;
18715 buckets[buckets_index2(2, 1)] = 3;
18716 buckets[3 * k] = 2;
18717 buckets[3 * k + 1] = 5;
18718
18719 place_lms_suffixes_interval_32s_4k(&mut sa, 5, k as SaSint, 5, &buckets);
18720
18721 assert_eq!(sa, vec![0, 0, 0, 0, 14]);
18722 }
18723
18724 #[test]
18725 fn place_lms_suffixes_interval_32s_2k_moves_suffixes_into_bucket_intervals() {
18726 let mut sa = vec![10, 11, 12, 13, 14];
18727 let mut buckets = vec![0; 2 * 3];
18728 buckets[buckets_index2(0, 0)] = 2;
18729 buckets[buckets_index2(0, 1)] = 0;
18730 buckets[buckets_index2(1, 0)] = 5;
18731 buckets[buckets_index2(1, 1)] = 2;
18732 buckets[buckets_index2(2, 0)] = 5;
18733 buckets[buckets_index2(2, 1)] = 3;
18734
18735 place_lms_suffixes_interval_32s_2k(&mut sa, 5, 3, 5, &buckets);
18736
18737 assert_eq!(sa, vec![0, 0, 0, 0, 14]);
18738 }
18739
18740 #[test]
18741 fn place_lms_suffixes_interval_32s_1k_places_suffixes_by_symbol_bucket() {
18742 let t = vec![0, 1, 1, 2, 2];
18743 let mut sa = vec![1, 2, 3, 4, 99];
18744 let buckets = vec![0, 2, 5];
18745
18746 place_lms_suffixes_interval_32s_1k(&t, &mut sa, 3, 4, &buckets);
18747
18748 assert_eq!(sa, vec![1, 2, 0, 3, 4]);
18749 }
18750
18751 #[test]
18752 fn final_bwt_scan_left_to_right_8u_rewrites_sa_and_induces_suffixes() {
18753 let t = vec![0_u8, 1, 2, 1, 0];
18754 let mut sa = vec![1, 0, 0];
18755 let mut induction_bucket = vec![0, 1, 3];
18756
18757 final_bwt_scan_left_to_right_8u(&t, &mut sa, &mut induction_bucket, 0, 1);
18758
18759 assert_eq!(sa[0], 0);
18760 assert_eq!(induction_bucket[0], 1);
18761 }
18762
18763 #[test]
18764 fn final_bwt_aux_scan_left_to_right_8u_updates_sampling_array() {
18765 let t = vec![0_u8, 1, 2, 1, 0];
18766 let mut sa = vec![1, 0, 0];
18767 let mut induction_bucket = vec![0, 1, 3];
18768 let mut i_out = vec![0; 2];
18769
18770 final_bwt_aux_scan_left_to_right_8u(
18771 &t,
18772 &mut sa,
18773 0,
18774 &mut i_out,
18775 &mut induction_bucket,
18776 0,
18777 1,
18778 );
18779
18780 assert_eq!(i_out[0], 1);
18781 }
18782
18783 #[test]
18784 fn final_sorting_scan_left_to_right_8u_clears_marker_and_places_suffix() {
18785 let t = vec![0_u8, 1, 2, 1, 0];
18786 let mut sa = vec![1, 0, 0];
18787 let mut induction_bucket = vec![0, 1, 3];
18788
18789 final_sorting_scan_left_to_right_8u(&t, &mut sa, &mut induction_bucket, 0, 1);
18790
18791 assert_eq!(sa[0], 0);
18792 assert_eq!(induction_bucket[0], 1);
18793 }
18794
18795 #[test]
18796 fn final_sorting_scan_left_to_right_32s_clears_marker_and_places_suffix() {
18797 let t = vec![0, 1, 2, 1, 0];
18798 let mut sa = vec![1, 0, 0];
18799 let mut induction_bucket = vec![0, 1, 3];
18800
18801 final_sorting_scan_left_to_right_32s(&t, &mut sa, &mut induction_bucket, 0, 1);
18802
18803 assert_eq!(sa[0], 0);
18804 assert_eq!(induction_bucket[0], 1);
18805 }
18806
18807 #[test]
18808 fn final_bwt_scan_left_to_right_8u_block_prepare_records_cache_and_counts() {
18809 let t = vec![0_u8, 1, 2, 1, 0];
18810 let mut sa = vec![1, 2, 0];
18811 let mut buckets = vec![99; ALPHABET_SIZE];
18812 let mut cache = vec![ThreadCache::default(); 4];
18813
18814 let count = final_bwt_scan_left_to_right_8u_block_prepare(
18815 &t,
18816 &mut sa,
18817 ALPHABET_SIZE as SaSint,
18818 &mut buckets,
18819 &mut cache,
18820 0,
18821 2,
18822 );
18823
18824 assert_eq!(count, 2);
18825 assert_eq!(sa[0] & SAINT_MAX, 0);
18826 assert_eq!(sa[1], 1 | SAINT_MIN);
18827 assert_eq!(buckets[0], 1);
18828 assert_eq!(buckets[1], 1);
18829 assert_eq!(cache[0].symbol, 0);
18830 assert_eq!(cache[0].index & SAINT_MAX, 0);
18831 assert_eq!(cache[1].symbol, 1);
18832 assert_eq!(cache[1].index & SAINT_MAX, 1);
18833 }
18834
18835 #[test]
18836 fn final_sorting_scan_left_to_right_32s_block_omp_places_cached_suffixes() {
18837 let t = vec![0, 1, 2, 1, 0];
18838 let mut sa = vec![1, 2, 0, 0];
18839 let mut induction_bucket = vec![0, 1, 3];
18840 let mut cache = vec![ThreadCache::default(); LIBSAIS_PER_THREAD_CACHE_SIZE];
18841
18842 final_sorting_scan_left_to_right_32s_block_omp(
18843 &t,
18844 &mut sa,
18845 &mut induction_bucket,
18846 &mut cache,
18847 0,
18848 2,
18849 2,
18850 );
18851
18852 assert_eq!(sa[0] & SAINT_MAX, 0);
18853 assert_eq!(sa[1] & SAINT_MAX, 1);
18854 assert_eq!(induction_bucket[0], 1);
18855 assert_eq!(induction_bucket[1], 2);
18856 }
18857
18858 #[test]
18859 fn final_sorting_scan_left_to_right_8u_omp_wraps_sequential_behavior() {
18860 let t = vec![0_u8, 1, 2, 1, 0];
18861 let mut sa = vec![0; t.len()];
18862 let mut induction_bucket = vec![0, 1, 3];
18863 let mut expected_sa = sa.clone();
18864 let mut expected_bucket = induction_bucket.clone();
18865
18866 final_sorting_scan_left_to_right_8u_omp(
18867 &t,
18868 &mut expected_sa,
18869 t.len() as FastSint,
18870 ALPHABET_SIZE as SaSint,
18871 &mut expected_bucket,
18872 1,
18873 &mut [],
18874 );
18875
18876 let mut thread_state = alloc_thread_state(2).unwrap();
18877
18878 final_sorting_scan_left_to_right_8u_omp(
18879 &t,
18880 &mut sa,
18881 t.len() as FastSint,
18882 ALPHABET_SIZE as SaSint,
18883 &mut induction_bucket,
18884 2,
18885 &mut thread_state,
18886 );
18887
18888 assert_eq!(sa, expected_sa);
18889 assert_eq!(induction_bucket, expected_bucket);
18890 }
18891
18892 #[test]
18893 fn final_sorting_scan_left_to_right_8u_block_omp_uses_thread_buckets() {
18894 let block_start = 20_000usize;
18895 let block_size = 16_384usize;
18896 let n = block_start + block_size + 8;
18897 let t = vec![1_u8; n];
18898 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
18899
18900 let mut expected_sa = vec![0; n];
18901 expected_sa[block_start..block_start + block_size].copy_from_slice(&suffixes);
18902 let mut threaded_sa = expected_sa.clone();
18903 let mut expected_bucket = vec![0; ALPHABET_SIZE];
18904 let mut threaded_bucket = expected_bucket.clone();
18905 let mut thread_state = alloc_thread_state(4).unwrap();
18906
18907 final_sorting_scan_left_to_right_8u(
18908 &t,
18909 &mut expected_sa,
18910 &mut expected_bucket,
18911 block_start as FastSint,
18912 block_size as FastSint,
18913 );
18914 final_sorting_scan_left_to_right_8u_block_omp(
18915 &t,
18916 &mut threaded_sa,
18917 ALPHABET_SIZE as SaSint,
18918 &mut threaded_bucket,
18919 block_start as FastSint,
18920 block_size as FastSint,
18921 4,
18922 &mut thread_state,
18923 );
18924
18925 assert_eq!(threaded_sa, expected_sa);
18926 assert_eq!(threaded_bucket, expected_bucket);
18927 }
18928
18929 #[test]
18930 fn final_bwt_left_to_right_8u_block_omp_uses_thread_buckets() {
18931 let block_start = 20_000usize;
18932 let block_size = 16_384usize;
18933 let n = block_start + block_size + 8;
18934 let t = vec![1_u8; n];
18935 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
18936
18937 let mut expected_sa = vec![0; n];
18938 expected_sa[block_start..block_start + block_size].copy_from_slice(&suffixes);
18939 let mut threaded_sa = expected_sa.clone();
18940 let mut expected_bucket = vec![0; ALPHABET_SIZE];
18941 let mut threaded_bucket = expected_bucket.clone();
18942 let mut thread_state = alloc_thread_state(4).unwrap();
18943
18944 final_bwt_scan_left_to_right_8u(
18945 &t,
18946 &mut expected_sa,
18947 &mut expected_bucket,
18948 block_start as FastSint,
18949 block_size as FastSint,
18950 );
18951 final_bwt_scan_left_to_right_8u_block_omp(
18952 &t,
18953 &mut threaded_sa,
18954 ALPHABET_SIZE as SaSint,
18955 &mut threaded_bucket,
18956 block_start as FastSint,
18957 block_size as FastSint,
18958 4,
18959 &mut thread_state,
18960 );
18961
18962 assert_eq!(threaded_sa, expected_sa);
18963 assert_eq!(threaded_bucket, expected_bucket);
18964 }
18965
18966 #[test]
18967 fn final_bwt_aux_left_to_right_8u_block_omp_uses_thread_buckets() {
18968 let block_start = 20_000usize;
18969 let block_size = 16_384usize;
18970 let n = block_start + block_size + 8;
18971 let t = vec![1_u8; n];
18972 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
18973
18974 let mut expected_sa = vec![0; n];
18975 expected_sa[block_start..block_start + block_size].copy_from_slice(&suffixes);
18976 let mut threaded_sa = expected_sa.clone();
18977 let mut expected_i = vec![0; n];
18978 let mut threaded_i = vec![0; n];
18979 let mut expected_bucket = vec![0; ALPHABET_SIZE];
18980 let mut threaded_bucket = expected_bucket.clone();
18981 let mut thread_state = alloc_thread_state(4).unwrap();
18982
18983 final_bwt_aux_scan_left_to_right_8u(
18984 &t,
18985 &mut expected_sa,
18986 0,
18987 &mut expected_i,
18988 &mut expected_bucket,
18989 block_start as FastSint,
18990 block_size as FastSint,
18991 );
18992 final_bwt_aux_scan_left_to_right_8u_block_omp(
18993 &t,
18994 &mut threaded_sa,
18995 ALPHABET_SIZE as SaSint,
18996 0,
18997 &mut threaded_i,
18998 &mut threaded_bucket,
18999 block_start as FastSint,
19000 block_size as FastSint,
19001 4,
19002 &mut thread_state,
19003 );
19004
19005 assert_eq!(threaded_sa, expected_sa);
19006 assert_eq!(threaded_i, expected_i);
19007 assert_eq!(threaded_bucket, expected_bucket);
19008 }
19009
19010 #[test]
19011 fn final_bwt_scan_right_to_left_8u_returns_zero_index_and_induces_suffixes() {
19012 let t = vec![0_u8, 1, 2, 1, 0];
19013 let mut sa = vec![0, 2, 0];
19014 let mut induction_bucket = vec![1, 2, 3];
19015
19016 let index = final_bwt_scan_right_to_left_8u(&t, &mut sa, &mut induction_bucket, 0, 2);
19017
19018 assert_eq!(index, 0);
19019 assert_eq!(sa[1], 1);
19020 assert_eq!(induction_bucket[1], 1);
19021 }
19022
19023 #[test]
19024 fn final_sorting_scan_right_to_left_32s_block_omp_runs_block_pipeline() {
19025 let t = vec![0, 1, 2, 1, 0];
19026 let mut sa = vec![0, 2, 0, 0];
19027 let mut induction_bucket = vec![1, 2, 3];
19028 let mut expected_sa = sa.clone();
19029 let mut expected_bucket = induction_bucket.clone();
19030 let mut cache = vec![ThreadCache::default(); LIBSAIS_PER_THREAD_CACHE_SIZE];
19031
19032 final_sorting_scan_right_to_left_32s(&t, &mut expected_sa, &mut expected_bucket, 0, 2);
19033 final_sorting_scan_right_to_left_32s_block_omp(
19034 &t,
19035 &mut sa,
19036 &mut induction_bucket,
19037 &mut cache,
19038 0,
19039 2,
19040 2,
19041 );
19042
19043 assert_eq!(sa, expected_sa);
19044 assert_eq!(induction_bucket, expected_bucket);
19045 }
19046
19047 #[test]
19048 fn final_sorting_scan_right_to_left_8u_block_omp_uses_thread_buckets() {
19049 let block_start = 20_000usize;
19050 let block_size = 16_384usize;
19051 let n = block_start + block_size + 8;
19052 let t = vec![1_u8; n];
19053 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
19054
19055 let mut expected_sa = vec![0; n];
19056 expected_sa[block_start..block_start + block_size].copy_from_slice(&suffixes);
19057 let mut threaded_sa = expected_sa.clone();
19058 let mut expected_bucket = vec![0; ALPHABET_SIZE];
19059 expected_bucket[1] = n as SaSint;
19060 let mut threaded_bucket = expected_bucket.clone();
19061 let mut thread_state = alloc_thread_state(4).unwrap();
19062
19063 final_sorting_scan_right_to_left_8u(
19064 &t,
19065 &mut expected_sa,
19066 &mut expected_bucket,
19067 block_start as FastSint,
19068 block_size as FastSint,
19069 );
19070 final_sorting_scan_right_to_left_8u_block_omp(
19071 &t,
19072 &mut threaded_sa,
19073 ALPHABET_SIZE as SaSint,
19074 &mut threaded_bucket,
19075 block_start as FastSint,
19076 block_size as FastSint,
19077 4,
19078 &mut thread_state,
19079 );
19080
19081 assert_eq!(threaded_sa, expected_sa);
19082 assert_eq!(threaded_bucket, expected_bucket);
19083 }
19084
19085 #[test]
19086 fn final_bwt_right_to_left_8u_block_omp_uses_thread_buckets() {
19087 let block_start = 20_000usize;
19088 let block_size = 16_384usize;
19089 let n = block_start + block_size + 8;
19090 let t = vec![1_u8; n];
19091 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
19092
19093 let mut expected_sa = vec![0; n];
19094 expected_sa[block_start..block_start + block_size].copy_from_slice(&suffixes);
19095 let mut threaded_sa = expected_sa.clone();
19096 let mut expected_bucket = vec![0; ALPHABET_SIZE];
19097 expected_bucket[1] = n as SaSint;
19098 let mut threaded_bucket = expected_bucket.clone();
19099 let mut thread_state = alloc_thread_state(4).unwrap();
19100
19101 final_bwt_scan_right_to_left_8u(
19102 &t,
19103 &mut expected_sa,
19104 &mut expected_bucket,
19105 block_start as FastSint,
19106 block_size as FastSint,
19107 );
19108 final_bwt_scan_right_to_left_8u_block_omp(
19109 &t,
19110 &mut threaded_sa,
19111 ALPHABET_SIZE as SaSint,
19112 &mut threaded_bucket,
19113 block_start as FastSint,
19114 block_size as FastSint,
19115 4,
19116 &mut thread_state,
19117 );
19118
19119 assert_eq!(threaded_sa, expected_sa);
19120 assert_eq!(threaded_bucket, expected_bucket);
19121 }
19122
19123 #[test]
19124 fn final_bwt_aux_right_to_left_8u_block_omp_uses_thread_buckets() {
19125 let block_start = 20_000usize;
19126 let block_size = 16_384usize;
19127 let n = block_start + block_size + 8;
19128 let t = vec![1_u8; n];
19129 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
19130
19131 let mut expected_sa = vec![0; n];
19132 expected_sa[block_start..block_start + block_size].copy_from_slice(&suffixes);
19133 let mut threaded_sa = expected_sa.clone();
19134 let mut expected_i = vec![0; n];
19135 let mut threaded_i = vec![0; n];
19136 let mut expected_bucket = vec![0; ALPHABET_SIZE];
19137 expected_bucket[1] = n as SaSint;
19138 let mut threaded_bucket = expected_bucket.clone();
19139 let mut thread_state = alloc_thread_state(4).unwrap();
19140
19141 final_bwt_aux_scan_right_to_left_8u(
19142 &t,
19143 &mut expected_sa,
19144 0,
19145 &mut expected_i,
19146 &mut expected_bucket,
19147 block_start as FastSint,
19148 block_size as FastSint,
19149 );
19150 final_bwt_aux_scan_right_to_left_8u_block_omp(
19151 &t,
19152 &mut threaded_sa,
19153 ALPHABET_SIZE as SaSint,
19154 0,
19155 &mut threaded_i,
19156 &mut threaded_bucket,
19157 block_start as FastSint,
19158 block_size as FastSint,
19159 4,
19160 &mut thread_state,
19161 );
19162
19163 assert_eq!(threaded_sa, expected_sa);
19164 assert_eq!(threaded_i, expected_i);
19165 assert_eq!(threaded_bucket, expected_bucket);
19166 }
19167
19168 #[test]
19169 fn final_gsa_right_to_left_8u_block_omp_uses_thread_buckets() {
19170 let block_start = 20_000usize;
19171 let block_size = 16_384usize;
19172 let n = block_start + block_size + 8;
19173 let t = vec![1_u8; n];
19174 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
19175
19176 let mut expected_sa = vec![0; n];
19177 expected_sa[block_start..block_start + block_size].copy_from_slice(&suffixes);
19178 let mut threaded_sa = expected_sa.clone();
19179 let mut expected_bucket = vec![0; ALPHABET_SIZE];
19180 expected_bucket[1] = n as SaSint;
19181 let mut threaded_bucket = expected_bucket.clone();
19182 let mut thread_state = alloc_thread_state(4).unwrap();
19183
19184 final_gsa_scan_right_to_left_8u(
19185 &t,
19186 &mut expected_sa,
19187 &mut expected_bucket,
19188 block_start as FastSint,
19189 block_size as FastSint,
19190 );
19191 final_gsa_scan_right_to_left_8u_block_omp(
19192 &t,
19193 &mut threaded_sa,
19194 ALPHABET_SIZE as SaSint,
19195 &mut threaded_bucket,
19196 block_start as FastSint,
19197 block_size as FastSint,
19198 4,
19199 &mut thread_state,
19200 );
19201
19202 assert_eq!(threaded_sa, expected_sa);
19203 assert_eq!(threaded_bucket, expected_bucket);
19204 }
19205
19206 #[test]
19207 fn final_sorting_scan_right_to_left_8u_omp_matches_sequential_path() {
19208 let t = vec![0_u8, 1, 2, 1, 0];
19209 let mut sa = vec![0, 2, 0, 0];
19210 let mut induction_bucket = vec![1, 2, 3];
19211 let mut expected_sa = sa.clone();
19212 let mut expected_bucket = induction_bucket.clone();
19213
19214 final_sorting_scan_right_to_left_8u_omp(
19215 &t,
19216 &mut expected_sa,
19217 0,
19218 2,
19219 ALPHABET_SIZE as SaSint,
19220 &mut expected_bucket,
19221 1,
19222 &mut [],
19223 );
19224
19225 let mut thread_state = alloc_thread_state(2).unwrap();
19226 final_sorting_scan_right_to_left_8u_omp(
19227 &t,
19228 &mut sa,
19229 0,
19230 2,
19231 ALPHABET_SIZE as SaSint,
19232 &mut induction_bucket,
19233 2,
19234 &mut thread_state,
19235 );
19236
19237 assert_eq!(sa, expected_sa);
19238 assert_eq!(induction_bucket, expected_bucket);
19239 }
19240
19241 #[test]
19242 fn clear_lms_suffixes_omp_zeroes_requested_bucket_ranges() {
19243 let mut sa = vec![5, 4, 3, 2, 1, 9];
19244 let n = sa.len() as SaSint;
19245 let bucket_start = vec![1, 4, 5];
19246 let bucket_end = vec![3, 5, 5];
19247
19248 clear_lms_suffixes_omp(&mut sa, n, 3, &bucket_start, &bucket_end, 2);
19249
19250 assert_eq!(sa, vec![5, 0, 0, 2, 0, 9]);
19251 }
19252
19253 #[test]
19254 fn induce_final_order_8u_omp_non_bwt_matches_direct_final_scans() {
19255 let t = vec![0_u8, 1, 2, 1, 0];
19256 let mut sa = vec![0, 2, 0, 0, 0];
19257 let mut buckets = vec![0; 8 * ALPHABET_SIZE];
19258 buckets[6 * ALPHABET_SIZE..6 * ALPHABET_SIZE + 3].copy_from_slice(&[0, 1, 3]);
19259 buckets[7 * ALPHABET_SIZE..7 * ALPHABET_SIZE + 3].copy_from_slice(&[2, 4, 5]);
19260
19261 let mut expected_sa = sa.clone();
19262 let mut expected_left = vec![0, 1, 3];
19263 let mut expected_right = vec![2, 4, 5];
19264 final_sorting_scan_left_to_right_8u_omp(
19265 &t,
19266 &mut expected_sa,
19267 t.len() as FastSint,
19268 ALPHABET_SIZE as SaSint,
19269 &mut expected_left,
19270 1,
19271 &mut [],
19272 );
19273 final_sorting_scan_right_to_left_8u_omp(
19274 &t,
19275 &mut expected_sa,
19276 0,
19277 t.len() as FastSint,
19278 ALPHABET_SIZE as SaSint,
19279 &mut expected_right,
19280 1,
19281 &mut [],
19282 );
19283
19284 let mut thread_state = alloc_thread_state(2).unwrap();
19285 let result = induce_final_order_8u_omp(
19286 &t,
19287 &mut sa,
19288 t.len() as SaSint,
19289 ALPHABET_SIZE as SaSint,
19290 LIBSAIS_FLAGS_NONE,
19291 0,
19292 None,
19293 &mut buckets,
19294 2,
19295 &mut thread_state,
19296 );
19297
19298 assert_eq!(result, 0);
19299 assert_eq!(sa, expected_sa);
19300 assert_eq!(
19301 &buckets[6 * ALPHABET_SIZE..6 * ALPHABET_SIZE + 3],
19302 expected_left.as_slice()
19303 );
19304 assert_eq!(
19305 &buckets[7 * ALPHABET_SIZE..7 * ALPHABET_SIZE + 3],
19306 expected_right.as_slice()
19307 );
19308 }
19309
19310 #[test]
19311 fn renumber_unique_and_nonunique_lms_suffixes_32s_marks_new_unique_names() {
19312 let mut t = vec![0, 0, 0, 0];
19313 let mut sa = vec![0, 2, -1, 5];
19314
19315 let f = renumber_unique_and_nonunique_lms_suffixes_32s(&mut t, &mut sa, 2, 0, 0, 2);
19316
19317 assert_eq!(f, 1);
19318 assert_eq!(t[0], SAINT_MIN);
19319 assert_eq!(sa[2], SAINT_MIN);
19320 assert_eq!(sa[3], 4);
19321 }
19322
19323 #[test]
19324 fn renumber_unique_and_nonunique_lms_suffixes_32s_matches_upstream_c_helper() {
19325 let mut t_rust = vec![0, 0, 0, 0];
19326 let mut sa_rust = vec![0, 2, -1, 5];
19327 let mut t_c = t_rust.clone();
19328 let mut sa_c = sa_rust.clone();
19329
19330 let rust_f =
19331 renumber_unique_and_nonunique_lms_suffixes_32s(&mut t_rust, &mut sa_rust, 2, 0, 0, 2);
19332 let c_f = unsafe {
19333 probe_renumber_unique_and_nonunique_lms_suffixes_32s(
19334 t_c.as_mut_ptr(),
19335 sa_c.as_mut_ptr(),
19336 2,
19337 0,
19338 0,
19339 2,
19340 )
19341 };
19342
19343 assert_eq!(rust_f, c_f);
19344 assert_eq!(t_rust, t_c);
19345 assert_eq!(sa_rust, sa_c);
19346 }
19347
19348 #[test]
19349 fn renumber_unique_and_nonunique_lms_suffixes_32s_omp_matches_upstream_c_helper() {
19350 let mut t_rust = vec![0, 0, 0, 0];
19351 let mut sa_rust = vec![0, 2, -1, 5];
19352 let mut t_c = t_rust.clone();
19353 let mut sa_c = sa_rust.clone();
19354 let mut thread_state = alloc_thread_state(1).unwrap();
19355
19356 let rust_f = renumber_unique_and_nonunique_lms_suffixes_32s_omp(
19357 &mut t_rust,
19358 &mut sa_rust,
19359 2,
19360 1,
19361 &mut thread_state,
19362 );
19363 let c_f = unsafe {
19364 probe_renumber_unique_and_nonunique_lms_suffixes_32s_omp(
19365 t_c.as_mut_ptr(),
19366 sa_c.as_mut_ptr(),
19367 2,
19368 1,
19369 )
19370 };
19371
19372 assert_eq!(rust_f, c_f);
19373 assert_eq!(t_rust, t_c);
19374 assert_eq!(sa_rust, sa_c);
19375 }
19376
19377 #[test]
19378 fn renumber_unique_and_nonunique_lms_suffixes_32s_omp_uses_block_partition() {
19379 let m = 65_600usize;
19380 let n = 2 * m;
19381 let t = vec![0; n];
19382 let mut sa = vec![0; n];
19383 for i in 0..m {
19384 sa[i] = (2 * i) as SaSint;
19385 sa[m + i] = if i % 5 == 0 {
19386 -((i as SaSint) + 1)
19387 } else {
19388 i as SaSint + 7
19389 };
19390 }
19391
19392 let mut single_t = t.clone();
19393 let mut single_sa = sa.clone();
19394 let mut threaded_t = t;
19395 let mut threaded_sa = sa;
19396 let mut thread_state = alloc_thread_state(4).unwrap();
19397 let single_f = renumber_unique_and_nonunique_lms_suffixes_32s(
19398 &mut single_t,
19399 &mut single_sa,
19400 m as SaSint,
19401 0,
19402 0,
19403 m as FastSint,
19404 );
19405 let threaded_f = renumber_unique_and_nonunique_lms_suffixes_32s_omp(
19406 &mut threaded_t,
19407 &mut threaded_sa,
19408 m as SaSint,
19409 4,
19410 &mut thread_state,
19411 );
19412
19413 assert_eq!(threaded_f, single_f);
19414 assert_eq!(threaded_t, single_t);
19415 assert_eq!(threaded_sa, single_sa);
19416 }
19417
19418 #[test]
19419 fn compact_unique_and_nonunique_lms_suffixes_32s_splits_unique_and_nonunique_ranges() {
19420 let mut sa = vec![0, 0, 0, 0, SAINT_MIN, 4];
19421 let mut l = 2;
19422 let mut r = 6;
19423
19424 compact_unique_and_nonunique_lms_suffixes_32s(&mut sa, 2, &mut l, &mut r, 0, 2);
19425
19426 assert_eq!(l, 2);
19427 assert_eq!(r, 6);
19428 assert_eq!(sa[2], 0);
19429 assert_eq!(sa[3] & SAINT_MAX, 0);
19430 }
19431
19432 #[test]
19433 fn compact_lms_suffixes_32s_omp_runs_renumber_then_compaction() {
19434 let mut t = vec![0, 0, 0, 0];
19435 let mut sa = vec![0, 2, -1, 5, 77, 88];
19436 let mut thread_state = alloc_thread_state(2).unwrap();
19437
19438 let f = compact_lms_suffixes_32s_omp(&mut t, &mut sa, 4, 2, 2, 2, &mut thread_state);
19439
19440 assert_eq!(f, 1);
19441 assert_eq!(sa[2] & SAINT_MAX, 0);
19442 assert_eq!(sa[5], 3);
19443 }
19444
19445 #[test]
19446 fn compact_unique_and_nonunique_lms_suffixes_32s_omp_uses_block_partition() {
19447 let n = 131_200usize;
19448 let m = 65_600usize;
19449 let fs = m + 32;
19450 let half_n = n >> 1;
19451 let f = m / 5;
19452 let mut sa = vec![0; n + fs];
19453 for i in 0..half_n {
19454 sa[m + i] = if i % 5 == 0 {
19455 SAINT_MIN | i as SaSint
19456 } else {
19457 i as SaSint + 1
19458 };
19459 }
19460 for i in 0..f {
19461 sa[m - f + i] = (10_000 + i) as SaSint;
19462 }
19463
19464 let mut single = sa.clone();
19465 let mut threaded = sa;
19466 let mut single_state = alloc_thread_state(1).unwrap();
19467 let mut threaded_state = alloc_thread_state(4).unwrap();
19468 compact_unique_and_nonunique_lms_suffixes_32s_omp(
19469 &mut single,
19470 n as SaSint,
19471 m as SaSint,
19472 fs as SaSint,
19473 f as SaSint,
19474 1,
19475 &mut single_state,
19476 );
19477 compact_unique_and_nonunique_lms_suffixes_32s_omp(
19478 &mut threaded,
19479 n as SaSint,
19480 m as SaSint,
19481 fs as SaSint,
19482 f as SaSint,
19483 4,
19484 &mut threaded_state,
19485 );
19486
19487 let unique_dst = n + fs - m;
19488 assert_eq!(
19489 &threaded[unique_dst..unique_dst + f],
19490 &single[unique_dst..unique_dst + f]
19491 );
19492 }
19493
19494 #[test]
19495 fn compact_lms_suffixes_32s_omp_uses_large_input_paths() {
19496 let n = 131_200usize;
19497 let m = 65_600usize;
19498 let fs = m + 32;
19499 let t = vec![0; n];
19500 let mut sa = vec![0; n + fs];
19501 for i in 0..m {
19502 sa[i] = (2 * i) as SaSint;
19503 sa[m + i] = if i % 5 == 0 {
19504 -((i as SaSint) + 1)
19505 } else {
19506 i as SaSint + 7
19507 };
19508 }
19509
19510 let mut single_t = t.clone();
19511 let mut single_sa = sa.clone();
19512 let mut threaded_t = t;
19513 let mut threaded_sa = sa;
19514 let mut single_state = alloc_thread_state(1).unwrap();
19515 let mut threaded_state = alloc_thread_state(4).unwrap();
19516 let single_f = compact_lms_suffixes_32s_omp(
19517 &mut single_t,
19518 &mut single_sa,
19519 n as SaSint,
19520 m as SaSint,
19521 fs as SaSint,
19522 1,
19523 &mut single_state,
19524 );
19525 let threaded_f = compact_lms_suffixes_32s_omp(
19526 &mut threaded_t,
19527 &mut threaded_sa,
19528 n as SaSint,
19529 m as SaSint,
19530 fs as SaSint,
19531 4,
19532 &mut threaded_state,
19533 );
19534
19535 assert_eq!(threaded_f, single_f);
19536 assert_eq!(threaded_t, single_t);
19537 let unique_dst = n + fs - m;
19538 let unique_len = usize::try_from(threaded_f).expect("f must be non-negative");
19539 assert_eq!(
19540 &threaded_sa[unique_dst..unique_dst + unique_len],
19541 &single_sa[unique_dst..unique_dst + unique_len]
19542 );
19543 }
19544
19545 #[test]
19546 fn merge_unique_lms_suffixes_32s_noops_for_empty_block() {
19547 let mut t = vec![1, SAINT_MIN, 2, SAINT_MIN];
19548 let mut sa = vec![0, 0, 1, 3];
19549 let before_t = t.clone();
19550 let before_sa = sa.clone();
19551
19552 merge_unique_lms_suffixes_32s(&mut t, &mut sa, 4, 1, 0, 0, 0);
19553
19554 assert_eq!(t, before_t);
19555 assert_eq!(sa, before_sa);
19556 }
19557
19558 #[test]
19559 fn merge_nonunique_lms_suffixes_32s_noops_for_empty_block() {
19560 let mut sa = vec![0, 7, 0, 13, 11];
19561 let before = sa.clone();
19562
19563 merge_nonunique_lms_suffixes_32s(&mut sa, 4, 1, 0, 0, 0);
19564
19565 assert_eq!(sa, before);
19566 }
19567
19568 #[test]
19569 fn merge_compacted_lms_suffixes_32s_omp_preserves_input_text_and_fills_zero_slots() {
19570 let mut t = vec![1, 2, 3, 4];
19571 let mut sa = vec![0, 1, 2, 3, 4, 5];
19572 let before_t = t.clone();
19573 let mut thread_state = alloc_thread_state(2).unwrap();
19574
19575 merge_compacted_lms_suffixes_32s_omp(&mut t, &mut sa, 4, 1, 1, 2, &mut thread_state);
19576
19577 assert_eq!(t, before_t);
19578 assert_eq!(sa[0], 3);
19579 assert_eq!(sa[1], 1);
19580 }
19581
19582 #[test]
19583 fn merge_unique_lms_suffixes_32s_omp_uses_block_partition_for_large_inputs() {
19584 let n = 65_600usize;
19585 let m = 1_024usize;
19586 let mut t = vec![1; n];
19587 for i in (0..n).step_by(257) {
19588 t[i] = SAINT_MIN | ((i % 251) as SaSint);
19589 }
19590 let f = t.iter().filter(|&&value| value < 0).count();
19591 let mut sa = vec![-1; n];
19592 let src = n - m - 1;
19593 for i in 0..f {
19594 sa[src + i] = i as SaSint;
19595 }
19596
19597 let mut single_t = t.clone();
19598 let mut single_sa = sa.clone();
19599 let mut threaded_t = t;
19600 let mut threaded_sa = sa;
19601 let mut thread_state = alloc_thread_state(4).unwrap();
19602 merge_unique_lms_suffixes_32s_omp(
19603 &mut single_t,
19604 &mut single_sa,
19605 n as SaSint,
19606 m as SaSint,
19607 1,
19608 &mut [],
19609 );
19610 merge_unique_lms_suffixes_32s_omp(
19611 &mut threaded_t,
19612 &mut threaded_sa,
19613 n as SaSint,
19614 m as SaSint,
19615 4,
19616 &mut thread_state,
19617 );
19618
19619 assert_eq!(threaded_t, single_t);
19620 assert_eq!(threaded_sa, single_sa);
19621 }
19622
19623 #[test]
19624 fn merge_nonunique_lms_suffixes_32s_omp_uses_block_partition_for_large_inputs() {
19625 let n = 131_200usize;
19626 let m = 65_600usize;
19627 let f = 7usize;
19628 let mut sa = vec![1; n];
19629 let zero_count = (0..m).filter(|i| i % 17 == 0).count();
19630 for i in (0..m).step_by(17) {
19631 sa[i] = 0;
19632 }
19633 let src = n - m - 1 + f;
19634 for i in 0..zero_count {
19635 sa[src + i] = 10_000 + i as SaSint;
19636 }
19637
19638 let mut single = sa.clone();
19639 let mut threaded = sa;
19640 let mut thread_state = alloc_thread_state(4).unwrap();
19641 merge_nonunique_lms_suffixes_32s_omp(
19642 &mut single,
19643 n as SaSint,
19644 m as SaSint,
19645 f as SaSint,
19646 1,
19647 &mut [],
19648 );
19649 merge_nonunique_lms_suffixes_32s_omp(
19650 &mut threaded,
19651 n as SaSint,
19652 m as SaSint,
19653 f as SaSint,
19654 4,
19655 &mut thread_state,
19656 );
19657
19658 assert_eq!(threaded, single);
19659 }
19660
19661 #[test]
19662 fn merge_compacted_lms_suffixes_32s_omp_uses_block_partition_for_large_inputs() {
19663 let n = 131_200usize;
19664 let m = 65_600usize;
19665 let mut t = vec![1; n];
19666 for i in (0..n).step_by(257) {
19667 t[i] = SAINT_MIN | ((i % 251) as SaSint);
19668 }
19669 let f = t.iter().filter(|&&value| value < 0).count();
19670
19671 let mut sa = vec![1; n];
19672 let zero_count = (0..m).filter(|i| i % 17 == 0).count();
19673 for i in (0..m).step_by(17) {
19674 sa[i] = 0;
19675 }
19676 let unique_src = n - m - 1;
19677 for i in 0..f {
19678 sa[unique_src + i] = i as SaSint;
19679 }
19680 for i in 0..zero_count {
19681 sa[unique_src + f + i] = 10_000 + i as SaSint;
19682 }
19683
19684 let mut single_t = t.clone();
19685 let mut single_sa = sa.clone();
19686 let mut threaded_t = t;
19687 let mut threaded_sa = sa;
19688 let mut single_state = alloc_thread_state(1).unwrap();
19689 let mut threaded_state = alloc_thread_state(4).unwrap();
19690 merge_compacted_lms_suffixes_32s_omp(
19691 &mut single_t,
19692 &mut single_sa,
19693 n as SaSint,
19694 m as SaSint,
19695 f as SaSint,
19696 1,
19697 &mut single_state,
19698 );
19699 merge_compacted_lms_suffixes_32s_omp(
19700 &mut threaded_t,
19701 &mut threaded_sa,
19702 n as SaSint,
19703 m as SaSint,
19704 f as SaSint,
19705 4,
19706 &mut threaded_state,
19707 );
19708
19709 assert_eq!(threaded_t, single_t);
19710 assert_eq!(threaded_sa, single_sa);
19711 }
19712
19713 #[test]
19714 fn bwt_copy_8u_copies_low_bytes_from_suffix_array_storage() {
19715 let a = vec![65, 255, 256, -1];
19716 let mut u = vec![0_u8; 4];
19717
19718 bwt_copy_8u(&mut u, &a, 4);
19719
19720 assert_eq!(u, vec![65, 255, 0, 255]);
19721 }
19722
19723 #[test]
19724 fn bwt_copy_8u_omp_matches_sequential_copy() {
19725 let a = vec![1, 2, 3, 4, 5];
19726 let mut u = vec![0_u8; 5];
19727
19728 bwt_copy_8u_omp(&mut u, &a, 5, 4);
19729
19730 assert_eq!(u, vec![1, 2, 3, 4, 5]);
19731 }
19732
19733 #[test]
19734 fn bwt_copy_8u_omp_uses_block_partition_for_large_inputs() {
19735 let n = 65_600usize;
19736 let a: Vec<SaSint> = (0..n).map(|i| (i * 17) as SaSint).collect();
19737 let mut threaded = vec![0; n];
19738 let mut sequential = vec![0; n];
19739
19740 bwt_copy_8u_omp(&mut threaded, &a, n as SaSint, 4);
19741 bwt_copy_8u(&mut sequential, &a, n as SaSint);
19742
19743 assert_eq!(threaded, sequential);
19744 }
19745
19746 #[test]
19747 fn plcp_lcp_omp_wrappers_match_single_thread_on_large_inputs() {
19748 let n = 65_600usize;
19749 let text: Vec<u8> = (0..n).map(|i| (1 + (i % 251)) as u8).collect();
19750 let sa: Vec<SaSint> = (0..n as SaSint).collect();
19751
19752 let mut plcp_single = vec![0; n];
19753 let mut plcp_threaded = vec![0; n];
19754 compute_phi_omp(&sa, &mut plcp_single, n as SaSint, 1);
19755 compute_phi_omp(&sa, &mut plcp_threaded, n as SaSint, 4);
19756 assert_eq!(plcp_threaded, plcp_single);
19757
19758 compute_plcp_omp(&text, &mut plcp_single, n as SaSint, 1);
19759 compute_plcp_omp(&text, &mut plcp_threaded, n as SaSint, 4);
19760 assert_eq!(plcp_threaded, plcp_single);
19761
19762 let mut lcp_single = vec![0; n];
19763 let mut lcp_threaded = vec![0; n];
19764 compute_lcp_omp(&plcp_single, &sa, &mut lcp_single, n as SaSint, 1);
19765 compute_lcp_omp(&plcp_threaded, &sa, &mut lcp_threaded, n as SaSint, 4);
19766 assert_eq!(lcp_threaded, lcp_single);
19767 }
19768
19769 #[test]
19770 fn count_and_gather_lms_suffixes_8u_omp_preserves_sequential_wrapper_behavior() {
19771 let t = vec![2_u8, 1, 3, 1, 0];
19772 let mut sa = vec![0; t.len()];
19773 let mut buckets = vec![0; 4 * ALPHABET_SIZE];
19774 let mut thread_state = alloc_thread_state(2).unwrap();
19775 let m = count_and_gather_lms_suffixes_8u_omp(
19776 &t,
19777 &mut sa,
19778 t.len() as SaSint,
19779 &mut buckets,
19780 2,
19781 &mut thread_state,
19782 );
19783 assert_eq!(m, 1);
19784 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
19785 }
19786
19787 #[test]
19788 fn count_and_gather_lms_suffixes_8u_omp_uses_block_partition_for_large_inputs() {
19789 let n = 65_600usize;
19790 let text: Vec<u8> = (0..n)
19791 .map(|i| 1 + ((i * 37 + i / 17) % 251) as u8)
19792 .collect();
19793
19794 let mut sa_threaded = vec![-99; n];
19795 let mut sa_scalar = vec![-99; n];
19796 let mut buckets_threaded = vec![0; 4 * ALPHABET_SIZE];
19797 let mut buckets_scalar = vec![0; 4 * ALPHABET_SIZE];
19798 let mut thread_state = alloc_thread_state(4).unwrap();
19799
19800 let m_threaded = count_and_gather_lms_suffixes_8u_omp(
19801 &text,
19802 &mut sa_threaded,
19803 n as SaSint,
19804 &mut buckets_threaded,
19805 4,
19806 &mut thread_state,
19807 );
19808 let m_scalar = count_and_gather_lms_suffixes_8u(
19809 &text,
19810 &mut sa_scalar,
19811 n as SaSint,
19812 &mut buckets_scalar,
19813 0,
19814 n as FastSint,
19815 );
19816
19817 assert_eq!(m_threaded, m_scalar);
19818 assert_eq!(
19819 &sa_threaded[n - m_threaded as usize..],
19820 &sa_scalar[n - m_scalar as usize..]
19821 );
19822 assert_eq!(buckets_threaded, buckets_scalar);
19823 }
19824
19825 #[test]
19826 fn gather_lms_suffixes_8u_omp_uses_thread_state_for_large_inputs() {
19827 let n = 65_600usize;
19828 let text: Vec<u8> = (0..n)
19829 .map(|i| 1 + ((i * 37 + i / 17) % 251) as u8)
19830 .collect();
19831 let mut thread_state = alloc_thread_state(4).unwrap();
19832 let mut count_sa = vec![-99; n];
19833 let mut buckets = vec![0; 4 * ALPHABET_SIZE];
19834 let m = count_and_gather_lms_suffixes_8u_omp(
19835 &text,
19836 &mut count_sa,
19837 n as SaSint,
19838 &mut buckets,
19839 4,
19840 &mut thread_state,
19841 );
19842
19843 let mut threaded = vec![-99; n];
19844 let mut scalar = vec![-99; n];
19845 gather_lms_suffixes_8u_omp(&text, &mut threaded, n as SaSint, 4, &mut thread_state);
19846 gather_lms_suffixes_8u(
19847 &text,
19848 &mut scalar,
19849 n as SaSint,
19850 n as FastSint - 1,
19851 0,
19852 n as FastSint,
19853 );
19854
19855 assert_eq!(&threaded[n - m as usize..], &scalar[n - m as usize..]);
19856 }
19857
19858 #[test]
19859 fn count_and_gather_lms_suffixes_32s_4k_updates_counts_and_suffixes() {
19860 let t = vec![2, 1, 3, 1, 0];
19861 let mut sa = vec![0; t.len()];
19862 let mut buckets = vec![0; 4 * 4];
19863 let m = count_and_gather_lms_suffixes_32s_4k(
19864 &t,
19865 &mut sa,
19866 t.len() as SaSint,
19867 4,
19868 &mut buckets,
19869 0,
19870 t.len() as FastSint,
19871 );
19872 assert!(m >= 0);
19873 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
19874 }
19875
19876 #[test]
19877 fn count_and_gather_lms_suffixes_32s_2k_updates_counts_and_suffixes() {
19878 let t = vec![2, 1, 3, 1, 0];
19879 let mut sa = vec![0; t.len()];
19880 let mut buckets = vec![0; 2 * 4];
19881 let m = count_and_gather_lms_suffixes_32s_2k(
19882 &t,
19883 &mut sa,
19884 t.len() as SaSint,
19885 4,
19886 &mut buckets,
19887 0,
19888 t.len() as FastSint,
19889 );
19890 assert!(m >= 0);
19891 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
19892 }
19893
19894 #[test]
19895 fn count_and_gather_compacted_lms_suffixes_32s_2k_updates_counts_and_suffixes() {
19896 let t = vec![2, SAINT_MIN | 1, 3, 1, 0];
19897 let mut sa = vec![0; t.len()];
19898 let mut buckets = vec![0; 2 * 4];
19899 let m = count_and_gather_compacted_lms_suffixes_32s_2k(
19900 &t,
19901 &mut sa,
19902 t.len() as SaSint,
19903 4,
19904 &mut buckets,
19905 0,
19906 t.len() as FastSint,
19907 );
19908 assert!(m >= 0);
19909 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
19910 }
19911
19912 #[test]
19913 fn count_and_gather_lms_suffixes_32s_4k_nofs_omp_wraps_sequential_version() {
19914 let t = vec![2, 1, 3, 1, 0];
19915 let mut sa = vec![0; t.len()];
19916 let mut buckets = vec![0; 4 * 4];
19917 let m = count_and_gather_lms_suffixes_32s_4k_nofs_omp(
19918 &t,
19919 &mut sa,
19920 t.len() as SaSint,
19921 4,
19922 &mut buckets,
19923 2,
19924 );
19925 assert!(m >= 0);
19926 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
19927 }
19928
19929 #[test]
19930 fn count_and_gather_lms_suffixes_32s_2k_nofs_omp_wraps_sequential_version() {
19931 let t = vec![2, 1, 3, 1, 0];
19932 let mut sa = vec![0; t.len()];
19933 let mut buckets = vec![0; 2 * 4];
19934 let m = count_and_gather_lms_suffixes_32s_2k_nofs_omp(
19935 &t,
19936 &mut sa,
19937 t.len() as SaSint,
19938 4,
19939 &mut buckets,
19940 2,
19941 );
19942 assert!(m >= 0);
19943 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
19944 }
19945
19946 #[test]
19947 fn count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp_wraps_sequential_version() {
19948 let t = vec![2, SAINT_MIN | 1, 3, 1, 0];
19949 let mut sa = vec![0; t.len()];
19950 let mut buckets = vec![0; 2 * 4];
19951 let m = count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(
19952 &t,
19953 &mut sa,
19954 t.len() as SaSint,
19955 4,
19956 &mut buckets,
19957 2,
19958 );
19959 assert!(m >= 0);
19960 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
19961 }
19962
19963 #[test]
19964 fn count_and_gather_lms_suffixes_32s_nofs_omp_uses_large_input_paths() {
19965 let n = 65_600usize;
19966 let k = 257usize;
19967 let text: Vec<SaSint> = (0..n)
19968 .map(|i| 1 + ((i * 37 + i / 17) % (k - 1)) as SaSint)
19969 .collect();
19970
19971 let mut sa_threaded = vec![-99; n];
19972 let mut sa_scalar = vec![-99; n];
19973 let mut buckets_threaded = vec![0; 4 * k];
19974 let mut buckets_scalar = vec![0; 4 * k];
19975 let m_threaded = count_and_gather_lms_suffixes_32s_4k_nofs_omp(
19976 &text,
19977 &mut sa_threaded,
19978 n as SaSint,
19979 k as SaSint,
19980 &mut buckets_threaded,
19981 4,
19982 );
19983 let m_scalar = count_and_gather_lms_suffixes_32s_4k(
19984 &text,
19985 &mut sa_scalar,
19986 n as SaSint,
19987 k as SaSint,
19988 &mut buckets_scalar,
19989 0,
19990 n as FastSint,
19991 );
19992 assert_eq!(m_threaded, m_scalar);
19993 assert_eq!(
19994 &sa_threaded[n - m_threaded as usize..],
19995 &sa_scalar[n - m_scalar as usize..]
19996 );
19997 assert_eq!(buckets_threaded, buckets_scalar);
19998
19999 let mut sa_threaded = vec![-99; n];
20000 let mut sa_scalar = vec![-99; n];
20001 let mut buckets_threaded = vec![0; 2 * k];
20002 let mut buckets_scalar = vec![0; 2 * k];
20003 let m_threaded = count_and_gather_lms_suffixes_32s_2k_nofs_omp(
20004 &text,
20005 &mut sa_threaded,
20006 n as SaSint,
20007 k as SaSint,
20008 &mut buckets_threaded,
20009 4,
20010 );
20011 let m_scalar = count_and_gather_lms_suffixes_32s_2k(
20012 &text,
20013 &mut sa_scalar,
20014 n as SaSint,
20015 k as SaSint,
20016 &mut buckets_scalar,
20017 0,
20018 n as FastSint,
20019 );
20020 assert_eq!(m_threaded, m_scalar);
20021 assert_eq!(
20022 &sa_threaded[n - m_threaded as usize..],
20023 &sa_scalar[n - m_scalar as usize..]
20024 );
20025 assert_eq!(buckets_threaded, buckets_scalar);
20026 }
20027
20028 #[test]
20029 fn count_and_gather_lms_suffixes_32s_fs_omp_uses_large_input_paths() {
20030 let n = 65_600usize;
20031 let k = 257usize;
20032 let text: Vec<SaSint> = (0..n)
20033 .map(|i| 1 + ((i * 37 + i / 17) % (k - 1)) as SaSint)
20034 .collect();
20035 let mut thread_state = alloc_thread_state(4).unwrap();
20036
20037 let mut sa_threaded = vec![-99; n];
20038 let mut sa_scalar = vec![-99; n];
20039 let mut buckets_threaded = vec![0; 4 * k];
20040 let mut buckets_scalar = vec![0; 4 * k];
20041 let m_threaded = count_and_gather_lms_suffixes_32s_4k_fs_omp(
20042 &text,
20043 &mut sa_threaded,
20044 n as SaSint,
20045 k as SaSint,
20046 &mut buckets_threaded,
20047 0,
20048 4,
20049 &mut thread_state,
20050 );
20051 let m_scalar = count_and_gather_lms_suffixes_32s_4k(
20052 &text,
20053 &mut sa_scalar,
20054 n as SaSint,
20055 k as SaSint,
20056 &mut buckets_scalar,
20057 0,
20058 n as FastSint,
20059 );
20060 assert_eq!(m_threaded, m_scalar);
20061 assert_eq!(
20062 &sa_threaded[n - m_threaded as usize..],
20063 &sa_scalar[n - m_scalar as usize..]
20064 );
20065 assert_eq!(buckets_threaded, buckets_scalar);
20066
20067 let mut sa_threaded = vec![-99; n];
20068 let mut sa_scalar = vec![-99; n];
20069 let mut buckets_threaded = vec![0; 2 * k];
20070 let mut buckets_scalar = vec![0; 2 * k];
20071 let m_threaded = count_and_gather_lms_suffixes_32s_2k_fs_omp(
20072 &text,
20073 &mut sa_threaded,
20074 n as SaSint,
20075 k as SaSint,
20076 &mut buckets_threaded,
20077 0,
20078 4,
20079 &mut thread_state,
20080 );
20081 let m_scalar = count_and_gather_lms_suffixes_32s_2k(
20082 &text,
20083 &mut sa_scalar,
20084 n as SaSint,
20085 k as SaSint,
20086 &mut buckets_scalar,
20087 0,
20088 n as FastSint,
20089 );
20090 assert_eq!(m_threaded, m_scalar);
20091 assert_eq!(
20092 &sa_threaded[n - m_threaded as usize..],
20093 &sa_scalar[n - m_scalar as usize..]
20094 );
20095 assert_eq!(buckets_threaded, buckets_scalar);
20096 }
20097
20098 #[test]
20099 fn count_and_gather_compacted_lms_suffixes_32s_nofs_omp_uses_large_input_path() {
20100 let n = 65_600usize;
20101 let k = 257usize;
20102 let text: Vec<SaSint> = (0..n)
20103 .map(|i| {
20104 let value = 1 + ((i * 37 + i / 17) % (k - 1)) as SaSint;
20105 if i % 19 == 0 {
20106 value | SAINT_MIN
20107 } else {
20108 value
20109 }
20110 })
20111 .collect();
20112
20113 let mut sa_threaded = vec![-99; n];
20114 let mut sa_split = vec![-99; n];
20115 let mut buckets_threaded = vec![0; 2 * k];
20116 let mut buckets_split = vec![0; 2 * k];
20117 let m_threaded = count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(
20118 &text,
20119 &mut sa_threaded,
20120 n as SaSint,
20121 k as SaSint,
20122 &mut buckets_threaded,
20123 4,
20124 );
20125 count_compacted_lms_suffixes_32s_2k(&text, n as SaSint, k as SaSint, &mut buckets_split);
20126 let m_split = gather_compacted_lms_suffixes_32s(&text, &mut sa_split, n as SaSint);
20127
20128 assert_eq!(m_threaded, m_split);
20129 assert_eq!(
20130 &sa_threaded[n - m_threaded as usize..],
20131 &sa_split[n - m_split as usize..]
20132 );
20133 assert_eq!(buckets_threaded, buckets_split);
20134 }
20135
20136 #[test]
20137 fn count_and_gather_compacted_lms_suffixes_32s_fs_omp_uses_large_input_path() {
20138 let n = 65_600usize;
20139 let k = 257usize;
20140 let text: Vec<SaSint> = (0..n)
20141 .map(|i| {
20142 let value = 1 + ((i * 37 + i / 17) % (k - 1)) as SaSint;
20143 if i % 19 == 0 {
20144 value | SAINT_MIN
20145 } else {
20146 value
20147 }
20148 })
20149 .collect();
20150
20151 let mut sa_threaded = vec![-99; 2 * n];
20152 let mut sa_scalar = vec![-99; n];
20153 let mut buckets_threaded = vec![0; 2 * k];
20154 let mut buckets_scalar = vec![0; 2 * k];
20155 let mut thread_state = alloc_thread_state(4).unwrap();
20156 count_and_gather_compacted_lms_suffixes_32s_2k_fs_omp(
20157 &text,
20158 &mut sa_threaded,
20159 n as SaSint,
20160 k as SaSint,
20161 &mut buckets_threaded,
20162 0,
20163 4,
20164 &mut thread_state,
20165 );
20166 let m_scalar = count_and_gather_compacted_lms_suffixes_32s_2k(
20167 &text,
20168 &mut sa_scalar,
20169 n as SaSint,
20170 k as SaSint,
20171 &mut buckets_scalar,
20172 0,
20173 n as FastSint,
20174 );
20175
20176 assert_eq!(
20177 &sa_threaded[n - m_scalar as usize..n],
20178 &sa_scalar[n - m_scalar as usize..]
20179 );
20180 assert_eq!(buckets_threaded, buckets_scalar);
20181 }
20182
20183 #[test]
20184 fn accumulate_counts_helpers_match_prefix_bucket_addition() {
20185 let mut bucket00 = vec![4, 5, 6];
20186 let bucket01 = vec![1, 2, 3];
20187 let bucket02 = vec![7, 8, 9];
20188 let bucket03 = vec![10, 11, 12];
20189 let bucket04 = vec![13, 14, 15];
20190 let bucket05 = vec![16, 17, 18];
20191 let bucket06 = vec![19, 20, 21];
20192 let bucket07 = vec![22, 23, 24];
20193 let bucket08 = vec![25, 26, 27];
20194
20195 accumulate_counts_s32_2(&mut bucket00, &bucket01);
20196 assert_eq!(bucket00, vec![5, 7, 9]);
20197
20198 accumulate_counts_s32_3(&mut bucket00, &bucket01, &bucket02);
20199 assert_eq!(bucket00, vec![13, 17, 21]);
20200
20201 accumulate_counts_s32_4(&mut bucket00, &bucket01, &bucket02, &bucket03);
20202 assert_eq!(bucket00, vec![31, 38, 45]);
20203
20204 accumulate_counts_s32_5(&mut bucket00, &bucket01, &bucket02, &bucket03, &bucket04);
20205 assert_eq!(bucket00, vec![62, 73, 84]);
20206
20207 accumulate_counts_s32_6(
20208 &mut bucket00,
20209 &bucket01,
20210 &bucket02,
20211 &bucket03,
20212 &bucket04,
20213 &bucket05,
20214 );
20215 assert_eq!(bucket00, vec![109, 125, 141]);
20216
20217 accumulate_counts_s32_7(
20218 &mut bucket00,
20219 &bucket01,
20220 &bucket02,
20221 &bucket03,
20222 &bucket04,
20223 &bucket05,
20224 &bucket06,
20225 );
20226 assert_eq!(bucket00, vec![175, 197, 219]);
20227
20228 accumulate_counts_s32_8(
20229 &mut bucket00,
20230 &bucket01,
20231 &bucket02,
20232 &bucket03,
20233 &bucket04,
20234 &bucket05,
20235 &bucket06,
20236 &bucket07,
20237 );
20238 assert_eq!(bucket00, vec![263, 292, 321]);
20239
20240 accumulate_counts_s32_9(
20241 &mut bucket00,
20242 &bucket01,
20243 &bucket02,
20244 &bucket03,
20245 &bucket04,
20246 &bucket05,
20247 &bucket06,
20248 &bucket07,
20249 &bucket08,
20250 );
20251 assert_eq!(bucket00, vec![376, 413, 450]);
20252 }
20253
20254 #[test]
20255 fn accumulate_counts_s32_matches_c_dispatch_for_small_bucket_counts() {
20256 let mut buckets = vec![1, 2, 3, 4, 5, 6, 7, 8];
20257 accumulate_counts_s32(&mut buckets, 2, 2, 4);
20258 assert_eq!(buckets, vec![1, 2, 3, 4, 5, 6, 16, 20]);
20259 }
20260
20261 #[test]
20262 fn accumulate_counts_s32_matches_c_dispatch_for_nine_buckets() {
20263 let mut buckets = vec![
20264 1, 10, 2, 20, 3, 30, 4, 40, 5, 50, 6, 60, 7, 70, 8, 80, 9, 90,
20265 ];
20266 accumulate_counts_s32(&mut buckets, 2, 2, 9);
20267 assert_eq!(
20268 buckets,
20269 vec![1, 10, 2, 20, 3, 30, 4, 40, 5, 50, 6, 60, 7, 70, 8, 80, 45, 450]
20270 );
20271 }
20272
20273 #[test]
20274 fn accumulate_counts_s32_matches_c_chunked_nine_then_tail_behavior() {
20275 let mut buckets = (1..=11).collect::<Vec<SaSint>>();
20276 accumulate_counts_s32(&mut buckets, 1, 1, 11);
20277 assert_eq!(buckets, vec![1, 2, 3, 4, 5, 6, 7, 8, 45, 10, 66]);
20278 }
20279
20280 fn deterministic_large_input(seed: u64, len: usize, alphabet: u32) -> Vec<u8> {
20281 let mut rng = seed | 1;
20282 (0..len)
20283 .map(|_| {
20284 rng = rng.wrapping_add(0x9e3779b97f4a7c15);
20285 let mut z = rng;
20286 z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
20287 z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
20288 z ^= z >> 31;
20289 (z % u64::from(alphabet)) as u8
20290 })
20291 .collect()
20292 }
20293
20294 #[test]
20298 fn libsais_omp_threads_2_matches_single_thread_on_large_input() {
20299 let text = deterministic_large_input(0xC0FFEE, 200_000, 4);
20300 let mut sa_single = vec![0 as SaSint; text.len() + 1];
20301 let mut sa_omp = vec![0 as SaSint; text.len() + 1];
20302 assert_eq!(libsais(&text, &mut sa_single, 0, None), 0);
20303 assert_eq!(libsais_omp(&text, &mut sa_omp, 0, None, 2), 0);
20304 assert_eq!(sa_single, sa_omp, "threads=2 SA must match threads=1 SA");
20305 }
20306
20307 #[test]
20308 fn libsais_omp_threads_4_matches_single_thread_on_large_input() {
20309 let text = deterministic_large_input(0xBADF00D, 250_000, 8);
20310 let mut sa_single = vec![0 as SaSint; text.len() + 1];
20311 let mut sa_omp = vec![0 as SaSint; text.len() + 1];
20312 assert_eq!(libsais(&text, &mut sa_single, 0, None), 0);
20313 assert_eq!(libsais_omp(&text, &mut sa_omp, 0, None, 4), 0);
20314 assert_eq!(sa_single, sa_omp, "threads=4 SA must match threads=1 SA");
20315 }
20316
20317 #[test]
20322 fn libsais_omp_does_not_deadlock_when_invoked_from_rayon_worker() {
20323 use rayon::prelude::*;
20324 let text = deterministic_large_input(0x5EED, 100_000, 4);
20325 let inputs: Vec<&[u8]> = vec![&text, &text, &text, &text];
20326 let results: Vec<SaSint> = inputs
20327 .par_iter()
20328 .map(|t| {
20329 let mut sa = vec![0 as SaSint; t.len() + 1];
20330 libsais_omp(t, &mut sa, 0, None, 4)
20331 })
20332 .collect();
20333 assert!(results.iter().all(|&rc| rc == 0));
20334 }
20335}