1use std::marker::PhantomData;
8use std::mem;
9#[cfg(feature = "upstream-c")]
10use std::mem::MaybeUninit;
11
12use crate::{run_rayon_with_threads, SyncMutPtr};
13use rayon::prelude::*;
14
15pub type SaSint = i64;
16pub type SaUint = u64;
17pub type FastSint = isize;
18pub type FastUint = usize;
19
20pub const SAINT_BIT: u32 = 64;
21pub const SAINT_MAX: SaSint = i64::MAX;
22pub const SAINT_MIN: SaSint = i64::MIN;
23
24pub const ALPHABET_SIZE: usize = 1usize << 8;
25pub const UNBWT_FASTBITS: usize = 17;
26
27pub const SUFFIX_GROUP_BIT: u32 = SAINT_BIT - 1;
28pub const SUFFIX_GROUP_MARKER: SaSint = 1_i64 << (SUFFIX_GROUP_BIT - 1);
29
30pub const LIBSAIS_LOCAL_BUFFER_SIZE: usize = 1000;
31pub const LIBSAIS_PER_THREAD_CACHE_SIZE: usize = 24_576;
32
33pub const LIBSAIS_FLAGS_NONE: SaSint = 0;
34pub const LIBSAIS_FLAGS_BWT: SaSint = 1;
35pub const LIBSAIS_FLAGS_GSA: SaSint = 2;
36
37#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
38pub struct ThreadCache {
39 pub symbol: SaSint,
40 pub index: SaSint,
41}
42
43#[derive(Clone, Debug, PartialEq, Eq)]
44pub struct ThreadState {
45 pub position: FastSint,
46 pub count: FastSint,
47 pub m: FastSint,
48 pub last_lms_suffix: FastSint,
49 pub buckets: Vec<SaSint>,
50 pub cache: Vec<ThreadCache>,
51}
52
53impl ThreadState {
54 fn new() -> Self {
55 Self {
56 position: 0,
57 count: 0,
58 m: 0,
59 last_lms_suffix: 0,
60 buckets: vec![0; 4 * ALPHABET_SIZE],
61 cache: vec![ThreadCache::default(); LIBSAIS_PER_THREAD_CACHE_SIZE],
62 }
63 }
64}
65
66#[derive(Clone, Debug, PartialEq, Eq)]
67pub struct Context {
68 pub buckets: Vec<SaSint>,
69 pub thread_state: Option<Vec<ThreadState>>,
70 pub threads: FastSint,
71}
72
73#[derive(Clone, Debug, PartialEq, Eq)]
74pub struct UnbwtContext {
75 pub bucket2: Vec<SaUint>,
76 pub fastbits: Vec<u16>,
77 pub buckets: Option<Vec<SaUint>>,
78 pub threads: FastSint,
79}
80
81#[doc(hidden)]
83pub fn buckets_index2(c: FastUint, s: FastUint) -> FastUint {
84 (c << 1) + s
85}
86
87#[doc(hidden)]
89pub fn buckets_index4(c: FastUint, s: FastUint) -> FastUint {
90 (c << 2) + s
91}
92
93#[doc(hidden)]
95pub fn align_up(value: usize, alignment: usize) -> usize {
96 debug_assert!(alignment.is_power_of_two());
97 (value + alignment - 1) & !(alignment - 1)
98}
99
100#[doc(hidden)]
102pub fn alloc_thread_state(threads: SaSint) -> Option<Vec<ThreadState>> {
103 if threads <= 0 {
104 return None;
105 }
106
107 let len = usize::try_from(threads).ok()?;
108 Some((0..len).map(|_| ThreadState::new()).collect())
109}
110
111#[doc(hidden)]
113pub fn create_ctx_main(threads: SaSint) -> Option<Context> {
114 if threads <= 0 {
115 return None;
116 }
117
118 let thread_state = if threads > 1 {
119 Some(alloc_thread_state(threads)?)
120 } else {
121 None
122 };
123
124 Some(Context {
125 buckets: vec![0; 8 * ALPHABET_SIZE],
126 thread_state,
127 threads: threads as FastSint,
128 })
129}
130
131pub fn create_ctx() -> Option<Context> {
137 create_ctx_main(1)
138}
139
140pub fn free_ctx(_ctx: Context) {}
142
143#[doc(hidden)]
145pub fn unbwt_create_ctx_main(threads: SaSint) -> Option<UnbwtContext> {
146 if threads <= 0 {
147 return None;
148 }
149
150 let buckets = if threads > 1 {
151 let len = usize::try_from(threads).ok()? * (ALPHABET_SIZE + ALPHABET_SIZE * ALPHABET_SIZE);
152 Some(vec![0; len])
153 } else {
154 None
155 };
156
157 Some(UnbwtContext {
158 bucket2: vec![0; ALPHABET_SIZE * ALPHABET_SIZE],
159 fastbits: vec![0; 1 + (1 << UNBWT_FASTBITS)],
160 buckets,
161 threads: threads as FastSint,
162 })
163}
164
165#[doc(hidden)]
167pub fn unbwt_free_ctx_main(_ctx: UnbwtContext) {}
168
169pub fn unbwt_create_ctx() -> Option<UnbwtContext> {
175 unbwt_create_ctx_main(1)
176}
177
178pub fn unbwt_free_ctx(_ctx: UnbwtContext) {}
180
181#[doc(hidden)]
183pub fn count_negative_marked_suffixes(
184 sa: &[SaSint],
185 block_start: FastSint,
186 block_size: FastSint,
187) -> SaSint {
188 block_slice(sa, block_start, block_size)
189 .iter()
190 .map(|&value| SaSint::from(value < 0))
191 .sum()
192}
193
194#[doc(hidden)]
196pub fn count_zero_marked_suffixes(
197 sa: &[SaSint],
198 block_start: FastSint,
199 block_size: FastSint,
200) -> SaSint {
201 block_slice(sa, block_start, block_size)
202 .iter()
203 .map(|&value| SaSint::from(value == 0))
204 .sum()
205}
206
207#[doc(hidden)]
209pub fn place_cached_suffixes(
210 sa: &mut [SaSint],
211 cache: &[ThreadCache],
212 block_start: FastSint,
213 block_size: FastSint,
214) {
215 let start = usize::try_from(block_start).expect("block_start must be non-negative");
216 let len = usize::try_from(block_size).expect("block_size must be non-negative");
217 let entries = if cache.len() >= start + len {
218 &cache[start..start + len]
219 } else {
220 &cache[..len]
221 };
222
223 for entry in entries {
224 let slot = usize::try_from(entry.symbol).expect("cache symbol must be non-negative");
225 sa[slot] = entry.index;
226 }
227}
228
229#[doc(hidden)]
231pub fn compact_and_place_cached_suffixes(
232 sa: &mut [SaSint],
233 cache: &mut [ThreadCache],
234 block_start: FastSint,
235 block_size: FastSint,
236) {
237 let start = usize::try_from(block_start).expect("block_start must be non-negative");
238 let len = usize::try_from(block_size).expect("block_size must be non-negative");
239 let read_start = if cache.len() >= start + len { start } else { 0 };
240 let read_end = read_start + len;
241
242 let mut write = read_start;
243 for read in read_start..read_end {
244 let entry = cache[read];
245 if entry.symbol >= 0 {
246 cache[write] = entry;
247 write += 1;
248 }
249 }
250
251 place_cached_suffixes(sa, cache, block_start, (write - read_start) as FastSint);
252}
253
254#[doc(hidden)]
256pub fn flip_suffix_markers_omp(sa: &mut [SaSint], l: SaSint, threads: SaSint) {
257 let len = usize::try_from(l).expect("l must be non-negative");
258 let omp_num_threads = if threads > 1 && l >= 65_536 {
259 usize::try_from(threads).expect("threads must be non-negative")
260 } else {
261 1
262 };
263 if omp_num_threads > 1 {
264 let chunk_size = ((len / omp_num_threads) & !15usize).max(16);
265 run_rayon_with_threads(omp_num_threads, || {
266 sa[..len].par_chunks_mut(chunk_size).for_each(|chunk| {
267 for value in chunk {
268 *value ^= SAINT_MIN;
269 }
270 });
271 });
272 return;
273 }
274
275 let omp_block_stride = (len / omp_num_threads) & !15usize;
276 for omp_thread_num in 0..omp_num_threads {
277 let omp_block_start = omp_thread_num * omp_block_stride;
278 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
279 omp_block_stride
280 } else {
281 len - omp_block_start
282 };
283 for value in &mut sa[omp_block_start..omp_block_start + omp_block_size] {
284 *value ^= SAINT_MIN;
285 }
286 }
287}
288
289#[doc(hidden)]
291pub fn gather_lms_suffixes_8u(
292 t: &[u8],
293 sa: &mut [SaSint],
294 n: SaSint,
295 mut m: FastSint,
296 omp_block_start: FastSint,
297 omp_block_size: FastSint,
298) {
299 if omp_block_size <= 0 {
300 return;
301 }
302
303 let n = usize::try_from(n).expect("n must be non-negative");
304 let block_start =
305 usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
306 let block_size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
307
308 let mut j = block_start + block_size;
309 let mut c0 = t[block_start + block_size - 1] as FastSint;
310 let mut c1 = -1;
311 while j < n {
312 c1 = t[j] as FastSint;
313 if c1 != c0 {
314 break;
315 }
316 j += 1;
317 }
318
319 let mut f0 = usize::from(c0 >= c1);
320 let mut f1: usize;
321 let mut i = block_start + block_size - 2;
322 let limit = block_start + 3;
323
324 while i >= limit {
325 c1 = t[i] as FastSint;
326 f1 = usize::from(c1 > (c0 - f0 as FastSint));
327 sa[usize::try_from(m).expect("m must be non-negative")] = (i + 1) as SaSint;
328 m -= (f1 & !f0) as FastSint;
329
330 c0 = t[i - 1] as FastSint;
331 f0 = usize::from(c0 > (c1 - f1 as FastSint));
332 sa[usize::try_from(m).expect("m must be non-negative")] = i as SaSint;
333 m -= (f0 & !f1) as FastSint;
334
335 c1 = t[i - 2] as FastSint;
336 f1 = usize::from(c1 > (c0 - f0 as FastSint));
337 sa[usize::try_from(m).expect("m must be non-negative")] = (i - 1) as SaSint;
338 m -= (f1 & !f0) as FastSint;
339
340 c0 = t[i - 3] as FastSint;
341 f0 = usize::from(c0 > (c1 - f1 as FastSint));
342 sa[usize::try_from(m).expect("m must be non-negative")] = (i - 2) as SaSint;
343 m -= (f0 & !f1) as FastSint;
344
345 if i < 4 {
346 break;
347 }
348 i -= 4;
349 }
350
351 let tail_limit = limit - 3;
352 while i >= tail_limit {
353 c1 = c0;
354 c0 = t[i] as FastSint;
355 f1 = f0;
356 f0 = usize::from(c0 > (c1 - f1 as FastSint));
357 sa[usize::try_from(m).expect("m must be non-negative")] = (i + 1) as SaSint;
358 m -= (f0 & !f1) as FastSint;
359 if i == 0 {
360 break;
361 }
362 i -= 1;
363 }
364
365 sa[usize::try_from(m).expect("m must be non-negative")] = (i + 1) as SaSint;
366}
367
368#[doc(hidden)]
370pub fn gather_lms_suffixes_8u_omp(
371 t: &[u8],
372 sa: &mut [SaSint],
373 n: SaSint,
374 threads: SaSint,
375 thread_state: &mut [ThreadState],
376) {
377 let n_usize = usize::try_from(n).expect("n must be non-negative");
378 let omp_num_threads = if threads > 1 && n >= 65_536 {
379 usize::try_from(threads)
380 .expect("threads must be non-negative")
381 .min(thread_state.len())
382 .max(1)
383 } else {
384 1
385 };
386 if omp_num_threads == 1 {
387 gather_lms_suffixes_8u(t, sa, n, n as FastSint - 1, 0, n as FastSint);
388 return;
389 }
390
391 let omp_block_stride = (n_usize / omp_num_threads) & !15usize;
392 let mut suffix_counts_after = vec![0 as FastSint; omp_num_threads];
393 let mut m = 0 as FastSint;
394 for omp_thread_num in (0..omp_num_threads).rev() {
395 suffix_counts_after[omp_thread_num] = m;
396 m += thread_state[omp_thread_num].m;
397 }
398
399 let sa_ptr = SyncMutPtr::new(sa);
400 let suffix_counts_after_slice: &[FastSint] = &suffix_counts_after;
401 let last_lms_suffixes: Vec<FastSint> = thread_state[..omp_num_threads]
402 .iter()
403 .map(|s| {
404 if s.m > 0 {
405 s.last_lms_suffix
406 } else {
407 FastSint::MIN
408 }
409 })
410 .collect();
411
412 run_rayon_with_threads(omp_num_threads, || {
413 (0..omp_num_threads)
414 .into_par_iter()
415 .for_each(|omp_thread_num| {
416 let omp_block_start = omp_thread_num * omp_block_stride;
417 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
418 omp_block_stride
419 } else {
420 n_usize - omp_block_start
421 };
422 let sa = unsafe { sa_ptr.as_slice() };
424 gather_lms_suffixes_8u(
425 t,
426 sa,
427 n,
428 n as FastSint - 1 - suffix_counts_after_slice[omp_thread_num],
429 omp_block_start as FastSint,
430 omp_block_size as FastSint,
431 );
432 });
433 });
434
435 for omp_thread_num in 0..omp_num_threads {
436 if last_lms_suffixes[omp_thread_num] != FastSint::MIN {
437 let dst = usize::try_from(n as FastSint - 1 - suffix_counts_after[omp_thread_num])
438 .expect("destination must be non-negative");
439 sa[dst] = last_lms_suffixes[omp_thread_num] as SaSint;
440 }
441 }
442}
443
444#[doc(hidden)]
446pub fn gather_lms_suffixes_32s(t: &[SaSint], sa: &mut [SaSint], n: SaSint) -> SaSint {
447 let n_usize = usize::try_from(n).expect("n must be non-negative");
448 let mut i = n as FastSint - 2;
449 let mut m = n_usize - 1;
450 let mut f0 = 1usize;
451 let mut f1: usize;
452 let mut c0 = t[n_usize - 1] as FastSint;
453 let mut c1: FastSint;
454
455 while i >= 3 {
456 c1 = t[i as usize] as FastSint;
457 f1 = usize::from(c1 > (c0 - f0 as FastSint));
458 sa[m] = (i + 1) as SaSint;
459 m -= f1 & !f0;
460
461 c0 = t[(i - 1) as usize] as FastSint;
462 f0 = usize::from(c0 > (c1 - f1 as FastSint));
463 sa[m] = i as SaSint;
464 m -= f0 & !f1;
465
466 c1 = t[(i - 2) as usize] as FastSint;
467 f1 = usize::from(c1 > (c0 - f0 as FastSint));
468 sa[m] = (i - 1) as SaSint;
469 m -= f1 & !f0;
470
471 c0 = t[(i - 3) as usize] as FastSint;
472 f0 = usize::from(c0 > (c1 - f1 as FastSint));
473 sa[m] = (i - 2) as SaSint;
474 m -= f0 & !f1;
475
476 i -= 4;
477 }
478
479 while i >= 0 {
480 c1 = c0;
481 c0 = t[i as usize] as FastSint;
482 f1 = f0;
483 f0 = usize::from(c0 > (c1 - f1 as FastSint));
484 sa[m] = (i + 1) as SaSint;
485 m -= f0 & !f1;
486 i -= 1;
487 }
488
489 (n_usize - 1 - m) as SaSint
490}
491
492#[doc(hidden)]
494pub fn gather_compacted_lms_suffixes_32s(t: &[SaSint], sa: &mut [SaSint], n: SaSint) -> SaSint {
495 let n_usize = usize::try_from(n).expect("n must be non-negative");
496 let mut i = n as FastSint - 2;
497 let mut m = n_usize - 1;
498 let mut f0 = 1usize;
499 let mut f1: usize;
500 let mut c0 = t[n_usize - 1] as FastSint;
501 let mut c1: FastSint;
502
503 while i >= 3 {
504 c1 = t[i as usize] as FastSint;
505 f1 = usize::from(c1 > (c0 - f0 as FastSint));
506 sa[m] = (i + 1) as SaSint;
507 m -= f1 & !f0 & usize::from(c0 >= 0);
508
509 c0 = t[(i - 1) as usize] as FastSint;
510 f0 = usize::from(c0 > (c1 - f1 as FastSint));
511 sa[m] = i as SaSint;
512 m -= f0 & !f1 & usize::from(c1 >= 0);
513
514 c1 = t[(i - 2) as usize] as FastSint;
515 f1 = usize::from(c1 > (c0 - f0 as FastSint));
516 sa[m] = (i - 1) as SaSint;
517 m -= f1 & !f0 & usize::from(c0 >= 0);
518
519 c0 = t[(i - 3) as usize] as FastSint;
520 f0 = usize::from(c0 > (c1 - f1 as FastSint));
521 sa[m] = (i - 2) as SaSint;
522 m -= f0 & !f1 & usize::from(c1 >= 0);
523
524 i -= 4;
525 }
526
527 while i >= 0 {
528 c1 = c0;
529 c0 = t[i as usize] as FastSint;
530 f1 = f0;
531 f0 = usize::from(c0 > (c1 - f1 as FastSint));
532 sa[m] = (i + 1) as SaSint;
533 m -= f0 & !f1 & usize::from(c1 >= 0);
534 i -= 1;
535 }
536
537 (n_usize - 1 - m) as SaSint
538}
539
540#[doc(hidden)]
542pub fn count_lms_suffixes_32s_4k(t: &[SaSint], n: SaSint, k: SaSint, buckets: &mut [SaSint]) {
543 buckets.fill(0);
544 let n_usize = usize::try_from(n).expect("n must be non-negative");
545 let _k_usize = usize::try_from(k).expect("k must be non-negative");
546 let mut i = n as FastSint - 2;
547 let mut f0 = 1usize;
548 let mut f1: usize;
549 let mut c0 = t[n_usize - 1] as FastSint;
550 let mut c1: FastSint;
551
552 while i >= 3 {
553 c1 = t[i as usize] as FastSint;
554 f1 = usize::from(c1 > (c0 - f0 as FastSint));
555 buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
556
557 c0 = t[(i - 1) as usize] as FastSint;
558 f0 = usize::from(c0 > (c1 - f1 as FastSint));
559 buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
560
561 c1 = t[(i - 2) as usize] as FastSint;
562 f1 = usize::from(c1 > (c0 - f0 as FastSint));
563 buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
564
565 c0 = t[(i - 3) as usize] as FastSint;
566 f0 = usize::from(c0 > (c1 - f1 as FastSint));
567 buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
568
569 i -= 4;
570 }
571
572 while i >= 0 {
573 c1 = c0;
574 c0 = t[i as usize] as FastSint;
575 f1 = f0;
576 f0 = usize::from(c0 > (c1 - f1 as FastSint));
577 buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
578 i -= 1;
579 }
580
581 buckets[buckets_index4(c0 as usize, f0 + f0)] += 1;
582}
583
584#[doc(hidden)]
586pub fn count_lms_suffixes_32s_2k(t: &[SaSint], n: SaSint, k: SaSint, buckets: &mut [SaSint]) {
587 buckets.fill(0);
588 let n_usize = usize::try_from(n).expect("n must be non-negative");
589 let _k_usize = usize::try_from(k).expect("k must be non-negative");
590 let mut i = n as FastSint - 2;
591 let mut f0 = 1usize;
592 let mut f1: usize;
593 let mut c0 = t[n_usize - 1] as FastSint;
594 let mut c1: FastSint;
595
596 while i >= 3 {
597 c1 = t[i as usize] as FastSint;
598 f1 = usize::from(c1 > (c0 - f0 as FastSint));
599 buckets[buckets_index2(c0 as usize, f1 & !f0)] += 1;
600
601 c0 = t[(i - 1) as usize] as FastSint;
602 f0 = usize::from(c0 > (c1 - f1 as FastSint));
603 buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
604
605 c1 = t[(i - 2) as usize] as FastSint;
606 f1 = usize::from(c1 > (c0 - f0 as FastSint));
607 buckets[buckets_index2(c0 as usize, f1 & !f0)] += 1;
608
609 c0 = t[(i - 3) as usize] as FastSint;
610 f0 = usize::from(c0 > (c1 - f1 as FastSint));
611 buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
612
613 i -= 4;
614 }
615
616 while i >= 0 {
617 c1 = c0;
618 c0 = t[i as usize] as FastSint;
619 f1 = f0;
620 f0 = usize::from(c0 > (c1 - f1 as FastSint));
621 buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
622 i -= 1;
623 }
624
625 buckets[buckets_index2(c0 as usize, 0)] += 1;
626}
627
628#[doc(hidden)]
630pub fn count_compacted_lms_suffixes_32s_2k(
631 t: &[SaSint],
632 n: SaSint,
633 k: SaSint,
634 buckets: &mut [SaSint],
635) {
636 buckets.fill(0);
637 let n_usize = usize::try_from(n).expect("n must be non-negative");
638 let _k_usize = usize::try_from(k).expect("k must be non-negative");
639 let mut i = n as FastSint - 2;
640 let mut f0 = 1usize;
641 let mut f1: usize;
642 let mut c0 = t[n_usize - 1] as FastSint;
643 let mut c1: FastSint;
644
645 while i >= 3 {
646 c1 = t[i as usize] as FastSint;
647 f1 = usize::from(c1 > (c0 - f0 as FastSint));
648 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
649
650 c0 = t[(i - 1) as usize] as FastSint;
651 f0 = usize::from(c0 > (c1 - f1 as FastSint));
652 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
653
654 c1 = t[(i - 2) as usize] as FastSint;
655 f1 = usize::from(c1 > (c0 - f0 as FastSint));
656 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
657
658 c0 = t[(i - 3) as usize] as FastSint;
659 f0 = usize::from(c0 > (c1 - f1 as FastSint));
660 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
661
662 i -= 4;
663 }
664
665 while i >= 0 {
666 c1 = c0;
667 c0 = t[i as usize] as FastSint;
668 f1 = f0;
669 f0 = usize::from(c0 > (c1 - f1 as FastSint));
670 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
671 i -= 1;
672 }
673
674 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, 0)] += 1;
675}
676
677#[doc(hidden)]
679pub fn count_and_gather_lms_suffixes_8u(
680 t: &[u8],
681 sa: &mut [SaSint],
682 n: SaSint,
683 buckets: &mut [SaSint],
684 omp_block_start: FastSint,
685 omp_block_size: FastSint,
686) -> SaSint {
687 buckets.fill(0);
688 let n = n as FastSint;
689 let mut m = omp_block_start + omp_block_size - 1;
690
691 if omp_block_size > 0 {
692 let prefetch_distance = 256 as FastSint;
693 let mut j = m + 1;
694 let mut c0 = t[m as usize] as FastSint;
695 let mut c1 = -1;
696 while j < n {
697 c1 = t[j as usize] as FastSint;
698 if c1 != c0 {
699 break;
700 }
701 j += 1;
702 }
703
704 let mut f0 = usize::from(c0 >= c1);
705 let mut f1: usize;
706 let mut i = m - 1;
707 let limit = omp_block_start + 3;
708
709 while i >= limit {
710 let _prefetch_index = i - prefetch_distance;
711 c1 = t[i as usize] as FastSint;
712 f1 = usize::from(c1 > (c0 - f0 as FastSint));
713 sa[m as usize] = (i + 1) as SaSint;
714 m -= (f1 & !f0) as FastSint;
715 buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
716
717 c0 = t[(i - 1) as usize] as FastSint;
718 f0 = usize::from(c0 > (c1 - f1 as FastSint));
719 sa[m as usize] = i as SaSint;
720 m -= (f0 & !f1) as FastSint;
721 buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
722
723 c1 = t[(i - 2) as usize] as FastSint;
724 f1 = usize::from(c1 > (c0 - f0 as FastSint));
725 sa[m as usize] = (i - 1) as SaSint;
726 m -= (f1 & !f0) as FastSint;
727 buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
728
729 c0 = t[(i - 3) as usize] as FastSint;
730 f0 = usize::from(c0 > (c1 - f1 as FastSint));
731 sa[m as usize] = (i - 2) as SaSint;
732 m -= (f0 & !f1) as FastSint;
733 buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
734
735 i -= 4;
736 }
737
738 let tail_limit = limit - 3;
739 while i >= tail_limit {
740 c1 = c0;
741 c0 = t[i as usize] as FastSint;
742 f1 = f0;
743 f0 = usize::from(c0 > (c1 - f1 as FastSint));
744 sa[m as usize] = (i + 1) as SaSint;
745 m -= (f0 & !f1) as FastSint;
746 buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
747 i -= 1;
748 }
749
750 c1 = if i >= 0 {
751 t[i as usize] as FastSint
752 } else {
753 -1
754 };
755 f1 = usize::from(c1 > (c0 - f0 as FastSint));
756 sa[m as usize] = (i + 1) as SaSint;
757 m -= (f1 & !f0) as FastSint;
758 buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
759 }
760
761 (omp_block_start + omp_block_size - 1 - m) as SaSint
762}
763
764#[doc(hidden)]
766pub fn count_and_gather_lms_suffixes_8u_omp(
767 t: &[u8],
768 sa: &mut [SaSint],
769 n: SaSint,
770 buckets: &mut [SaSint],
771 threads: SaSint,
772 thread_state: &mut [ThreadState],
773) -> SaSint {
774 let mut m = 0;
775 let n_usize = usize::try_from(n).expect("n must be non-negative");
776 let omp_num_threads = if threads > 1 && n >= 65_536 {
777 usize::try_from(threads)
778 .expect("threads must be non-negative")
779 .min(thread_state.len())
780 .max(1)
781 } else {
782 1
783 };
784 let omp_block_stride = (n_usize / omp_num_threads) & !15usize;
785
786 if omp_num_threads == 1 {
787 return count_and_gather_lms_suffixes_8u(t, sa, n, buckets, 0, n as FastSint);
788 }
789
790 let sa_ptr = SyncMutPtr::new(sa);
791 run_rayon_with_threads(omp_num_threads, || {
792 thread_state[..omp_num_threads]
793 .par_iter_mut()
794 .enumerate()
795 .for_each(|(omp_thread_num, state)| {
796 let omp_block_start = omp_thread_num * omp_block_stride;
797 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
798 omp_block_stride
799 } else {
800 n_usize - omp_block_start
801 };
802
803 let sa = unsafe { sa_ptr.as_slice() };
805
806 state.position = FastSint::try_from(omp_block_start + omp_block_size)
807 .expect("position must fit FastSint");
808 state.m = FastSint::try_from(count_and_gather_lms_suffixes_8u(
809 t,
810 sa,
811 n,
812 &mut state.buckets,
813 FastSint::try_from(omp_block_start).expect("block start must fit FastSint"),
814 FastSint::try_from(omp_block_size).expect("block size must fit FastSint"),
815 ))
816 .expect("m must fit FastSint");
817
818 if state.m > 0 {
819 let position =
820 usize::try_from(state.position).expect("position must be non-negative");
821 state.last_lms_suffix =
822 FastSint::try_from(sa[position - 1]).expect("suffix must fit FastSint");
823 }
824 });
825 });
826
827 buckets.fill(0);
828
829 for tnum in (0..omp_num_threads).rev() {
830 let state = &mut thread_state[tnum];
831 m += SaSint::try_from(state.m).expect("m must fit SaSint");
832
833 if tnum + 1 < omp_num_threads && state.m > 0 {
834 let position = usize::try_from(state.position).expect("position must be non-negative");
835 let count = usize::try_from(state.m).expect("m must be non-negative");
836 let dst = n_usize - usize::try_from(m).expect("m must be non-negative");
837 sa.copy_within(position - count..position, dst);
838 }
839
840 for s in 0..4 * ALPHABET_SIZE {
841 let a = buckets[s];
842 let b = state.buckets[s];
843 buckets[s] = a + b;
844 state.buckets[s] = a;
845 }
846 }
847
848 m
849}
850
851#[doc(hidden)]
853pub fn count_and_gather_lms_suffixes_32s_4k(
854 t: &[SaSint],
855 sa: &mut [SaSint],
856 n: SaSint,
857 k: SaSint,
858 buckets: &mut [SaSint],
859 omp_block_start: FastSint,
860 omp_block_size: FastSint,
861) -> SaSint {
862 buckets.fill(0);
863 let n = n as FastSint;
864 let _k = k as FastSint;
865 let mut m = omp_block_start + omp_block_size - 1;
866
867 if omp_block_size > 0 {
868 let prefetch_distance = 64 as FastSint;
869 let mut j = m + 1;
870 let mut c0 = t[m as usize] as FastSint;
871 let mut c1 = -1;
872
873 while j < n {
874 c1 = t[j as usize] as FastSint;
875 if c1 != c0 {
876 break;
877 }
878 j += 1;
879 }
880
881 let mut f0 = usize::from(c0 >= c1);
882 let mut f1: usize;
883 let mut i = m - 1;
884 let limit = omp_block_start + prefetch_distance + 3;
885
886 while i >= limit {
887 let _prefetch_index = i - 2 * prefetch_distance;
888 c1 = t[i as usize] as FastSint;
889 f1 = usize::from(c1 > (c0 - f0 as FastSint));
890 sa[m as usize] = (i + 1) as SaSint;
891 m -= (f1 & !f0) as FastSint;
892 buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
893
894 c0 = t[(i - 1) as usize] as FastSint;
895 f0 = usize::from(c0 > (c1 - f1 as FastSint));
896 sa[m as usize] = i as SaSint;
897 m -= (f0 & !f1) as FastSint;
898 buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
899
900 c1 = t[(i - 2) as usize] as FastSint;
901 f1 = usize::from(c1 > (c0 - f0 as FastSint));
902 sa[m as usize] = (i - 1) as SaSint;
903 m -= (f1 & !f0) as FastSint;
904 buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
905
906 c0 = t[(i - 3) as usize] as FastSint;
907 f0 = usize::from(c0 > (c1 - f1 as FastSint));
908 sa[m as usize] = (i - 2) as SaSint;
909 m -= (f0 & !f1) as FastSint;
910 buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
911
912 i -= 4;
913 }
914
915 let tail_limit = omp_block_start;
916 while i >= tail_limit {
917 c1 = c0;
918 c0 = t[i as usize] as FastSint;
919 f1 = f0;
920 f0 = usize::from(c0 > (c1 - f1 as FastSint));
921 sa[m as usize] = (i + 1) as SaSint;
922 m -= (f0 & !f1) as FastSint;
923 buckets[buckets_index4(c1 as usize, f1 + f1 + f0)] += 1;
924 i -= 1;
925 }
926
927 c1 = if i >= 0 {
928 t[i as usize] as FastSint
929 } else {
930 -1
931 };
932 f1 = usize::from(c1 > (c0 - f0 as FastSint));
933 sa[m as usize] = (i + 1) as SaSint;
934 m -= (f1 & !f0) as FastSint;
935 buckets[buckets_index4(c0 as usize, f0 + f0 + f1)] += 1;
936 }
937
938 (omp_block_start + omp_block_size - 1 - m) as SaSint
939}
940
941#[doc(hidden)]
943pub fn count_and_gather_lms_suffixes_32s_2k(
944 t: &[SaSint],
945 sa: &mut [SaSint],
946 n: SaSint,
947 k: SaSint,
948 buckets: &mut [SaSint],
949 omp_block_start: FastSint,
950 omp_block_size: FastSint,
951) -> SaSint {
952 buckets.fill(0);
953 let n = n as FastSint;
954 let _k = k as FastSint;
955 let mut m = omp_block_start + omp_block_size - 1;
956
957 if omp_block_size > 0 {
958 let prefetch_distance = 64 as FastSint;
959 let mut j = m + 1;
960 let mut c0 = t[m as usize] as FastSint;
961 let mut c1 = -1;
962
963 while j < n {
964 c1 = t[j as usize] as FastSint;
965 if c1 != c0 {
966 break;
967 }
968 j += 1;
969 }
970
971 let mut f0 = usize::from(c0 >= c1);
972 let mut f1: usize;
973 let mut i = m - 1;
974 let limit = omp_block_start + prefetch_distance + 3;
975
976 while i >= limit {
977 let _prefetch_index = i - 2 * prefetch_distance;
978 c1 = t[i as usize] as FastSint;
979 f1 = usize::from(c1 > (c0 - f0 as FastSint));
980 sa[m as usize] = (i + 1) as SaSint;
981 m -= (f1 & !f0) as FastSint;
982 buckets[buckets_index2(c0 as usize, f1 & !f0)] += 1;
983
984 c0 = t[(i - 1) as usize] as FastSint;
985 f0 = usize::from(c0 > (c1 - f1 as FastSint));
986 sa[m as usize] = i as SaSint;
987 m -= (f0 & !f1) as FastSint;
988 buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
989
990 c1 = t[(i - 2) as usize] as FastSint;
991 f1 = usize::from(c1 > (c0 - f0 as FastSint));
992 sa[m as usize] = (i - 1) as SaSint;
993 m -= (f1 & !f0) as FastSint;
994 buckets[buckets_index2(c0 as usize, f1 & !f0)] += 1;
995
996 c0 = t[(i - 3) as usize] as FastSint;
997 f0 = usize::from(c0 > (c1 - f1 as FastSint));
998 sa[m as usize] = (i - 2) as SaSint;
999 m -= (f0 & !f1) as FastSint;
1000 buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
1001
1002 i -= 4;
1003 }
1004
1005 let tail_limit = omp_block_start;
1006 while i >= tail_limit {
1007 c1 = c0;
1008 c0 = t[i as usize] as FastSint;
1009 f1 = f0;
1010 f0 = usize::from(c0 > (c1 - f1 as FastSint));
1011 sa[m as usize] = (i + 1) as SaSint;
1012 m -= (f0 & !f1) as FastSint;
1013 buckets[buckets_index2(c1 as usize, f0 & !f1)] += 1;
1014 i -= 1;
1015 }
1016
1017 c1 = if i >= 0 {
1018 t[i as usize] as FastSint
1019 } else {
1020 -1
1021 };
1022 f1 = usize::from(c1 > (c0 - f0 as FastSint));
1023 sa[m as usize] = (i + 1) as SaSint;
1024 m -= (f1 & !f0) as FastSint;
1025 buckets[buckets_index2(c0 as usize, f1 & !f0)] += 1;
1026 }
1027
1028 (omp_block_start + omp_block_size - 1 - m) as SaSint
1029}
1030
1031#[doc(hidden)]
1033pub fn count_and_gather_compacted_lms_suffixes_32s_2k(
1034 t: &[SaSint],
1035 sa: &mut [SaSint],
1036 n: SaSint,
1037 k: SaSint,
1038 buckets: &mut [SaSint],
1039 omp_block_start: FastSint,
1040 omp_block_size: FastSint,
1041) -> SaSint {
1042 buckets.fill(0);
1043 let n_usize = usize::try_from(n).expect("n must be non-negative");
1044 let _k_usize = usize::try_from(k).expect("k must be non-negative");
1045 let block_start =
1046 usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
1047 let block_size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
1048 let mut m = block_start + block_size - 1;
1049
1050 if omp_block_size > 0 {
1051 let mut j = m + 1;
1052 let mut c0 = t[m] as FastSint;
1053 let mut c1 = -1;
1054
1055 while j < n_usize {
1056 c1 = t[j] as FastSint;
1057 if c1 != c0 {
1058 break;
1059 }
1060 j += 1;
1061 }
1062
1063 let mut f0 = usize::from(c0 >= c1);
1064 let mut f1: usize;
1065 let mut i = m as FastSint - 1;
1066 let limit = block_start as FastSint + 3;
1067
1068 while i >= limit {
1069 c1 = t[i as usize] as FastSint;
1070 f1 = usize::from(c1 > (c0 - f0 as FastSint));
1071 sa[m] = (i + 1) as SaSint;
1072 m -= f1 & !f0 & usize::from(c0 >= 0);
1073 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
1074
1075 c0 = t[(i - 1) as usize] as FastSint;
1076 f0 = usize::from(c0 > (c1 - f1 as FastSint));
1077 sa[m] = i as SaSint;
1078 m -= f0 & !f1 & usize::from(c1 >= 0);
1079 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
1080
1081 c1 = t[(i - 2) as usize] as FastSint;
1082 f1 = usize::from(c1 > (c0 - f0 as FastSint));
1083 sa[m] = (i - 1) as SaSint;
1084 m -= f1 & !f0 & usize::from(c0 >= 0);
1085 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
1086
1087 c0 = t[(i - 3) as usize] as FastSint;
1088 f0 = usize::from(c0 > (c1 - f1 as FastSint));
1089 sa[m] = (i - 2) as SaSint;
1090 m -= f0 & !f1 & usize::from(c1 >= 0);
1091 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
1092
1093 i -= 4;
1094 }
1095
1096 let tail_limit = block_start as FastSint;
1097 while i >= tail_limit {
1098 c1 = c0;
1099 c0 = t[i as usize] as FastSint;
1100 f1 = f0;
1101 f0 = usize::from(c0 > (c1 - f1 as FastSint));
1102 sa[m] = (i + 1) as SaSint;
1103 m -= f0 & !f1 & usize::from(c1 >= 0);
1104 buckets[buckets_index2((c1 as SaSint & SAINT_MAX) as usize, f0 & !f1)] += 1;
1105 i -= 1;
1106 }
1107
1108 c1 = if i >= 0 {
1109 t[i as usize] as FastSint
1110 } else {
1111 -1
1112 };
1113 f1 = usize::from(c1 > (c0 - f0 as FastSint));
1114 sa[m] = (i + 1) as SaSint;
1115 m -= f1 & !f0 & usize::from(c0 >= 0);
1116 buckets[buckets_index2((c0 as SaSint & SAINT_MAX) as usize, f1 & !f0)] += 1;
1117 }
1118
1119 (block_start + block_size - 1 - m) as SaSint
1120}
1121
1122#[doc(hidden)]
1124pub fn get_bucket_stride(
1125 free_space: FastSint,
1126 bucket_size: FastSint,
1127 num_buckets: FastSint,
1128) -> FastSint {
1129 let bucket_size_1024 = (bucket_size + 1023) & (-1024);
1130 if free_space / (num_buckets - 1) >= bucket_size_1024 {
1131 return bucket_size_1024;
1132 }
1133 let bucket_size_16 = (bucket_size + 15) & (-16);
1134 if free_space / (num_buckets - 1) >= bucket_size_16 {
1135 return bucket_size_16;
1136 }
1137 bucket_size
1138}
1139
1140#[doc(hidden)]
1142pub fn count_and_gather_lms_suffixes_32s_4k_nofs_omp(
1143 t: &[SaSint],
1144 sa: &mut [SaSint],
1145 n: SaSint,
1146 k: SaSint,
1147 buckets: &mut [SaSint],
1148 threads: SaSint,
1149) -> SaSint {
1150 let m;
1151 let omp_num_threads = if threads > 1 && n >= 65_536 { 2 } else { 1 };
1152
1153 if omp_num_threads == 1 {
1154 m = count_and_gather_lms_suffixes_32s_4k(t, sa, n, k, buckets, 0, n as FastSint);
1155 } else {
1156 count_lms_suffixes_32s_4k(t, n, k, buckets);
1157 m = gather_lms_suffixes_32s(t, sa, n);
1158 }
1159
1160 m
1161}
1162
1163#[doc(hidden)]
1165pub fn count_and_gather_lms_suffixes_32s_2k_nofs_omp(
1166 t: &[SaSint],
1167 sa: &mut [SaSint],
1168 n: SaSint,
1169 k: SaSint,
1170 buckets: &mut [SaSint],
1171 threads: SaSint,
1172) -> SaSint {
1173 let m;
1174 let omp_num_threads = if threads > 1 && n >= 65_536 { 2 } else { 1 };
1175
1176 if omp_num_threads == 1 {
1177 m = count_and_gather_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as FastSint);
1178 } else {
1179 count_lms_suffixes_32s_2k(t, n, k, buckets);
1180 m = gather_lms_suffixes_32s(t, sa, n);
1181 }
1182
1183 m
1184}
1185
1186#[doc(hidden)]
1188pub fn count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(
1189 t: &[SaSint],
1190 sa: &mut [SaSint],
1191 n: SaSint,
1192 k: SaSint,
1193 buckets: &mut [SaSint],
1194 threads: SaSint,
1195) -> SaSint {
1196 let m;
1197 let omp_num_threads = if threads > 1 && n >= 65_536 { 2 } else { 1 };
1198
1199 if omp_num_threads == 1 {
1200 m = count_and_gather_compacted_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as FastSint);
1201 } else {
1202 count_compacted_lms_suffixes_32s_2k(t, n, k, buckets);
1203 m = gather_compacted_lms_suffixes_32s(t, sa, n);
1204 }
1205
1206 m
1207}
1208
1209#[doc(hidden)]
1211pub fn count_and_gather_lms_suffixes_32s_4k_fs_omp(
1212 t: &[SaSint],
1213 sa: &mut [SaSint],
1214 n: SaSint,
1215 k: SaSint,
1216 buckets: &mut [SaSint],
1217 local_buckets: SaSint,
1218 threads: SaSint,
1219 thread_state: &mut [ThreadState],
1220) -> SaSint {
1221 let n_usize = usize::try_from(n).expect("n must be non-negative");
1222 let k_usize = usize::try_from(k).expect("k must be non-negative");
1223 let omp_num_threads = usize::try_from(threads).expect("threads must be non-negative");
1224 let bucket_size = FastSint::try_from(4 * k_usize).expect("bucket size must fit FastSint");
1225
1226 if omp_num_threads <= 1 || n < 65_536 {
1227 return count_and_gather_lms_suffixes_32s_4k(t, sa, n, k, buckets, 0, n as FastSint);
1228 }
1229
1230 let omp_block_stride = (n_usize / omp_num_threads) & !15usize;
1231 let free_space = if local_buckets == 1 {
1232 FastSint::try_from(LIBSAIS_LOCAL_BUFFER_SIZE).expect("free space must fit FastSint")
1233 } else if local_buckets > 1 {
1234 FastSint::try_from(local_buckets).expect("free space must fit FastSint")
1235 } else {
1236 FastSint::try_from(buckets.len()).expect("free space must fit FastSint")
1237 };
1238 let bucket_stride = get_bucket_stride(
1239 free_space,
1240 bucket_size,
1241 FastSint::try_from(omp_num_threads).expect("thread count must fit FastSint"),
1242 );
1243 let bucket_size_usize = usize::try_from(bucket_size).expect("bucket size must be non-negative");
1244 let bucket_stride_usize =
1245 usize::try_from(bucket_stride).expect("bucket stride must be non-negative");
1246 let workspace_len =
1247 bucket_size_usize + bucket_stride_usize.saturating_mul(omp_num_threads.saturating_sub(1));
1248 let mut workspace = vec![0; workspace_len];
1249
1250 {
1251 let sa_ptr = SyncMutPtr::new(sa);
1252 let ws_ptr = SyncMutPtr::new(&mut workspace);
1253 run_rayon_with_threads(omp_num_threads, || {
1254 thread_state[..omp_num_threads]
1255 .par_iter_mut()
1256 .enumerate()
1257 .for_each(|(omp_thread_num, state)| {
1258 let omp_block_start = omp_thread_num * omp_block_stride;
1259 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
1260 omp_block_stride
1261 } else {
1262 n_usize - omp_block_start
1263 };
1264 let workspace_end = workspace_len - omp_thread_num * bucket_stride_usize;
1265 let workspace_start = workspace_end - bucket_size_usize;
1266 let sa = unsafe { sa_ptr.as_slice() };
1268 let workspace = unsafe { ws_ptr.as_slice() };
1269 let count = count_and_gather_lms_suffixes_32s_4k(
1270 t,
1271 sa,
1272 n,
1273 k,
1274 &mut workspace[workspace_start..workspace_end],
1275 omp_block_start as FastSint,
1276 omp_block_size as FastSint,
1277 );
1278 state.position = (omp_block_start + omp_block_size) as FastSint;
1279 state.count = count as FastSint;
1280 });
1281 });
1282 }
1283
1284 let mut m = 0;
1285 for t in (0..omp_num_threads).rev() {
1286 m += thread_state[t].count as SaSint;
1287
1288 if t + 1 != omp_num_threads && thread_state[t].count > 0 {
1289 let src_end =
1290 usize::try_from(thread_state[t].position).expect("position must be non-negative");
1291 let src_start = src_end
1292 - usize::try_from(thread_state[t].count).expect("count must be non-negative");
1293 let dst_start = usize::try_from(n - m).expect("destination must be non-negative");
1294 sa.copy_within(src_start..src_end, dst_start);
1295 }
1296 }
1297
1298 let omp_num_threads = omp_num_threads - 1;
1299 let omp_block_stride = (bucket_size_usize / omp_num_threads) & !15usize;
1300 {
1301 let ws_ptr = SyncMutPtr::new(&mut workspace);
1302 run_rayon_with_threads(omp_num_threads, || {
1303 (0..omp_num_threads)
1304 .into_par_iter()
1305 .for_each(|omp_thread_num| {
1306 let omp_block_start = omp_thread_num * omp_block_stride;
1307 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
1308 omp_block_stride
1309 } else {
1310 bucket_size_usize - omp_block_start
1311 };
1312 let workspace = unsafe { ws_ptr.as_slice() };
1314 accumulate_counts_s32(
1315 &mut workspace[omp_block_start..],
1316 omp_block_size as FastSint,
1317 bucket_stride,
1318 FastSint::try_from(omp_num_threads + 1)
1319 .expect("thread count must fit FastSint"),
1320 );
1321 });
1322 });
1323 }
1324
1325 let accumulated_start = omp_num_threads * bucket_stride_usize;
1326 buckets[..bucket_size_usize]
1327 .copy_from_slice(&workspace[accumulated_start..accumulated_start + bucket_size_usize]);
1328 m
1329}
1330
1331#[doc(hidden)]
1333pub fn count_and_gather_lms_suffixes_32s_2k_fs_omp(
1334 t: &[SaSint],
1335 sa: &mut [SaSint],
1336 n: SaSint,
1337 k: SaSint,
1338 buckets: &mut [SaSint],
1339 local_buckets: SaSint,
1340 threads: SaSint,
1341 thread_state: &mut [ThreadState],
1342) -> SaSint {
1343 let n_usize = usize::try_from(n).expect("n must be non-negative");
1344 let k_usize = usize::try_from(k).expect("k must be non-negative");
1345 let omp_num_threads = usize::try_from(threads).expect("threads must be non-negative");
1346 let bucket_size = FastSint::try_from(2 * k_usize).expect("bucket size must fit FastSint");
1347
1348 if omp_num_threads <= 1 || n < 65_536 {
1349 return count_and_gather_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as FastSint);
1350 }
1351
1352 let omp_block_stride = (n_usize / omp_num_threads) & !15usize;
1353 let free_space = if local_buckets == 1 {
1354 FastSint::try_from(LIBSAIS_LOCAL_BUFFER_SIZE).expect("free space must fit FastSint")
1355 } else if local_buckets > 1 {
1356 FastSint::try_from(local_buckets).expect("free space must fit FastSint")
1357 } else {
1358 FastSint::try_from(buckets.len()).expect("free space must fit FastSint")
1359 };
1360 let bucket_stride = get_bucket_stride(
1361 free_space,
1362 bucket_size,
1363 FastSint::try_from(omp_num_threads).expect("thread count must fit FastSint"),
1364 );
1365 let bucket_size_usize = usize::try_from(bucket_size).expect("bucket size must be non-negative");
1366 let bucket_stride_usize =
1367 usize::try_from(bucket_stride).expect("bucket stride must be non-negative");
1368 let workspace_len =
1369 bucket_size_usize + bucket_stride_usize.saturating_mul(omp_num_threads.saturating_sub(1));
1370 let mut workspace = vec![0; workspace_len];
1371
1372 {
1373 let sa_ptr = SyncMutPtr::new(sa);
1374 let ws_ptr = SyncMutPtr::new(&mut workspace);
1375 run_rayon_with_threads(omp_num_threads, || {
1376 thread_state[..omp_num_threads]
1377 .par_iter_mut()
1378 .enumerate()
1379 .for_each(|(omp_thread_num, state)| {
1380 let omp_block_start = omp_thread_num * omp_block_stride;
1381 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
1382 omp_block_stride
1383 } else {
1384 n_usize - omp_block_start
1385 };
1386 let workspace_end = workspace_len - omp_thread_num * bucket_stride_usize;
1387 let workspace_start = workspace_end - bucket_size_usize;
1388 let sa = unsafe { sa_ptr.as_slice() };
1390 let workspace = unsafe { ws_ptr.as_slice() };
1391 let count = count_and_gather_lms_suffixes_32s_2k(
1392 t,
1393 sa,
1394 n,
1395 k,
1396 &mut workspace[workspace_start..workspace_end],
1397 omp_block_start as FastSint,
1398 omp_block_size as FastSint,
1399 );
1400 state.position = (omp_block_start + omp_block_size) as FastSint;
1401 state.count = count as FastSint;
1402 });
1403 });
1404 }
1405
1406 let mut m = 0;
1407 for t in (0..omp_num_threads).rev() {
1408 m += thread_state[t].count as SaSint;
1409 if t + 1 != omp_num_threads && thread_state[t].count > 0 {
1410 let src_end =
1411 usize::try_from(thread_state[t].position).expect("position must be non-negative");
1412 let src_start = src_end
1413 - usize::try_from(thread_state[t].count).expect("count must be non-negative");
1414 let dst_start = usize::try_from(n - m).expect("destination must be non-negative");
1415 sa.copy_within(src_start..src_end, dst_start);
1416 }
1417 }
1418
1419 let omp_num_threads = omp_num_threads - 1;
1420 let omp_block_stride = (bucket_size_usize / omp_num_threads) & !15usize;
1421 {
1422 let ws_ptr = SyncMutPtr::new(&mut workspace);
1423 run_rayon_with_threads(omp_num_threads, || {
1424 (0..omp_num_threads)
1425 .into_par_iter()
1426 .for_each(|omp_thread_num| {
1427 let omp_block_start = omp_thread_num * omp_block_stride;
1428 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
1429 omp_block_stride
1430 } else {
1431 bucket_size_usize - omp_block_start
1432 };
1433 let workspace = unsafe { ws_ptr.as_slice() };
1435 accumulate_counts_s32(
1436 &mut workspace[omp_block_start..],
1437 omp_block_size as FastSint,
1438 bucket_stride,
1439 FastSint::try_from(omp_num_threads + 1)
1440 .expect("thread count must fit FastSint"),
1441 );
1442 });
1443 });
1444 }
1445
1446 let accumulated_start = omp_num_threads * bucket_stride_usize;
1447 buckets[..bucket_size_usize]
1448 .copy_from_slice(&workspace[accumulated_start..accumulated_start + bucket_size_usize]);
1449 m
1450}
1451
1452#[doc(hidden)]
1454pub fn count_and_gather_compacted_lms_suffixes_32s_2k_fs_omp(
1455 t: &[SaSint],
1456 sa: &mut [SaSint],
1457 n: SaSint,
1458 k: SaSint,
1459 buckets: &mut [SaSint],
1460 _local_buckets: SaSint,
1461 threads: SaSint,
1462 thread_state: &mut [ThreadState],
1463) {
1464 let n_usize = usize::try_from(n).expect("n must be non-negative");
1465 let k_usize = usize::try_from(k).expect("k must be non-negative");
1466 let thread_count = usize::try_from(threads).expect("threads must be non-negative");
1467 let bucket_size = 2 * k_usize;
1468
1469 if thread_count <= 1 || n < 65_536 {
1470 let _ =
1471 count_and_gather_compacted_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as FastSint);
1472 return;
1473 }
1474
1475 if thread_state.len() < thread_count || sa.len() < 2 * n_usize {
1476 let _ =
1477 count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(t, sa, n, k, buckets, threads);
1478 return;
1479 }
1480
1481 let omp_block_stride = (n_usize / thread_count) & !15usize;
1482 let free_space = if _local_buckets != 0 {
1483 FastSint::try_from(LIBSAIS_LOCAL_BUFFER_SIZE).expect("free space must fit FastSint")
1484 } else {
1485 FastSint::try_from(buckets.len()).expect("free space must fit FastSint")
1486 };
1487 let bucket_stride = get_bucket_stride(
1488 free_space,
1489 FastSint::try_from(bucket_size).expect("bucket size must fit FastSint"),
1490 FastSint::try_from(thread_count).expect("thread count must fit FastSint"),
1491 );
1492 let bucket_stride_usize =
1493 usize::try_from(bucket_stride).expect("bucket stride must be non-negative");
1494 let workspace_len =
1495 bucket_size + bucket_stride_usize.saturating_mul(thread_count.saturating_sub(1));
1496 let mut workspace = vec![0; workspace_len];
1497
1498 let usable_thread_state_len = thread_count.min(thread_state.len());
1499
1500 {
1501 let sa_ptr = SyncMutPtr::new(sa);
1502 let ws_ptr = SyncMutPtr::new(&mut workspace);
1503 let state_ptr = SyncMutPtr::new(thread_state);
1504 run_rayon_with_threads(thread_count, || {
1505 (0..thread_count)
1506 .into_par_iter()
1507 .for_each(|omp_thread_num| {
1508 let omp_block_start = omp_thread_num * omp_block_stride;
1509 let omp_block_size = if omp_thread_num + 1 < thread_count {
1510 omp_block_stride
1511 } else {
1512 n_usize - omp_block_start
1513 };
1514
1515 let workspace_end = workspace_len - omp_thread_num * bucket_stride_usize;
1516 let workspace_start = workspace_end - bucket_size;
1517 let sa = unsafe { sa_ptr.as_slice() };
1519 let workspace = unsafe { ws_ptr.as_slice() };
1520 let count = count_and_gather_compacted_lms_suffixes_32s_2k(
1521 t,
1522 &mut sa[n_usize..],
1523 n,
1524 k,
1525 &mut workspace[workspace_start..workspace_end],
1526 omp_block_start as FastSint,
1527 omp_block_size as FastSint,
1528 );
1529
1530 if omp_thread_num < usable_thread_state_len {
1531 let states = unsafe { state_ptr.as_slice() };
1532 states[omp_thread_num].position =
1533 (omp_block_start + omp_block_size) as FastSint;
1534 states[omp_thread_num].count = count as FastSint;
1535 }
1536 });
1537 });
1538 }
1539
1540 let mut m = 0usize;
1541 for omp_thread_num in (0..thread_count).rev() {
1542 let count = usize::try_from(thread_state[omp_thread_num].count)
1543 .expect("count must be non-negative");
1544 m += count;
1545 if count > 0 {
1546 let position = usize::try_from(thread_state[omp_thread_num].position)
1547 .expect("position must be non-negative");
1548 let src_start = n_usize + position - count;
1549 let src_end = n_usize + position;
1550 let dst_start = n_usize - m;
1551 sa.copy_within(src_start..src_end, dst_start);
1552 }
1553 }
1554
1555 let accumulation_threads = thread_count;
1556 let omp_block_stride = (bucket_size / accumulation_threads) & !15usize;
1557 {
1558 let ws_ptr = SyncMutPtr::new(&mut workspace);
1559 run_rayon_with_threads(accumulation_threads, || {
1560 (0..accumulation_threads)
1561 .into_par_iter()
1562 .for_each(|omp_thread_num| {
1563 let omp_block_start = omp_thread_num * omp_block_stride;
1564 let omp_block_size = if omp_thread_num + 1 < accumulation_threads {
1565 omp_block_stride
1566 } else {
1567 bucket_size - omp_block_start
1568 };
1569 let workspace = unsafe { ws_ptr.as_slice() };
1571 accumulate_counts_s32(
1572 &mut workspace[omp_block_start..],
1573 omp_block_size as FastSint,
1574 bucket_stride,
1575 FastSint::try_from(thread_count).expect("thread count must fit FastSint"),
1576 );
1577 });
1578 });
1579 }
1580 let accumulated_start = (accumulation_threads - 1) * bucket_stride_usize;
1581 buckets[..bucket_size]
1582 .copy_from_slice(&workspace[accumulated_start..accumulated_start + bucket_size]);
1583}
1584
1585#[doc(hidden)]
1587pub fn count_and_gather_lms_suffixes_32s_4k_omp(
1588 t: &[SaSint],
1589 sa: &mut [SaSint],
1590 n: SaSint,
1591 k: SaSint,
1592 buckets: &mut [SaSint],
1593 local_buckets: SaSint,
1594 threads: SaSint,
1595 thread_state: &mut [ThreadState],
1596) -> SaSint {
1597 let free_space = if local_buckets != 0 {
1598 LIBSAIS_LOCAL_BUFFER_SIZE as FastSint
1599 } else {
1600 FastSint::try_from(buckets.len()).expect("bucket length must fit FastSint")
1601 };
1602 let threads_fast = threads as FastSint;
1603 let mut max_threads = (free_space / (((4 * k as FastSint) + 15) & -16)).min(threads_fast);
1604
1605 if max_threads > 1 && n >= 65_536 && n / k >= 2 {
1606 let thread_cap = (n / (16 * k)) as FastSint;
1607 if max_threads > thread_cap {
1608 max_threads = thread_cap;
1609 }
1610 return count_and_gather_lms_suffixes_32s_4k_fs_omp(
1611 t,
1612 sa,
1613 n,
1614 k,
1615 buckets,
1616 local_buckets,
1617 max_threads.max(2) as SaSint,
1618 thread_state,
1619 );
1620 }
1621
1622 if threads > 1 && n >= 65_536 {
1623 count_lms_suffixes_32s_4k(t, n, k, buckets);
1624 gather_lms_suffixes_32s(t, sa, n)
1625 } else {
1626 count_and_gather_lms_suffixes_32s_4k(t, sa, n, k, buckets, 0, n as FastSint)
1627 }
1628}
1629
1630#[doc(hidden)]
1632pub fn count_and_gather_lms_suffixes_32s_2k_omp(
1633 t: &[SaSint],
1634 sa: &mut [SaSint],
1635 n: SaSint,
1636 k: SaSint,
1637 buckets: &mut [SaSint],
1638 local_buckets: SaSint,
1639 threads: SaSint,
1640 thread_state: &mut [ThreadState],
1641) -> SaSint {
1642 let free_space = if local_buckets != 0 {
1643 LIBSAIS_LOCAL_BUFFER_SIZE as FastSint
1644 } else {
1645 FastSint::try_from(buckets.len()).expect("bucket length must fit FastSint")
1646 };
1647 let threads_fast = threads as FastSint;
1648 let mut max_threads = (free_space / (((2 * k as FastSint) + 15) & -16)).min(threads_fast);
1649
1650 if max_threads > 1 && n >= 65_536 && n / k >= 2 {
1651 let thread_cap = (n / (8 * k)) as FastSint;
1652 if max_threads > thread_cap {
1653 max_threads = thread_cap;
1654 }
1655 return count_and_gather_lms_suffixes_32s_2k_fs_omp(
1656 t,
1657 sa,
1658 n,
1659 k,
1660 buckets,
1661 local_buckets,
1662 max_threads.max(2) as SaSint,
1663 thread_state,
1664 );
1665 }
1666
1667 if threads > 1 && n >= 65_536 {
1668 count_lms_suffixes_32s_2k(t, n, k, buckets);
1669 gather_lms_suffixes_32s(t, sa, n)
1670 } else {
1671 count_and_gather_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as FastSint)
1672 }
1673}
1674
1675#[doc(hidden)]
1677pub fn count_and_gather_compacted_lms_suffixes_32s_2k_omp(
1678 t: &[SaSint],
1679 sa: &mut [SaSint],
1680 n: SaSint,
1681 k: SaSint,
1682 buckets: &mut [SaSint],
1683 local_buckets: SaSint,
1684 threads: SaSint,
1685 thread_state: &mut [ThreadState],
1686) {
1687 let free_space = if local_buckets != 0 {
1688 LIBSAIS_LOCAL_BUFFER_SIZE as FastSint
1689 } else {
1690 FastSint::try_from(buckets.len()).expect("bucket length must fit FastSint")
1691 };
1692 let threads_fast = threads as FastSint;
1693 let mut max_threads = (free_space / (((2 * k as FastSint) + 15) & -16)).min(threads_fast);
1694
1695 if local_buckets == 0 && max_threads > 1 && n >= 65_536 && n / k >= 2 {
1696 let thread_cap = (n / (8 * k)) as FastSint;
1697 if max_threads > thread_cap {
1698 max_threads = thread_cap;
1699 }
1700 count_and_gather_compacted_lms_suffixes_32s_2k_fs_omp(
1701 t,
1702 sa,
1703 n,
1704 k,
1705 buckets,
1706 local_buckets,
1707 max_threads.max(2) as SaSint,
1708 thread_state,
1709 );
1710 return;
1711 }
1712
1713 let _ = count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(t, sa, n, k, buckets, threads);
1714}
1715
1716#[doc(hidden)]
1718pub fn count_suffixes_32s(t: &[SaSint], n: SaSint, k: SaSint, buckets: &mut [SaSint]) {
1719 let n_usize = usize::try_from(n).expect("n must be non-negative");
1720 let k_usize = usize::try_from(k).expect("k must be non-negative");
1721 buckets[..k_usize].fill(0);
1722
1723 let mut i = 0usize;
1724 let mut j = n_usize.saturating_sub(7);
1725 while i < j {
1726 buckets[t[i] as usize] += 1;
1727 buckets[t[i + 1] as usize] += 1;
1728 buckets[t[i + 2] as usize] += 1;
1729 buckets[t[i + 3] as usize] += 1;
1730 buckets[t[i + 4] as usize] += 1;
1731 buckets[t[i + 5] as usize] += 1;
1732 buckets[t[i + 6] as usize] += 1;
1733 buckets[t[i + 7] as usize] += 1;
1734 i += 8;
1735 }
1736
1737 j += 7;
1738 while i < j {
1739 buckets[t[i] as usize] += 1;
1740 i += 1;
1741 }
1742}
1743
1744#[doc(hidden)]
1746pub fn initialize_buckets_start_and_end_8u(
1747 buckets: &mut [SaSint],
1748 freq: Option<&mut [SaSint]>,
1749) -> SaSint {
1750 let start_offset = 6 * ALPHABET_SIZE;
1751 let end_offset = 7 * ALPHABET_SIZE;
1752 let mut k = -1isize;
1753 let mut sum = 0;
1754
1755 match freq {
1756 Some(freq) => {
1757 for j in 0..ALPHABET_SIZE {
1758 let i = buckets_index4(j, 0);
1759 let total = buckets[i] + buckets[i + 1] + buckets[i + 2] + buckets[i + 3];
1760 buckets[start_offset + j] = sum;
1761 sum += total;
1762 buckets[end_offset + j] = sum;
1763 if total > 0 {
1764 k = j as isize;
1765 }
1766 freq[j] = total;
1767 }
1768 }
1769 None => {
1770 for j in 0..ALPHABET_SIZE {
1771 let i = buckets_index4(j, 0);
1772 let total = buckets[i] + buckets[i + 1] + buckets[i + 2] + buckets[i + 3];
1773 buckets[start_offset + j] = sum;
1774 sum += total;
1775 buckets[end_offset + j] = sum;
1776 if total > 0 {
1777 k = j as isize;
1778 }
1779 }
1780 }
1781 }
1782
1783 (k + 1) as SaSint
1784}
1785
1786#[doc(hidden)]
1788pub fn initialize_buckets_start_and_end_32s_6k(k: SaSint, buckets: &mut [SaSint]) {
1789 let k_usize = usize::try_from(k).expect("k must be non-negative");
1790 let start_offset = 4 * k_usize;
1791 let end_offset = 5 * k_usize;
1792 let mut sum = 0;
1793 for j in 0..k_usize {
1794 let i = buckets_index4(j, 0);
1795 buckets[start_offset + j] = sum;
1796 sum += buckets[i] + buckets[i + 1] + buckets[i + 2] + buckets[i + 3];
1797 buckets[end_offset + j] = sum;
1798 }
1799}
1800
1801#[doc(hidden)]
1803pub fn initialize_buckets_start_and_end_32s_4k(k: SaSint, buckets: &mut [SaSint]) {
1804 let k_usize = usize::try_from(k).expect("k must be non-negative");
1805 let start_offset = 2 * k_usize;
1806 let end_offset = 3 * k_usize;
1807 let mut sum = 0;
1808 for j in 0..k_usize {
1809 let i = buckets_index2(j, 0);
1810 buckets[start_offset + j] = sum;
1811 sum += buckets[i] + buckets[i + 1];
1812 buckets[end_offset + j] = sum;
1813 }
1814}
1815
1816#[doc(hidden)]
1818pub fn initialize_buckets_end_32s_2k(k: SaSint, buckets: &mut [SaSint]) {
1819 let k_usize = usize::try_from(k).expect("k must be non-negative");
1820 let mut sum0 = 0;
1821 for j in 0..k_usize {
1822 let i = buckets_index2(j, 0);
1823 sum0 += buckets[i] + buckets[i + 1];
1824 buckets[i] = sum0;
1825 }
1826}
1827
1828#[doc(hidden)]
1830pub fn initialize_buckets_start_and_end_32s_2k(k: SaSint, buckets: &mut [SaSint]) {
1831 let k_usize = usize::try_from(k).expect("k must be non-negative");
1832 for j in 0..k_usize {
1833 let i = buckets_index2(j, 0);
1834 buckets[j] = buckets[i];
1835 }
1836 buckets[k_usize] = 0;
1837 for j in 1..k_usize {
1838 buckets[k_usize + j] = buckets[j - 1];
1839 }
1840}
1841
1842#[doc(hidden)]
1844pub fn initialize_buckets_start_32s_1k(k: SaSint, buckets: &mut [SaSint]) {
1845 let k_usize = usize::try_from(k).expect("k must be non-negative");
1846 let mut sum = 0;
1847 for bucket in buckets.iter_mut().take(k_usize) {
1848 let tmp = *bucket;
1849 *bucket = sum;
1850 sum += tmp;
1851 }
1852}
1853
1854#[doc(hidden)]
1856pub fn initialize_buckets_end_32s_1k(k: SaSint, buckets: &mut [SaSint]) {
1857 let k_usize = usize::try_from(k).expect("k must be non-negative");
1858 let mut sum = 0;
1859 for bucket in buckets.iter_mut().take(k_usize) {
1860 sum += *bucket;
1861 *bucket = sum;
1862 }
1863}
1864
1865#[doc(hidden)]
1867pub fn initialize_buckets_for_lms_suffixes_radix_sort_8u(
1868 t: &[u8],
1869 buckets: &mut [SaSint],
1870 mut first_lms_suffix: SaSint,
1871) -> SaSint {
1872 let mut f0 = 0usize;
1873 let mut f1: usize;
1874 let mut c0 = t[first_lms_suffix as usize] as FastSint;
1875 let mut c1: FastSint;
1876
1877 while {
1878 first_lms_suffix -= 1;
1879 first_lms_suffix >= 0
1880 } {
1881 c1 = c0;
1882 c0 = t[first_lms_suffix as usize] as FastSint;
1883 f1 = f0;
1884 f0 = usize::from(c0 > (c1 - f1 as FastSint));
1885 let idx = 4 * c1 as usize + (f1 + f1 + f0);
1886 buckets[idx] -= 1;
1887 }
1888 buckets[4 * c0 as usize + (f0 + f0)] -= 1;
1889
1890 let temp_offset = 4 * ALPHABET_SIZE;
1891 let mut sum = 0;
1892 for j in 0..ALPHABET_SIZE {
1893 let i = 4 * j;
1894 let tj = 2 * j;
1895 buckets[temp_offset + tj + 1] = sum;
1896 sum += buckets[i + 1] + buckets[i + 3];
1897 buckets[temp_offset + tj] = sum;
1898 }
1899 sum
1900}
1901
1902#[doc(hidden)]
1904pub fn initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
1905 t: &[SaSint],
1906 k: SaSint,
1907 buckets: &mut [SaSint],
1908 first_lms_suffix: SaSint,
1909) {
1910 let _k_usize = usize::try_from(k).expect("k must be non-negative");
1911 buckets[buckets_index2(t[first_lms_suffix as usize] as usize, 0)] += 1;
1912 buckets[buckets_index2(t[first_lms_suffix as usize] as usize, 1)] -= 1;
1913
1914 let mut sum0 = 0;
1915 let mut sum1 = 0;
1916 for j in 0..usize::try_from(k).unwrap() {
1917 let i = buckets_index2(j, 0);
1918 sum0 += buckets[i] + buckets[i + 1];
1919 sum1 += buckets[i + 1];
1920 buckets[i] = sum0;
1921 buckets[i + 1] = sum1;
1922 }
1923}
1924
1925#[doc(hidden)]
1927pub fn initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
1928 t: &[SaSint],
1929 k: SaSint,
1930 buckets: &mut [SaSint],
1931 mut first_lms_suffix: SaSint,
1932) -> SaSint {
1933 let mut f0 = 0usize;
1934 let mut f1: usize;
1935 let mut c0 = t[first_lms_suffix as usize] as FastSint;
1936 let mut c1: FastSint;
1937
1938 while {
1939 first_lms_suffix -= 1;
1940 first_lms_suffix >= 0
1941 } {
1942 c1 = c0;
1943 c0 = t[first_lms_suffix as usize] as FastSint;
1944 f1 = f0;
1945 f0 = usize::from(c0 > (c1 - f1 as FastSint));
1946 buckets[4 * c1 as usize + (f1 + f1 + f0)] -= 1;
1947 }
1948 buckets[4 * c0 as usize + (f0 + f0)] -= 1;
1949
1950 let temp_offset = 4 * usize::try_from(k).unwrap();
1951 let mut sum = 0;
1952 for j in 0..usize::try_from(k).unwrap() {
1953 let i = 4 * j;
1954 sum += buckets[i + 1] + buckets[i + 3];
1955 buckets[temp_offset + j] = sum;
1956 }
1957 sum
1958}
1959
1960#[doc(hidden)]
1962pub fn initialize_buckets_for_radix_and_partial_sorting_32s_4k(
1963 t: &[SaSint],
1964 k: SaSint,
1965 buckets: &mut [SaSint],
1966 first_lms_suffix: SaSint,
1967) {
1968 let k_usize = usize::try_from(k).expect("k must be non-negative");
1969 let start_offset = 2 * k_usize;
1970 let end_offset = 3 * k_usize;
1971
1972 buckets[buckets_index2(t[first_lms_suffix as usize] as usize, 0)] += 1;
1973 buckets[buckets_index2(t[first_lms_suffix as usize] as usize, 1)] -= 1;
1974
1975 let mut sum0 = 0;
1976 let mut sum1 = 0;
1977 for j in 0..k_usize {
1978 let i = buckets_index2(j, 0);
1979 buckets[start_offset + j] = sum1;
1980 sum0 += buckets[i + 1];
1981 sum1 += buckets[i] + buckets[i + 1];
1982 buckets[i + 1] = sum0;
1983 buckets[end_offset + j] = sum1;
1984 }
1985}
1986
1987#[doc(hidden)]
1989pub fn radix_sort_lms_suffixes_8u(
1990 t: &[u8],
1991 sa: &mut [SaSint],
1992 induction_bucket: &mut [SaSint],
1993 omp_block_start: FastSint,
1994 omp_block_size: FastSint,
1995) {
1996 let prefetch_distance = 64 as FastSint;
1997 let mut i = omp_block_start + omp_block_size - 1;
1998 let mut j = omp_block_start + prefetch_distance + 3;
1999
2000 while i >= j {
2001 let p0 = sa[i as usize];
2002 let idx0 = buckets_index2(t[p0 as usize] as usize, 0);
2003 induction_bucket[idx0] -= 1;
2004 sa[induction_bucket[idx0] as usize] = p0;
2005
2006 let p1 = sa[(i - 1) as usize];
2007 let idx1 = buckets_index2(t[p1 as usize] as usize, 0);
2008 induction_bucket[idx1] -= 1;
2009 sa[induction_bucket[idx1] as usize] = p1;
2010
2011 let p2 = sa[(i - 2) as usize];
2012 let idx2 = buckets_index2(t[p2 as usize] as usize, 0);
2013 induction_bucket[idx2] -= 1;
2014 sa[induction_bucket[idx2] as usize] = p2;
2015
2016 let p3 = sa[(i - 3) as usize];
2017 let idx3 = buckets_index2(t[p3 as usize] as usize, 0);
2018 induction_bucket[idx3] -= 1;
2019 sa[induction_bucket[idx3] as usize] = p3;
2020
2021 i -= 4;
2022 }
2023
2024 j -= prefetch_distance + 3;
2025 while i >= j {
2026 let p = sa[i as usize];
2027 let idx = buckets_index2(t[p as usize] as usize, 0);
2028 induction_bucket[idx] -= 1;
2029 sa[induction_bucket[idx] as usize] = p;
2030 i -= 1;
2031 }
2032}
2033
2034#[doc(hidden)]
2036pub fn radix_sort_lms_suffixes_8u_omp(
2037 t: &[u8],
2038 sa: &mut [SaSint],
2039 n: SaSint,
2040 m: SaSint,
2041 flags: SaSint,
2042 buckets: &mut [SaSint],
2043 threads: SaSint,
2044 thread_state: &mut [ThreadState],
2045) {
2046 if (flags & LIBSAIS_FLAGS_GSA) != 0 {
2047 buckets[4 * ALPHABET_SIZE] -= 1;
2048 }
2049
2050 let omp_num_threads = if threads > 1 && n >= 65_536 && m >= 65_536 {
2051 usize::try_from(threads)
2052 .expect("threads must be non-negative")
2053 .min(thread_state.len())
2054 .max(1)
2055 } else {
2056 1
2057 };
2058
2059 if omp_num_threads == 1 {
2060 radix_sort_lms_suffixes_8u(
2061 t,
2062 sa,
2063 &mut buckets[4 * ALPHABET_SIZE..],
2064 n as FastSint - m as FastSint + 1,
2065 m as FastSint - 1,
2066 );
2067 return;
2068 }
2069
2070 let src_bucket: &[SaSint] = &buckets[4 * ALPHABET_SIZE..];
2071 let m_values: Vec<FastSint> = thread_state[..omp_num_threads]
2072 .iter()
2073 .map(|s| s.m)
2074 .collect();
2075 let m_total = m as FastSint;
2076 let sa_ptr = SyncMutPtr::new(sa);
2077
2078 run_rayon_with_threads(omp_num_threads, || {
2079 thread_state[..omp_num_threads]
2080 .par_iter_mut()
2081 .enumerate()
2082 .for_each(|(thread_num, state)| {
2083 for (i, j) in (0..=buckets_index2(ALPHABET_SIZE - 1, 0))
2084 .step_by(buckets_index2(1, 0))
2085 .zip((buckets_index4(0, 1)..).step_by(buckets_index4(1, 0)))
2086 {
2087 state.buckets[i] = src_bucket[i] - state.buckets[j];
2088 }
2089
2090 let mut omp_block_start: FastSint = 0;
2091 for &other_m in m_values[thread_num..omp_num_threads].iter().rev() {
2092 omp_block_start += other_m;
2093 }
2094
2095 let mut omp_block_size = m_values[thread_num];
2096 if omp_block_start == m_total && omp_block_size > 0 {
2097 omp_block_start -= 1;
2098 omp_block_size -= 1;
2099 }
2100
2101 let sa = unsafe { sa_ptr.as_slice() };
2103 radix_sort_lms_suffixes_8u(
2104 t,
2105 sa,
2106 &mut state.buckets,
2107 n as FastSint - omp_block_start,
2108 omp_block_size,
2109 );
2110 });
2111 });
2112}
2113
2114#[doc(hidden)]
2116pub fn radix_sort_lms_suffixes_32s_6k(
2117 t: &[SaSint],
2118 sa: &mut [SaSint],
2119 induction_bucket: &mut [SaSint],
2120 omp_block_start: FastSint,
2121 omp_block_size: FastSint,
2122) {
2123 let prefetch_distance = 64 as FastSint;
2124 let mut i = omp_block_start + omp_block_size - 1;
2125 let mut j = omp_block_start + 2 * prefetch_distance + 3;
2126
2127 while i >= j {
2128 let p0 = sa[i as usize];
2129 let idx0 = t[p0 as usize] as usize;
2130 induction_bucket[idx0] -= 1;
2131 sa[induction_bucket[idx0] as usize] = p0;
2132
2133 let p1 = sa[(i - 1) as usize];
2134 let idx1 = t[p1 as usize] as usize;
2135 induction_bucket[idx1] -= 1;
2136 sa[induction_bucket[idx1] as usize] = p1;
2137
2138 let p2 = sa[(i - 2) as usize];
2139 let idx2 = t[p2 as usize] as usize;
2140 induction_bucket[idx2] -= 1;
2141 sa[induction_bucket[idx2] as usize] = p2;
2142
2143 let p3 = sa[(i - 3) as usize];
2144 let idx3 = t[p3 as usize] as usize;
2145 induction_bucket[idx3] -= 1;
2146 sa[induction_bucket[idx3] as usize] = p3;
2147
2148 i -= 4;
2149 }
2150
2151 j -= 2 * prefetch_distance + 3;
2152 while i >= j {
2153 let p = sa[i as usize];
2154 let idx = t[p as usize] as usize;
2155 induction_bucket[idx] -= 1;
2156 sa[induction_bucket[idx] as usize] = p;
2157 i -= 1;
2158 }
2159}
2160
2161#[doc(hidden)]
2163pub fn radix_sort_lms_suffixes_32s_2k(
2164 t: &[SaSint],
2165 sa: &mut [SaSint],
2166 induction_bucket: &mut [SaSint],
2167 omp_block_start: FastSint,
2168 omp_block_size: FastSint,
2169) {
2170 let prefetch_distance = 64 as FastSint;
2171 let mut i = omp_block_start + omp_block_size - 1;
2172 let mut j = omp_block_start + 2 * prefetch_distance + 3;
2173
2174 while i >= j {
2175 let p0 = sa[i as usize];
2176 let idx0 = buckets_index2(t[p0 as usize] as usize, 0);
2177 induction_bucket[idx0] -= 1;
2178 sa[induction_bucket[idx0] as usize] = p0;
2179
2180 let p1 = sa[(i - 1) as usize];
2181 let idx1 = buckets_index2(t[p1 as usize] as usize, 0);
2182 induction_bucket[idx1] -= 1;
2183 sa[induction_bucket[idx1] as usize] = p1;
2184
2185 let p2 = sa[(i - 2) as usize];
2186 let idx2 = buckets_index2(t[p2 as usize] as usize, 0);
2187 induction_bucket[idx2] -= 1;
2188 sa[induction_bucket[idx2] as usize] = p2;
2189
2190 let p3 = sa[(i - 3) as usize];
2191 let idx3 = buckets_index2(t[p3 as usize] as usize, 0);
2192 induction_bucket[idx3] -= 1;
2193 sa[induction_bucket[idx3] as usize] = p3;
2194
2195 i -= 4;
2196 }
2197
2198 j -= 2 * prefetch_distance + 3;
2199 while i >= j {
2200 let p = sa[i as usize];
2201 let idx = buckets_index2(t[p as usize] as usize, 0);
2202 induction_bucket[idx] -= 1;
2203 sa[induction_bucket[idx] as usize] = p;
2204 i -= 1;
2205 }
2206}
2207
2208#[doc(hidden)]
2210pub fn radix_sort_lms_suffixes_32s_block_gather(
2211 t: &[SaSint],
2212 sa: &[SaSint],
2213 cache: &mut [ThreadCache],
2214 omp_block_start: FastSint,
2215 omp_block_size: FastSint,
2216) {
2217 if omp_block_size <= 0 {
2218 return;
2219 }
2220
2221 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
2222 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
2223 let mut i = start;
2224 let mut j = if size > 67 { start + size - 67 } else { start };
2225
2226 while i < j {
2227 for current in [i, i + 1, i + 2, i + 3] {
2228 let ci = current - start;
2229 let index = sa[current];
2230 cache[ci].index = index;
2231 cache[ci].symbol = t[index as usize];
2232 }
2233 i += 4;
2234 }
2235
2236 j = if size > 67 { j + 67 } else { start + size };
2237 while i < j {
2238 let ci = i - start;
2239 let index = sa[i];
2240 cache[ci].index = index;
2241 cache[ci].symbol = t[index as usize];
2242 i += 1;
2243 }
2244}
2245
2246#[doc(hidden)]
2248pub fn radix_sort_lms_suffixes_32s_6k_block_sort(
2249 induction_bucket: &mut [SaSint],
2250 cache: &mut [ThreadCache],
2251 omp_block_start: FastSint,
2252 omp_block_size: FastSint,
2253) {
2254 if omp_block_size <= 0 {
2255 return;
2256 }
2257
2258 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
2259 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
2260 let mut i = start + size - 1;
2261 let mut j = start + 64 + 3;
2262
2263 while i >= j {
2264 for current in [i, i - 1, i - 2, i - 3] {
2265 let ci = current - start;
2266 let v = cache[ci].symbol as usize;
2267 induction_bucket[v] -= 1;
2268 cache[ci].symbol = induction_bucket[v];
2269 }
2270 i -= 4;
2271 }
2272
2273 j -= 64 + 3;
2274 while i >= j {
2275 let ci = i - start;
2276 let v = cache[ci].symbol as usize;
2277 induction_bucket[v] -= 1;
2278 cache[ci].symbol = induction_bucket[v];
2279 if i == 0 {
2280 break;
2281 }
2282 i -= 1;
2283 }
2284}
2285
2286#[doc(hidden)]
2288pub fn radix_sort_lms_suffixes_32s_2k_block_sort(
2289 induction_bucket: &mut [SaSint],
2290 cache: &mut [ThreadCache],
2291 omp_block_start: FastSint,
2292 omp_block_size: FastSint,
2293) {
2294 if omp_block_size <= 0 {
2295 return;
2296 }
2297
2298 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
2299 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
2300 let mut i = start + size - 1;
2301 let mut j = start + 64 + 3;
2302
2303 while i >= j {
2304 for current in [i, i - 1, i - 2, i - 3] {
2305 let ci = current - start;
2306 let v = buckets_index2(cache[ci].symbol as usize, 0);
2307 induction_bucket[v] -= 1;
2308 cache[ci].symbol = induction_bucket[v];
2309 }
2310 i -= 4;
2311 }
2312
2313 j -= 64 + 3;
2314 while i >= j {
2315 let ci = i - start;
2316 let v = buckets_index2(cache[ci].symbol as usize, 0);
2317 induction_bucket[v] -= 1;
2318 cache[ci].symbol = induction_bucket[v];
2319 if i == 0 {
2320 break;
2321 }
2322 i -= 1;
2323 }
2324}
2325
2326#[doc(hidden)]
2328pub fn radix_sort_lms_suffixes_32s_6k_block_omp(
2329 t: &[SaSint],
2330 sa: &mut [SaSint],
2331 induction_bucket: &mut [SaSint],
2332 cache: &mut [ThreadCache],
2333 block_start: FastSint,
2334 block_size: FastSint,
2335 threads: SaSint,
2336) {
2337 if threads <= 1 || block_size < 16_384 {
2338 radix_sort_lms_suffixes_32s_6k(t, sa, induction_bucket, block_start, block_size);
2339 return;
2340 }
2341
2342 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
2343 let threads_usize = usize::try_from(threads)
2344 .expect("threads must be positive")
2345 .min(block_size_usize.max(1));
2346 let omp_block_stride = (block_size_usize / threads_usize) & !15usize;
2347
2348 {
2349 let sa_ro: &[SaSint] = sa;
2350 let t_ro: &[SaSint] = t;
2351 let cache_ptr = SyncMutPtr::new(cache);
2352 run_rayon_with_threads(threads_usize, || {
2353 (0..threads_usize)
2354 .into_par_iter()
2355 .for_each(|omp_thread_num| {
2356 let omp_block_start = omp_thread_num * omp_block_stride;
2357 let omp_block_size = if omp_thread_num + 1 < threads_usize {
2358 omp_block_stride
2359 } else {
2360 block_size_usize - omp_block_start
2361 };
2362 if omp_block_size > 0 {
2363 let cache = unsafe { cache_ptr.as_slice() };
2365 radix_sort_lms_suffixes_32s_block_gather(
2366 t_ro,
2367 sa_ro,
2368 &mut cache[omp_block_start..],
2369 block_start + omp_block_start as FastSint,
2370 omp_block_size as FastSint,
2371 );
2372 }
2373 });
2374 });
2375 }
2376
2377 radix_sort_lms_suffixes_32s_6k_block_sort(induction_bucket, cache, block_start, block_size);
2378
2379 {
2380 let sa_ptr = SyncMutPtr::new(sa);
2381 let cache_ro: &[ThreadCache] = cache;
2382 run_rayon_with_threads(threads_usize, || {
2383 (0..threads_usize)
2384 .into_par_iter()
2385 .for_each(|omp_thread_num| {
2386 let omp_block_start = omp_thread_num * omp_block_stride;
2387 let omp_block_size = if omp_thread_num + 1 < threads_usize {
2388 omp_block_stride
2389 } else {
2390 block_size_usize - omp_block_start
2391 };
2392 if omp_block_size > 0 {
2393 let sa = unsafe { sa_ptr.as_slice() };
2395 place_cached_suffixes(
2396 sa,
2397 &cache_ro[omp_block_start..],
2398 0,
2399 omp_block_size as FastSint,
2400 );
2401 }
2402 });
2403 });
2404 }
2405}
2406
2407#[doc(hidden)]
2409pub fn radix_sort_lms_suffixes_32s_2k_block_omp(
2410 t: &[SaSint],
2411 sa: &mut [SaSint],
2412 induction_bucket: &mut [SaSint],
2413 cache: &mut [ThreadCache],
2414 block_start: FastSint,
2415 block_size: FastSint,
2416 threads: SaSint,
2417) {
2418 if threads <= 1 || block_size < 16_384 {
2419 radix_sort_lms_suffixes_32s_2k(t, sa, induction_bucket, block_start, block_size);
2420 return;
2421 }
2422
2423 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
2424 let threads_usize = usize::try_from(threads)
2425 .expect("threads must be positive")
2426 .min(block_size_usize.max(1));
2427 let omp_block_stride = (block_size_usize / threads_usize) & !15usize;
2428
2429 {
2430 let sa_ro: &[SaSint] = sa;
2431 let t_ro: &[SaSint] = t;
2432 let cache_ptr = SyncMutPtr::new(cache);
2433 run_rayon_with_threads(threads_usize, || {
2434 (0..threads_usize)
2435 .into_par_iter()
2436 .for_each(|omp_thread_num| {
2437 let omp_block_start = omp_thread_num * omp_block_stride;
2438 let omp_block_size = if omp_thread_num + 1 < threads_usize {
2439 omp_block_stride
2440 } else {
2441 block_size_usize - omp_block_start
2442 };
2443 if omp_block_size > 0 {
2444 let cache = unsafe { cache_ptr.as_slice() };
2445 radix_sort_lms_suffixes_32s_block_gather(
2446 t_ro,
2447 sa_ro,
2448 &mut cache[omp_block_start..],
2449 block_start + omp_block_start as FastSint,
2450 omp_block_size as FastSint,
2451 );
2452 }
2453 });
2454 });
2455 }
2456
2457 radix_sort_lms_suffixes_32s_2k_block_sort(induction_bucket, cache, block_start, block_size);
2458
2459 {
2460 let sa_ptr = SyncMutPtr::new(sa);
2461 let cache_ro: &[ThreadCache] = cache;
2462 run_rayon_with_threads(threads_usize, || {
2463 (0..threads_usize)
2464 .into_par_iter()
2465 .for_each(|omp_thread_num| {
2466 let omp_block_start = omp_thread_num * omp_block_stride;
2467 let omp_block_size = if omp_thread_num + 1 < threads_usize {
2468 omp_block_stride
2469 } else {
2470 block_size_usize - omp_block_start
2471 };
2472 if omp_block_size > 0 {
2473 let sa = unsafe { sa_ptr.as_slice() };
2474 place_cached_suffixes(
2475 sa,
2476 &cache_ro[omp_block_start..],
2477 0,
2478 omp_block_size as FastSint,
2479 );
2480 }
2481 });
2482 });
2483 }
2484}
2485
2486#[doc(hidden)]
2488pub fn radix_sort_lms_suffixes_32s_6k_omp(
2489 t: &[SaSint],
2490 sa: &mut [SaSint],
2491 n: SaSint,
2492 m: SaSint,
2493 induction_bucket: &mut [SaSint],
2494 threads: SaSint,
2495 _thread_state: &mut [ThreadState],
2496) {
2497 if threads <= 1 || m < 65_536 {
2498 radix_sort_lms_suffixes_32s_6k(
2499 t,
2500 sa,
2501 induction_bucket,
2502 n as FastSint - m as FastSint + 1,
2503 m as FastSint - 1,
2504 );
2505 return;
2506 }
2507
2508 let threads_usize = usize::try_from(threads).expect("threads must be positive");
2509 let mut cache = vec![ThreadCache::default(); threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE];
2510 let mut block_start = 0usize;
2511 let m_usize = usize::try_from(m).expect("m must be non-negative");
2512 let n_usize = usize::try_from(n).expect("n must be non-negative");
2513 let last = m_usize - 1;
2514
2515 while block_start < last {
2516 let block_end = (block_start + threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE).min(last);
2517 radix_sort_lms_suffixes_32s_6k_block_omp(
2518 t,
2519 sa,
2520 induction_bucket,
2521 &mut cache,
2522 (n_usize - block_end) as FastSint,
2523 (block_end - block_start) as FastSint,
2524 threads,
2525 );
2526 block_start = block_end;
2527 }
2528}
2529
2530#[doc(hidden)]
2532pub fn radix_sort_lms_suffixes_32s_2k_omp(
2533 t: &[SaSint],
2534 sa: &mut [SaSint],
2535 n: SaSint,
2536 m: SaSint,
2537 induction_bucket: &mut [SaSint],
2538 threads: SaSint,
2539 _thread_state: &mut [ThreadState],
2540) {
2541 if threads <= 1 || m < 65_536 {
2542 radix_sort_lms_suffixes_32s_2k(
2543 t,
2544 sa,
2545 induction_bucket,
2546 n as FastSint - m as FastSint + 1,
2547 m as FastSint - 1,
2548 );
2549 return;
2550 }
2551
2552 let threads_usize = usize::try_from(threads).expect("threads must be positive");
2553 let mut cache = vec![ThreadCache::default(); threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE];
2554 let mut block_start = 0usize;
2555 let m_usize = usize::try_from(m).expect("m must be non-negative");
2556 let n_usize = usize::try_from(n).expect("n must be non-negative");
2557 let last = m_usize - 1;
2558
2559 while block_start < last {
2560 let block_end = (block_start + threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE).min(last);
2561 radix_sort_lms_suffixes_32s_2k_block_omp(
2562 t,
2563 sa,
2564 induction_bucket,
2565 &mut cache,
2566 (n_usize - block_end) as FastSint,
2567 (block_end - block_start) as FastSint,
2568 threads,
2569 );
2570 block_start = block_end;
2571 }
2572}
2573
2574#[doc(hidden)]
2576pub fn radix_sort_lms_suffixes_32s_1k(
2577 t: &[SaSint],
2578 sa: &mut [SaSint],
2579 n: SaSint,
2580 buckets: &mut [SaSint],
2581) -> SaSint {
2582 let n_usize = usize::try_from(n).expect("n must be non-negative");
2583 let mut i = n as FastSint - 2;
2584 let mut m = 0;
2585 let mut f0 = 1usize;
2586 let mut f1: usize;
2587 let mut c0 = t[n_usize - 1] as FastSint;
2588 let mut c1: FastSint;
2589 let mut c2 = 0 as FastSint;
2590
2591 while i >= 67 {
2592 c1 = t[i as usize] as FastSint;
2593 f1 = usize::from(c1 > (c0 - f0 as FastSint));
2594 if (f1 & !f0) != 0 {
2595 c2 = c0;
2596 buckets[c2 as usize] -= 1;
2597 sa[buckets[c2 as usize] as usize] = (i + 1) as SaSint;
2598 m += 1;
2599 }
2600
2601 c0 = t[(i - 1) as usize] as FastSint;
2602 f0 = usize::from(c0 > (c1 - f1 as FastSint));
2603 if (f0 & !f1) != 0 {
2604 c2 = c1;
2605 buckets[c2 as usize] -= 1;
2606 sa[buckets[c2 as usize] as usize] = i as SaSint;
2607 m += 1;
2608 }
2609
2610 c1 = t[(i - 2) as usize] as FastSint;
2611 f1 = usize::from(c1 > (c0 - f0 as FastSint));
2612 if (f1 & !f0) != 0 {
2613 c2 = c0;
2614 buckets[c2 as usize] -= 1;
2615 sa[buckets[c2 as usize] as usize] = (i - 1) as SaSint;
2616 m += 1;
2617 }
2618
2619 c0 = t[(i - 3) as usize] as FastSint;
2620 f0 = usize::from(c0 > (c1 - f1 as FastSint));
2621 if (f0 & !f1) != 0 {
2622 c2 = c1;
2623 buckets[c2 as usize] -= 1;
2624 sa[buckets[c2 as usize] as usize] = (i - 2) as SaSint;
2625 m += 1;
2626 }
2627
2628 i -= 4;
2629 }
2630
2631 while i >= 0 {
2632 c1 = c0;
2633 c0 = t[i as usize] as FastSint;
2634 f1 = f0;
2635 f0 = usize::from(c0 > (c1 - f1 as FastSint));
2636 if (f0 & !f1) != 0 {
2637 c2 = c1;
2638 buckets[c2 as usize] -= 1;
2639 sa[buckets[c2 as usize] as usize] = (i + 1) as SaSint;
2640 m += 1;
2641 }
2642 i -= 1;
2643 }
2644
2645 if m > 1 {
2646 sa[buckets[c2 as usize] as usize] = 0;
2647 }
2648
2649 m
2650}
2651
2652#[doc(hidden)]
2654pub fn radix_sort_set_markers_32s_6k(
2655 sa: &mut [SaSint],
2656 induction_bucket: &[SaSint],
2657 omp_block_start: FastSint,
2658 omp_block_size: FastSint,
2659) {
2660 let mut i = omp_block_start;
2661 let mut j = omp_block_start + omp_block_size - 67;
2662
2663 while i < j {
2664 sa[induction_bucket[i as usize] as usize] |= SAINT_MIN;
2665 sa[induction_bucket[(i + 1) as usize] as usize] |= SAINT_MIN;
2666 sa[induction_bucket[(i + 2) as usize] as usize] |= SAINT_MIN;
2667 sa[induction_bucket[(i + 3) as usize] as usize] |= SAINT_MIN;
2668 i += 4;
2669 }
2670
2671 j += 67;
2672 while i < j {
2673 sa[induction_bucket[i as usize] as usize] |= SAINT_MIN;
2674 i += 1;
2675 }
2676}
2677
2678#[doc(hidden)]
2680pub fn radix_sort_set_markers_32s_4k(
2681 sa: &mut [SaSint],
2682 induction_bucket: &[SaSint],
2683 omp_block_start: FastSint,
2684 omp_block_size: FastSint,
2685) {
2686 let mut i = omp_block_start;
2687 let mut j = omp_block_start + omp_block_size - 67;
2688
2689 while i < j {
2690 sa[induction_bucket[buckets_index2(i as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2691 sa[induction_bucket[buckets_index2((i + 1) as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2692 sa[induction_bucket[buckets_index2((i + 2) as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2693 sa[induction_bucket[buckets_index2((i + 3) as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2694 i += 4;
2695 }
2696
2697 j += 67;
2698 while i < j {
2699 sa[induction_bucket[buckets_index2(i as usize, 0)] as usize] |= SUFFIX_GROUP_MARKER;
2700 i += 1;
2701 }
2702}
2703
2704#[doc(hidden)]
2706pub fn radix_sort_set_markers_32s_6k_omp(
2707 sa: &mut [SaSint],
2708 k: SaSint,
2709 induction_bucket: &[SaSint],
2710 threads: SaSint,
2711) {
2712 if k <= 1 {
2713 return;
2714 }
2715
2716 if threads <= 1 || k < 65_536 {
2717 radix_sort_set_markers_32s_6k(sa, induction_bucket, 0, k as FastSint - 1);
2718 return;
2719 }
2720
2721 let threads_usize = usize::try_from(threads).expect("threads must be positive");
2722 let last = usize::try_from(k - 1).expect("k must be positive");
2723 let stride = (last / threads_usize) & !15usize;
2724
2725 {
2726 let sa_ptr = SyncMutPtr::new(sa);
2727 run_rayon_with_threads(threads_usize, || {
2728 (0..threads_usize).into_par_iter().for_each(|thread| {
2729 let start = thread * stride;
2730 let end = if thread + 1 == threads_usize {
2731 last
2732 } else {
2733 start + stride
2734 };
2735 if end > start {
2736 let sa = unsafe { sa_ptr.as_slice() };
2738 radix_sort_set_markers_32s_6k(
2739 sa,
2740 induction_bucket,
2741 start as FastSint,
2742 (end - start) as FastSint,
2743 );
2744 }
2745 });
2746 });
2747 }
2748}
2749
2750#[doc(hidden)]
2752pub fn radix_sort_set_markers_32s_4k_omp(
2753 sa: &mut [SaSint],
2754 k: SaSint,
2755 induction_bucket: &[SaSint],
2756 threads: SaSint,
2757) {
2758 if k <= 1 {
2759 return;
2760 }
2761
2762 if threads <= 1 || k < 65_536 {
2763 radix_sort_set_markers_32s_4k(sa, induction_bucket, 0, k as FastSint - 1);
2764 return;
2765 }
2766
2767 let threads_usize = usize::try_from(threads).expect("threads must be positive");
2768 let last = usize::try_from(k - 1).expect("k must be positive");
2769 let stride = (last / threads_usize) & !15usize;
2770
2771 {
2772 let sa_ptr = SyncMutPtr::new(sa);
2773 run_rayon_with_threads(threads_usize, || {
2774 (0..threads_usize).into_par_iter().for_each(|thread| {
2775 let start = thread * stride;
2776 let end = if thread + 1 == threads_usize {
2777 last
2778 } else {
2779 start + stride
2780 };
2781 if end > start {
2782 let sa = unsafe { sa_ptr.as_slice() };
2783 radix_sort_set_markers_32s_4k(
2784 sa,
2785 induction_bucket,
2786 start as FastSint,
2787 (end - start) as FastSint,
2788 );
2789 }
2790 });
2791 });
2792 }
2793}
2794
2795#[doc(hidden)]
2797pub fn initialize_buckets_for_partial_sorting_8u(
2798 t: &[u8],
2799 buckets: &mut [SaSint],
2800 first_lms_suffix: SaSint,
2801 left_suffixes_count: SaSint,
2802) {
2803 let temp_offset = 4 * ALPHABET_SIZE;
2804 buckets[buckets_index4(t[first_lms_suffix as usize] as usize, 1)] += 1;
2805
2806 let mut sum0 = left_suffixes_count + 1;
2807 let mut sum1 = 0;
2808 for j in 0..ALPHABET_SIZE {
2809 let i = buckets_index4(j, 0);
2810 let tj = buckets_index2(j, 0);
2811 buckets[temp_offset + tj] = sum0;
2812 sum0 += buckets[i] + buckets[i + 2];
2813 sum1 += buckets[i + 1];
2814 buckets[tj] = sum0;
2815 buckets[tj + 1] = sum1;
2816 }
2817}
2818
2819#[doc(hidden)]
2821pub fn initialize_buckets_for_partial_sorting_32s_6k(
2822 t: &[SaSint],
2823 k: SaSint,
2824 buckets: &mut [SaSint],
2825 first_lms_suffix: SaSint,
2826 left_suffixes_count: SaSint,
2827) {
2828 let k_usize = usize::try_from(k).expect("k must be non-negative");
2829 let temp_offset = 4 * k_usize;
2830 let first_symbol = t[first_lms_suffix as usize] as usize;
2831 let mut sum0 = left_suffixes_count + 1;
2832 let mut sum1 = 0;
2833 let mut sum2 = 0;
2834
2835 for j in 0..first_symbol {
2836 let i = buckets_index4(j, 0);
2837 let tj = buckets_index2(j, 0);
2838 let ss = buckets[i];
2839 let ls = buckets[i + 1];
2840 let sl = buckets[i + 2];
2841 let ll = buckets[i + 3];
2842
2843 buckets[i] = sum0;
2844 buckets[i + 1] = sum2;
2845 buckets[i + 2] = 0;
2846 buckets[i + 3] = 0;
2847
2848 sum0 += ss + sl;
2849 sum1 += ls;
2850 sum2 += ls + ll;
2851
2852 buckets[temp_offset + tj] = sum0;
2853 buckets[temp_offset + tj + 1] = sum1;
2854 }
2855
2856 sum1 += 1;
2857 for j in first_symbol..k_usize {
2858 let i = buckets_index4(j, 0);
2859 let tj = buckets_index2(j, 0);
2860 let ss = buckets[i];
2861 let ls = buckets[i + 1];
2862 let sl = buckets[i + 2];
2863 let ll = buckets[i + 3];
2864
2865 buckets[i] = sum0;
2866 buckets[i + 1] = sum2;
2867 buckets[i + 2] = 0;
2868 buckets[i + 3] = 0;
2869
2870 sum0 += ss + sl;
2871 sum1 += ls;
2872 sum2 += ls + ll;
2873
2874 buckets[temp_offset + tj] = sum0;
2875 buckets[temp_offset + tj + 1] = sum1;
2876 }
2877}
2878
2879#[doc(hidden)]
2881pub fn partial_sorting_scan_left_to_right_8u(
2882 t: &[u8],
2883 sa: &mut [SaSint],
2884 buckets: &mut [SaSint],
2885 mut d: SaSint,
2886 omp_block_start: FastSint,
2887 omp_block_size: FastSint,
2888) -> SaSint {
2889 let induction_offset = 4 * ALPHABET_SIZE;
2890 let distinct_offset = 2 * ALPHABET_SIZE;
2891 let prefetch_distance = 64 as FastSint;
2892 let mut i = omp_block_start;
2893 let mut j = if omp_block_size > prefetch_distance + 1 {
2894 omp_block_start + omp_block_size - prefetch_distance - 1
2895 } else {
2896 omp_block_start
2897 };
2898
2899 while i < j {
2900 let mut p0 = sa[i as usize];
2901 d += SaSint::from(p0 < 0);
2902 p0 &= SAINT_MAX;
2903 let v0 = buckets_index2(
2904 t[(p0 - 1) as usize] as usize,
2905 usize::from(t[(p0 - 2) as usize] >= t[(p0 - 1) as usize]),
2906 );
2907 let pos0 = buckets[induction_offset + v0] as usize;
2908 sa[pos0] = (p0 - 1) | (((buckets[distinct_offset + v0] != d) as SaSint) << (SAINT_BIT - 1));
2909 buckets[induction_offset + v0] += 1;
2910 buckets[distinct_offset + v0] = d;
2911
2912 let mut p1 = sa[(i + 1) as usize];
2913 d += SaSint::from(p1 < 0);
2914 p1 &= SAINT_MAX;
2915 let v1 = buckets_index2(
2916 t[(p1 - 1) as usize] as usize,
2917 usize::from(t[(p1 - 2) as usize] >= t[(p1 - 1) as usize]),
2918 );
2919 let pos1 = buckets[induction_offset + v1] as usize;
2920 sa[pos1] = (p1 - 1) | (((buckets[distinct_offset + v1] != d) as SaSint) << (SAINT_BIT - 1));
2921 buckets[induction_offset + v1] += 1;
2922 buckets[distinct_offset + v1] = d;
2923
2924 i += 2;
2925 }
2926
2927 j = omp_block_start + omp_block_size;
2928 while i < j {
2929 let mut p = sa[i as usize];
2930 d += SaSint::from(p < 0);
2931 p &= SAINT_MAX;
2932 let v = buckets_index2(
2933 t[(p - 1) as usize] as usize,
2934 usize::from(t[(p - 2) as usize] >= t[(p - 1) as usize]),
2935 );
2936 let pos = buckets[induction_offset + v] as usize;
2937 sa[pos] = (p - 1) | (((buckets[distinct_offset + v] != d) as SaSint) << (SAINT_BIT - 1));
2938 buckets[induction_offset + v] += 1;
2939 buckets[distinct_offset + v] = d;
2940 i += 1;
2941 }
2942
2943 d
2944}
2945
2946#[doc(hidden)]
2948pub fn partial_sorting_scan_left_to_right_8u_omp(
2949 t: &[u8],
2950 sa: &mut [SaSint],
2951 n: SaSint,
2952 k: SaSint,
2953 buckets: &mut [SaSint],
2954 left_suffixes_count: SaSint,
2955 mut d: SaSint,
2956 threads: SaSint,
2957 thread_state: &mut [ThreadState],
2958) -> SaSint {
2959 let v = buckets_index2(
2960 t[(n - 1) as usize] as usize,
2961 usize::from(t[(n - 2) as usize] >= t[(n - 1) as usize]),
2962 );
2963 let induction_offset = 4 * ALPHABET_SIZE;
2964 let distinct_offset = 2 * ALPHABET_SIZE;
2965 let pos = buckets[induction_offset + v] as usize;
2966 sa[pos] = (n - 1) | SAINT_MIN;
2967 buckets[induction_offset + v] += 1;
2968 d += 1;
2969 buckets[distinct_offset + v] = d;
2970
2971 if threads == 1 || left_suffixes_count < 65_536 {
2972 return partial_sorting_scan_left_to_right_8u(
2973 t,
2974 sa,
2975 buckets,
2976 d,
2977 0,
2978 left_suffixes_count as FastSint,
2979 );
2980 }
2981
2982 let mut block_start = 0usize;
2983 let left_suffixes_count =
2984 usize::try_from(left_suffixes_count).expect("left_suffixes_count must be non-negative");
2985 let threads_usize = usize::try_from(threads)
2986 .expect("threads must be non-negative")
2987 .min(thread_state.len())
2988 .max(1);
2989 while block_start < left_suffixes_count {
2990 if sa[block_start] == 0 {
2991 block_start += 1;
2992 } else {
2993 let mut block_max_end =
2994 block_start + threads_usize * (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * threads_usize);
2995 if block_max_end > left_suffixes_count {
2996 block_max_end = left_suffixes_count;
2997 }
2998 let mut block_end = block_start + 1;
2999 while block_end < block_max_end && sa[block_end] != 0 {
3000 block_end += 1;
3001 }
3002 let block_size = block_end - block_start;
3003
3004 if block_size < 32 {
3005 while block_start < block_end {
3006 let p = sa[block_start];
3007 d += SaSint::from(p < 0);
3008 let p = p & SAINT_MAX;
3009 let v = buckets_index2(
3010 t[(p - 1) as usize] as usize,
3011 usize::from(t[(p - 2) as usize] >= t[(p - 1) as usize]),
3012 );
3013 let pos = buckets[induction_offset + v] as usize;
3014 sa[pos] = (p - 1)
3015 | (((buckets[distinct_offset + v] != d) as SaSint) << (SAINT_BIT - 1));
3016 buckets[induction_offset + v] += 1;
3017 buckets[distinct_offset + v] = d;
3018 block_start += 1;
3019 }
3020 } else {
3021 d = partial_sorting_scan_left_to_right_8u_block_omp(
3022 t,
3023 sa,
3024 k,
3025 buckets,
3026 d,
3027 block_start as FastSint,
3028 block_size as FastSint,
3029 threads,
3030 thread_state,
3031 );
3032 block_start = block_end;
3033 }
3034 }
3035 }
3036
3037 d
3038}
3039
3040#[doc(hidden)]
3042pub fn partial_sorting_scan_left_to_right_32s_6k(
3043 t: &[SaSint],
3044 sa: &mut [SaSint],
3045 buckets: &mut [SaSint],
3046 mut d: SaSint,
3047 omp_block_start: FastSint,
3048 omp_block_size: FastSint,
3049) -> SaSint {
3050 let prefetch_distance: FastSint = 64;
3051
3052 let mut i = omp_block_start;
3053 let mut j = omp_block_start + omp_block_size - 2 * prefetch_distance - 1;
3054 while i < j {
3055 let mut p0 = sa[i as usize];
3056 d += SaSint::from(p0 < 0);
3057 p0 &= SAINT_MAX;
3058 let p0u = p0 as usize;
3059 let v0 = buckets_index4(t[p0u - 1] as usize, usize::from(t[p0u - 2] >= t[p0u - 1]));
3060 let pos0 = buckets[v0] as usize;
3061 sa[pos0] = (p0 - 1) | (((buckets[2 + v0] != d) as SaSint) << (SAINT_BIT - 1));
3062 buckets[v0] += 1;
3063 buckets[2 + v0] = d;
3064
3065 let mut p1 = sa[(i + 1) as usize];
3066 d += SaSint::from(p1 < 0);
3067 p1 &= SAINT_MAX;
3068 let p1u = p1 as usize;
3069 let v1 = buckets_index4(t[p1u - 1] as usize, usize::from(t[p1u - 2] >= t[p1u - 1]));
3070 let pos1 = buckets[v1] as usize;
3071 sa[pos1] = (p1 - 1) | (((buckets[2 + v1] != d) as SaSint) << (SAINT_BIT - 1));
3072 buckets[v1] += 1;
3073 buckets[2 + v1] = d;
3074
3075 i += 2;
3076 }
3077
3078 j += 2 * prefetch_distance + 1;
3079 while i < j {
3080 let mut p = sa[i as usize];
3081 d += SaSint::from(p < 0);
3082 p &= SAINT_MAX;
3083 let pu = p as usize;
3084 let v = buckets_index4(t[pu - 1] as usize, usize::from(t[pu - 2] >= t[pu - 1]));
3085 let pos = buckets[v] as usize;
3086 sa[pos] = (p - 1) | (((buckets[2 + v] != d) as SaSint) << (SAINT_BIT - 1));
3087 buckets[v] += 1;
3088 buckets[2 + v] = d;
3089 i += 1;
3090 }
3091
3092 d
3093}
3094
3095#[doc(hidden)]
3097pub fn partial_sorting_scan_left_to_right_32s_4k(
3098 t: &[SaSint],
3099 sa: &mut [SaSint],
3100 k: SaSint,
3101 buckets: &mut [SaSint],
3102 mut d: SaSint,
3103 omp_block_start: FastSint,
3104 omp_block_size: FastSint,
3105) -> SaSint {
3106 let k_usize = usize::try_from(k).expect("k must be non-negative");
3107 let prefetch_distance: FastSint = 64;
3108 let induction_offset = 2 * k_usize;
3109 let mut i = omp_block_start;
3110 let mut j = omp_block_start + omp_block_size - 2 * prefetch_distance - 1;
3111
3112 while i < j {
3113 let i0 = i as usize;
3114 let mut p0 = sa[i0];
3115 sa[i0] = p0 & SAINT_MAX;
3116 if p0 > 0 {
3117 sa[i0] = 0;
3118 d += p0 >> (SUFFIX_GROUP_BIT - 1);
3119 p0 &= !SUFFIX_GROUP_MARKER;
3120 let p0u = p0 as usize;
3121 let c0 = t[p0u - 1];
3122 let f0 = usize::from(t[p0u - 2] < c0);
3123 let v0 = buckets_index2(c0 as usize, f0);
3124 let c0u = c0 as usize;
3125 let pos0 = buckets[induction_offset + c0u] as usize;
3126 sa[pos0] = (p0 - 1)
3127 | ((f0 as SaSint) << (SAINT_BIT - 1))
3128 | (((buckets[v0] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3129 buckets[induction_offset + c0u] += 1;
3130 buckets[v0] = d;
3131 }
3132
3133 let i1 = (i + 1) as usize;
3134 let mut p1 = sa[i1];
3135 sa[i1] = p1 & SAINT_MAX;
3136 if p1 > 0 {
3137 sa[i1] = 0;
3138 d += p1 >> (SUFFIX_GROUP_BIT - 1);
3139 p1 &= !SUFFIX_GROUP_MARKER;
3140 let p1u = p1 as usize;
3141 let c1 = t[p1u - 1];
3142 let f1 = usize::from(t[p1u - 2] < c1);
3143 let v1 = buckets_index2(c1 as usize, f1);
3144 let c1u = c1 as usize;
3145 let pos1 = buckets[induction_offset + c1u] as usize;
3146 sa[pos1] = (p1 - 1)
3147 | ((f1 as SaSint) << (SAINT_BIT - 1))
3148 | (((buckets[v1] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3149 buckets[induction_offset + c1u] += 1;
3150 buckets[v1] = d;
3151 }
3152
3153 i += 2;
3154 }
3155
3156 j += 2 * prefetch_distance + 1;
3157 while i < j {
3158 let iu = i as usize;
3159 let mut p = sa[iu];
3160 sa[iu] = p & SAINT_MAX;
3161 if p > 0 {
3162 sa[iu] = 0;
3163 d += p >> (SUFFIX_GROUP_BIT - 1);
3164 p &= !SUFFIX_GROUP_MARKER;
3165 let pu = p as usize;
3166 let c = t[pu - 1];
3167 let f = usize::from(t[pu - 2] < c);
3168 let v = buckets_index2(c as usize, f);
3169 let cu = c as usize;
3170 let pos = buckets[induction_offset + cu] as usize;
3171 sa[pos] = (p - 1)
3172 | ((f as SaSint) << (SAINT_BIT - 1))
3173 | (((buckets[v] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3174 buckets[induction_offset + cu] += 1;
3175 buckets[v] = d;
3176 }
3177 i += 1;
3178 }
3179
3180 d
3181}
3182
3183#[doc(hidden)]
3185pub fn partial_sorting_scan_left_to_right_32s_1k(
3186 t: &[SaSint],
3187 sa: &mut [SaSint],
3188 induction_bucket: &mut [SaSint],
3189 omp_block_start: FastSint,
3190 omp_block_size: FastSint,
3191) {
3192 let prefetch_distance = 64 as FastSint;
3193 let mut i = omp_block_start;
3194 let mut j = omp_block_start + omp_block_size - 2 * prefetch_distance - 1;
3195
3196 while i < j {
3197 let p0 = sa[i as usize];
3198 sa[i as usize] = p0 & SAINT_MAX;
3199 if p0 > 0 {
3200 sa[i as usize] = 0;
3201 let c0 = t[(p0 - 1) as usize] as usize;
3202 let pos0 = induction_bucket[c0] as usize;
3203 induction_bucket[c0] += 1;
3204 sa[pos0] = (p0 - 1)
3205 | ((usize::from(t[(p0 - 2) as usize] < t[(p0 - 1) as usize]) as SaSint)
3206 << (SAINT_BIT - 1));
3207 }
3208
3209 let p1 = sa[(i + 1) as usize];
3210 sa[(i + 1) as usize] = p1 & SAINT_MAX;
3211 if p1 > 0 {
3212 sa[(i + 1) as usize] = 0;
3213 let c1 = t[(p1 - 1) as usize] as usize;
3214 let pos1 = induction_bucket[c1] as usize;
3215 induction_bucket[c1] += 1;
3216 sa[pos1] = (p1 - 1)
3217 | ((usize::from(t[(p1 - 2) as usize] < t[(p1 - 1) as usize]) as SaSint)
3218 << (SAINT_BIT - 1));
3219 }
3220
3221 i += 2;
3222 }
3223
3224 j += 2 * prefetch_distance + 1;
3225 while i < j {
3226 let p = sa[i as usize];
3227 sa[i as usize] = p & SAINT_MAX;
3228 if p > 0 {
3229 sa[i as usize] = 0;
3230 let c = t[(p - 1) as usize] as usize;
3231 let pos = induction_bucket[c] as usize;
3232 induction_bucket[c] += 1;
3233 sa[pos] = (p - 1)
3234 | ((usize::from(t[(p - 2) as usize] < t[(p - 1) as usize]) as SaSint)
3235 << (SAINT_BIT - 1));
3236 }
3237 i += 1;
3238 }
3239}
3240
3241#[doc(hidden)]
3243pub fn partial_sorting_scan_left_to_right_32s_6k_omp(
3244 t: &[SaSint],
3245 sa: &mut [SaSint],
3246 n: SaSint,
3247 buckets: &mut [SaSint],
3248 left_suffixes_count: SaSint,
3249 mut d: SaSint,
3250 threads: SaSint,
3251 thread_state: &mut [ThreadState],
3252) -> SaSint {
3253 let v = buckets_index4(
3254 t[(n - 1) as usize] as usize,
3255 usize::from(t[(n - 2) as usize] >= t[(n - 1) as usize]),
3256 );
3257 let pos = buckets[v] as usize;
3258 sa[pos] = (n - 1) | SAINT_MIN;
3259 buckets[v] += 1;
3260 d += 1;
3261 buckets[2 + v] = d;
3262 if threads == 1 || left_suffixes_count < 65_536 {
3263 return partial_sorting_scan_left_to_right_32s_6k(
3264 t,
3265 sa,
3266 buckets,
3267 d,
3268 0,
3269 left_suffixes_count as FastSint,
3270 );
3271 }
3272 if thread_state.is_empty() {
3273 return partial_sorting_scan_left_to_right_32s_6k(
3274 t,
3275 sa,
3276 buckets,
3277 d,
3278 0,
3279 left_suffixes_count as FastSint,
3280 );
3281 }
3282
3283 let left_suffixes_count =
3284 usize::try_from(left_suffixes_count).expect("left_suffixes_count must be non-negative");
3285 let threads_usize = usize::try_from(threads)
3286 .expect("threads must be non-negative")
3287 .max(1);
3288 let mut block_start = 0usize;
3289 let block_span = threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE;
3290 let mut cache = vec![ThreadCache::default(); block_span];
3291 while block_start < left_suffixes_count {
3292 let mut block_end = block_start + block_span;
3293 if block_end > left_suffixes_count {
3294 block_end = left_suffixes_count;
3295 }
3296
3297 d = partial_sorting_scan_left_to_right_32s_6k_block_omp(
3298 t,
3299 sa,
3300 buckets,
3301 d,
3302 &mut cache,
3303 block_start as FastSint,
3304 (block_end - block_start) as FastSint,
3305 threads,
3306 );
3307
3308 block_start = block_end;
3309 }
3310
3311 d
3312}
3313
3314#[doc(hidden)]
3316pub fn partial_sorting_scan_left_to_right_32s_4k_omp(
3317 t: &[SaSint],
3318 sa: &mut [SaSint],
3319 n: SaSint,
3320 k: SaSint,
3321 buckets: &mut [SaSint],
3322 mut d: SaSint,
3323 threads: SaSint,
3324 thread_state: &mut [ThreadState],
3325) -> SaSint {
3326 let k_usize = usize::try_from(k).expect("k must be non-negative");
3327 let induction_offset = 2 * k_usize;
3328 let distinct_offset = 0usize;
3329 let symbol = t[(n - 1) as usize] as usize;
3330 let is_s = usize::from(t[(n - 2) as usize] < t[(n - 1) as usize]);
3331 let pos = buckets[induction_offset + symbol] as usize;
3332 sa[pos] = (n - 1) | ((is_s as SaSint) << (SAINT_BIT - 1)) | SUFFIX_GROUP_MARKER;
3333 buckets[induction_offset + symbol] += 1;
3334 d += 1;
3335 buckets[distinct_offset + buckets_index2(symbol, is_s)] = d;
3336
3337 if threads == 1 || n < 65_536 {
3338 d = partial_sorting_scan_left_to_right_32s_4k(t, sa, k, buckets, d, 0, n as FastSint);
3339 } else {
3340 if thread_state.is_empty() {
3341 return partial_sorting_scan_left_to_right_32s_4k(
3342 t,
3343 sa,
3344 k,
3345 buckets,
3346 d,
3347 0,
3348 n as FastSint,
3349 );
3350 }
3351 let mut block_start = 0usize;
3352 let n_usize = usize::try_from(n).expect("n must be non-negative");
3353 let threads_usize = usize::try_from(threads)
3354 .expect("threads must be non-negative")
3355 .max(1);
3356 let chunk_capacity = threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE;
3357 let mut cache = vec![ThreadCache::default(); chunk_capacity];
3358
3359 while block_start < n_usize {
3360 let mut block_end = block_start + chunk_capacity;
3361 if block_end > n_usize {
3362 block_end = n_usize;
3363 }
3364
3365 d = partial_sorting_scan_left_to_right_32s_4k_block_omp(
3366 t,
3367 sa,
3368 k,
3369 buckets,
3370 d,
3371 &mut cache,
3372 block_start as FastSint,
3373 (block_end - block_start) as FastSint,
3374 threads,
3375 );
3376
3377 block_start = block_end;
3378 }
3379 }
3380
3381 d
3382}
3383
3384#[doc(hidden)]
3386pub fn partial_sorting_scan_left_to_right_32s_1k_omp(
3387 t: &[SaSint],
3388 sa: &mut [SaSint],
3389 n: SaSint,
3390 buckets: &mut [SaSint],
3391 threads: SaSint,
3392 thread_state: &mut [ThreadState],
3393) {
3394 let symbol = t[(n - 1) as usize] as usize;
3395 let pos = buckets[symbol] as usize;
3396 sa[pos] = (n - 1)
3397 | ((usize::from(t[(n - 2) as usize] < t[(n - 1) as usize]) as SaSint) << (SAINT_BIT - 1));
3398 buckets[symbol] += 1;
3399 if threads == 1 || n < 65_536 {
3400 partial_sorting_scan_left_to_right_32s_1k(t, sa, buckets, 0, n as FastSint);
3401 } else {
3402 if thread_state.is_empty() {
3403 partial_sorting_scan_left_to_right_32s_1k(t, sa, buckets, 0, n as FastSint);
3404 return;
3405 }
3406 let n_usize = usize::try_from(n).expect("n must be non-negative");
3407 let threads_usize = usize::try_from(threads)
3408 .expect("threads must be non-negative")
3409 .max(1);
3410 let mut block_start = 0usize;
3411 let block_span = threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE;
3412 let mut cache = vec![ThreadCache::default(); block_span];
3413
3414 while block_start < n_usize {
3415 let mut block_end = block_start + block_span;
3416 if block_end > n_usize {
3417 block_end = n_usize;
3418 }
3419
3420 partial_sorting_scan_left_to_right_32s_1k_block_omp(
3421 t,
3422 sa,
3423 buckets,
3424 &mut cache,
3425 block_start as FastSint,
3426 (block_end - block_start) as FastSint,
3427 threads,
3428 );
3429
3430 block_start = block_end;
3431 }
3432 }
3433}
3434
3435#[doc(hidden)]
3437pub fn partial_sorting_scan_left_to_right_8u_block_prepare(
3438 t: &[u8],
3439 sa: &[SaSint],
3440 k: SaSint,
3441 buckets: &mut [SaSint],
3442 cache: &mut [ThreadCache],
3443 omp_block_start: FastSint,
3444 omp_block_size: FastSint,
3445) -> (FastSint, FastSint) {
3446 let k_usize = usize::try_from(k).expect("k must be non-negative");
3447 buckets[..2 * k_usize].fill(0);
3448 buckets[2 * k_usize..4 * k_usize].fill(0);
3449
3450 let mut i = omp_block_start;
3451 let mut j = omp_block_start + omp_block_size - 65;
3452 let mut count = 0usize;
3453 let mut d: SaSint = 1;
3454
3455 while i < j {
3456 let mut p0 = sa[i as usize];
3457 cache[count].index = p0;
3458 d += SaSint::from(p0 < 0);
3459 p0 &= SAINT_MAX;
3460 let v0 = buckets_index2(
3461 t[(p0 - 1) as usize] as usize,
3462 usize::from(t[(p0 - 2) as usize] >= t[(p0 - 1) as usize]),
3463 );
3464 cache[count].symbol = v0 as SaSint;
3465 count += 1;
3466 buckets[v0] += 1;
3467 buckets[2 * k_usize + v0] = d;
3468
3469 let mut p1 = sa[(i + 1) as usize];
3470 cache[count].index = p1;
3471 d += SaSint::from(p1 < 0);
3472 p1 &= SAINT_MAX;
3473 let v1 = buckets_index2(
3474 t[(p1 - 1) as usize] as usize,
3475 usize::from(t[(p1 - 2) as usize] >= t[(p1 - 1) as usize]),
3476 );
3477 cache[count].symbol = v1 as SaSint;
3478 count += 1;
3479 buckets[v1] += 1;
3480 buckets[2 * k_usize + v1] = d;
3481
3482 i += 2;
3483 }
3484
3485 j += 65;
3486 while i < j {
3487 let mut p = sa[i as usize];
3488 cache[count].index = p;
3489 d += SaSint::from(p < 0);
3490 p &= SAINT_MAX;
3491 let v = buckets_index2(
3492 t[(p - 1) as usize] as usize,
3493 usize::from(t[(p - 2) as usize] >= t[(p - 1) as usize]),
3494 );
3495 cache[count].symbol = v as SaSint;
3496 count += 1;
3497 buckets[v] += 1;
3498 buckets[2 * k_usize + v] = d;
3499 i += 1;
3500 }
3501
3502 (d as FastSint - 1, count as FastSint)
3503}
3504
3505#[doc(hidden)]
3507pub fn partial_sorting_scan_left_to_right_8u_block_place(
3508 sa: &mut [SaSint],
3509 buckets: &mut [SaSint],
3510 k: SaSint,
3511 cache: &[ThreadCache],
3512 count: FastSint,
3513 mut d: SaSint,
3514) {
3515 let split = 2 * usize::try_from(k).expect("k must be non-negative");
3516 let (induction_bucket, distinct_names) = buckets.split_at_mut(split);
3517
3518 let mut i = 0usize;
3519 let mut j = usize::try_from(count)
3520 .expect("count must be non-negative")
3521 .saturating_sub(1);
3522 while i < j {
3523 let p0 = cache[i].index;
3524 d += SaSint::from(p0 < 0);
3525 let v0 = cache[i].symbol as usize;
3526 let pos0 = induction_bucket[v0] as usize;
3527 sa[pos0] = (p0 - 1) | (((distinct_names[v0] != d) as SaSint) << (SAINT_BIT - 1));
3528 induction_bucket[v0] += 1;
3529 distinct_names[v0] = d;
3530
3531 let p1 = cache[i + 1].index;
3532 d += SaSint::from(p1 < 0);
3533 let v1 = cache[i + 1].symbol as usize;
3534 let pos1 = induction_bucket[v1] as usize;
3535 sa[pos1] = (p1 - 1) | (((distinct_names[v1] != d) as SaSint) << (SAINT_BIT - 1));
3536 induction_bucket[v1] += 1;
3537 distinct_names[v1] = d;
3538
3539 i += 2;
3540 }
3541
3542 j += 1;
3543 while i < j {
3544 let p = cache[i].index;
3545 d += SaSint::from(p < 0);
3546 let v = cache[i].symbol as usize;
3547 let pos = induction_bucket[v] as usize;
3548 sa[pos] = (p - 1) | (((distinct_names[v] != d) as SaSint) << (SAINT_BIT - 1));
3549 induction_bucket[v] += 1;
3550 distinct_names[v] = d;
3551 i += 1;
3552 }
3553}
3554
3555#[doc(hidden)]
3557pub fn partial_sorting_scan_left_to_right_8u_block_omp(
3558 t: &[u8],
3559 sa: &mut [SaSint],
3560 k: SaSint,
3561 buckets: &mut [SaSint],
3562 d: SaSint,
3563 block_start: FastSint,
3564 block_size: FastSint,
3565 threads: SaSint,
3566 thread_state: &mut [ThreadState],
3567) -> SaSint {
3568 let mut d = d;
3569 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
3570 let k_usize = usize::try_from(k).expect("k must be non-negative");
3571 let omp_num_threads = if threads > 1 && block_size_usize >= 64 * k_usize.max(256) {
3572 usize::try_from(threads)
3573 .expect("threads must be non-negative")
3574 .min(thread_state.len())
3575 .max(1)
3576 } else {
3577 1
3578 };
3579 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
3580
3581 if omp_num_threads == 1 {
3582 return partial_sorting_scan_left_to_right_8u(t, sa, buckets, d, block_start, block_size);
3583 }
3584
3585 {
3586 let sa_ro: &[SaSint] = sa;
3587 run_rayon_with_threads(omp_num_threads, || {
3588 thread_state[..omp_num_threads]
3589 .par_iter_mut()
3590 .enumerate()
3591 .for_each(|(omp_thread_num, state)| {
3592 let mut omp_block_start = omp_thread_num * omp_block_stride;
3593 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
3594 omp_block_stride
3595 } else {
3596 block_size_usize - omp_block_start
3597 };
3598 omp_block_start +=
3599 usize::try_from(block_start).expect("block_start must be non-negative");
3600
3601 let (position, count) = partial_sorting_scan_left_to_right_8u_block_prepare(
3602 t,
3603 sa_ro,
3604 k,
3605 &mut state.buckets,
3606 &mut state.cache,
3607 FastSint::try_from(omp_block_start).expect("block start must fit FastSint"),
3608 FastSint::try_from(omp_block_size).expect("block size must fit FastSint"),
3609 );
3610 state.position = position;
3611 state.count = count;
3612 });
3613 });
3614 }
3615
3616 let induction_offset = 4 * ALPHABET_SIZE;
3617 let distinct_offset = 2 * ALPHABET_SIZE;
3618 let (prefix, induction_tail) = buckets.split_at_mut(induction_offset);
3619 let induction_bucket = &mut induction_tail[..2 * k_usize];
3620 let distinct_names = &mut prefix[distinct_offset..distinct_offset + 2 * k_usize];
3621
3622 for tnum in 0..omp_num_threads {
3623 let state = &mut thread_state[tnum];
3624 let (temp_induction_bucket, temp_tail) = state.buckets.split_at_mut(2 * k_usize);
3625 let temp_distinct_names = &mut temp_tail[..2 * k_usize];
3626
3627 for c in 0..2 * k_usize {
3628 let a = induction_bucket[c];
3629 let b = temp_induction_bucket[c];
3630 induction_bucket[c] = a + b;
3631 temp_induction_bucket[c] = a;
3632 }
3633
3634 d -= 1;
3635 for c in 0..2 * k_usize {
3636 let a = distinct_names[c];
3637 let b = temp_distinct_names[c];
3638 let next_d = b + d;
3639 distinct_names[c] = if b > 0 { next_d } else { a };
3640 temp_distinct_names[c] = a;
3641 }
3642 d += 1 + SaSint::try_from(state.position).expect("position must fit SaSint");
3643 state.position = FastSint::try_from(d).expect("d must fit FastSint") - state.position;
3644 }
3645
3646 {
3647 let sa_ptr = SyncMutPtr::new(sa);
3648 run_rayon_with_threads(omp_num_threads, || {
3649 thread_state[..omp_num_threads]
3650 .par_iter_mut()
3651 .for_each(|state| {
3652 let sa = unsafe { sa_ptr.as_slice() };
3653 partial_sorting_scan_left_to_right_8u_block_place(
3654 sa,
3655 &mut state.buckets,
3656 k,
3657 &state.cache,
3658 state.count,
3659 state.position as SaSint,
3660 );
3661 });
3662 });
3663 }
3664
3665 d
3666}
3667
3668#[doc(hidden)]
3670pub fn partial_sorting_shift_markers_8u_omp(
3671 sa: &mut [SaSint],
3672 n: SaSint,
3673 buckets: &[SaSint],
3674 threads: SaSint,
3675) {
3676 let temp_bucket = &buckets[4 * ALPHABET_SIZE..];
3677 let thread_count = if threads > 1 && n >= 65536 {
3678 usize::try_from(threads).expect("threads must be positive")
3679 } else {
3680 1
3681 };
3682 let c_step = buckets_index2(1, 0) as isize;
3683 let c_min = buckets_index2(1, 0) as isize;
3684 let c_max = buckets_index2(ALPHABET_SIZE - 1, 0) as isize;
3685 {
3686 let sa_ptr = SyncMutPtr::new(sa);
3687 let buckets_ref: &[SaSint] = buckets;
3688 let temp_bucket_ref: &[SaSint] = temp_bucket;
3689 run_rayon_with_threads(thread_count, || {
3690 (0..thread_count).into_par_iter().for_each(|t| {
3691 let mut c = c_max - (t as isize * c_step);
3692 let sa = unsafe { sa_ptr.as_slice() };
3693 while c >= c_min {
3694 let c_usize = c as usize;
3695 let mut i = temp_bucket_ref[c_usize] as isize - 1;
3696 let mut j = buckets_ref[c_usize - buckets_index2(1, 0)] as isize + 3;
3697 let mut s = SAINT_MIN;
3698
3699 while i >= j {
3700 let p0 = sa[i as usize];
3701 let q0 = (p0 & SAINT_MIN) ^ s;
3702 s ^= q0;
3703 sa[i as usize] = p0 ^ q0;
3704
3705 let p1 = sa[(i - 1) as usize];
3706 let q1 = (p1 & SAINT_MIN) ^ s;
3707 s ^= q1;
3708 sa[(i - 1) as usize] = p1 ^ q1;
3709
3710 let p2 = sa[(i - 2) as usize];
3711 let q2 = (p2 & SAINT_MIN) ^ s;
3712 s ^= q2;
3713 sa[(i - 2) as usize] = p2 ^ q2;
3714
3715 let p3 = sa[(i - 3) as usize];
3716 let q3 = (p3 & SAINT_MIN) ^ s;
3717 s ^= q3;
3718 sa[(i - 3) as usize] = p3 ^ q3;
3719
3720 i -= 4;
3721 }
3722
3723 j -= 3;
3724 while i >= j {
3725 let p = sa[i as usize];
3726 let q = (p & SAINT_MIN) ^ s;
3727 s ^= q;
3728 sa[i as usize] = p ^ q;
3729 i -= 1;
3730 }
3731
3732 c -= c_step * thread_count as isize;
3733 }
3734 });
3735 });
3736 }
3737}
3738
3739#[doc(hidden)]
3741pub fn partial_sorting_shift_markers_32s_6k_omp(
3742 sa: &mut [SaSint],
3743 k: SaSint,
3744 buckets: &[SaSint],
3745 threads: SaSint,
3746) {
3747 let k_usize = usize::try_from(k).expect("k must be non-negative");
3748 let temp_bucket = &buckets[4 * k_usize..];
3749 let thread_count = if threads > 1 && k >= 65536 {
3750 usize::try_from(threads).expect("threads must be positive")
3751 } else {
3752 1
3753 };
3754 {
3755 let sa_ptr = SyncMutPtr::new(sa);
3756 let buckets_ref: &[SaSint] = buckets;
3757 let temp_bucket_ref: &[SaSint] = temp_bucket;
3758 run_rayon_with_threads(thread_count, || {
3759 (0..thread_count).into_par_iter().for_each(|t| {
3760 let mut c = k_usize as isize - 1 - t as isize;
3761 let sa = unsafe { sa_ptr.as_slice() };
3762 while c >= 1 {
3763 let c_usize = c as usize;
3764 let mut i = buckets_ref[buckets_index4(c_usize, 0)] as isize - 1;
3765 let mut j = temp_bucket_ref[buckets_index2(c_usize - 1, 0)] as isize + 3;
3766 let mut s = SAINT_MIN;
3767
3768 while i >= j {
3769 let p0 = sa[i as usize];
3770 let q0 = (p0 & SAINT_MIN) ^ s;
3771 s ^= q0;
3772 sa[i as usize] = p0 ^ q0;
3773
3774 let p1 = sa[(i - 1) as usize];
3775 let q1 = (p1 & SAINT_MIN) ^ s;
3776 s ^= q1;
3777 sa[(i - 1) as usize] = p1 ^ q1;
3778
3779 let p2 = sa[(i - 2) as usize];
3780 let q2 = (p2 & SAINT_MIN) ^ s;
3781 s ^= q2;
3782 sa[(i - 2) as usize] = p2 ^ q2;
3783
3784 let p3 = sa[(i - 3) as usize];
3785 let q3 = (p3 & SAINT_MIN) ^ s;
3786 s ^= q3;
3787 sa[(i - 3) as usize] = p3 ^ q3;
3788
3789 i -= 4;
3790 }
3791
3792 j -= 3;
3793 while i >= j {
3794 let p = sa[i as usize];
3795 let q = (p & SAINT_MIN) ^ s;
3796 s ^= q;
3797 sa[i as usize] = p ^ q;
3798 i -= 1;
3799 }
3800
3801 c -= thread_count as isize;
3802 }
3803 });
3804 });
3805 }
3806}
3807
3808#[doc(hidden)]
3810pub fn partial_sorting_shift_markers_32s_4k(sa: &mut [SaSint], n: SaSint) {
3811 let mut i = n as isize - 1;
3812 let mut s = SUFFIX_GROUP_MARKER;
3813 while i >= 3 {
3814 let p0 = sa[i as usize];
3815 let q0 =
3816 ((p0 & SUFFIX_GROUP_MARKER) ^ s) & (((p0 > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3817 s ^= q0;
3818 sa[i as usize] = p0 ^ q0;
3819
3820 let p1 = sa[(i - 1) as usize];
3821 let q1 =
3822 ((p1 & SUFFIX_GROUP_MARKER) ^ s) & (((p1 > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3823 s ^= q1;
3824 sa[(i - 1) as usize] = p1 ^ q1;
3825
3826 let p2 = sa[(i - 2) as usize];
3827 let q2 =
3828 ((p2 & SUFFIX_GROUP_MARKER) ^ s) & (((p2 > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3829 s ^= q2;
3830 sa[(i - 2) as usize] = p2 ^ q2;
3831
3832 let p3 = sa[(i - 3) as usize];
3833 let q3 =
3834 ((p3 & SUFFIX_GROUP_MARKER) ^ s) & (((p3 > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3835 s ^= q3;
3836 sa[(i - 3) as usize] = p3 ^ q3;
3837
3838 i -= 4;
3839 }
3840
3841 while i >= 0 {
3842 let p = sa[i as usize];
3843 let q = ((p & SUFFIX_GROUP_MARKER) ^ s) & (((p > 0) as SaSint) << (SUFFIX_GROUP_BIT - 1));
3844 s ^= q;
3845 sa[i as usize] = p ^ q;
3846 i -= 1;
3847 }
3848}
3849
3850#[doc(hidden)]
3852pub fn partial_sorting_shift_buckets_32s_6k(k: SaSint, buckets: &mut [SaSint]) {
3853 let k_usize = usize::try_from(k).expect("k must be non-negative");
3854 let temp_offset = 4 * k_usize;
3855 for i in 0..k_usize {
3856 let src = buckets_index2(i, 0);
3857 let dst = 2 * src;
3858 buckets[dst] = buckets[temp_offset + src];
3859 buckets[dst + 1] = buckets[temp_offset + src + 1];
3860 }
3861}
3862
3863#[doc(hidden)]
3865pub fn partial_sorting_scan_right_to_left_8u(
3866 t: &[u8],
3867 sa: &mut [SaSint],
3868 buckets: &mut [SaSint],
3869 mut d: SaSint,
3870 omp_block_start: FastSint,
3871 omp_block_size: FastSint,
3872) -> SaSint {
3873 if omp_block_size <= 0 {
3874 return d;
3875 }
3876
3877 let prefetch_distance = 64usize;
3878 let (induction_bucket, distinct_names_all) = buckets.split_at_mut(2 * ALPHABET_SIZE);
3879 let distinct_names = &mut distinct_names_all[..2 * ALPHABET_SIZE];
3880
3881 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
3882 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
3883 let mut i = start + size - 1;
3884 let mut j = start + prefetch_distance + 1;
3885
3886 while i >= j {
3887 let mut p0 = sa[i];
3888 d += SaSint::from(p0 < 0);
3889 p0 &= SAINT_MAX;
3890
3891 let p0_usize = p0 as usize;
3892 let v0 = buckets_index2(
3893 t[p0_usize - 1] as usize,
3894 usize::from(t[p0_usize - 2] > t[p0_usize - 1]),
3895 );
3896
3897 induction_bucket[v0] -= 1;
3898 let slot0 = induction_bucket[v0] as usize;
3899 sa[slot0] = (p0 - 1) | (((distinct_names[v0] != d) as SaSint) << (SAINT_BIT - 1));
3900 distinct_names[v0] = d;
3901
3902 let mut p1 = sa[i - 1];
3903 d += SaSint::from(p1 < 0);
3904 p1 &= SAINT_MAX;
3905
3906 let p1_usize = p1 as usize;
3907 let v1 = buckets_index2(
3908 t[p1_usize - 1] as usize,
3909 usize::from(t[p1_usize - 2] > t[p1_usize - 1]),
3910 );
3911
3912 induction_bucket[v1] -= 1;
3913 let slot1 = induction_bucket[v1] as usize;
3914 sa[slot1] = (p1 - 1) | (((distinct_names[v1] != d) as SaSint) << (SAINT_BIT - 1));
3915 distinct_names[v1] = d;
3916
3917 i -= 2;
3918 }
3919
3920 j = if start + prefetch_distance < start + size {
3921 start
3922 } else {
3923 start
3924 };
3925 while i >= j {
3926 let mut p = sa[i];
3927 d += SaSint::from(p < 0);
3928 p &= SAINT_MAX;
3929
3930 let p_usize = p as usize;
3931 let v = buckets_index2(
3932 t[p_usize - 1] as usize,
3933 usize::from(t[p_usize - 2] > t[p_usize - 1]),
3934 );
3935
3936 induction_bucket[v] -= 1;
3937 let slot = induction_bucket[v] as usize;
3938 sa[slot] = (p - 1) | (((distinct_names[v] != d) as SaSint) << (SAINT_BIT - 1));
3939 distinct_names[v] = d;
3940
3941 if i == 0 {
3942 break;
3943 }
3944 i -= 1;
3945 }
3946
3947 d
3948}
3949
3950#[doc(hidden)]
3952pub fn partial_gsa_scan_right_to_left_8u(
3953 t: &[u8],
3954 sa: &mut [SaSint],
3955 buckets: &mut [SaSint],
3956 mut d: SaSint,
3957 omp_block_start: FastSint,
3958 omp_block_size: FastSint,
3959) -> SaSint {
3960 if omp_block_size <= 0 {
3961 return d;
3962 }
3963
3964 let prefetch_distance = 64usize;
3965 let (induction_bucket, distinct_names_all) = buckets.split_at_mut(2 * ALPHABET_SIZE);
3966 let distinct_names = &mut distinct_names_all[..2 * ALPHABET_SIZE];
3967
3968 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
3969 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
3970 let mut i = start + size - 1;
3971 let mut j = start + prefetch_distance + 1;
3972
3973 while i >= j {
3974 let mut p0 = sa[i];
3975 d += SaSint::from(p0 < 0);
3976 p0 &= SAINT_MAX;
3977
3978 let p0_usize = p0 as usize;
3979 let v0 = buckets_index2(
3980 t[p0_usize - 1] as usize,
3981 usize::from(t[p0_usize - 2] > t[p0_usize - 1]),
3982 );
3983
3984 if v0 != 1 {
3985 induction_bucket[v0] -= 1;
3986 let slot0 = induction_bucket[v0] as usize;
3987 sa[slot0] = (p0 - 1) | (((distinct_names[v0] != d) as SaSint) << (SAINT_BIT - 1));
3988 distinct_names[v0] = d;
3989 }
3990
3991 let mut p1 = sa[i - 1];
3992 d += SaSint::from(p1 < 0);
3993 p1 &= SAINT_MAX;
3994
3995 let p1_usize = p1 as usize;
3996 let v1 = buckets_index2(
3997 t[p1_usize - 1] as usize,
3998 usize::from(t[p1_usize - 2] > t[p1_usize - 1]),
3999 );
4000
4001 if v1 != 1 {
4002 induction_bucket[v1] -= 1;
4003 let slot1 = induction_bucket[v1] as usize;
4004 sa[slot1] = (p1 - 1) | (((distinct_names[v1] != d) as SaSint) << (SAINT_BIT - 1));
4005 distinct_names[v1] = d;
4006 }
4007
4008 i -= 2;
4009 }
4010
4011 j = start;
4012 while i >= j {
4013 let mut p = sa[i];
4014 d += SaSint::from(p < 0);
4015 p &= SAINT_MAX;
4016
4017 let p_usize = p as usize;
4018 let v = buckets_index2(
4019 t[p_usize - 1] as usize,
4020 usize::from(t[p_usize - 2] > t[p_usize - 1]),
4021 );
4022
4023 if v != 1 {
4024 induction_bucket[v] -= 1;
4025 let slot = induction_bucket[v] as usize;
4026 sa[slot] = (p - 1) | (((distinct_names[v] != d) as SaSint) << (SAINT_BIT - 1));
4027 distinct_names[v] = d;
4028 }
4029
4030 if i == 0 {
4031 break;
4032 }
4033 i -= 1;
4034 }
4035
4036 d
4037}
4038
4039#[doc(hidden)]
4041pub fn partial_sorting_scan_right_to_left_8u_block_prepare(
4042 t: &[u8],
4043 sa: &[SaSint],
4044 k: SaSint,
4045 buckets: &mut [SaSint],
4046 cache: &mut [ThreadCache],
4047 omp_block_start: FastSint,
4048 omp_block_size: FastSint,
4049) -> (FastSint, FastSint) {
4050 let k_usize = usize::try_from(k).expect("k must be non-negative");
4051 let (induction_bucket, distinct_names_all) = buckets.split_at_mut(2 * k_usize);
4052 let distinct_names = &mut distinct_names_all[..2 * k_usize];
4053 induction_bucket.fill(0);
4054 distinct_names.fill(0);
4055
4056 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
4057 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
4058 let mut count = 0usize;
4059 let mut d = 1;
4060
4061 let mut i = start + size;
4062 while i > start {
4063 i -= 1;
4064
4065 let mut p = sa[i];
4066 cache[count].index = p;
4067 d += SaSint::from(p < 0);
4068 p &= SAINT_MAX;
4069
4070 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
4071 let v = buckets_index2(
4072 t[p_usize - 1] as usize,
4073 usize::from(t[p_usize - 2] > t[p_usize - 1]),
4074 );
4075
4076 cache[count].symbol = v as SaSint;
4077 induction_bucket[v] += 1;
4078 distinct_names[v] = d;
4079 count += 1;
4080 }
4081
4082 ((d - 1) as FastSint, count as FastSint)
4083}
4084
4085#[doc(hidden)]
4087pub fn partial_sorting_scan_right_to_left_8u_block_place(
4088 sa: &mut [SaSint],
4089 buckets: &mut [SaSint],
4090 k: SaSint,
4091 cache: &[ThreadCache],
4092 count: FastSint,
4093 mut d: SaSint,
4094) {
4095 let split = 2 * usize::try_from(k).expect("k must be non-negative");
4096 let (induction_bucket, distinct_names) = buckets.split_at_mut(split);
4097
4098 let count = usize::try_from(count).expect("count must be non-negative");
4099 for entry in &cache[..count] {
4100 let p = entry.index;
4101 d += SaSint::from(p < 0);
4102 let v = usize::try_from(entry.symbol).expect("cache symbol must be non-negative");
4103 induction_bucket[v] -= 1;
4104 let slot = usize::try_from(induction_bucket[v]).expect("bucket slot must be non-negative");
4105 sa[slot] = (p - 1) | (((distinct_names[v] != d) as SaSint) << (SAINT_BIT - 1));
4106 distinct_names[v] = d;
4107 }
4108}
4109
4110#[doc(hidden)]
4112pub fn partial_gsa_scan_right_to_left_8u_block_place(
4113 sa: &mut [SaSint],
4114 buckets: &mut [SaSint],
4115 k: SaSint,
4116 cache: &[ThreadCache],
4117 count: FastSint,
4118 mut d: SaSint,
4119) {
4120 let split = 2 * usize::try_from(k).expect("k must be non-negative");
4121 let (induction_bucket, distinct_names) = buckets.split_at_mut(split);
4122
4123 let count = usize::try_from(count).expect("count must be non-negative");
4124 for entry in &cache[..count] {
4125 let p = entry.index;
4126 d += SaSint::from(p < 0);
4127 let v = usize::try_from(entry.symbol).expect("cache symbol must be non-negative");
4128 if v != 1 {
4129 induction_bucket[v] -= 1;
4130 let slot =
4131 usize::try_from(induction_bucket[v]).expect("bucket slot must be non-negative");
4132 sa[slot] = (p - 1) | (((distinct_names[v] != d) as SaSint) << (SAINT_BIT - 1));
4133 distinct_names[v] = d;
4134 }
4135 }
4136}
4137
4138#[doc(hidden)]
4140pub fn partial_sorting_scan_right_to_left_8u_block_omp(
4141 t: &[u8],
4142 sa: &mut [SaSint],
4143 k: SaSint,
4144 buckets: &mut [SaSint],
4145 d: SaSint,
4146 block_start: FastSint,
4147 block_size: FastSint,
4148 threads: SaSint,
4149 thread_state: &mut [ThreadState],
4150) -> SaSint {
4151 let mut d = d;
4152 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
4153 let k_usize = usize::try_from(k).expect("k must be non-negative");
4154 let omp_num_threads = if threads > 1 && block_size_usize >= 64 * k_usize.max(256) {
4155 usize::try_from(threads)
4156 .expect("threads must be non-negative")
4157 .min(thread_state.len())
4158 .max(1)
4159 } else {
4160 1
4161 };
4162 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
4163
4164 if omp_num_threads == 1 {
4165 return partial_sorting_scan_right_to_left_8u(t, sa, buckets, d, block_start, block_size);
4166 }
4167
4168 {
4169 let sa_ro: &[SaSint] = sa;
4170 run_rayon_with_threads(omp_num_threads, || {
4171 thread_state[..omp_num_threads]
4172 .par_iter_mut()
4173 .enumerate()
4174 .for_each(|(omp_thread_num, state)| {
4175 let mut omp_block_start = omp_thread_num * omp_block_stride;
4176 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4177 omp_block_stride
4178 } else {
4179 block_size_usize - omp_block_start
4180 };
4181 omp_block_start +=
4182 usize::try_from(block_start).expect("block_start must be non-negative");
4183
4184 let (position, count) = partial_sorting_scan_right_to_left_8u_block_prepare(
4185 t,
4186 sa_ro,
4187 k,
4188 &mut state.buckets,
4189 &mut state.cache,
4190 FastSint::try_from(omp_block_start).expect("block start must fit FastSint"),
4191 FastSint::try_from(omp_block_size).expect("block size must fit FastSint"),
4192 );
4193 state.position = position;
4194 state.count = count;
4195 });
4196 });
4197 }
4198
4199 let distinct_offset = 2 * ALPHABET_SIZE;
4200 let (induction_bucket, distinct_tail) = buckets.split_at_mut(distinct_offset);
4201 let distinct_names = &mut distinct_tail[..2 * k_usize];
4202
4203 for tnum in (0..omp_num_threads).rev() {
4204 let state = &mut thread_state[tnum];
4205 let (temp_induction_bucket, temp_tail) = state.buckets.split_at_mut(2 * k_usize);
4206 let temp_distinct_names = &mut temp_tail[..2 * k_usize];
4207
4208 for c in 0..2 * k_usize {
4209 let a = induction_bucket[c];
4210 let b = temp_induction_bucket[c];
4211 induction_bucket[c] = a - b;
4212 temp_induction_bucket[c] = a;
4213 }
4214
4215 d -= 1;
4216 for c in 0..2 * k_usize {
4217 let a = distinct_names[c];
4218 let b = temp_distinct_names[c];
4219 let next_d = b + d;
4220 distinct_names[c] = if b > 0 { next_d } else { a };
4221 temp_distinct_names[c] = a;
4222 }
4223 d += 1 + SaSint::try_from(state.position).expect("position must fit SaSint");
4224 state.position = FastSint::try_from(d).expect("d must fit FastSint") - state.position;
4225 }
4226
4227 {
4228 let sa_ptr = SyncMutPtr::new(sa);
4229 run_rayon_with_threads(omp_num_threads, || {
4230 thread_state[..omp_num_threads]
4231 .par_iter_mut()
4232 .for_each(|state| {
4233 let sa = unsafe { sa_ptr.as_slice() };
4234 partial_sorting_scan_right_to_left_8u_block_place(
4235 sa,
4236 &mut state.buckets,
4237 k,
4238 &state.cache,
4239 state.count,
4240 state.position as SaSint,
4241 );
4242 });
4243 });
4244 }
4245
4246 d
4247}
4248
4249#[doc(hidden)]
4251pub fn partial_gsa_scan_right_to_left_8u_block_omp(
4252 t: &[u8],
4253 sa: &mut [SaSint],
4254 k: SaSint,
4255 buckets: &mut [SaSint],
4256 d: SaSint,
4257 block_start: FastSint,
4258 block_size: FastSint,
4259 threads: SaSint,
4260 thread_state: &mut [ThreadState],
4261) -> SaSint {
4262 let mut d = d;
4263 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
4264 let k_usize = usize::try_from(k).expect("k must be non-negative");
4265 let omp_num_threads = if threads > 1 && block_size_usize >= 64 * k_usize.max(256) {
4266 usize::try_from(threads)
4267 .expect("threads must be non-negative")
4268 .min(thread_state.len())
4269 .max(1)
4270 } else {
4271 1
4272 };
4273 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
4274
4275 if omp_num_threads == 1 {
4276 return partial_gsa_scan_right_to_left_8u(t, sa, buckets, d, block_start, block_size);
4277 }
4278
4279 {
4280 let sa_ro: &[SaSint] = sa;
4281 run_rayon_with_threads(omp_num_threads, || {
4282 thread_state[..omp_num_threads]
4283 .par_iter_mut()
4284 .enumerate()
4285 .for_each(|(omp_thread_num, state)| {
4286 let mut omp_block_start = omp_thread_num * omp_block_stride;
4287 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
4288 omp_block_stride
4289 } else {
4290 block_size_usize - omp_block_start
4291 };
4292 omp_block_start +=
4293 usize::try_from(block_start).expect("block_start must be non-negative");
4294
4295 let (position, count) = partial_sorting_scan_right_to_left_8u_block_prepare(
4296 t,
4297 sa_ro,
4298 k,
4299 &mut state.buckets,
4300 &mut state.cache,
4301 FastSint::try_from(omp_block_start).expect("block start must fit FastSint"),
4302 FastSint::try_from(omp_block_size).expect("block size must fit FastSint"),
4303 );
4304 state.position = position;
4305 state.count = count;
4306 });
4307 });
4308 }
4309
4310 let distinct_offset = 2 * ALPHABET_SIZE;
4311 let (induction_bucket, distinct_tail) = buckets.split_at_mut(distinct_offset);
4312 let distinct_names = &mut distinct_tail[..2 * k_usize];
4313
4314 for tnum in (0..omp_num_threads).rev() {
4315 let state = &mut thread_state[tnum];
4316 let (temp_induction_bucket, temp_tail) = state.buckets.split_at_mut(2 * k_usize);
4317 let temp_distinct_names = &mut temp_tail[..2 * k_usize];
4318
4319 for c in 0..2 * k_usize {
4320 let a = induction_bucket[c];
4321 let b = temp_induction_bucket[c];
4322 induction_bucket[c] = a - b;
4323 temp_induction_bucket[c] = a;
4324 }
4325
4326 d -= 1;
4327 for c in 0..2 * k_usize {
4328 let a = distinct_names[c];
4329 let b = temp_distinct_names[c];
4330 let next_d = b + d;
4331 distinct_names[c] = if b > 0 { next_d } else { a };
4332 temp_distinct_names[c] = a;
4333 }
4334 d += 1 + SaSint::try_from(state.position).expect("position must fit SaSint");
4335 state.position = FastSint::try_from(d).expect("d must fit FastSint") - state.position;
4336 }
4337
4338 {
4339 let sa_ptr = SyncMutPtr::new(sa);
4340 run_rayon_with_threads(omp_num_threads, || {
4341 thread_state[..omp_num_threads]
4342 .par_iter_mut()
4343 .for_each(|state| {
4344 let sa = unsafe { sa_ptr.as_slice() };
4345 partial_gsa_scan_right_to_left_8u_block_place(
4346 sa,
4347 &mut state.buckets,
4348 k,
4349 &state.cache,
4350 state.count,
4351 state.position as SaSint,
4352 );
4353 });
4354 });
4355 }
4356
4357 d
4358}
4359
4360#[doc(hidden)]
4362pub fn partial_sorting_scan_right_to_left_8u_omp(
4363 t: &[u8],
4364 sa: &mut [SaSint],
4365 n: SaSint,
4366 k: SaSint,
4367 buckets: &mut [SaSint],
4368 first_lms_suffix: SaSint,
4369 left_suffixes_count: SaSint,
4370 mut d: SaSint,
4371 threads: SaSint,
4372 thread_state: &mut [ThreadState],
4373) {
4374 let scan_start = left_suffixes_count as FastSint + 1;
4375 let scan_end = n as FastSint - first_lms_suffix as FastSint;
4376
4377 if threads == 1 || (scan_end - scan_start) < 65_536 {
4378 let _ = partial_sorting_scan_right_to_left_8u(
4379 t,
4380 sa,
4381 buckets,
4382 d,
4383 scan_start,
4384 scan_end - scan_start,
4385 );
4386 return;
4387 }
4388
4389 let distinct_offset = 2 * ALPHABET_SIZE;
4390
4391 let mut block_start = usize::try_from(scan_end - 1).expect("scan end must be positive");
4392 let scan_start_usize = usize::try_from(scan_start).expect("scan_start must be non-negative");
4393 let threads_usize = usize::try_from(threads)
4394 .expect("threads must be non-negative")
4395 .min(thread_state.len())
4396 .max(1);
4397
4398 while block_start >= scan_start_usize {
4399 if sa[block_start] == 0 {
4400 if block_start == 0 {
4401 break;
4402 }
4403 block_start -= 1;
4404 } else {
4405 let mut block_max_end = block_start.saturating_sub(
4406 threads_usize * (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * threads_usize),
4407 );
4408 if block_max_end + 1 < scan_start_usize {
4409 block_max_end = scan_start_usize.saturating_sub(1);
4410 }
4411 let mut block_end = block_start - 1;
4412 while block_end > block_max_end && sa[block_end] != 0 {
4413 block_end -= 1;
4414 }
4415 let block_size = block_start - block_end;
4416
4417 if block_size < 32 {
4418 while block_start > block_end {
4419 let p = sa[block_start];
4420 d += SaSint::from(p < 0);
4421 let p = p & SAINT_MAX;
4422 let v = buckets_index2(
4423 t[(p - 1) as usize] as usize,
4424 usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
4425 );
4426 buckets[v] -= 1;
4427 let slot =
4428 usize::try_from(buckets[v]).expect("bucket slot must be non-negative");
4429 sa[slot] = (p - 1)
4430 | (((buckets[distinct_offset + v] != d) as SaSint) << (SAINT_BIT - 1));
4431 buckets[distinct_offset + v] = d;
4432
4433 if block_start == 0 {
4434 break;
4435 }
4436 block_start -= 1;
4437 }
4438 } else {
4439 d = partial_sorting_scan_right_to_left_8u_block_omp(
4440 t,
4441 sa,
4442 k,
4443 buckets,
4444 d,
4445 FastSint::try_from(block_end + 1).expect("block start must fit FastSint"),
4446 FastSint::try_from(block_size).expect("block size must fit FastSint"),
4447 threads,
4448 thread_state,
4449 );
4450 block_start = block_end;
4451 }
4452 }
4453 }
4454}
4455
4456#[doc(hidden)]
4458pub fn partial_gsa_scan_right_to_left_8u_omp(
4459 t: &[u8],
4460 sa: &mut [SaSint],
4461 n: SaSint,
4462 k: SaSint,
4463 buckets: &mut [SaSint],
4464 first_lms_suffix: SaSint,
4465 left_suffixes_count: SaSint,
4466 mut d: SaSint,
4467 threads: SaSint,
4468 thread_state: &mut [ThreadState],
4469) {
4470 let scan_start = left_suffixes_count as FastSint + 1;
4471 let scan_end = n as FastSint - first_lms_suffix as FastSint;
4472
4473 if threads == 1 || (scan_end - scan_start) < 65_536 {
4474 let _ =
4475 partial_gsa_scan_right_to_left_8u(t, sa, buckets, d, scan_start, scan_end - scan_start);
4476 return;
4477 }
4478
4479 let distinct_offset = 2 * ALPHABET_SIZE;
4480 let mut block_start = usize::try_from(scan_end - 1).expect("scan end must be positive");
4481 let scan_start_usize = usize::try_from(scan_start).expect("scan_start must be non-negative");
4482 let threads_usize = usize::try_from(threads)
4483 .expect("threads must be non-negative")
4484 .min(thread_state.len())
4485 .max(1);
4486
4487 while block_start >= scan_start_usize {
4488 if sa[block_start] == 0 {
4489 if block_start == 0 {
4490 break;
4491 }
4492 block_start -= 1;
4493 } else {
4494 let mut block_max_end = block_start.saturating_sub(
4495 threads_usize * (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * threads_usize),
4496 );
4497 if block_max_end + 1 < scan_start_usize {
4498 block_max_end = scan_start_usize.saturating_sub(1);
4499 }
4500 let mut block_end = block_start - 1;
4501 while block_end > block_max_end && sa[block_end] != 0 {
4502 block_end -= 1;
4503 }
4504 let block_size = block_start - block_end;
4505
4506 if block_size < 32 {
4507 while block_start > block_end {
4508 let p = sa[block_start];
4509 d += SaSint::from(p < 0);
4510 let p = p & SAINT_MAX;
4511 let v = buckets_index2(
4512 t[(p - 1) as usize] as usize,
4513 usize::from(t[(p - 2) as usize] > t[(p - 1) as usize]),
4514 );
4515 if v != 1 {
4516 buckets[v] -= 1;
4517 let slot =
4518 usize::try_from(buckets[v]).expect("bucket slot must be non-negative");
4519 sa[slot] = (p - 1)
4520 | (((buckets[distinct_offset + v] != d) as SaSint) << (SAINT_BIT - 1));
4521 buckets[distinct_offset + v] = d;
4522 }
4523
4524 if block_start == 0 {
4525 break;
4526 }
4527 block_start -= 1;
4528 }
4529 } else {
4530 d = partial_gsa_scan_right_to_left_8u_block_omp(
4531 t,
4532 sa,
4533 k,
4534 buckets,
4535 d,
4536 FastSint::try_from(block_end + 1).expect("block start must fit FastSint"),
4537 FastSint::try_from(block_size).expect("block size must fit FastSint"),
4538 threads,
4539 thread_state,
4540 );
4541 block_start = block_end;
4542 }
4543 }
4544 }
4545}
4546
4547#[doc(hidden)]
4549pub fn partial_sorting_scan_right_to_left_32s_6k(
4550 t: &[SaSint],
4551 sa: &mut [SaSint],
4552 buckets: &mut [SaSint],
4553 mut d: SaSint,
4554 omp_block_start: FastSint,
4555 omp_block_size: FastSint,
4556) -> SaSint {
4557 if omp_block_size <= 0 {
4558 return d;
4559 }
4560
4561 let prefetch_distance: FastSint = 64;
4562 let mut i = omp_block_start + omp_block_size - 1;
4563 let mut j = omp_block_start + 2 * prefetch_distance + 1;
4564
4565 while i >= j {
4566 let mut p0 = sa[i as usize];
4567 d += SaSint::from(p0 < 0);
4568 p0 &= SAINT_MAX;
4569 let p0u = p0 as usize;
4570 let v0 = buckets_index4(t[p0u - 1] as usize, usize::from(t[p0u - 2] > t[p0u - 1]));
4571 buckets[v0] -= 1;
4572 let slot0 = buckets[v0] as usize;
4573 sa[slot0] = (p0 - 1) | (((buckets[2 + v0] != d) as SaSint) << (SAINT_BIT - 1));
4574 buckets[2 + v0] = d;
4575
4576 let mut p1 = sa[(i - 1) as usize];
4577 d += SaSint::from(p1 < 0);
4578 p1 &= SAINT_MAX;
4579 let p1u = p1 as usize;
4580 let v1 = buckets_index4(t[p1u - 1] as usize, usize::from(t[p1u - 2] > t[p1u - 1]));
4581 buckets[v1] -= 1;
4582 let slot1 = buckets[v1] as usize;
4583 sa[slot1] = (p1 - 1) | (((buckets[2 + v1] != d) as SaSint) << (SAINT_BIT - 1));
4584 buckets[2 + v1] = d;
4585
4586 i -= 2;
4587 }
4588
4589 j -= 2 * prefetch_distance + 1;
4590 while i >= j {
4591 let mut p = sa[i as usize];
4592 d += SaSint::from(p < 0);
4593 p &= SAINT_MAX;
4594 let pu = p as usize;
4595 let v = buckets_index4(t[pu - 1] as usize, usize::from(t[pu - 2] > t[pu - 1]));
4596
4597 buckets[v] -= 1;
4598 let slot = buckets[v] as usize;
4599 sa[slot] = (p - 1) | (((buckets[2 + v] != d) as SaSint) << (SAINT_BIT - 1));
4600 buckets[2 + v] = d;
4601 i -= 1;
4602 }
4603
4604 d
4605}
4606
4607#[doc(hidden)]
4609pub fn partial_sorting_scan_right_to_left_32s_4k(
4610 t: &[SaSint],
4611 sa: &mut [SaSint],
4612 k: SaSint,
4613 buckets: &mut [SaSint],
4614 mut d: SaSint,
4615 omp_block_start: FastSint,
4616 omp_block_size: FastSint,
4617) -> SaSint {
4618 if omp_block_size <= 0 {
4619 return d;
4620 }
4621
4622 let k_usize = usize::try_from(k).expect("k must be non-negative");
4623 let prefetch_distance: FastSint = 64;
4624 let induction_offset = 3 * k_usize;
4625
4626 let mut i = omp_block_start + omp_block_size - 1;
4627 let mut j = omp_block_start + 2 * prefetch_distance + 1;
4628
4629 while i >= j {
4630 let i0 = i as usize;
4631 let mut p0 = sa[i0];
4632 if p0 > 0 {
4633 sa[i0] = 0;
4634 d += p0 >> (SUFFIX_GROUP_BIT - 1);
4635 p0 &= !SUFFIX_GROUP_MARKER;
4636
4637 let p0u = p0 as usize;
4638 let c0 = t[p0u - 1];
4639 let f0 = usize::from(t[p0u - 2] > c0);
4640 let v0 = buckets_index2(c0 as usize, f0);
4641 let c0u = c0 as usize;
4642 buckets[induction_offset + c0u] -= 1;
4643 let slot0 = buckets[induction_offset + c0u] as usize;
4644 sa[slot0] = (p0 - 1)
4645 | ((f0 as SaSint) << (SAINT_BIT - 1))
4646 | (((buckets[v0] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
4647 buckets[v0] = d;
4648 }
4649
4650 let i1 = (i - 1) as usize;
4651 let mut p1 = sa[i1];
4652 if p1 > 0 {
4653 sa[i1] = 0;
4654 d += p1 >> (SUFFIX_GROUP_BIT - 1);
4655 p1 &= !SUFFIX_GROUP_MARKER;
4656
4657 let p1u = p1 as usize;
4658 let c1 = t[p1u - 1];
4659 let f1 = usize::from(t[p1u - 2] > c1);
4660 let v1 = buckets_index2(c1 as usize, f1);
4661 let c1u = c1 as usize;
4662 buckets[induction_offset + c1u] -= 1;
4663 let slot1 = buckets[induction_offset + c1u] as usize;
4664 sa[slot1] = (p1 - 1)
4665 | ((f1 as SaSint) << (SAINT_BIT - 1))
4666 | (((buckets[v1] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
4667 buckets[v1] = d;
4668 }
4669
4670 i -= 2;
4671 }
4672
4673 j -= 2 * prefetch_distance + 1;
4674 while i >= j {
4675 let iu = i as usize;
4676 let mut p = sa[iu];
4677 if p > 0 {
4678 sa[iu] = 0;
4679 d += p >> (SUFFIX_GROUP_BIT - 1);
4680 p &= !SUFFIX_GROUP_MARKER;
4681
4682 let pu = p as usize;
4683 let c = t[pu - 1];
4684 let f = usize::from(t[pu - 2] > c);
4685 let v = buckets_index2(c as usize, f);
4686 let cu = c as usize;
4687 buckets[induction_offset + cu] -= 1;
4688 let slot = buckets[induction_offset + cu] as usize;
4689 sa[slot] = (p - 1)
4690 | ((f as SaSint) << (SAINT_BIT - 1))
4691 | (((buckets[v] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
4692 buckets[v] = d;
4693 }
4694 i -= 1;
4695 }
4696
4697 d
4698}
4699
4700#[doc(hidden)]
4702pub fn partial_sorting_scan_right_to_left_32s_1k(
4703 t: &[SaSint],
4704 sa: &mut [SaSint],
4705 induction_bucket: &mut [SaSint],
4706 omp_block_start: FastSint,
4707 omp_block_size: FastSint,
4708) {
4709 if omp_block_size <= 0 {
4710 return;
4711 }
4712
4713 let prefetch_distance = 64usize;
4714 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
4715 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
4716 let mut i = (start + size - 1) as isize;
4717 let mut j = (start + 2 * prefetch_distance + 1) as isize;
4718
4719 while i >= j {
4720 let p0 = sa[i as usize];
4721 if p0 > 0 {
4722 sa[i as usize] = 0;
4723 let p0_usize = usize::try_from(p0).expect("suffix index must be non-negative");
4724 let bucket_index0 =
4725 usize::try_from(t[p0_usize - 1]).expect("bucket symbol must be non-negative");
4726 induction_bucket[bucket_index0] -= 1;
4727 let slot0 = usize::try_from(induction_bucket[bucket_index0])
4728 .expect("bucket slot must be non-negative");
4729 sa[slot0] = (p0 - 1)
4730 | ((usize::from(t[p0_usize - 2] > t[p0_usize - 1]) as SaSint) << (SAINT_BIT - 1));
4731 }
4732 let p1 = sa[(i - 1) as usize];
4733 if p1 > 0 {
4734 sa[(i - 1) as usize] = 0;
4735 let p1_usize = usize::try_from(p1).expect("suffix index must be non-negative");
4736 let bucket_index1 =
4737 usize::try_from(t[p1_usize - 1]).expect("bucket symbol must be non-negative");
4738 induction_bucket[bucket_index1] -= 1;
4739 let slot1 = usize::try_from(induction_bucket[bucket_index1])
4740 .expect("bucket slot must be non-negative");
4741 sa[slot1] = (p1 - 1)
4742 | ((usize::from(t[p1_usize - 2] > t[p1_usize - 1]) as SaSint) << (SAINT_BIT - 1));
4743 }
4744
4745 i -= 2;
4746 }
4747
4748 j -= (2 * prefetch_distance + 1) as isize;
4749 while i >= j {
4750 let p = sa[i as usize];
4751 if p > 0 {
4752 sa[i as usize] = 0;
4753 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
4754 let bucket_index =
4755 usize::try_from(t[p_usize - 1]).expect("bucket symbol must be non-negative");
4756 induction_bucket[bucket_index] -= 1;
4757 let slot = usize::try_from(induction_bucket[bucket_index])
4758 .expect("bucket slot must be non-negative");
4759 sa[slot] = (p - 1)
4760 | ((usize::from(t[p_usize - 2] > t[p_usize - 1]) as SaSint) << (SAINT_BIT - 1));
4761 }
4762 if i == 0 {
4763 break;
4764 }
4765 i -= 1;
4766 }
4767}
4768
4769#[doc(hidden)]
4771pub fn partial_sorting_scan_right_to_left_32s_6k_block_gather(
4772 t: &[SaSint],
4773 sa: &[SaSint],
4774 cache: &mut [ThreadCache],
4775 omp_block_start: FastSint,
4776 omp_block_size: FastSint,
4777) {
4778 if omp_block_size <= 0 {
4779 return;
4780 }
4781
4782 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
4783 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
4784 for offset in 0..size {
4785 let i = start + offset;
4786 let mut p = sa[i];
4787 let mut symbol = 0usize;
4788 p &= SAINT_MAX;
4789 if p != 0 {
4790 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
4791 symbol = buckets_index4(
4792 usize::try_from(t[p_usize - 1]).expect("bucket symbol must be non-negative"),
4793 usize::from(t[p_usize - 2] > t[p_usize - 1]),
4794 );
4795 }
4796 cache[offset].index = sa[i];
4797 cache[offset].symbol = symbol as SaSint;
4798 }
4799}
4800
4801#[doc(hidden)]
4803pub fn partial_sorting_scan_right_to_left_32s_4k_block_gather(
4804 t: &[SaSint],
4805 sa: &mut [SaSint],
4806 cache: &mut [ThreadCache],
4807 omp_block_start: FastSint,
4808 omp_block_size: FastSint,
4809) {
4810 if omp_block_size <= 0 {
4811 return;
4812 }
4813
4814 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
4815 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
4816 for offset in 0..size {
4817 let i = start + offset;
4818 let mut symbol = SAINT_MIN;
4819 let mut p = sa[i];
4820 if p > 0 {
4821 sa[i] = 0;
4822 cache[offset].index = p;
4823 p &= !SUFFIX_GROUP_MARKER;
4824 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
4825 symbol = buckets_index2(
4826 usize::try_from(t[p_usize - 1]).expect("bucket symbol must be non-negative"),
4827 usize::from(t[p_usize - 2] > t[p_usize - 1]),
4828 ) as SaSint;
4829 }
4830 cache[offset].symbol = symbol;
4831 }
4832}
4833
4834#[doc(hidden)]
4836pub fn partial_sorting_scan_right_to_left_32s_1k_block_gather(
4837 t: &[SaSint],
4838 sa: &mut [SaSint],
4839 cache: &mut [ThreadCache],
4840 omp_block_start: FastSint,
4841 omp_block_size: FastSint,
4842) {
4843 if omp_block_size <= 0 {
4844 return;
4845 }
4846 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
4847 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
4848 for offset in 0..size {
4849 let i = start + offset;
4850 let mut symbol = SAINT_MIN;
4851 let p = sa[i];
4852 if p > 0 {
4853 sa[i] = 0;
4854 cache[offset].index = (p - 1)
4855 | ((usize::from(t[p as usize - 2] > t[p as usize - 1]) as SaSint)
4856 << (SAINT_BIT - 1));
4857 symbol = t[p as usize - 1];
4858 }
4859 cache[offset].symbol = symbol;
4860 }
4861}
4862
4863#[doc(hidden)]
4865pub fn partial_sorting_scan_right_to_left_32s_6k_block_sort(
4866 t: &[SaSint],
4867 buckets: &mut [SaSint],
4868 mut d: SaSint,
4869 cache: &mut [ThreadCache],
4870 omp_block_start: FastSint,
4871 omp_block_size: FastSint,
4872) -> SaSint {
4873 if omp_block_size <= 0 {
4874 return d;
4875 }
4876
4877 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
4878 let mut i = size;
4879 while i > 0 {
4880 i -= 1;
4881
4882 let v = usize::try_from(cache[i].symbol).expect("cache symbol must be non-negative");
4883 let p = cache[i].index;
4884 d += SaSint::from(p < 0);
4885 buckets[v] -= 1;
4886 let target = buckets[v];
4887 cache[i].symbol = target;
4888 cache[i].index = (p - 1) | (((buckets[2 + v] != d) as SaSint) << (SAINT_BIT - 1));
4889 buckets[2 + v] = d;
4890
4891 if target >= omp_block_start as SaSint
4892 && target < (omp_block_start + omp_block_size) as SaSint
4893 {
4894 let s = usize::try_from(target - omp_block_start as SaSint)
4895 .expect("cache slot must be non-negative");
4896 let q = cache[i].index & SAINT_MAX;
4897 let q_usize = usize::try_from(q).expect("suffix index must be non-negative");
4898 cache[s].index = cache[i].index;
4899 cache[s].symbol = buckets_index4(
4900 usize::try_from(t[q_usize - 1]).expect("bucket symbol must be non-negative"),
4901 usize::from(t[q_usize - 2] > t[q_usize - 1]),
4902 ) as SaSint;
4903 }
4904 }
4905
4906 d
4907}
4908
4909#[doc(hidden)]
4911pub fn partial_sorting_scan_right_to_left_32s_4k_block_sort(
4912 t: &[SaSint],
4913 k: SaSint,
4914 buckets: &mut [SaSint],
4915 mut d: SaSint,
4916 cache: &mut [ThreadCache],
4917 omp_block_start: FastSint,
4918 omp_block_size: FastSint,
4919) -> SaSint {
4920 if omp_block_size <= 0 {
4921 return d;
4922 }
4923
4924 let k_usize = usize::try_from(k).expect("k must be non-negative");
4925 let (distinct_names, tail) = buckets.split_at_mut(2 * k_usize);
4926 let induction_bucket = &mut tail[k_usize..2 * k_usize];
4927
4928 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
4929 let mut i = size;
4930 while i > 0 {
4931 i -= 1;
4932
4933 let v = cache[i].symbol;
4934 if v >= 0 {
4935 let p = cache[i].index;
4936 d += p >> (SUFFIX_GROUP_BIT - 1);
4937 let bucket_index = usize::try_from(v >> 1).expect("bucket symbol must be non-negative");
4938 induction_bucket[bucket_index] -= 1;
4939 let target = induction_bucket[bucket_index];
4940 cache[i].symbol = target;
4941 cache[i].index = (p - 1)
4942 | ((v & 1) << (SAINT_BIT - 1))
4943 | (((distinct_names
4944 [usize::try_from(v).expect("bucket symbol must be non-negative")]
4945 != d) as SaSint)
4946 << (SUFFIX_GROUP_BIT - 1));
4947 distinct_names[usize::try_from(v).expect("bucket symbol must be non-negative")] = d;
4948
4949 if target >= omp_block_start as SaSint
4950 && target < (omp_block_start + omp_block_size) as SaSint
4951 {
4952 let ni = usize::try_from(target - omp_block_start as SaSint)
4953 .expect("cache slot must be non-negative");
4954 let mut np = cache[i].index;
4955 if np > 0 {
4956 cache[i].index = 0;
4957 cache[ni].index = np;
4958 np &= !SUFFIX_GROUP_MARKER;
4959 let np_usize = usize::try_from(np).expect("suffix index must be non-negative");
4960 cache[ni].symbol = buckets_index2(
4961 usize::try_from(t[np_usize - 1])
4962 .expect("bucket symbol must be non-negative"),
4963 usize::from(t[np_usize - 2] > t[np_usize - 1]),
4964 ) as SaSint;
4965 }
4966 }
4967 }
4968 }
4969
4970 d
4971}
4972
4973#[doc(hidden)]
4975pub fn partial_sorting_scan_right_to_left_32s_1k_block_sort(
4976 t: &[SaSint],
4977 induction_bucket: &mut [SaSint],
4978 cache: &mut [ThreadCache],
4979 omp_block_start: FastSint,
4980 omp_block_size: FastSint,
4981) {
4982 if omp_block_size <= 0 {
4983 return;
4984 }
4985 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
4986 let mut offset = size;
4987
4988 while offset > 0 {
4989 offset -= 1;
4990 let v = cache[offset].symbol;
4991 if v >= 0 {
4992 let bucket_index = v as usize;
4993 induction_bucket[bucket_index] -= 1;
4994 let target = induction_bucket[bucket_index];
4995 cache[offset].symbol = target;
4996 let block_end = omp_block_start as SaSint + omp_block_size as SaSint;
4997 if target >= omp_block_start as SaSint && target < block_end {
4998 let ni = usize::try_from(target - omp_block_start as SaSint)
4999 .expect("cache slot must be non-negative");
5000 let np = cache[offset].index;
5001 if np > 0 {
5002 cache[offset].index = 0;
5003 cache[ni].index = (np - 1)
5004 | ((usize::from(t[np as usize - 2] > t[np as usize - 1]) as SaSint)
5005 << (SAINT_BIT - 1));
5006 cache[ni].symbol = t[np as usize - 1];
5007 }
5008 }
5009 }
5010 }
5011}
5012
5013#[doc(hidden)]
5015pub fn partial_sorting_scan_right_to_left_32s_6k_block_omp(
5016 t: &[SaSint],
5017 sa: &mut [SaSint],
5018 buckets: &mut [SaSint],
5019 mut d: SaSint,
5020 cache: &mut [ThreadCache],
5021 block_start: FastSint,
5022 block_size: FastSint,
5023 threads: SaSint,
5024) -> SaSint {
5025 if block_size <= 0 {
5026 return d;
5027 }
5028 if threads == 1 || block_size < 16_384 {
5029 return partial_sorting_scan_right_to_left_32s_6k(
5030 t,
5031 sa,
5032 buckets,
5033 d,
5034 block_start,
5035 block_size,
5036 );
5037 }
5038
5039 let threads_usize = usize::try_from(threads)
5040 .expect("threads must be non-negative")
5041 .max(1);
5042 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
5043 let omp_num_threads = threads_usize.min(block_size_usize.max(1));
5044 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
5045
5046 {
5047 let sa_ro: &[SaSint] = sa;
5048 let t_ro: &[SaSint] = t;
5049 let cache_ptr = SyncMutPtr::new(cache);
5050 run_rayon_with_threads(omp_num_threads, || {
5051 (0..omp_num_threads)
5052 .into_par_iter()
5053 .for_each(|omp_thread_num| {
5054 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5055 omp_block_stride
5056 } else {
5057 block_size_usize - omp_thread_num * omp_block_stride
5058 };
5059 let omp_block_start = usize::try_from(block_start)
5060 .expect("block_start must be non-negative")
5061 + omp_thread_num * omp_block_stride;
5062 if omp_block_size > 0 {
5063 let cache = unsafe { cache_ptr.as_slice() };
5064 partial_sorting_scan_right_to_left_32s_6k_block_gather(
5065 t_ro,
5066 sa_ro,
5067 &mut cache[omp_thread_num * omp_block_stride
5068 ..omp_thread_num * omp_block_stride + omp_block_size],
5069 omp_block_start as FastSint,
5070 omp_block_size as FastSint,
5071 );
5072 }
5073 });
5074 });
5075 }
5076
5077 d = partial_sorting_scan_right_to_left_32s_6k_block_sort(
5078 t,
5079 buckets,
5080 d,
5081 &mut cache[..block_size_usize],
5082 block_start,
5083 block_size,
5084 );
5085
5086 {
5087 let sa_ptr = SyncMutPtr::new(sa);
5088 let cache_ro: &[ThreadCache] = cache;
5089 run_rayon_with_threads(omp_num_threads, || {
5090 (0..omp_num_threads)
5091 .into_par_iter()
5092 .for_each(|omp_thread_num| {
5093 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5094 omp_block_stride
5095 } else {
5096 block_size_usize - omp_thread_num * omp_block_stride
5097 };
5098 let cache_start = omp_thread_num * omp_block_stride;
5099 if omp_block_size > 0 {
5100 let sa = unsafe { sa_ptr.as_slice() };
5101 place_cached_suffixes(
5102 sa,
5103 &cache_ro[cache_start..],
5104 0,
5105 omp_block_size as FastSint,
5106 );
5107 }
5108 });
5109 });
5110 }
5111
5112 d
5113}
5114
5115#[doc(hidden)]
5117pub fn partial_sorting_scan_right_to_left_32s_4k_block_omp(
5118 t: &[SaSint],
5119 sa: &mut [SaSint],
5120 k: SaSint,
5121 buckets: &mut [SaSint],
5122 mut d: SaSint,
5123 cache: &mut [ThreadCache],
5124 block_start: FastSint,
5125 block_size: FastSint,
5126 threads: SaSint,
5127) -> SaSint {
5128 if block_size <= 0 {
5129 return d;
5130 }
5131 if threads == 1 || block_size < 16_384 {
5132 return partial_sorting_scan_right_to_left_32s_4k(
5133 t,
5134 sa,
5135 k,
5136 buckets,
5137 d,
5138 block_start,
5139 block_size,
5140 );
5141 }
5142
5143 let threads_usize = usize::try_from(threads)
5144 .expect("threads must be non-negative")
5145 .max(1);
5146 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
5147 let omp_num_threads = threads_usize.min(block_size_usize.max(1));
5148 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
5149
5150 {
5151 let sa_ptr = SyncMutPtr::new(sa);
5152 let t_ro: &[SaSint] = t;
5153 let cache_ptr = SyncMutPtr::new(cache);
5154 run_rayon_with_threads(omp_num_threads, || {
5155 (0..omp_num_threads)
5156 .into_par_iter()
5157 .for_each(|omp_thread_num| {
5158 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5159 omp_block_stride
5160 } else {
5161 block_size_usize - omp_thread_num * omp_block_stride
5162 };
5163 let omp_block_start = usize::try_from(block_start)
5164 .expect("block_start must be non-negative")
5165 + omp_thread_num * omp_block_stride;
5166 if omp_block_size > 0 {
5167 let cache = unsafe { cache_ptr.as_slice() };
5168 let sa = unsafe { sa_ptr.as_slice() };
5169 partial_sorting_scan_right_to_left_32s_4k_block_gather(
5170 t_ro,
5171 sa,
5172 &mut cache[omp_thread_num * omp_block_stride
5173 ..omp_thread_num * omp_block_stride + omp_block_size],
5174 omp_block_start as FastSint,
5175 omp_block_size as FastSint,
5176 );
5177 }
5178 });
5179 });
5180 }
5181
5182 d = partial_sorting_scan_right_to_left_32s_4k_block_sort(
5183 t,
5184 k,
5185 buckets,
5186 d,
5187 &mut cache[..block_size_usize],
5188 block_start,
5189 block_size,
5190 );
5191
5192 {
5193 let sa_ptr = SyncMutPtr::new(sa);
5194 let cache_ptr = SyncMutPtr::new(cache);
5195 run_rayon_with_threads(omp_num_threads, || {
5196 (0..omp_num_threads)
5197 .into_par_iter()
5198 .for_each(|omp_thread_num| {
5199 let omp_block_start = omp_thread_num * omp_block_stride;
5200 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5201 omp_block_stride
5202 } else {
5203 block_size_usize - omp_block_start
5204 };
5205 if omp_block_size > 0 {
5206 let sa = unsafe { sa_ptr.as_slice() };
5207 let cache = unsafe { cache_ptr.as_slice() };
5208 compact_and_place_cached_suffixes(
5209 sa,
5210 &mut cache[omp_block_start..],
5211 0,
5212 omp_block_size as FastSint,
5213 );
5214 }
5215 });
5216 });
5217 }
5218
5219 d
5220}
5221
5222#[doc(hidden)]
5224pub fn partial_sorting_scan_right_to_left_32s_1k_block_omp(
5225 t: &[SaSint],
5226 sa: &mut [SaSint],
5227 buckets: &mut [SaSint],
5228 cache: &mut [ThreadCache],
5229 block_start: FastSint,
5230 block_size: FastSint,
5231 threads: SaSint,
5232) {
5233 if block_size <= 0 {
5234 return;
5235 }
5236 if threads == 1 || block_size < 16_384 {
5237 partial_sorting_scan_right_to_left_32s_1k(t, sa, buckets, block_start, block_size);
5238 return;
5239 }
5240
5241 let threads_usize = usize::try_from(threads)
5242 .expect("threads must be non-negative")
5243 .max(1);
5244 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
5245 let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
5246 let omp_num_threads = threads_usize.min(block_size_usize.max(1));
5247 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
5248
5249 {
5250 let sa_ptr = SyncMutPtr::new(sa);
5251 let t_ro: &[SaSint] = t;
5252 let cache_ptr = SyncMutPtr::new(cache);
5253 run_rayon_with_threads(omp_num_threads, || {
5254 (0..omp_num_threads)
5255 .into_par_iter()
5256 .for_each(|omp_thread_num| {
5257 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5258 omp_block_stride
5259 } else {
5260 block_size_usize - omp_thread_num * omp_block_stride
5261 };
5262 let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
5263 if omp_block_size > 0 {
5264 let cache = unsafe { cache_ptr.as_slice() };
5265 let sa = unsafe { sa_ptr.as_slice() };
5266 partial_sorting_scan_right_to_left_32s_1k_block_gather(
5267 t_ro,
5268 sa,
5269 &mut cache[omp_thread_num * omp_block_stride
5270 ..omp_thread_num * omp_block_stride + omp_block_size],
5271 omp_block_start as FastSint,
5272 omp_block_size as FastSint,
5273 );
5274 }
5275 });
5276 });
5277 }
5278
5279 let cache = &mut cache[..block_size_usize];
5280 partial_sorting_scan_right_to_left_32s_1k_block_sort(
5281 t,
5282 buckets,
5283 cache,
5284 block_start,
5285 block_size,
5286 );
5287 {
5288 let sa_ptr = SyncMutPtr::new(sa);
5289 let cache_ptr = SyncMutPtr::new(cache);
5290 run_rayon_with_threads(omp_num_threads, || {
5291 (0..omp_num_threads)
5292 .into_par_iter()
5293 .for_each(|omp_thread_num| {
5294 let omp_block_start = omp_thread_num * omp_block_stride;
5295 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5296 omp_block_stride
5297 } else {
5298 block_size_usize - omp_block_start
5299 };
5300 if omp_block_size > 0 {
5301 let sa = unsafe { sa_ptr.as_slice() };
5302 let cache = unsafe { cache_ptr.as_slice() };
5303 compact_and_place_cached_suffixes(
5304 sa,
5305 &mut cache[omp_block_start..],
5306 0,
5307 omp_block_size as FastSint,
5308 );
5309 }
5310 });
5311 });
5312 }
5313}
5314
5315#[doc(hidden)]
5317pub fn partial_sorting_scan_left_to_right_32s_6k_block_gather(
5318 t: &[SaSint],
5319 sa: &mut [SaSint],
5320 cache: &mut [ThreadCache],
5321 omp_block_start: FastSint,
5322 omp_block_size: FastSint,
5323) {
5324 if omp_block_size <= 0 {
5325 return;
5326 }
5327
5328 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5329 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5330 for offset in 0..size {
5331 let i = start + offset;
5332 let p = sa[i];
5333 cache[offset].index = p;
5334 let q = p & SAINT_MAX;
5335 cache[offset].symbol = if q != 0 {
5336 buckets_index4(
5337 usize::try_from(t[q as usize - 1]).expect("bucket symbol must be non-negative"),
5338 usize::from(t[q as usize - 2] >= t[q as usize - 1]),
5339 ) as SaSint
5340 } else {
5341 0
5342 };
5343 }
5344}
5345
5346#[doc(hidden)]
5348pub fn partial_sorting_scan_left_to_right_32s_4k_block_gather(
5349 t: &[SaSint],
5350 sa: &mut [SaSint],
5351 cache: &mut [ThreadCache],
5352 omp_block_start: FastSint,
5353 omp_block_size: FastSint,
5354) {
5355 if omp_block_size <= 0 {
5356 return;
5357 }
5358
5359 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5360 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5361 for offset in 0..size {
5362 let i = start + offset;
5363 let mut symbol = SAINT_MIN;
5364 let mut p = sa[i];
5365 if p > 0 {
5366 cache[offset].index = p;
5367 p &= !SUFFIX_GROUP_MARKER;
5368 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
5369 symbol = buckets_index2(
5370 usize::try_from(t[p_usize - 1]).expect("bucket symbol must be non-negative"),
5371 usize::from(t[p_usize - 2] < t[p_usize - 1]),
5372 ) as SaSint;
5373 p = 0;
5374 }
5375 cache[offset].symbol = symbol;
5376 sa[i] = p & SAINT_MAX;
5377 }
5378}
5379
5380#[doc(hidden)]
5382pub fn partial_sorting_scan_left_to_right_32s_1k_block_gather(
5383 t: &[SaSint],
5384 sa: &mut [SaSint],
5385 cache: &mut [ThreadCache],
5386 omp_block_start: FastSint,
5387 omp_block_size: FastSint,
5388) {
5389 if omp_block_size <= 0 {
5390 return;
5391 }
5392 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5393 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5394 for offset in 0..size {
5395 let i = start + offset;
5396 let mut symbol = SAINT_MIN;
5397 let mut p = sa[i];
5398 if p > 0 {
5399 cache[offset].index = (p - 1)
5400 | ((usize::from(t[p as usize - 2] < t[p as usize - 1]) as SaSint)
5401 << (SAINT_BIT - 1));
5402 symbol = t[p as usize - 1];
5403 p = 0;
5404 }
5405 cache[offset].symbol = symbol;
5406 sa[i] = p & SAINT_MAX;
5407 }
5408}
5409
5410#[doc(hidden)]
5412pub fn partial_sorting_scan_left_to_right_32s_6k_block_sort(
5413 t: &[SaSint],
5414 buckets: &mut [SaSint],
5415 mut d: SaSint,
5416 cache: &mut [ThreadCache],
5417 omp_block_start: FastSint,
5418 omp_block_size: FastSint,
5419) -> SaSint {
5420 if omp_block_size <= 0 {
5421 return d;
5422 }
5423
5424 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5425 let block_end =
5426 start + usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5427
5428 let mut i = start;
5429 let mut j = block_end.saturating_sub(65);
5430 while i < j {
5431 let cache_i0 = i - start;
5432 let cache_i1 = cache_i0 + 1;
5433
5434 let v0 =
5435 usize::try_from(cache[cache_i0].symbol).expect("cache symbol must be non-negative");
5436 let p0 = cache[cache_i0].index;
5437 d += SaSint::from(p0 < 0);
5438 cache[cache_i0].symbol = buckets[v0];
5439 buckets[v0] += 1;
5440 cache[cache_i0].index =
5441 (p0 - 1) | ((SaSint::from(buckets[2 + v0] != d)) << (SAINT_BIT - 1));
5442 buckets[2 + v0] = d;
5443 if cache[cache_i0].symbol >= omp_block_start as SaSint
5444 && cache[cache_i0].symbol < block_end as SaSint
5445 {
5446 let s = usize::try_from(cache[cache_i0].symbol - omp_block_start as SaSint)
5447 .expect("cache slot must be non-negative");
5448 let q = cache[cache_i0].index & SAINT_MAX;
5449 cache[s].index = cache[cache_i0].index;
5450 let q_usize = usize::try_from(q).expect("suffix index must be non-negative");
5451 cache[s].symbol = buckets_index4(
5452 usize::try_from(t[q_usize - 1]).expect("bucket symbol must be non-negative"),
5453 usize::from(t[q_usize - 2] >= t[q_usize - 1]),
5454 ) as SaSint;
5455 }
5456
5457 let v1 =
5458 usize::try_from(cache[cache_i1].symbol).expect("cache symbol must be non-negative");
5459 let p1 = cache[cache_i1].index;
5460 d += SaSint::from(p1 < 0);
5461 cache[cache_i1].symbol = buckets[v1];
5462 buckets[v1] += 1;
5463 cache[cache_i1].index =
5464 (p1 - 1) | ((SaSint::from(buckets[2 + v1] != d)) << (SAINT_BIT - 1));
5465 buckets[2 + v1] = d;
5466 if cache[cache_i1].symbol >= omp_block_start as SaSint
5467 && cache[cache_i1].symbol < block_end as SaSint
5468 {
5469 let s = usize::try_from(cache[cache_i1].symbol - omp_block_start as SaSint)
5470 .expect("cache slot must be non-negative");
5471 let q = cache[cache_i1].index & SAINT_MAX;
5472 cache[s].index = cache[cache_i1].index;
5473 let q_usize = usize::try_from(q).expect("suffix index must be non-negative");
5474 cache[s].symbol = buckets_index4(
5475 usize::try_from(t[q_usize - 1]).expect("bucket symbol must be non-negative"),
5476 usize::from(t[q_usize - 2] >= t[q_usize - 1]),
5477 ) as SaSint;
5478 }
5479
5480 i += 2;
5481 }
5482
5483 j += 65;
5484 while i < j {
5485 let cache_i = i - start;
5486 let v = usize::try_from(cache[cache_i].symbol).expect("cache symbol must be non-negative");
5487 let p = cache[cache_i].index;
5488 d += SaSint::from(p < 0);
5489 cache[cache_i].symbol = buckets[v];
5490 buckets[v] += 1;
5491 cache[cache_i].index = (p - 1) | ((SaSint::from(buckets[2 + v] != d)) << (SAINT_BIT - 1));
5492 buckets[2 + v] = d;
5493 if cache[cache_i].symbol >= omp_block_start as SaSint
5494 && cache[cache_i].symbol < block_end as SaSint
5495 {
5496 let s = usize::try_from(cache[cache_i].symbol - omp_block_start as SaSint)
5497 .expect("cache slot must be non-negative");
5498 let q = cache[cache_i].index & SAINT_MAX;
5499 cache[s].index = cache[cache_i].index;
5500 let q_usize = usize::try_from(q).expect("suffix index must be non-negative");
5501 cache[s].symbol = buckets_index4(
5502 usize::try_from(t[q_usize - 1]).expect("bucket symbol must be non-negative"),
5503 usize::from(t[q_usize - 2] >= t[q_usize - 1]),
5504 ) as SaSint;
5505 }
5506 i += 1;
5507 }
5508
5509 d
5510}
5511
5512#[doc(hidden)]
5514pub fn partial_sorting_scan_left_to_right_32s_4k_block_sort(
5515 t: &[SaSint],
5516 k: SaSint,
5517 buckets: &mut [SaSint],
5518 mut d: SaSint,
5519 cache: &mut [ThreadCache],
5520 omp_block_start: FastSint,
5521 omp_block_size: FastSint,
5522) -> SaSint {
5523 if omp_block_size <= 0 {
5524 return d;
5525 }
5526
5527 let k_usize = usize::try_from(k).expect("k must be non-negative");
5528 let (distinct_names, tail) = buckets.split_at_mut(2 * k_usize);
5529 let induction_bucket = &mut tail[..k_usize];
5530
5531 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5532 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5533 let block_end = start + size;
5534
5535 for offset in 0..size {
5536 let v = cache[offset].symbol;
5537 if v >= 0 {
5538 let p = cache[offset].index;
5539 d += p >> (SUFFIX_GROUP_BIT - 1);
5540
5541 let bucket_index = usize::try_from(v >> 1).expect("bucket index must be non-negative");
5542 let v_usize = usize::try_from(v).expect("cache symbol must be non-negative");
5543 let target = induction_bucket[bucket_index];
5544 induction_bucket[bucket_index] += 1;
5545
5546 cache[offset].symbol = target;
5547 cache[offset].index = (p - 1)
5548 | ((v & 1) << (SAINT_BIT - 1))
5549 | (((distinct_names[v_usize] != d) as SaSint) << (SUFFIX_GROUP_BIT - 1));
5550 distinct_names[v_usize] = d;
5551
5552 if target >= omp_block_start as SaSint && target < block_end as SaSint {
5553 let ni = usize::try_from(target - omp_block_start as SaSint)
5554 .expect("cache slot must be non-negative");
5555 let mut np = cache[offset].index;
5556 if np > 0 {
5557 cache[ni].index = np;
5558 np &= !SUFFIX_GROUP_MARKER;
5559 let np_usize = usize::try_from(np).expect("suffix index must be non-negative");
5560 cache[ni].symbol = buckets_index2(
5561 usize::try_from(t[np_usize - 1])
5562 .expect("bucket symbol must be non-negative"),
5563 usize::from(t[np_usize - 2] < t[np_usize - 1]),
5564 ) as SaSint;
5565 np = 0;
5566 }
5567 cache[offset].index = np & SAINT_MAX;
5568 }
5569 }
5570 }
5571
5572 d
5573}
5574
5575#[doc(hidden)]
5577pub fn partial_sorting_scan_left_to_right_32s_1k_block_sort(
5578 t: &[SaSint],
5579 induction_bucket: &mut [SaSint],
5580 cache: &mut [ThreadCache],
5581 omp_block_start: FastSint,
5582 omp_block_size: FastSint,
5583) {
5584 if omp_block_size <= 0 {
5585 return;
5586 }
5587 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
5588 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
5589 let block_end = start + size;
5590
5591 for offset in 0..size {
5592 let v = cache[offset].symbol;
5593 if v >= 0 {
5594 let v_usize = v as usize;
5595 let target = induction_bucket[v_usize];
5596 cache[offset].symbol = target;
5597 induction_bucket[v_usize] += 1;
5598 if target >= omp_block_start as SaSint && target < block_end as SaSint {
5599 let ni = usize::try_from(target - omp_block_start as SaSint)
5600 .expect("cache slot must be non-negative");
5601 let mut np = cache[offset].index;
5602 if np > 0 {
5603 cache[ni].index = (np - 1)
5604 | ((usize::from(t[np as usize - 2] < t[np as usize - 1]) as SaSint)
5605 << (SAINT_BIT - 1));
5606 cache[ni].symbol = t[np as usize - 1];
5607 np = 0;
5608 }
5609 cache[offset].index = np & SAINT_MAX;
5610 }
5611 }
5612 }
5613}
5614
5615#[doc(hidden)]
5617pub fn partial_sorting_scan_left_to_right_32s_6k_block_omp(
5618 t: &[SaSint],
5619 sa: &mut [SaSint],
5620 buckets: &mut [SaSint],
5621 d: SaSint,
5622 cache: &mut [ThreadCache],
5623 block_start: FastSint,
5624 block_size: FastSint,
5625 threads: SaSint,
5626) -> SaSint {
5627 if block_size <= 0 {
5628 return d;
5629 }
5630 if threads == 1 || block_size < 16_384 {
5631 return partial_sorting_scan_left_to_right_32s_6k(
5632 t,
5633 sa,
5634 buckets,
5635 d,
5636 block_start,
5637 block_size,
5638 );
5639 }
5640
5641 let threads_usize = usize::try_from(threads)
5642 .expect("threads must be non-negative")
5643 .max(1);
5644 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
5645 let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
5646 let omp_num_threads = threads_usize.min(block_size_usize.max(1));
5647 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
5648
5649 {
5650 let sa_ptr = SyncMutPtr::new(sa);
5651 let t_ro: &[SaSint] = t;
5652 let cache_ptr = SyncMutPtr::new(cache);
5653 run_rayon_with_threads(omp_num_threads, || {
5654 (0..omp_num_threads)
5655 .into_par_iter()
5656 .for_each(|omp_thread_num| {
5657 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5658 omp_block_stride
5659 } else {
5660 block_size_usize - omp_thread_num * omp_block_stride
5661 };
5662 let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
5663 if omp_block_size > 0 {
5664 let cache = unsafe { cache_ptr.as_slice() };
5665 let sa = unsafe { sa_ptr.as_slice() };
5666 partial_sorting_scan_left_to_right_32s_6k_block_gather(
5667 t_ro,
5668 sa,
5669 &mut cache[omp_thread_num * omp_block_stride
5670 ..omp_thread_num * omp_block_stride + omp_block_size],
5671 omp_block_start as FastSint,
5672 omp_block_size as FastSint,
5673 );
5674 }
5675 });
5676 });
5677 }
5678
5679 let d = partial_sorting_scan_left_to_right_32s_6k_block_sort(
5680 t,
5681 buckets,
5682 d,
5683 &mut cache[..block_size_usize],
5684 block_start,
5685 block_size,
5686 );
5687
5688 {
5689 let sa_ptr = SyncMutPtr::new(sa);
5690 let cache_ro: &[ThreadCache] = cache;
5691 run_rayon_with_threads(omp_num_threads, || {
5692 (0..omp_num_threads)
5693 .into_par_iter()
5694 .for_each(|omp_thread_num| {
5695 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5696 omp_block_stride
5697 } else {
5698 block_size_usize - omp_thread_num * omp_block_stride
5699 };
5700 if omp_block_size > 0 {
5701 let sa = unsafe { sa_ptr.as_slice() };
5702 place_cached_suffixes(
5703 sa,
5704 &cache_ro[omp_thread_num * omp_block_stride..],
5705 0,
5706 omp_block_size as FastSint,
5707 );
5708 }
5709 });
5710 });
5711 }
5712 d
5713}
5714
5715#[doc(hidden)]
5717pub fn partial_sorting_scan_left_to_right_32s_4k_block_omp(
5718 t: &[SaSint],
5719 sa: &mut [SaSint],
5720 k: SaSint,
5721 buckets: &mut [SaSint],
5722 d: SaSint,
5723 cache: &mut [ThreadCache],
5724 block_start: FastSint,
5725 block_size: FastSint,
5726 threads: SaSint,
5727) -> SaSint {
5728 if block_size <= 0 {
5729 return d;
5730 }
5731 if threads == 1 || block_size < 16_384 {
5732 return partial_sorting_scan_left_to_right_32s_4k(
5733 t,
5734 sa,
5735 k,
5736 buckets,
5737 d,
5738 block_start,
5739 block_size,
5740 );
5741 }
5742
5743 let threads_usize = usize::try_from(threads)
5744 .expect("threads must be non-negative")
5745 .max(1);
5746 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
5747 let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
5748 let omp_num_threads = threads_usize.min(block_size_usize.max(1));
5749 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
5750
5751 {
5752 let sa_ptr = SyncMutPtr::new(sa);
5753 let t_ro: &[SaSint] = t;
5754 let cache_ptr = SyncMutPtr::new(cache);
5755 run_rayon_with_threads(omp_num_threads, || {
5756 (0..omp_num_threads)
5757 .into_par_iter()
5758 .for_each(|omp_thread_num| {
5759 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5760 omp_block_stride
5761 } else {
5762 block_size_usize - omp_thread_num * omp_block_stride
5763 };
5764 let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
5765 if omp_block_size > 0 {
5766 let cache = unsafe { cache_ptr.as_slice() };
5767 let sa = unsafe { sa_ptr.as_slice() };
5768 partial_sorting_scan_left_to_right_32s_4k_block_gather(
5769 t_ro,
5770 sa,
5771 &mut cache[omp_thread_num * omp_block_stride
5772 ..omp_thread_num * omp_block_stride + omp_block_size],
5773 omp_block_start as FastSint,
5774 omp_block_size as FastSint,
5775 );
5776 }
5777 });
5778 });
5779 }
5780
5781 let cache = &mut cache[..block_size_usize];
5782 let d = partial_sorting_scan_left_to_right_32s_4k_block_sort(
5783 t,
5784 k,
5785 buckets,
5786 d,
5787 cache,
5788 block_start,
5789 block_size,
5790 );
5791
5792 {
5793 let sa_ptr = SyncMutPtr::new(sa);
5794 let cache_ptr = SyncMutPtr::new(cache);
5795 run_rayon_with_threads(omp_num_threads, || {
5796 (0..omp_num_threads)
5797 .into_par_iter()
5798 .for_each(|omp_thread_num| {
5799 let omp_block_start = omp_thread_num * omp_block_stride;
5800 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5801 omp_block_stride
5802 } else {
5803 block_size_usize - omp_block_start
5804 };
5805 if omp_block_size > 0 {
5806 let sa = unsafe { sa_ptr.as_slice() };
5807 let cache = unsafe { cache_ptr.as_slice() };
5808 compact_and_place_cached_suffixes(
5809 sa,
5810 &mut cache[omp_block_start..],
5811 0,
5812 omp_block_size as FastSint,
5813 );
5814 }
5815 });
5816 });
5817 }
5818
5819 d
5820}
5821
5822#[doc(hidden)]
5824pub fn partial_sorting_scan_left_to_right_32s_1k_block_omp(
5825 t: &[SaSint],
5826 sa: &mut [SaSint],
5827 buckets: &mut [SaSint],
5828 cache: &mut [ThreadCache],
5829 block_start: FastSint,
5830 block_size: FastSint,
5831 threads: SaSint,
5832) {
5833 if block_size <= 0 {
5834 return;
5835 }
5836 if threads == 1 || block_size < 16_384 {
5837 partial_sorting_scan_left_to_right_32s_1k(t, sa, buckets, block_start, block_size);
5838 return;
5839 }
5840
5841 let threads_usize = usize::try_from(threads)
5842 .expect("threads must be non-negative")
5843 .max(1);
5844 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
5845 let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
5846 let omp_num_threads = threads_usize.min(block_size_usize.max(1));
5847 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
5848
5849 {
5850 let sa_ptr = SyncMutPtr::new(sa);
5851 let t_ro: &[SaSint] = t;
5852 let cache_ptr = SyncMutPtr::new(cache);
5853 run_rayon_with_threads(omp_num_threads, || {
5854 (0..omp_num_threads)
5855 .into_par_iter()
5856 .for_each(|omp_thread_num| {
5857 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5858 omp_block_stride
5859 } else {
5860 block_size_usize - omp_thread_num * omp_block_stride
5861 };
5862 let omp_block_start = block_start_usize + omp_thread_num * omp_block_stride;
5863 if omp_block_size > 0 {
5864 let cache = unsafe { cache_ptr.as_slice() };
5865 let sa = unsafe { sa_ptr.as_slice() };
5866 partial_sorting_scan_left_to_right_32s_1k_block_gather(
5867 t_ro,
5868 sa,
5869 &mut cache[omp_thread_num * omp_block_stride
5870 ..omp_thread_num * omp_block_stride + omp_block_size],
5871 omp_block_start as FastSint,
5872 omp_block_size as FastSint,
5873 );
5874 }
5875 });
5876 });
5877 }
5878
5879 let cache = &mut cache[..block_size_usize];
5880 partial_sorting_scan_left_to_right_32s_1k_block_sort(
5881 t,
5882 buckets,
5883 cache,
5884 block_start,
5885 block_size,
5886 );
5887 {
5888 let sa_ptr = SyncMutPtr::new(sa);
5889 let cache_ptr = SyncMutPtr::new(cache);
5890 run_rayon_with_threads(omp_num_threads, || {
5891 (0..omp_num_threads)
5892 .into_par_iter()
5893 .for_each(|omp_thread_num| {
5894 let omp_block_start = omp_thread_num * omp_block_stride;
5895 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
5896 omp_block_stride
5897 } else {
5898 block_size_usize - omp_block_start
5899 };
5900 if omp_block_size > 0 {
5901 let sa = unsafe { sa_ptr.as_slice() };
5902 let cache = unsafe { cache_ptr.as_slice() };
5903 compact_and_place_cached_suffixes(
5904 sa,
5905 &mut cache[omp_block_start..],
5906 0,
5907 omp_block_size as FastSint,
5908 );
5909 }
5910 });
5911 });
5912 }
5913}
5914
5915#[doc(hidden)]
5917pub fn partial_sorting_scan_right_to_left_32s_6k_omp(
5918 t: &[SaSint],
5919 sa: &mut [SaSint],
5920 n: SaSint,
5921 buckets: &mut [SaSint],
5922 first_lms_suffix: SaSint,
5923 left_suffixes_count: SaSint,
5924 mut d: SaSint,
5925 threads: SaSint,
5926 thread_state: &mut [ThreadState],
5927) -> SaSint {
5928 let scan_start = left_suffixes_count as FastSint + 1;
5929 let scan_end = n as FastSint - first_lms_suffix as FastSint;
5930 if threads == 1 || (scan_end - scan_start) < 65_536 {
5931 return partial_sorting_scan_right_to_left_32s_6k(
5932 t,
5933 sa,
5934 buckets,
5935 d,
5936 scan_start,
5937 scan_end - scan_start,
5938 );
5939 }
5940 if thread_state.is_empty() {
5941 return partial_sorting_scan_right_to_left_32s_6k(
5942 t,
5943 sa,
5944 buckets,
5945 d,
5946 scan_start,
5947 scan_end - scan_start,
5948 );
5949 }
5950
5951 let threads_usize = usize::try_from(threads)
5952 .expect("threads must be non-negative")
5953 .max(1);
5954 let mut cache = vec![ThreadCache::default(); threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE];
5955 let mut block_start = scan_end - 1;
5956 let block_span = FastSint::try_from(threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE)
5957 .expect("block span must fit FastSint");
5958 while block_start >= scan_start {
5959 let mut block_end = block_start - block_span;
5960 if block_end < scan_start {
5961 block_end = scan_start - 1;
5962 }
5963
5964 d = partial_sorting_scan_right_to_left_32s_6k_block_omp(
5965 t,
5966 sa,
5967 buckets,
5968 d,
5969 &mut cache,
5970 block_end + 1,
5971 block_start - block_end,
5972 threads,
5973 );
5974
5975 if block_end < scan_start {
5976 break;
5977 }
5978 block_start = block_end;
5979 }
5980
5981 d
5982}
5983
5984#[doc(hidden)]
5986pub fn partial_sorting_scan_right_to_left_32s_4k_omp(
5987 t: &[SaSint],
5988 sa: &mut [SaSint],
5989 n: SaSint,
5990 k: SaSint,
5991 buckets: &mut [SaSint],
5992 mut d: SaSint,
5993 threads: SaSint,
5994 thread_state: &mut [ThreadState],
5995) -> SaSint {
5996 if threads == 1 || n < 65_536 {
5997 return partial_sorting_scan_right_to_left_32s_4k(t, sa, k, buckets, d, 0, n as FastSint);
5998 }
5999 if thread_state.is_empty() {
6000 return partial_sorting_scan_right_to_left_32s_4k(t, sa, k, buckets, d, 0, n as FastSint);
6001 }
6002 let threads_usize = usize::try_from(threads)
6003 .expect("threads must be non-negative")
6004 .max(1);
6005 let mut cache = vec![ThreadCache::default(); threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE];
6006 let mut block_start = FastSint::try_from(n).expect("n must fit FastSint") - 1;
6007 let block_span = FastSint::try_from(threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE)
6008 .expect("block span must fit FastSint");
6009 while block_start >= 0 {
6010 let mut block_end = block_start - block_span;
6011 if block_end < 0 {
6012 block_end = -1;
6013 }
6014
6015 d = partial_sorting_scan_right_to_left_32s_4k_block_omp(
6016 t,
6017 sa,
6018 k,
6019 buckets,
6020 d,
6021 &mut cache,
6022 block_end + 1,
6023 block_start - block_end,
6024 threads,
6025 );
6026
6027 if block_end < 0 {
6028 break;
6029 }
6030 block_start = block_end;
6031 }
6032
6033 d
6034}
6035
6036#[doc(hidden)]
6038pub fn partial_sorting_scan_right_to_left_32s_1k_omp(
6039 t: &[SaSint],
6040 sa: &mut [SaSint],
6041 n: SaSint,
6042 buckets: &mut [SaSint],
6043 threads: SaSint,
6044 thread_state: &mut [ThreadState],
6045) {
6046 if threads == 1 || n < 65_536 {
6047 partial_sorting_scan_right_to_left_32s_1k(t, sa, buckets, 0, n as FastSint);
6048 return;
6049 }
6050 if thread_state.is_empty() {
6051 partial_sorting_scan_right_to_left_32s_1k(t, sa, buckets, 0, n as FastSint);
6052 return;
6053 }
6054
6055 let threads_usize = usize::try_from(threads)
6056 .expect("threads must be non-negative")
6057 .max(1);
6058 let mut cache = vec![ThreadCache::default(); threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE];
6059 let mut block_start = FastSint::try_from(n).expect("n must fit FastSint") - 1;
6060 let block_span = FastSint::try_from(threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE)
6061 .expect("block span must fit FastSint");
6062 while block_start >= 0 {
6063 let mut block_end = block_start - block_span;
6064 if block_end < 0 {
6065 block_end = -1;
6066 }
6067
6068 partial_sorting_scan_right_to_left_32s_1k_block_omp(
6069 t,
6070 sa,
6071 buckets,
6072 &mut cache,
6073 block_end + 1,
6074 block_start - block_end,
6075 threads,
6076 );
6077
6078 if block_end < 0 {
6079 break;
6080 }
6081 block_start = block_end;
6082 }
6083}
6084
6085#[doc(hidden)]
6087pub fn partial_sorting_gather_lms_suffixes_32s_4k(
6088 sa: &mut [SaSint],
6089 omp_block_start: FastSint,
6090 omp_block_size: FastSint,
6091) -> FastSint {
6092 if omp_block_size <= 0 {
6093 return omp_block_start;
6094 }
6095
6096 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
6097 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
6098 let mut l = start;
6099
6100 for i in start..start + size {
6101 let s = sa[i] as SaUint;
6102 sa[l] = ((s.wrapping_sub(SUFFIX_GROUP_MARKER as SaUint)) & !(SUFFIX_GROUP_MARKER as SaUint))
6103 as SaSint;
6104 l += usize::from((s as SaSint) < 0);
6105 }
6106
6107 l as FastSint
6108}
6109
6110#[doc(hidden)]
6112pub fn partial_sorting_gather_lms_suffixes_32s_1k(
6113 sa: &mut [SaSint],
6114 omp_block_start: FastSint,
6115 omp_block_size: FastSint,
6116) -> FastSint {
6117 if omp_block_size <= 0 {
6118 return omp_block_start;
6119 }
6120
6121 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
6122 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
6123 let mut l = start;
6124
6125 for i in start..start + size {
6126 let s = sa[i];
6127 sa[l] = s & SAINT_MAX;
6128 l += usize::from(s < 0);
6129 }
6130
6131 l as FastSint
6132}
6133
6134#[doc(hidden)]
6136pub fn partial_sorting_gather_lms_suffixes_32s_4k_omp(
6137 sa: &mut [SaSint],
6138 n: SaSint,
6139 threads: SaSint,
6140 thread_state: &mut [ThreadState],
6141) {
6142 let n_usize = usize::try_from(n).expect("n must be non-negative");
6143 let omp_num_threads = if threads > 1 && n >= 65_536 {
6144 usize::try_from(threads)
6145 .expect("threads must be non-negative")
6146 .min(thread_state.len())
6147 .max(1)
6148 } else {
6149 1
6150 };
6151
6152 if omp_num_threads == 1 {
6153 let _ = partial_sorting_gather_lms_suffixes_32s_4k(sa, 0, n as FastSint);
6154 return;
6155 }
6156
6157 let omp_block_stride = (n_usize / omp_num_threads) & !15usize;
6158 for (thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
6159 let block_start = thread_num * omp_block_stride;
6160 let block_size = if thread_num + 1 < omp_num_threads {
6161 omp_block_stride
6162 } else {
6163 n_usize - block_start
6164 };
6165 state.position = block_start as FastSint;
6166 state.count = partial_sorting_gather_lms_suffixes_32s_4k(
6167 sa,
6168 block_start as FastSint,
6169 block_size as FastSint,
6170 ) - block_start as FastSint;
6171 }
6172
6173 let mut position = 0usize;
6174 for (thread_num, state) in thread_state.iter().take(omp_num_threads).enumerate() {
6175 let count = usize::try_from(state.count).expect("count must be non-negative");
6176 let src = usize::try_from(state.position).expect("position must be non-negative");
6177 if thread_num > 0 && count > 0 {
6178 sa.copy_within(src..src + count, position);
6179 }
6180 position += count;
6181 }
6182}
6183
6184#[doc(hidden)]
6186pub fn partial_sorting_gather_lms_suffixes_32s_1k_omp(
6187 sa: &mut [SaSint],
6188 n: SaSint,
6189 threads: SaSint,
6190 thread_state: &mut [ThreadState],
6191) {
6192 let n_usize = usize::try_from(n).expect("n must be non-negative");
6193 let omp_num_threads = if threads > 1 && n >= 65_536 {
6194 usize::try_from(threads)
6195 .expect("threads must be non-negative")
6196 .min(thread_state.len())
6197 .max(1)
6198 } else {
6199 1
6200 };
6201
6202 if omp_num_threads == 1 {
6203 let _ = partial_sorting_gather_lms_suffixes_32s_1k(sa, 0, n as FastSint);
6204 return;
6205 }
6206
6207 let omp_block_stride = (n_usize / omp_num_threads) & !15usize;
6208 for (thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
6209 let block_start = thread_num * omp_block_stride;
6210 let block_size = if thread_num + 1 < omp_num_threads {
6211 omp_block_stride
6212 } else {
6213 n_usize - block_start
6214 };
6215 state.position = block_start as FastSint;
6216 state.count = partial_sorting_gather_lms_suffixes_32s_1k(
6217 sa,
6218 block_start as FastSint,
6219 block_size as FastSint,
6220 ) - block_start as FastSint;
6221 }
6222
6223 let mut position = 0usize;
6224 for (thread_num, state) in thread_state.iter().take(omp_num_threads).enumerate() {
6225 let count = usize::try_from(state.count).expect("count must be non-negative");
6226 let src = usize::try_from(state.position).expect("position must be non-negative");
6227 if thread_num > 0 && count > 0 {
6228 sa.copy_within(src..src + count, position);
6229 }
6230 position += count;
6231 }
6232}
6233
6234#[doc(hidden)]
6236pub fn induce_partial_order_8u_omp(
6237 t: &[u8],
6238 sa: &mut [SaSint],
6239 n: SaSint,
6240 k: SaSint,
6241 flags: SaSint,
6242 buckets: &mut [SaSint],
6243 first_lms_suffix: SaSint,
6244 left_suffixes_count: SaSint,
6245 threads: SaSint,
6246 thread_state: &mut [ThreadState],
6247) {
6248 buckets[2 * ALPHABET_SIZE..4 * ALPHABET_SIZE].fill(0);
6249
6250 if (flags & LIBSAIS_FLAGS_GSA) != 0 {
6251 let left = 4 * ALPHABET_SIZE + buckets_index2(0, 1);
6252 let right = 4 * ALPHABET_SIZE + buckets_index2(1, 1);
6253 buckets[left] = buckets[right] - 1;
6254 flip_suffix_markers_omp(sa, buckets[left], threads);
6255 }
6256
6257 let d = partial_sorting_scan_left_to_right_8u_omp(
6258 t,
6259 sa,
6260 n,
6261 k,
6262 buckets,
6263 left_suffixes_count,
6264 0,
6265 threads,
6266 thread_state,
6267 );
6268 partial_sorting_shift_markers_8u_omp(sa, n, buckets, threads);
6269
6270 if (flags & LIBSAIS_FLAGS_GSA) != 0 {
6271 partial_gsa_scan_right_to_left_8u_omp(
6272 t,
6273 sa,
6274 n,
6275 k,
6276 buckets,
6277 first_lms_suffix,
6278 left_suffixes_count,
6279 d,
6280 threads,
6281 thread_state,
6282 );
6283
6284 if t[usize::try_from(first_lms_suffix).expect("first_lms_suffix must be non-negative")] == 0
6285 {
6286 let count = usize::try_from(buckets[buckets_index2(1, 1)] - 1)
6287 .expect("count must be non-negative");
6288 sa.copy_within(0..count, 1);
6289 sa[0] = first_lms_suffix | SAINT_MIN;
6290 }
6291
6292 buckets[buckets_index2(0, 1)] = 0;
6293 } else {
6294 partial_sorting_scan_right_to_left_8u_omp(
6295 t,
6296 sa,
6297 n,
6298 k,
6299 buckets,
6300 first_lms_suffix,
6301 left_suffixes_count,
6302 d,
6303 threads,
6304 thread_state,
6305 );
6306 }
6307}
6308
6309#[doc(hidden)]
6311pub fn induce_partial_order_32s_6k_omp(
6312 t: &[SaSint],
6313 sa: &mut [SaSint],
6314 n: SaSint,
6315 k: SaSint,
6316 buckets: &mut [SaSint],
6317 first_lms_suffix: SaSint,
6318 left_suffixes_count: SaSint,
6319 threads: SaSint,
6320 thread_state: &mut [ThreadState],
6321) {
6322 let d = partial_sorting_scan_left_to_right_32s_6k_omp(
6323 t,
6324 sa,
6325 n,
6326 buckets,
6327 left_suffixes_count,
6328 0,
6329 threads,
6330 thread_state,
6331 );
6332 partial_sorting_shift_markers_32s_6k_omp(sa, k, buckets, threads);
6333 partial_sorting_shift_buckets_32s_6k(k, buckets);
6334 let _ = partial_sorting_scan_right_to_left_32s_6k_omp(
6335 t,
6336 sa,
6337 n,
6338 buckets,
6339 first_lms_suffix,
6340 left_suffixes_count,
6341 d,
6342 threads,
6343 thread_state,
6344 );
6345}
6346
6347#[doc(hidden)]
6349pub fn induce_partial_order_32s_4k_omp(
6350 t: &[SaSint],
6351 sa: &mut [SaSint],
6352 n: SaSint,
6353 k: SaSint,
6354 buckets: &mut [SaSint],
6355 threads: SaSint,
6356 thread_state: &mut [ThreadState],
6357) {
6358 let zero_len = 2 * usize::try_from(k).expect("k must be non-negative");
6359 buckets[..zero_len].fill(0);
6360
6361 let d = partial_sorting_scan_left_to_right_32s_4k_omp(
6362 t,
6363 sa,
6364 n,
6365 k,
6366 buckets,
6367 0,
6368 threads,
6369 thread_state,
6370 );
6371 partial_sorting_shift_markers_32s_4k(sa, n);
6372 let _ = partial_sorting_scan_right_to_left_32s_4k_omp(
6373 t,
6374 sa,
6375 n,
6376 k,
6377 buckets,
6378 d,
6379 threads,
6380 thread_state,
6381 );
6382 partial_sorting_gather_lms_suffixes_32s_4k_omp(sa, n, threads, thread_state);
6383}
6384
6385#[doc(hidden)]
6387pub fn induce_partial_order_32s_2k_omp(
6388 t: &[SaSint],
6389 sa: &mut [SaSint],
6390 n: SaSint,
6391 k: SaSint,
6392 buckets: &mut [SaSint],
6393 threads: SaSint,
6394 thread_state: &mut [ThreadState],
6395) {
6396 let k_usize = usize::try_from(k).expect("k must be non-negative");
6397 let (left, right) = buckets.split_at_mut(k_usize);
6398 partial_sorting_scan_left_to_right_32s_1k_omp(t, sa, n, right, threads, thread_state);
6399 partial_sorting_scan_right_to_left_32s_1k_omp(t, sa, n, left, threads, thread_state);
6400 partial_sorting_gather_lms_suffixes_32s_1k_omp(sa, n, threads, thread_state);
6401}
6402
6403#[doc(hidden)]
6405pub fn induce_partial_order_32s_1k_omp(
6406 t: &[SaSint],
6407 sa: &mut [SaSint],
6408 n: SaSint,
6409 k: SaSint,
6410 buckets: &mut [SaSint],
6411 threads: SaSint,
6412 thread_state: &mut [ThreadState],
6413) {
6414 count_suffixes_32s(t, n, k, buckets);
6415 initialize_buckets_start_32s_1k(k, buckets);
6416 partial_sorting_scan_left_to_right_32s_1k_omp(t, sa, n, buckets, threads, thread_state);
6417
6418 count_suffixes_32s(t, n, k, buckets);
6419 initialize_buckets_end_32s_1k(k, buckets);
6420 partial_sorting_scan_right_to_left_32s_1k_omp(t, sa, n, buckets, threads, thread_state);
6421
6422 partial_sorting_gather_lms_suffixes_32s_1k_omp(sa, n, threads, thread_state);
6423}
6424
6425#[doc(hidden)]
6427pub fn renumber_lms_suffixes_8u(
6428 sa: &mut [SaSint],
6429 m: SaSint,
6430 mut name: SaSint,
6431 omp_block_start: FastSint,
6432 omp_block_size: FastSint,
6433) -> SaSint {
6434 if omp_block_size <= 0 {
6435 return name;
6436 }
6437
6438 let m_usize = usize::try_from(m).expect("m must be non-negative");
6439 let (sa_head, sam) = sa.split_at_mut(m_usize);
6440 let mut i = omp_block_start;
6441 let mut j = omp_block_start + omp_block_size - 64 - 3;
6442
6443 while i < j {
6444 let i0 = i as usize;
6445 let p0 = sa_head[i0];
6446 let d0 = ((p0 & SAINT_MAX) >> 1) as usize;
6447 sam[d0] = name | SAINT_MIN;
6448 name += SaSint::from(p0 < 0);
6449
6450 let p1 = sa_head[i0 + 1];
6451 let d1 = ((p1 & SAINT_MAX) >> 1) as usize;
6452 sam[d1] = name | SAINT_MIN;
6453 name += SaSint::from(p1 < 0);
6454
6455 let p2 = sa_head[i0 + 2];
6456 let d2 = ((p2 & SAINT_MAX) >> 1) as usize;
6457 sam[d2] = name | SAINT_MIN;
6458 name += SaSint::from(p2 < 0);
6459
6460 let p3 = sa_head[i0 + 3];
6461 let d3 = ((p3 & SAINT_MAX) >> 1) as usize;
6462 sam[d3] = name | SAINT_MIN;
6463 name += SaSint::from(p3 < 0);
6464
6465 i += 4;
6466 }
6467
6468 j += 64 + 3;
6469 while i < j {
6470 let p = sa_head[i as usize];
6471 let d = ((p & SAINT_MAX) >> 1) as usize;
6472 sam[d] = name | SAINT_MIN;
6473 name += SaSint::from(p < 0);
6474 i += 1;
6475 }
6476
6477 name
6478}
6479
6480#[doc(hidden)]
6482pub fn gather_marked_lms_suffixes(
6483 sa: &mut [SaSint],
6484 m: SaSint,
6485 l: FastSint,
6486 omp_block_start: FastSint,
6487 omp_block_size: FastSint,
6488) -> FastSint {
6489 if omp_block_size <= 0 {
6490 return l;
6491 }
6492
6493 let mut l = l - 1;
6494 let mut i = m as FastSint + omp_block_start + omp_block_size - 1;
6495 let mut j = m as FastSint + omp_block_start + 3;
6496
6497 while i >= j {
6498 let i0 = i as usize;
6499 let s0 = sa[i0];
6500 sa[l as usize] = s0 & SAINT_MAX;
6501 l -= FastSint::from(s0 < 0);
6502
6503 let s1 = sa[i0 - 1];
6504 sa[l as usize] = s1 & SAINT_MAX;
6505 l -= FastSint::from(s1 < 0);
6506
6507 let s2 = sa[i0 - 2];
6508 sa[l as usize] = s2 & SAINT_MAX;
6509 l -= FastSint::from(s2 < 0);
6510
6511 let s3 = sa[i0 - 3];
6512 sa[l as usize] = s3 & SAINT_MAX;
6513 l -= FastSint::from(s3 < 0);
6514
6515 i -= 4;
6516 }
6517
6518 j -= 3;
6519 while i >= j {
6520 let s = sa[i as usize];
6521 sa[l as usize] = s & SAINT_MAX;
6522 l -= FastSint::from(s < 0);
6523 i -= 1;
6524 }
6525
6526 l + 1
6527}
6528
6529#[doc(hidden)]
6531pub fn renumber_lms_suffixes_8u_omp(
6532 sa: &mut [SaSint],
6533 m: SaSint,
6534 threads: SaSint,
6535 thread_state: &mut [ThreadState],
6536) -> SaSint {
6537 let omp_num_threads = if threads > 1 && m >= 65_536 {
6538 usize::try_from(threads)
6539 .expect("threads must be non-negative")
6540 .min(thread_state.len())
6541 .max(1)
6542 } else {
6543 1
6544 };
6545 let omp_block_stride = (m as FastSint / omp_num_threads as FastSint) & !15;
6546
6547 let name = if omp_num_threads == 1 {
6548 renumber_lms_suffixes_8u(sa, m, 0, 0, m as FastSint)
6549 } else {
6550 {
6551 let sa_ro: &[SaSint] = sa;
6552 run_rayon_with_threads(omp_num_threads, || {
6553 thread_state[..omp_num_threads]
6554 .par_iter_mut()
6555 .enumerate()
6556 .for_each(|(omp_thread_num, state)| {
6557 let omp_block_start = omp_thread_num as FastSint * omp_block_stride;
6558 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
6559 omp_block_stride
6560 } else {
6561 m as FastSint - omp_block_start
6562 };
6563 state.count =
6564 count_negative_marked_suffixes(sa_ro, omp_block_start, omp_block_size)
6565 as FastSint;
6566 });
6567 });
6568 }
6569
6570 let counts: Vec<FastSint> = thread_state[..omp_num_threads]
6571 .iter()
6572 .map(|s| s.count)
6573 .collect();
6574 let name = counts.iter().sum::<FastSint>() as SaSint;
6575
6576 {
6577 let sa_ptr = SyncMutPtr::new(sa);
6578 let counts_ref: &[FastSint] = &counts;
6579 run_rayon_with_threads(omp_num_threads, || {
6580 (0..omp_num_threads)
6581 .into_par_iter()
6582 .for_each(|omp_thread_num| {
6583 let omp_block_start = omp_thread_num as FastSint * omp_block_stride;
6584 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
6585 omp_block_stride
6586 } else {
6587 m as FastSint - omp_block_start
6588 };
6589 let mut count: FastSint = 0;
6590 for tt in 0..omp_thread_num {
6591 count += counts_ref[tt];
6592 }
6593 let sa = unsafe { sa_ptr.as_slice() };
6594 let _ = renumber_lms_suffixes_8u(
6595 sa,
6596 m,
6597 count as SaSint,
6598 omp_block_start,
6599 omp_block_size,
6600 );
6601 });
6602 });
6603 }
6604 name
6605 };
6606
6607 name
6608}
6609
6610#[doc(hidden)]
6612pub fn gather_marked_lms_suffixes_omp(
6613 sa: &mut [SaSint],
6614 n: SaSint,
6615 m: SaSint,
6616 fs: SaSint,
6617 threads: SaSint,
6618 thread_state: &mut [ThreadState],
6619) {
6620 let n_fast = n as FastSint;
6621 let m_fast = m as FastSint;
6622 let omp_num_threads = if threads > 1 && n >= 131_072 {
6623 usize::try_from(threads)
6624 .expect("threads must be non-negative")
6625 .min(thread_state.len())
6626 .max(1)
6627 } else {
6628 1
6629 };
6630 let omp_block_stride = ((n_fast >> 1) / omp_num_threads as FastSint) & !15;
6631
6632 if omp_num_threads == 1 {
6633 let _ = gather_marked_lms_suffixes(sa, m, n_fast + fs as FastSint, 0, n_fast >> 1);
6634 } else {
6635 {
6636 let sa_ptr = SyncMutPtr::new(sa);
6637 run_rayon_with_threads(omp_num_threads, || {
6638 thread_state[..omp_num_threads]
6639 .par_iter_mut()
6640 .enumerate()
6641 .for_each(|(omp_thread_num, state)| {
6642 let omp_block_start = omp_thread_num as FastSint * omp_block_stride;
6643 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
6644 omp_block_stride
6645 } else {
6646 (n_fast >> 1) - omp_block_start
6647 };
6648
6649 let sa = unsafe { sa_ptr.as_slice() };
6650 if omp_thread_num < omp_num_threads - 1 {
6651 state.position = gather_marked_lms_suffixes(
6652 sa,
6653 m,
6654 m_fast + omp_block_start + omp_block_size,
6655 omp_block_start,
6656 omp_block_size,
6657 );
6658 state.count =
6659 m_fast + omp_block_start + omp_block_size - state.position;
6660 } else {
6661 state.position = gather_marked_lms_suffixes(
6662 sa,
6663 m,
6664 n_fast + fs as FastSint,
6665 omp_block_start,
6666 omp_block_size,
6667 );
6668 state.count = n_fast + fs as FastSint - state.position;
6669 }
6670 });
6671 });
6672 }
6673
6674 let mut position = n_fast + fs as FastSint;
6675 for t in (0..omp_num_threads).rev() {
6676 position -= thread_state[t].count;
6677 if t + 1 != omp_num_threads && thread_state[t].count > 0 {
6678 let src = usize::try_from(thread_state[t].position)
6679 .expect("position must be non-negative");
6680 let len =
6681 usize::try_from(thread_state[t].count).expect("count must be non-negative");
6682 let dst = usize::try_from(position).expect("position must be non-negative");
6683 sa.copy_within(src..src + len, dst);
6684 }
6685 }
6686 }
6687}
6688
6689#[doc(hidden)]
6691pub fn renumber_and_gather_lms_suffixes_omp(
6692 sa: &mut [SaSint],
6693 n: SaSint,
6694 m: SaSint,
6695 fs: SaSint,
6696 threads: SaSint,
6697 thread_state: &mut [ThreadState],
6698) -> SaSint {
6699 let m_usize = usize::try_from(m).expect("m must be non-negative");
6700 let half_n = usize::try_from(n >> 1).expect("n must be non-negative");
6701 sa[m_usize..m_usize + half_n].fill(0);
6702
6703 let name = renumber_lms_suffixes_8u_omp(sa, m, threads, thread_state);
6704 if name < m {
6705 gather_marked_lms_suffixes_omp(sa, n, m, fs, threads, thread_state);
6706 } else {
6707 let mut i = 0;
6708 while i < m_usize {
6709 sa[i] &= SAINT_MAX;
6710 i += 1;
6711 }
6712 }
6713
6714 name
6715}
6716
6717#[doc(hidden)]
6719pub fn renumber_distinct_lms_suffixes_32s_4k(
6720 sa: &mut [SaSint],
6721 m: SaSint,
6722 mut name: SaSint,
6723 omp_block_start: FastSint,
6724 omp_block_size: FastSint,
6725) -> SaSint {
6726 if omp_block_size <= 0 {
6727 return name;
6728 }
6729
6730 let prefetch_distance = 64usize;
6731 let m_usize = usize::try_from(m).expect("m must be non-negative");
6732 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
6733 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
6734 let (sa_head, sam) = sa.split_at_mut(m_usize);
6735 let mut i = start;
6736 let mut j = start
6737 .saturating_add(size)
6738 .saturating_sub(prefetch_distance + 3);
6739 let mut p0;
6740 let mut p1;
6741 let mut p2;
6742 let mut p3 = 0;
6743
6744 while i < j {
6745 p0 = sa_head[i];
6746 sa_head[i] = p0 & SAINT_MAX;
6747 sam[(sa_head[i] >> 1) as usize] = name | (p0 & p3 & SAINT_MIN);
6748 name += SaSint::from(p0 < 0);
6749
6750 p1 = sa_head[i + 1];
6751 sa_head[i + 1] = p1 & SAINT_MAX;
6752 sam[(sa_head[i + 1] >> 1) as usize] = name | (p1 & p0 & SAINT_MIN);
6753 name += SaSint::from(p1 < 0);
6754
6755 p2 = sa_head[i + 2];
6756 sa_head[i + 2] = p2 & SAINT_MAX;
6757 sam[(sa_head[i + 2] >> 1) as usize] = name | (p2 & p1 & SAINT_MIN);
6758 name += SaSint::from(p2 < 0);
6759
6760 p3 = sa_head[i + 3];
6761 sa_head[i + 3] = p3 & SAINT_MAX;
6762 sam[(sa_head[i + 3] >> 1) as usize] = name | (p3 & p2 & SAINT_MIN);
6763 name += SaSint::from(p3 < 0);
6764
6765 i += 4;
6766 }
6767
6768 j = start + size;
6769 while i < j {
6770 p2 = p3;
6771 p3 = sa_head[i];
6772 sa_head[i] = p3 & SAINT_MAX;
6773 sam[(sa_head[i] >> 1) as usize] = name | (p3 & p2 & SAINT_MIN);
6774 name += SaSint::from(p3 < 0);
6775 i += 1;
6776 }
6777
6778 name
6779}
6780
6781#[doc(hidden)]
6783pub fn mark_distinct_lms_suffixes_32s(
6784 sa: &mut [SaSint],
6785 m: SaSint,
6786 omp_block_start: FastSint,
6787 omp_block_size: FastSint,
6788) {
6789 if omp_block_size <= 0 {
6790 return;
6791 }
6792
6793 let m_usize = usize::try_from(m).expect("m must be non-negative");
6794 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
6795 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
6796 let mut i = m_usize + start;
6797 let mut j = m_usize + start + size.saturating_sub(3);
6798 let mut p3 = 0;
6799
6800 while i < j {
6801 let mut p0 = sa[i];
6802 sa[i] = p0 & (p3 | SAINT_MAX);
6803 p0 = if p0 == 0 { p3 } else { p0 };
6804
6805 let mut p1 = sa[i + 1];
6806 sa[i + 1] = p1 & (p0 | SAINT_MAX);
6807 p1 = if p1 == 0 { p0 } else { p1 };
6808
6809 let mut p2 = sa[i + 2];
6810 sa[i + 2] = p2 & (p1 | SAINT_MAX);
6811 p2 = if p2 == 0 { p1 } else { p2 };
6812
6813 p3 = sa[i + 3];
6814 sa[i + 3] = p3 & (p2 | SAINT_MAX);
6815 p3 = if p3 == 0 { p2 } else { p3 };
6816
6817 i += 4;
6818 }
6819
6820 j = m_usize + start + size;
6821 while i < j {
6822 let p2 = p3;
6823 p3 = sa[i];
6824 sa[i] = p3 & (p2 | SAINT_MAX);
6825 p3 = if p3 == 0 { p2 } else { p3 };
6826 i += 1;
6827 }
6828}
6829
6830#[doc(hidden)]
6832pub fn clamp_lms_suffixes_length_32s(
6833 sa: &mut [SaSint],
6834 m: SaSint,
6835 omp_block_start: FastSint,
6836 omp_block_size: FastSint,
6837) {
6838 if omp_block_size <= 0 {
6839 return;
6840 }
6841
6842 let m_usize = usize::try_from(m).expect("m must be non-negative");
6843 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
6844 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
6845 let mut i = m_usize + start;
6846 let mut j = m_usize + start + size.saturating_sub(3);
6847
6848 while i < j {
6849 let s0 = sa[i];
6850 sa[i] = if s0 < 0 { s0 } else { 0 } & SAINT_MAX;
6851
6852 let s1 = sa[i + 1];
6853 sa[i + 1] = if s1 < 0 { s1 } else { 0 } & SAINT_MAX;
6854
6855 let s2 = sa[i + 2];
6856 sa[i + 2] = if s2 < 0 { s2 } else { 0 } & SAINT_MAX;
6857
6858 let s3 = sa[i + 3];
6859 sa[i + 3] = if s3 < 0 { s3 } else { 0 } & SAINT_MAX;
6860
6861 i += 4;
6862 }
6863
6864 j = m_usize + start + size;
6865 while i < j {
6866 let s = sa[i];
6867 sa[i] = if s < 0 { s } else { 0 } & SAINT_MAX;
6868 i += 1;
6869 }
6870}
6871
6872#[doc(hidden)]
6874pub fn renumber_distinct_lms_suffixes_32s_4k_omp(
6875 sa: &mut [SaSint],
6876 m: SaSint,
6877 threads: SaSint,
6878 thread_state: &mut [ThreadState],
6879) -> SaSint {
6880 let m_usize = usize::try_from(m).expect("m must be non-negative");
6881 let omp_num_threads = if threads > 1 && m >= 65_536 {
6882 usize::try_from(threads)
6883 .expect("threads must be non-negative")
6884 .min(thread_state.len())
6885 .max(1)
6886 } else {
6887 1
6888 };
6889 let omp_block_stride = (m_usize / omp_num_threads) & !15usize;
6890
6891 let name = if omp_num_threads == 1 {
6892 let omp_block_start = 0usize;
6893 let omp_block_size = m_usize - omp_block_start;
6894 renumber_distinct_lms_suffixes_32s_4k(
6895 sa,
6896 m,
6897 1,
6898 omp_block_start as FastSint,
6899 omp_block_size as FastSint,
6900 )
6901 } else {
6902 {
6903 let sa_ro: &[SaSint] = sa;
6904 run_rayon_with_threads(omp_num_threads, || {
6905 thread_state[..omp_num_threads]
6906 .par_iter_mut()
6907 .enumerate()
6908 .for_each(|(omp_thread_num, state)| {
6909 let omp_block_start = omp_thread_num * omp_block_stride;
6910 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
6911 omp_block_stride
6912 } else {
6913 m_usize - omp_block_start
6914 };
6915 state.count = count_negative_marked_suffixes(
6916 sa_ro,
6917 omp_block_start as FastSint,
6918 omp_block_size as FastSint,
6919 ) as FastSint;
6920 });
6921 });
6922 }
6923
6924 let counts: Vec<FastSint> = thread_state[..omp_num_threads]
6925 .iter()
6926 .map(|s| s.count)
6927 .collect();
6928 let name = (1 + counts.iter().sum::<FastSint>()) as SaSint;
6929
6930 {
6931 let sa_ptr = SyncMutPtr::new(sa);
6932 let counts_ref: &[FastSint] = &counts;
6933 run_rayon_with_threads(omp_num_threads, || {
6934 (0..omp_num_threads)
6935 .into_par_iter()
6936 .for_each(|omp_thread_num| {
6937 let omp_block_start = omp_thread_num * omp_block_stride;
6938 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
6939 omp_block_stride
6940 } else {
6941 m_usize - omp_block_start
6942 };
6943 let mut count: FastSint = 1;
6944 for tt in 0..omp_thread_num {
6945 count += counts_ref[tt];
6946 }
6947 let sa = unsafe { sa_ptr.as_slice() };
6948 let _ = renumber_distinct_lms_suffixes_32s_4k(
6949 sa,
6950 m,
6951 count as SaSint,
6952 omp_block_start as FastSint,
6953 omp_block_size as FastSint,
6954 );
6955 });
6956 });
6957 }
6958 name
6959 };
6960
6961 name - 1
6962}
6963
6964#[doc(hidden)]
6966pub fn mark_distinct_lms_suffixes_32s_omp(
6967 sa: &mut [SaSint],
6968 n: SaSint,
6969 m: SaSint,
6970 threads: SaSint,
6971) {
6972 let half_n = usize::try_from(n >> 1).expect("n must be non-negative");
6973 let omp_num_threads = if threads > 1 && n >= 131_072 {
6974 usize::try_from(threads)
6975 .expect("threads must be non-negative")
6976 .max(1)
6977 } else {
6978 1
6979 };
6980 let omp_block_stride = (half_n / omp_num_threads) & !15usize;
6981
6982 {
6983 let sa_ptr = SyncMutPtr::new(sa);
6984 run_rayon_with_threads(omp_num_threads, || {
6985 (0..omp_num_threads)
6986 .into_par_iter()
6987 .for_each(|omp_thread_num| {
6988 let omp_block_start = omp_thread_num * omp_block_stride;
6989 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
6990 omp_block_stride
6991 } else {
6992 half_n - omp_block_start
6993 };
6994 let sa = unsafe { sa_ptr.as_slice() };
6995 mark_distinct_lms_suffixes_32s(
6996 sa,
6997 m,
6998 omp_block_start as FastSint,
6999 omp_block_size as FastSint,
7000 );
7001 });
7002 });
7003 }
7004}
7005
7006#[doc(hidden)]
7008pub fn clamp_lms_suffixes_length_32s_omp(sa: &mut [SaSint], n: SaSint, m: SaSint, threads: SaSint) {
7009 let half_n = usize::try_from(n >> 1).expect("n must be non-negative");
7010 let omp_num_threads = if threads > 1 && n >= 131_072 {
7011 usize::try_from(threads)
7012 .expect("threads must be non-negative")
7013 .max(1)
7014 } else {
7015 1
7016 };
7017 let omp_block_stride = (half_n / omp_num_threads) & !15usize;
7018
7019 {
7020 let sa_ptr = SyncMutPtr::new(sa);
7021 run_rayon_with_threads(omp_num_threads, || {
7022 (0..omp_num_threads)
7023 .into_par_iter()
7024 .for_each(|omp_thread_num| {
7025 let omp_block_start = omp_thread_num * omp_block_stride;
7026 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
7027 omp_block_stride
7028 } else {
7029 half_n - omp_block_start
7030 };
7031 let sa = unsafe { sa_ptr.as_slice() };
7032 clamp_lms_suffixes_length_32s(
7033 sa,
7034 m,
7035 omp_block_start as FastSint,
7036 omp_block_size as FastSint,
7037 );
7038 });
7039 });
7040 }
7041}
7042
7043#[doc(hidden)]
7045pub fn renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
7046 sa: &mut [SaSint],
7047 n: SaSint,
7048 m: SaSint,
7049 threads: SaSint,
7050 thread_state: &mut [ThreadState],
7051) -> SaSint {
7052 let m_usize = usize::try_from(m).expect("m must be non-negative");
7053 let half_n = usize::try_from(n >> 1).expect("n must be non-negative");
7054 sa[m_usize..m_usize + half_n].fill(0);
7055
7056 let name = renumber_distinct_lms_suffixes_32s_4k_omp(sa, m, threads, thread_state);
7057 if name < m {
7058 mark_distinct_lms_suffixes_32s_omp(sa, n, m, threads);
7059 }
7060
7061 name
7062}
7063
7064#[doc(hidden)]
7066pub fn renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(
7067 t: &[SaSint],
7068 sa: &mut [SaSint],
7069 n: SaSint,
7070 m: SaSint,
7071 threads: SaSint,
7072) -> SaSint {
7073 let m_usize = usize::try_from(m).expect("m must be non-negative");
7074 let n_usize = usize::try_from(n).expect("n must be non-negative");
7075
7076 let _ = gather_lms_suffixes_32s(t, sa, n);
7077
7078 let zero_len = n_usize
7079 .checked_sub(m_usize)
7080 .and_then(|v| v.checked_sub(m_usize))
7081 .expect("n must be at least 2*m");
7082 sa[m_usize..m_usize + zero_len].fill(0);
7083
7084 {
7085 let prefetch_distance: FastSint = 64;
7086 let mut i = n as FastSint - m as FastSint;
7087 let mut j = n as FastSint - 1 - prefetch_distance - 3;
7088
7089 while i < j {
7090 let iu = i as usize;
7091 let s0 = (sa[iu] as SaUint >> 1) as usize;
7092 let s1 = (sa[iu + 1] as SaUint >> 1) as usize;
7093 let s2 = (sa[iu + 2] as SaUint >> 1) as usize;
7094 let s3 = (sa[iu + 3] as SaUint >> 1) as usize;
7095
7096 sa[m_usize + s0] = sa[iu + 1] - sa[iu] + 1 + SAINT_MIN;
7097 sa[m_usize + s1] = sa[iu + 2] - sa[iu + 1] + 1 + SAINT_MIN;
7098 sa[m_usize + s2] = sa[iu + 3] - sa[iu + 2] + 1 + SAINT_MIN;
7099 sa[m_usize + s3] = sa[iu + 4] - sa[iu + 3] + 1 + SAINT_MIN;
7100 i += 4;
7101 }
7102
7103 j += prefetch_distance + 3;
7104 while i < j {
7105 let iu = i as usize;
7106 let s = (sa[iu] as SaUint >> 1) as usize;
7107 sa[m_usize + s] = sa[iu + 1] - sa[iu] + 1 + SAINT_MIN;
7108 i += 1;
7109 }
7110
7111 let tail = (sa[n_usize - 1] as SaUint >> 1) as usize;
7112 sa[m_usize + tail] = 1 + SAINT_MIN;
7113 }
7114
7115 clamp_lms_suffixes_length_32s_omp(sa, n, m, threads);
7116
7117 let mut name = 1;
7118 if m_usize > 0 {
7119 let (sa_head, sam) = sa.split_at_mut(m_usize);
7120 let mut i = 1usize;
7121 let prefetch_distance = 64usize;
7122 let mut j = m_usize.saturating_sub(prefetch_distance + 1);
7123 let mut p = usize::try_from(sa_head[0]).expect("suffix index must be non-negative");
7124 let mut plen = sam[p >> 1];
7125 let mut pdiff = SAINT_MIN;
7126
7127 while i < j {
7128 let q = usize::try_from(sa_head[i]).expect("suffix index must be non-negative");
7129 let qlen = sam[q >> 1];
7130 let mut qdiff = SAINT_MIN;
7131 if plen == qlen {
7132 let mut l = 0usize;
7133 while l < qlen as usize {
7134 if t[p + l] != t[q + l] {
7135 break;
7136 }
7137 l += 1;
7138 }
7139 qdiff = ((l as SaSint) - qlen) & SAINT_MIN;
7140 }
7141 sam[p >> 1] = name | (pdiff & qdiff);
7142 name += SaSint::from(qdiff < 0);
7143
7144 p = usize::try_from(sa_head[i + 1]).expect("suffix index must be non-negative");
7145 plen = sam[p >> 1];
7146 pdiff = SAINT_MIN;
7147 if qlen == plen {
7148 let mut l = 0usize;
7149 while l < plen as usize {
7150 if t[q + l] != t[p + l] {
7151 break;
7152 }
7153 l += 1;
7154 }
7155 pdiff = ((l as SaSint) - plen) & SAINT_MIN;
7156 }
7157 sam[q >> 1] = name | (qdiff & pdiff);
7158 name += SaSint::from(pdiff < 0);
7159 i += 2;
7160 }
7161
7162 j = m_usize;
7163 while i < j {
7164 let q = usize::try_from(sa_head[i]).expect("suffix index must be non-negative");
7165 let qlen = sam[q >> 1];
7166 let mut qdiff = SAINT_MIN;
7167 if plen == qlen {
7168 let mut l = 0usize;
7169 while l < plen as usize {
7170 if t[p + l] != t[q + l] {
7171 break;
7172 }
7173 l += 1;
7174 }
7175 qdiff = ((l as SaSint) - plen) & SAINT_MIN;
7176 }
7177 sam[p >> 1] = name | (pdiff & qdiff);
7178 name += SaSint::from(qdiff < 0);
7179
7180 p = q;
7181 plen = qlen;
7182 pdiff = qdiff;
7183 i += 1;
7184 }
7185
7186 sam[p >> 1] = name | pdiff;
7187 name += 1;
7188 }
7189
7190 if name <= m {
7191 mark_distinct_lms_suffixes_32s_omp(sa, n, m, threads);
7192 }
7193
7194 name - 1
7195}
7196
7197#[doc(hidden)]
7199pub fn reconstruct_lms_suffixes(
7200 sa: &mut [SaSint],
7201 n: SaSint,
7202 m: SaSint,
7203 omp_block_start: FastSint,
7204 omp_block_size: FastSint,
7205) {
7206 if omp_block_size <= 0 {
7207 return;
7208 }
7209
7210 let prefetch_distance: FastSint = 64;
7211 let base = (n - m) as usize;
7212 let mut i = omp_block_start;
7213 let mut j = omp_block_start + omp_block_size - prefetch_distance - 3;
7214
7215 while i < j {
7216 let iu = i as usize;
7217 let s0 = sa[iu] as usize;
7218 let s1 = sa[iu + 1] as usize;
7219 let s2 = sa[iu + 2] as usize;
7220 let s3 = sa[iu + 3] as usize;
7221 sa[iu] = sa[base + s0];
7222 sa[iu + 1] = sa[base + s1];
7223 sa[iu + 2] = sa[base + s2];
7224 sa[iu + 3] = sa[base + s3];
7225 i += 4;
7226 }
7227
7228 j += prefetch_distance + 3;
7229 while i < j {
7230 let iu = i as usize;
7231 let s = sa[iu] as usize;
7232 sa[iu] = sa[base + s];
7233 i += 1;
7234 }
7235}
7236
7237#[doc(hidden)]
7239pub fn reconstruct_lms_suffixes_omp(sa: &mut [SaSint], n: SaSint, m: SaSint, threads: SaSint) {
7240 let m_usize = usize::try_from(m).expect("m must be non-negative");
7241 let omp_num_threads = if threads > 1 && m >= 65_536 {
7242 usize::try_from(threads)
7243 .expect("threads must be non-negative")
7244 .max(1)
7245 } else {
7246 1
7247 };
7248 let omp_block_stride = (m_usize / omp_num_threads) & !15usize;
7249
7250 {
7251 let sa_ptr = SyncMutPtr::new(sa);
7252 run_rayon_with_threads(omp_num_threads, || {
7253 (0..omp_num_threads)
7254 .into_par_iter()
7255 .for_each(|omp_thread_num| {
7256 let omp_block_start = omp_thread_num * omp_block_stride;
7257 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
7258 omp_block_stride
7259 } else {
7260 m_usize - omp_block_start
7261 };
7262 let sa = unsafe { sa_ptr.as_slice() };
7263 reconstruct_lms_suffixes(
7264 sa,
7265 n,
7266 m,
7267 omp_block_start as FastSint,
7268 omp_block_size as FastSint,
7269 );
7270 });
7271 });
7272 }
7273}
7274
7275#[doc(hidden)]
7277pub fn place_lms_suffixes_interval_8u(
7278 sa: &mut [SaSint],
7279 n: SaSint,
7280 mut m: SaSint,
7281 flags: SaSint,
7282 buckets: &mut [SaSint],
7283) {
7284 let bucket_end_base = 7 * ALPHABET_SIZE;
7285 if (flags & LIBSAIS_FLAGS_GSA) != 0 {
7286 buckets[bucket_end_base] -= 1;
7287 }
7288
7289 let mut j = usize::try_from(n).expect("n must be non-negative");
7290 for c in (0..ALPHABET_SIZE - 1).rev() {
7291 let l = usize::try_from(
7292 buckets[buckets_index2(c, 1) + buckets_index2(1, 0)] - buckets[buckets_index2(c, 1)],
7293 )
7294 .expect("interval length must be non-negative");
7295 if l > 0 {
7296 let i = usize::try_from(buckets[bucket_end_base + c])
7297 .expect("bucket end must be non-negative");
7298 if j > i {
7299 sa[i..j].fill(0);
7300 }
7301
7302 let new_j = i - l;
7303 let src_end = usize::try_from(m).expect("m must be non-negative");
7304 let src_start = src_end - l;
7305 sa.copy_within(src_start..src_end, new_j);
7306 m -= l as SaSint;
7307 j = new_j;
7308 }
7309 }
7310
7311 sa[..j].fill(0);
7312
7313 if (flags & LIBSAIS_FLAGS_GSA) != 0 {
7314 buckets[bucket_end_base] += 1;
7315 }
7316}
7317
7318#[doc(hidden)]
7320pub fn place_lms_suffixes_interval_32s_4k(
7321 sa: &mut [SaSint],
7322 n: SaSint,
7323 k: SaSint,
7324 mut m: SaSint,
7325 buckets: &[SaSint],
7326) {
7327 let k_usize = usize::try_from(k).expect("k must be non-negative");
7328 let bucket_end = &buckets[3 * k_usize..4 * k_usize];
7329
7330 let mut j = usize::try_from(n).expect("n must be non-negative");
7331 for c in (0..k_usize - 1).rev() {
7332 let l = usize::try_from(
7333 buckets[buckets_index2(c, 1) + buckets_index2(1, 0)] - buckets[buckets_index2(c, 1)],
7334 )
7335 .expect("interval length must be non-negative");
7336 if l > 0 {
7337 let i = usize::try_from(bucket_end[c]).expect("bucket end must be non-negative");
7338 if j > i {
7339 sa[i..j].fill(0);
7340 }
7341
7342 let new_j = i - l;
7343 let src_end = usize::try_from(m).expect("m must be non-negative");
7344 let src_start = src_end - l;
7345 sa.copy_within(src_start..src_end, new_j);
7346 m -= l as SaSint;
7347 j = new_j;
7348 }
7349 }
7350
7351 sa[..j].fill(0);
7352}
7353
7354#[doc(hidden)]
7356pub fn place_lms_suffixes_interval_32s_2k(
7357 sa: &mut [SaSint],
7358 n: SaSint,
7359 k: SaSint,
7360 mut m: SaSint,
7361 buckets: &[SaSint],
7362) {
7363 let k_usize = usize::try_from(k).expect("k must be non-negative");
7364 let mut j = usize::try_from(n).expect("n must be non-negative");
7365
7366 if k_usize > 1 {
7367 let mut c = buckets_index2(k_usize - 2, 0) as isize;
7368 while c >= buckets_index2(0, 0) as isize {
7369 let c_usize = c as usize;
7370 let l = usize::try_from(
7371 buckets[c_usize + buckets_index2(1, 1)] - buckets[c_usize + buckets_index2(0, 1)],
7372 )
7373 .expect("interval length must be non-negative");
7374 if l > 0 {
7375 let i =
7376 usize::try_from(buckets[c_usize]).expect("bucket start must be non-negative");
7377 if j > i {
7378 sa[i..j].fill(0);
7379 }
7380
7381 let new_j = i - l;
7382 let src_end = usize::try_from(m).expect("m must be non-negative");
7383 let src_start = src_end - l;
7384 sa.copy_within(src_start..src_end, new_j);
7385 m -= l as SaSint;
7386 j = new_j;
7387 }
7388 c -= buckets_index2(1, 0) as isize;
7389 }
7390 }
7391
7392 sa[..j].fill(0);
7393}
7394
7395#[doc(hidden)]
7397pub fn place_lms_suffixes_interval_32s_1k(
7398 t: &[SaSint],
7399 sa: &mut [SaSint],
7400 k: SaSint,
7401 m: SaSint,
7402 buckets: &[SaSint],
7403) {
7404 let mut c = k - 1;
7405 let c_usize = usize::try_from(c).expect("k must be positive");
7406 let mut l = usize::try_from(buckets[c_usize]).expect("bucket end must be non-negative");
7407
7408 let m_usize = usize::try_from(m).expect("m must be non-negative");
7409 for i in (0..m_usize).rev() {
7410 let p = usize::try_from(sa[i]).expect("suffix index must be non-negative");
7411 let tp = t[p];
7412 if tp != c {
7413 c = tp;
7414 let bucket = usize::try_from(c).expect("bucket index must be non-negative");
7415 let bucket_pos =
7416 usize::try_from(buckets[bucket]).expect("bucket end must be non-negative");
7417 if l > bucket_pos {
7418 sa[bucket_pos..l].fill(0);
7419 }
7420 l = bucket_pos;
7421 }
7422 l -= 1;
7423 sa[l] = p as SaSint;
7424 }
7425
7426 sa[..l].fill(0);
7427}
7428
7429#[doc(hidden)]
7431pub fn place_lms_suffixes_histogram_32s_6k(
7432 sa: &mut [SaSint],
7433 n: SaSint,
7434 k: SaSint,
7435 mut m: SaSint,
7436 buckets: &[SaSint],
7437) {
7438 let k_usize = usize::try_from(k).expect("k must be non-negative");
7439 let bucket_end = &buckets[5 * k_usize..6 * k_usize];
7440
7441 let mut j = usize::try_from(n).expect("n must be non-negative");
7442 for c in (0..k_usize - 1).rev() {
7443 let l = usize::try_from(buckets[buckets_index4(c, 1)])
7444 .expect("histogram length must be non-negative");
7445 if l > 0 {
7446 let i = usize::try_from(bucket_end[c]).expect("bucket end must be non-negative");
7447 if j > i {
7448 sa[i..j].fill(0);
7449 }
7450
7451 let new_j = i - l;
7452 let src_end = usize::try_from(m).expect("m must be non-negative");
7453 let src_start = src_end - l;
7454 sa.copy_within(src_start..src_end, new_j);
7455 m -= l as SaSint;
7456 j = new_j;
7457 }
7458 }
7459
7460 sa[..j].fill(0);
7461}
7462
7463#[doc(hidden)]
7465pub fn place_lms_suffixes_histogram_32s_4k(
7466 sa: &mut [SaSint],
7467 n: SaSint,
7468 k: SaSint,
7469 mut m: SaSint,
7470 buckets: &[SaSint],
7471) {
7472 let k_usize = usize::try_from(k).expect("k must be non-negative");
7473 let bucket_end = &buckets[3 * k_usize..4 * k_usize];
7474
7475 let mut j = usize::try_from(n).expect("n must be non-negative");
7476 for c in (0..k_usize - 1).rev() {
7477 let l = usize::try_from(buckets[buckets_index2(c, 1)])
7478 .expect("histogram length must be non-negative");
7479 if l > 0 {
7480 let i = usize::try_from(bucket_end[c]).expect("bucket end must be non-negative");
7481 if j > i {
7482 sa[i..j].fill(0);
7483 }
7484
7485 let new_j = i - l;
7486 let src_end = usize::try_from(m).expect("m must be non-negative");
7487 let src_start = src_end - l;
7488 sa.copy_within(src_start..src_end, new_j);
7489 m -= l as SaSint;
7490 j = new_j;
7491 }
7492 }
7493
7494 sa[..j].fill(0);
7495}
7496
7497#[doc(hidden)]
7499pub fn place_lms_suffixes_histogram_32s_2k(
7500 sa: &mut [SaSint],
7501 n: SaSint,
7502 k: SaSint,
7503 mut m: SaSint,
7504 buckets: &[SaSint],
7505) {
7506 let k_usize = usize::try_from(k).expect("k must be non-negative");
7507 let mut j = usize::try_from(n).expect("n must be non-negative");
7508
7509 if k_usize > 1 {
7510 let mut c = buckets_index2(k_usize - 2, 0) as isize;
7511 while c >= buckets_index2(0, 0) as isize {
7512 let c_usize = c as usize;
7513 let l = usize::try_from(buckets[c_usize + buckets_index2(0, 1)])
7514 .expect("histogram length must be non-negative");
7515 if l > 0 {
7516 let i =
7517 usize::try_from(buckets[c_usize]).expect("bucket start must be non-negative");
7518 if j > i {
7519 sa[i..j].fill(0);
7520 }
7521
7522 let new_j = i - l;
7523 let src_end = usize::try_from(m).expect("m must be non-negative");
7524 let src_start = src_end - l;
7525 sa.copy_within(src_start..src_end, new_j);
7526 m -= l as SaSint;
7527 j = new_j;
7528 }
7529 c -= buckets_index2(1, 0) as isize;
7530 }
7531 }
7532
7533 sa[..j].fill(0);
7534}
7535
7536#[doc(hidden)]
7538pub fn final_bwt_scan_left_to_right_8u(
7539 t: &[u8],
7540 sa: &mut [SaSint],
7541 induction_bucket: &mut [SaSint],
7542 omp_block_start: FastSint,
7543 omp_block_size: FastSint,
7544) {
7545 if omp_block_size <= 0 {
7546 return;
7547 }
7548
7549 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
7550 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
7551 for i in start..start + size {
7552 let mut p = sa[i];
7553 sa[i] = p & SAINT_MAX;
7554 if p > 0 {
7555 p -= 1;
7556 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
7557 sa[i] = t[p_usize] as SaSint | SAINT_MIN;
7558 let bucket = t[p_usize] as usize;
7559 let slot = usize::try_from(induction_bucket[bucket])
7560 .expect("bucket slot must be non-negative");
7561 sa[slot] = p
7562 | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
7563 << (SAINT_BIT - 1));
7564 induction_bucket[bucket] += 1;
7565 }
7566 }
7567}
7568
7569#[doc(hidden)]
7571pub fn final_bwt_aux_scan_left_to_right_8u(
7572 t: &[u8],
7573 sa: &mut [SaSint],
7574 rm: SaSint,
7575 i_out: &mut [SaSint],
7576 induction_bucket: &mut [SaSint],
7577 omp_block_start: FastSint,
7578 omp_block_size: FastSint,
7579) {
7580 if omp_block_size <= 0 {
7581 return;
7582 }
7583
7584 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
7585 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
7586 for i in start..start + size {
7587 let mut p = sa[i];
7588 sa[i] = p & SAINT_MAX;
7589 if p > 0 {
7590 p -= 1;
7591 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
7592 sa[i] = t[p_usize] as SaSint | SAINT_MIN;
7593 let bucket = t[p_usize] as usize;
7594 let slot = usize::try_from(induction_bucket[bucket])
7595 .expect("bucket slot must be non-negative");
7596 sa[slot] = p
7597 | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
7598 << (SAINT_BIT - 1));
7599 induction_bucket[bucket] += 1;
7600 if (p & rm) == 0 {
7601 let out_idx =
7602 usize::try_from(p / (rm + 1)).expect("sample index must be non-negative");
7603 i_out[out_idx] = induction_bucket[bucket];
7604 }
7605 }
7606 }
7607}
7608
7609#[doc(hidden)]
7611pub fn final_sorting_scan_left_to_right_8u(
7612 t: &[u8],
7613 sa: &mut [SaSint],
7614 induction_bucket: &mut [SaSint],
7615 omp_block_start: FastSint,
7616 omp_block_size: FastSint,
7617) {
7618 if omp_block_size <= 0 {
7619 return;
7620 }
7621
7622 let prefetch_distance = 64usize;
7623 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
7624 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
7625
7626 let mut i = start;
7627 let mut j = if size > prefetch_distance + 1 {
7628 start + size - (prefetch_distance + 1)
7629 } else {
7630 start
7631 };
7632 while i < j {
7633 let mut p0 = sa[i];
7634 sa[i] = p0 ^ SAINT_MIN;
7635 if p0 > 0 {
7636 p0 -= 1;
7637 let p0_usize = p0 as usize;
7638 let bucket0 = t[p0_usize] as usize;
7639 let slot0 = induction_bucket[bucket0] as usize;
7640 sa[slot0] = p0
7641 | ((usize::from(t[p0_usize - usize::from(p0 > 0)] < t[p0_usize]) as SaSint)
7642 << (SAINT_BIT - 1));
7643 induction_bucket[bucket0] += 1;
7644 }
7645
7646 let mut p1 = sa[i + 1];
7647 sa[i + 1] = p1 ^ SAINT_MIN;
7648 if p1 > 0 {
7649 p1 -= 1;
7650 let p1_usize = p1 as usize;
7651 let bucket1 = t[p1_usize] as usize;
7652 let slot1 = induction_bucket[bucket1] as usize;
7653 sa[slot1] = p1
7654 | ((usize::from(t[p1_usize - usize::from(p1 > 0)] < t[p1_usize]) as SaSint)
7655 << (SAINT_BIT - 1));
7656 induction_bucket[bucket1] += 1;
7657 }
7658
7659 i += 2;
7660 }
7661
7662 j = start + size;
7663 while i < j {
7664 let mut p = sa[i];
7665 sa[i] = p ^ SAINT_MIN;
7666 if p > 0 {
7667 p -= 1;
7668 let p_usize = p as usize;
7669 let bucket = t[p_usize] as usize;
7670 let slot = induction_bucket[bucket] as usize;
7671 sa[slot] = p
7672 | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
7673 << (SAINT_BIT - 1));
7674 induction_bucket[bucket] += 1;
7675 }
7676 i += 1;
7677 }
7678}
7679
7680#[doc(hidden)]
7682pub fn final_sorting_scan_left_to_right_32s(
7683 t: &[SaSint],
7684 sa: &mut [SaSint],
7685 induction_bucket: &mut [SaSint],
7686 omp_block_start: FastSint,
7687 omp_block_size: FastSint,
7688) {
7689 if omp_block_size <= 0 {
7690 return;
7691 }
7692
7693 let prefetch_distance: FastSint = 64;
7694 let mut i = omp_block_start;
7695 let mut j = omp_block_start + omp_block_size - 2 * prefetch_distance - 1;
7696
7697 while i < j {
7698 let i0 = i as usize;
7699 let mut p0 = sa[i0];
7700 sa[i0] = p0 ^ SAINT_MIN;
7701 if p0 > 0 {
7702 p0 -= 1;
7703 let p0u = p0 as usize;
7704 let bucket0 = t[p0u] as usize;
7705 let slot0 = induction_bucket[bucket0] as usize;
7706 sa[slot0] = p0
7707 | ((usize::from(t[p0u - usize::from(p0 > 0)] < t[p0u]) as SaSint)
7708 << (SAINT_BIT - 1));
7709 induction_bucket[bucket0] += 1;
7710 }
7711
7712 let i1 = (i + 1) as usize;
7713 let mut p1 = sa[i1];
7714 sa[i1] = p1 ^ SAINT_MIN;
7715 if p1 > 0 {
7716 p1 -= 1;
7717 let p1u = p1 as usize;
7718 let bucket1 = t[p1u] as usize;
7719 let slot1 = induction_bucket[bucket1] as usize;
7720 sa[slot1] = p1
7721 | ((usize::from(t[p1u - usize::from(p1 > 0)] < t[p1u]) as SaSint)
7722 << (SAINT_BIT - 1));
7723 induction_bucket[bucket1] += 1;
7724 }
7725 i += 2;
7726 }
7727
7728 j += 2 * prefetch_distance + 1;
7729 while i < j {
7730 let iu = i as usize;
7731 let mut p = sa[iu];
7732 sa[iu] = p ^ SAINT_MIN;
7733 if p > 0 {
7734 p -= 1;
7735 let pu = p as usize;
7736 let bucket = t[pu] as usize;
7737 let slot = induction_bucket[bucket] as usize;
7738 sa[slot] = p
7739 | ((usize::from(t[pu - usize::from(p > 0)] < t[pu]) as SaSint) << (SAINT_BIT - 1));
7740 induction_bucket[bucket] += 1;
7741 }
7742 i += 1;
7743 }
7744}
7745
7746#[doc(hidden)]
7748pub fn final_bwt_scan_left_to_right_8u_block_prepare(
7749 t: &[u8],
7750 sa: &mut [SaSint],
7751 k: SaSint,
7752 buckets: &mut [SaSint],
7753 cache: &mut [ThreadCache],
7754 omp_block_start: FastSint,
7755 omp_block_size: FastSint,
7756) -> FastSint {
7757 if omp_block_size <= 0 {
7758 return 0;
7759 }
7760
7761 let k_usize = usize::try_from(k).expect("k must be non-negative");
7762 buckets[..k_usize].fill(0);
7763
7764 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
7765 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
7766 let mut count = 0usize;
7767 for i in start..start + size {
7768 let mut p = sa[i];
7769 sa[i] = p & SAINT_MAX;
7770 if p > 0 {
7771 p -= 1;
7772 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
7773 let symbol = t[p_usize] as usize;
7774 sa[i] = t[p_usize] as SaSint | SAINT_MIN;
7775 buckets[symbol] += 1;
7776 cache[count].symbol = symbol as SaSint;
7777 cache[count].index = p
7778 | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
7779 << (SAINT_BIT - 1));
7780 count += 1;
7781 }
7782 }
7783
7784 count as FastSint
7785}
7786
7787#[doc(hidden)]
7789pub fn final_sorting_scan_left_to_right_8u_block_prepare(
7790 t: &[u8],
7791 sa: &mut [SaSint],
7792 k: SaSint,
7793 buckets: &mut [SaSint],
7794 cache: &mut [ThreadCache],
7795 omp_block_start: FastSint,
7796 omp_block_size: FastSint,
7797) -> FastSint {
7798 if omp_block_size <= 0 {
7799 return 0;
7800 }
7801
7802 let k_usize = usize::try_from(k).expect("k must be non-negative");
7803 buckets[..k_usize].fill(0);
7804
7805 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
7806 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
7807 let mut count = 0usize;
7808 for i in start..start + size {
7809 let mut p = sa[i];
7810 sa[i] = p ^ SAINT_MIN;
7811 if p > 0 {
7812 p -= 1;
7813 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
7814 let symbol = t[p_usize] as usize;
7815 buckets[symbol] += 1;
7816 cache[count].symbol = symbol as SaSint;
7817 cache[count].index = p
7818 | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
7819 << (SAINT_BIT - 1));
7820 count += 1;
7821 }
7822 }
7823
7824 count as FastSint
7825}
7826
7827#[doc(hidden)]
7829pub fn final_order_scan_left_to_right_8u_block_place(
7830 sa: &mut [SaSint],
7831 buckets: &mut [SaSint],
7832 cache: &[ThreadCache],
7833 count: FastSint,
7834) {
7835 if count <= 0 {
7836 return;
7837 }
7838
7839 let count_usize = usize::try_from(count).expect("count must be non-negative");
7840 for entry in &cache[..count_usize] {
7841 let symbol = usize::try_from(entry.symbol).expect("cache symbol must be non-negative");
7842 let slot = usize::try_from(buckets[symbol]).expect("bucket slot must be non-negative");
7843 sa[slot] = entry.index;
7844 buckets[symbol] += 1;
7845 }
7846}
7847
7848#[doc(hidden)]
7850pub fn final_bwt_aux_scan_left_to_right_8u_block_place(
7851 sa: &mut [SaSint],
7852 rm: SaSint,
7853 i_out: &mut [SaSint],
7854 buckets: &mut [SaSint],
7855 cache: &[ThreadCache],
7856 count: FastSint,
7857) {
7858 if count <= 0 {
7859 return;
7860 }
7861
7862 let count_usize = usize::try_from(count).expect("count must be non-negative");
7863 for entry in &cache[..count_usize] {
7864 let symbol = usize::try_from(entry.symbol).expect("cache symbol must be non-negative");
7865 let slot = usize::try_from(buckets[symbol]).expect("bucket slot must be non-negative");
7866 sa[slot] = entry.index;
7867 buckets[symbol] += 1;
7868 if (entry.index & rm) == 0 {
7869 let sample_index = usize::try_from((entry.index & SAINT_MAX) / (rm + 1))
7870 .expect("sample index must be non-negative");
7871 i_out[sample_index] = buckets[symbol];
7872 }
7873 }
7874}
7875
7876#[doc(hidden)]
7878pub fn final_sorting_scan_left_to_right_32s_block_gather(
7879 t: &[SaSint],
7880 sa: &mut [SaSint],
7881 cache: &mut [ThreadCache],
7882 omp_block_start: FastSint,
7883 omp_block_size: FastSint,
7884) {
7885 if omp_block_size <= 0 {
7886 return;
7887 }
7888 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
7889 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
7890 for offset in 0..size {
7891 let i = start + offset;
7892 let mut symbol = SAINT_MIN;
7893 let mut p = sa[i];
7894 sa[i] = p ^ SAINT_MIN;
7895 if p > 0 {
7896 p -= 1;
7897 let p_usize = p as usize;
7898 cache[offset].index = p
7899 | ((usize::from(t[p_usize - usize::from(p > 0)] < t[p_usize]) as SaSint)
7900 << (SAINT_BIT - 1));
7901 symbol = t[p_usize];
7902 }
7903 cache[offset].symbol = symbol;
7904 }
7905}
7906
7907#[doc(hidden)]
7909pub fn final_sorting_scan_left_to_right_32s_block_sort(
7910 t: &[SaSint],
7911 induction_bucket: &mut [SaSint],
7912 cache: &mut [ThreadCache],
7913 omp_block_start: FastSint,
7914 omp_block_size: FastSint,
7915) {
7916 if omp_block_size <= 0 {
7917 return;
7918 }
7919 let prefetch_distance = 64usize;
7920 let start = omp_block_start as usize;
7921 let block_end = start + omp_block_size as usize;
7922 let mut i = start;
7923 let mut j = start + (omp_block_size as usize).saturating_sub(prefetch_distance + 1);
7924
7925 while i < j {
7926 let ci = i - start;
7927 let v0 = cache[ci].symbol;
7928 if v0 >= 0 {
7929 let bucket_index0 = v0 as usize;
7930 cache[ci].symbol = induction_bucket[bucket_index0];
7931 induction_bucket[bucket_index0] += 1;
7932 if cache[ci].symbol < block_end as SaSint {
7933 let ni = cache[ci].symbol as usize;
7934 let cni = ni - start;
7935 let mut np = cache[ci].index;
7936 cache[ci].index = np ^ SAINT_MIN;
7937 if np > 0 {
7938 np -= 1;
7939 let np_usize = np as usize;
7940 cache[cni].index = np
7941 | ((usize::from(t[np_usize - usize::from(np > 0)] < t[np_usize])
7942 as SaSint)
7943 << (SAINT_BIT - 1));
7944 cache[cni].symbol = t[np_usize];
7945 }
7946 }
7947 }
7948
7949 let i1 = i + 1;
7950 let ci1 = i1 - start;
7951 let v1 = cache[ci1].symbol;
7952 if v1 >= 0 {
7953 let bucket_index1 = v1 as usize;
7954 cache[ci1].symbol = induction_bucket[bucket_index1];
7955 induction_bucket[bucket_index1] += 1;
7956 if cache[ci1].symbol < block_end as SaSint {
7957 let ni = cache[ci1].symbol as usize;
7958 let cni = ni - start;
7959 let mut np = cache[ci1].index;
7960 cache[ci1].index = np ^ SAINT_MIN;
7961 if np > 0 {
7962 np -= 1;
7963 let np_usize = np as usize;
7964 cache[cni].index = np
7965 | ((usize::from(t[np_usize - usize::from(np > 0)] < t[np_usize])
7966 as SaSint)
7967 << (SAINT_BIT - 1));
7968 cache[cni].symbol = t[np_usize];
7969 }
7970 }
7971 }
7972
7973 i += 2;
7974 }
7975
7976 j = block_end;
7977 while i < j {
7978 let ci = i - start;
7979 let v = cache[ci].symbol;
7980 if v >= 0 {
7981 let bucket_index = v as usize;
7982 cache[ci].symbol = induction_bucket[bucket_index];
7983 induction_bucket[bucket_index] += 1;
7984 if cache[ci].symbol < block_end as SaSint {
7985 let ni = cache[ci].symbol as usize;
7986 let cni = ni - start;
7987 let mut np = cache[ci].index;
7988 cache[ci].index = np ^ SAINT_MIN;
7989 if np > 0 {
7990 np -= 1;
7991 let np_usize = np as usize;
7992 cache[cni].index = np
7993 | ((usize::from(t[np_usize - usize::from(np > 0)] < t[np_usize])
7994 as SaSint)
7995 << (SAINT_BIT - 1));
7996 cache[cni].symbol = t[np_usize];
7997 }
7998 }
7999 }
8000 i += 1;
8001 }
8002}
8003
8004#[doc(hidden)]
8006pub fn final_bwt_scan_left_to_right_8u_block_omp(
8007 t: &[u8],
8008 sa: &mut [SaSint],
8009 k: SaSint,
8010 induction_bucket: &mut [SaSint],
8011 block_start: FastSint,
8012 block_size: FastSint,
8013 threads: SaSint,
8014 thread_state: &mut [ThreadState],
8015) {
8016 if block_size <= 0 {
8017 return;
8018 }
8019
8020 let k_usize = usize::try_from(k).expect("k must be non-negative");
8021 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
8022 let omp_num_threads = if threads > 1 && block_size_usize >= 64 * k_usize.max(256) {
8023 usize::try_from(threads)
8024 .expect("threads must be non-negative")
8025 .min(thread_state.len())
8026 .max(1)
8027 } else {
8028 1
8029 };
8030
8031 if omp_num_threads == 1 {
8032 final_bwt_scan_left_to_right_8u(t, sa, induction_bucket, block_start, block_size);
8033 return;
8034 }
8035
8036 let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
8037 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
8038 for (thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
8039 let relative_start = thread_num * omp_block_stride;
8040 let size = if thread_num + 1 < omp_num_threads {
8041 omp_block_stride
8042 } else {
8043 block_size_usize - relative_start
8044 };
8045 state.count = final_bwt_scan_left_to_right_8u_block_prepare(
8046 t,
8047 sa,
8048 k,
8049 &mut state.buckets,
8050 &mut state.cache,
8051 (block_start_usize + relative_start) as FastSint,
8052 size as FastSint,
8053 );
8054 }
8055
8056 for state in thread_state.iter_mut().take(omp_num_threads) {
8057 for (c, bucket) in induction_bucket.iter_mut().take(k_usize).enumerate() {
8058 let a = *bucket;
8059 let b = state.buckets[c];
8060 *bucket = a + b;
8061 state.buckets[c] = a;
8062 }
8063 }
8064
8065 for state in thread_state.iter_mut().take(omp_num_threads) {
8066 final_order_scan_left_to_right_8u_block_place(
8067 sa,
8068 &mut state.buckets,
8069 &state.cache,
8070 state.count,
8071 );
8072 }
8073}
8074
8075#[doc(hidden)]
8077pub fn final_bwt_aux_scan_left_to_right_8u_block_omp(
8078 t: &[u8],
8079 sa: &mut [SaSint],
8080 k: SaSint,
8081 rm: SaSint,
8082 i_out: &mut [SaSint],
8083 induction_bucket: &mut [SaSint],
8084 block_start: FastSint,
8085 block_size: FastSint,
8086 threads: SaSint,
8087 thread_state: &mut [ThreadState],
8088) {
8089 if block_size <= 0 {
8090 return;
8091 }
8092
8093 let k_usize = usize::try_from(k).expect("k must be non-negative");
8094 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
8095 let omp_num_threads = if threads > 1 && block_size_usize >= 64 * k_usize.max(256) {
8096 usize::try_from(threads)
8097 .expect("threads must be non-negative")
8098 .min(thread_state.len())
8099 .max(1)
8100 } else {
8101 1
8102 };
8103
8104 if omp_num_threads == 1 {
8105 final_bwt_aux_scan_left_to_right_8u(
8106 t,
8107 sa,
8108 rm,
8109 i_out,
8110 induction_bucket,
8111 block_start,
8112 block_size,
8113 );
8114 return;
8115 }
8116
8117 let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
8118 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
8119 for (thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
8120 let relative_start = thread_num * omp_block_stride;
8121 let size = if thread_num + 1 < omp_num_threads {
8122 omp_block_stride
8123 } else {
8124 block_size_usize - relative_start
8125 };
8126 state.count = final_bwt_scan_left_to_right_8u_block_prepare(
8127 t,
8128 sa,
8129 k,
8130 &mut state.buckets,
8131 &mut state.cache,
8132 (block_start_usize + relative_start) as FastSint,
8133 size as FastSint,
8134 );
8135 }
8136
8137 for state in thread_state.iter_mut().take(omp_num_threads) {
8138 for (c, bucket) in induction_bucket.iter_mut().take(k_usize).enumerate() {
8139 let a = *bucket;
8140 let b = state.buckets[c];
8141 *bucket = a + b;
8142 state.buckets[c] = a;
8143 }
8144 }
8145
8146 for state in thread_state.iter_mut().take(omp_num_threads) {
8147 final_bwt_aux_scan_left_to_right_8u_block_place(
8148 sa,
8149 rm,
8150 i_out,
8151 &mut state.buckets,
8152 &state.cache,
8153 state.count,
8154 );
8155 }
8156}
8157
8158#[doc(hidden)]
8160pub fn final_sorting_scan_left_to_right_8u_block_omp(
8161 t: &[u8],
8162 sa: &mut [SaSint],
8163 k: SaSint,
8164 induction_bucket: &mut [SaSint],
8165 block_start: FastSint,
8166 block_size: FastSint,
8167 threads: SaSint,
8168 thread_state: &mut [ThreadState],
8169) {
8170 if block_size <= 0 {
8171 return;
8172 }
8173
8174 let k_usize = usize::try_from(k).expect("k must be non-negative");
8175 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
8176 let omp_num_threads = if threads > 1 && block_size_usize >= 64 * k_usize.max(256) {
8177 usize::try_from(threads)
8178 .expect("threads must be non-negative")
8179 .min(thread_state.len())
8180 .max(1)
8181 } else {
8182 1
8183 };
8184
8185 if omp_num_threads == 1 {
8186 final_sorting_scan_left_to_right_8u(t, sa, induction_bucket, block_start, block_size);
8187 return;
8188 }
8189
8190 let block_start_usize = usize::try_from(block_start).expect("block_start must be non-negative");
8191 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
8192 for (thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
8193 let relative_start = thread_num * omp_block_stride;
8194 let size = if thread_num + 1 < omp_num_threads {
8195 omp_block_stride
8196 } else {
8197 block_size_usize - relative_start
8198 };
8199 state.count = final_sorting_scan_left_to_right_8u_block_prepare(
8200 t,
8201 sa,
8202 k,
8203 &mut state.buckets,
8204 &mut state.cache,
8205 (block_start_usize + relative_start) as FastSint,
8206 size as FastSint,
8207 );
8208 }
8209
8210 for state in thread_state.iter_mut().take(omp_num_threads) {
8211 for (c, bucket) in induction_bucket.iter_mut().take(k_usize).enumerate() {
8212 let a = *bucket;
8213 let b = state.buckets[c];
8214 *bucket = a + b;
8215 state.buckets[c] = a;
8216 }
8217 }
8218
8219 for state in thread_state.iter_mut().take(omp_num_threads) {
8220 final_order_scan_left_to_right_8u_block_place(
8221 sa,
8222 &mut state.buckets,
8223 &state.cache,
8224 state.count,
8225 );
8226 }
8227}
8228
8229#[doc(hidden)]
8231pub fn final_sorting_scan_left_to_right_32s_block_omp(
8232 t: &[SaSint],
8233 sa: &mut [SaSint],
8234 buckets: &mut [SaSint],
8235 cache: &mut [ThreadCache],
8236 block_start: FastSint,
8237 block_size: FastSint,
8238 threads: SaSint,
8239) {
8240 if threads <= 1 || block_size < 16_384 {
8241 final_sorting_scan_left_to_right_32s(t, sa, buckets, block_start, block_size);
8242 return;
8243 }
8244
8245 final_sorting_scan_left_to_right_32s_block_gather(t, sa, cache, block_start, block_size);
8246 final_sorting_scan_left_to_right_32s_block_sort(t, buckets, cache, block_start, block_size);
8247 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
8248 let threads_usize = usize::try_from(threads.max(1)).expect("threads must be positive");
8249 let omp_num_threads = threads_usize.min(block_size_usize);
8250 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
8251 {
8252 let sa_ptr = SyncMutPtr::new(sa);
8253 let cache_ptr = SyncMutPtr::new(cache);
8254 run_rayon_with_threads(omp_num_threads, || {
8255 (0..omp_num_threads)
8256 .into_par_iter()
8257 .for_each(|omp_thread_num| {
8258 let omp_block_start = omp_thread_num * omp_block_stride;
8259 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
8260 omp_block_stride
8261 } else {
8262 block_size_usize - omp_block_start
8263 };
8264 let sa = unsafe { sa_ptr.as_slice() };
8265 let cache = unsafe { cache_ptr.as_slice() };
8266 compact_and_place_cached_suffixes(
8267 sa,
8268 cache,
8269 omp_block_start as FastSint,
8270 omp_block_size as FastSint,
8271 );
8272 });
8273 });
8274 }
8275}
8276
8277#[doc(hidden)]
8279pub fn final_bwt_scan_left_to_right_8u_omp(
8280 t: &[u8],
8281 sa: &mut [SaSint],
8282 n: FastSint,
8283 k: SaSint,
8284 induction_bucket: &mut [SaSint],
8285 threads: SaSint,
8286 thread_state: &mut [ThreadState],
8287) {
8288 let n_usize = usize::try_from(n).expect("n must be non-negative");
8289 let last = n_usize - 1;
8290 let bucket = t[last] as usize;
8291 let slot = usize::try_from(induction_bucket[bucket]).expect("bucket slot must be non-negative");
8292 sa[slot] =
8293 (n as SaSint - 1) | ((usize::from(t[last - 1] < t[last]) as SaSint) << (SAINT_BIT - 1));
8294 induction_bucket[bucket] += 1;
8295
8296 if threads == 1 || n < 65_536 {
8297 final_bwt_scan_left_to_right_8u(t, sa, induction_bucket, 0, n);
8298 return;
8299 }
8300
8301 let mut block_start = 0usize;
8302 while block_start < n_usize {
8303 if sa[block_start] == 0 {
8304 block_start += 1;
8305 } else {
8306 let threads_usize = usize::try_from(threads)
8307 .expect("threads must be non-negative")
8308 .min(thread_state.len())
8309 .max(1);
8310 let max_span = threads_usize * (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * threads_usize);
8311 let block_max_end = (block_start + max_span).min(n_usize);
8312 let mut block_end = block_start + 1;
8313 while block_end < block_max_end && sa[block_end] != 0 {
8314 block_end += 1;
8315 }
8316 let size = block_end - block_start;
8317
8318 if size < 32 {
8319 final_bwt_scan_left_to_right_8u(
8320 t,
8321 sa,
8322 induction_bucket,
8323 block_start as FastSint,
8324 size as FastSint,
8325 );
8326 } else {
8327 final_bwt_scan_left_to_right_8u_block_omp(
8328 t,
8329 sa,
8330 k,
8331 induction_bucket,
8332 block_start as FastSint,
8333 size as FastSint,
8334 threads,
8335 thread_state,
8336 );
8337 }
8338 block_start = block_end;
8339 }
8340 }
8341}
8342
8343#[doc(hidden)]
8345pub fn final_bwt_aux_scan_left_to_right_8u_omp(
8346 t: &[u8],
8347 sa: &mut [SaSint],
8348 n: FastSint,
8349 k: SaSint,
8350 rm: SaSint,
8351 i_out: &mut [SaSint],
8352 induction_bucket: &mut [SaSint],
8353 threads: SaSint,
8354 thread_state: &mut [ThreadState],
8355) {
8356 let n_usize = usize::try_from(n).expect("n must be non-negative");
8357 let last = n_usize - 1;
8358 let bucket = t[last] as usize;
8359 let slot = usize::try_from(induction_bucket[bucket]).expect("bucket slot must be non-negative");
8360 sa[slot] =
8361 (n as SaSint - 1) | ((usize::from(t[last - 1] < t[last]) as SaSint) << (SAINT_BIT - 1));
8362 induction_bucket[bucket] += 1;
8363 if (((n as SaSint) - 1) & rm) == 0 {
8364 i_out[last / usize::try_from(rm + 1).expect("rm must allow positive step")] =
8365 induction_bucket[bucket];
8366 }
8367
8368 if threads == 1 || n < 65_536 {
8369 final_bwt_aux_scan_left_to_right_8u(t, sa, rm, i_out, induction_bucket, 0, n);
8370 return;
8371 }
8372
8373 let mut block_start = 0usize;
8374 while block_start < n_usize {
8375 if sa[block_start] == 0 {
8376 block_start += 1;
8377 } else {
8378 let threads_usize = usize::try_from(threads)
8379 .expect("threads must be non-negative")
8380 .min(thread_state.len())
8381 .max(1);
8382 let max_span = threads_usize * (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * threads_usize);
8383 let block_max_end = (block_start + max_span).min(n_usize);
8384 let mut block_end = block_start + 1;
8385 while block_end < block_max_end && sa[block_end] != 0 {
8386 block_end += 1;
8387 }
8388 let size = block_end - block_start;
8389
8390 if size < 32 {
8391 final_bwt_aux_scan_left_to_right_8u(
8392 t,
8393 sa,
8394 rm,
8395 i_out,
8396 induction_bucket,
8397 block_start as FastSint,
8398 size as FastSint,
8399 );
8400 } else {
8401 final_bwt_aux_scan_left_to_right_8u_block_omp(
8402 t,
8403 sa,
8404 k,
8405 rm,
8406 i_out,
8407 induction_bucket,
8408 block_start as FastSint,
8409 size as FastSint,
8410 threads,
8411 thread_state,
8412 );
8413 }
8414 block_start = block_end;
8415 }
8416 }
8417}
8418
8419#[doc(hidden)]
8421pub fn final_sorting_scan_left_to_right_8u_omp(
8422 t: &[u8],
8423 sa: &mut [SaSint],
8424 n: FastSint,
8425 k: SaSint,
8426 induction_bucket: &mut [SaSint],
8427 threads: SaSint,
8428 thread_state: &mut [ThreadState],
8429) {
8430 let n_usize = usize::try_from(n).expect("n must be non-negative");
8431 let last = n_usize - 1;
8432 let bucket = t[last] as usize;
8433 let slot = usize::try_from(induction_bucket[bucket]).expect("bucket slot must be non-negative");
8434 sa[slot] =
8435 (n as SaSint - 1) | ((usize::from(t[last - 1] < t[last]) as SaSint) << (SAINT_BIT - 1));
8436 induction_bucket[bucket] += 1;
8437
8438 if threads == 1 || n < 65_536 {
8439 final_sorting_scan_left_to_right_8u(t, sa, induction_bucket, 0, n);
8440 return;
8441 }
8442
8443 let mut block_start = 0usize;
8444 while block_start < n_usize {
8445 if sa[block_start] == 0 {
8446 block_start += 1;
8447 } else {
8448 let threads_usize = usize::try_from(threads)
8449 .expect("threads must be non-negative")
8450 .min(thread_state.len())
8451 .max(1);
8452 let max_span = threads_usize * (LIBSAIS_PER_THREAD_CACHE_SIZE - 16 * threads_usize);
8453 let block_max_end = (block_start + max_span).min(n_usize);
8454 let mut block_end = block_start + 1;
8455 while block_end < block_max_end && sa[block_end] != 0 {
8456 block_end += 1;
8457 }
8458 let size = block_end - block_start;
8459
8460 if size < 32 {
8461 final_sorting_scan_left_to_right_8u(
8462 t,
8463 sa,
8464 induction_bucket,
8465 block_start as FastSint,
8466 size as FastSint,
8467 );
8468 } else {
8469 final_sorting_scan_left_to_right_8u_block_omp(
8470 t,
8471 sa,
8472 k,
8473 induction_bucket,
8474 block_start as FastSint,
8475 size as FastSint,
8476 threads,
8477 thread_state,
8478 );
8479 }
8480 block_start = block_end;
8481 }
8482 }
8483}
8484
8485#[doc(hidden)]
8487pub fn final_sorting_scan_left_to_right_32s_omp(
8488 t: &[SaSint],
8489 sa: &mut [SaSint],
8490 n: SaSint,
8491 induction_bucket: &mut [SaSint],
8492 threads: SaSint,
8493 thread_state: &mut [ThreadState],
8494) {
8495 let n_usize = usize::try_from(n).expect("n must be non-negative");
8496 let last = n_usize - 1;
8497 let bucket = usize::try_from(t[last]).expect("bucket symbol must be non-negative");
8498 let slot = usize::try_from(induction_bucket[bucket]).expect("bucket slot must be non-negative");
8499 sa[slot] = (n - 1) | ((usize::from(t[last - 1] < t[last]) as SaSint) << (SAINT_BIT - 1));
8500 induction_bucket[bucket] += 1;
8501
8502 if threads == 1 || n < 65_536 {
8503 final_sorting_scan_left_to_right_32s(t, sa, induction_bucket, 0, n as FastSint);
8504 return;
8505 }
8506
8507 if thread_state.is_empty() {
8508 final_sorting_scan_left_to_right_32s(t, sa, induction_bucket, 0, n as FastSint);
8509 return;
8510 }
8511
8512 let threads_usize = usize::try_from(threads)
8513 .expect("threads must be non-negative")
8514 .max(1);
8515 let mut cache = vec![ThreadCache::default(); threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE];
8516 let mut block_start = 0usize;
8517 while block_start < n_usize {
8518 let block_end = (block_start + threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE).min(n_usize);
8519 final_sorting_scan_left_to_right_32s_block_omp(
8520 t,
8521 sa,
8522 induction_bucket,
8523 &mut cache,
8524 block_start as FastSint,
8525 (block_end - block_start) as FastSint,
8526 threads,
8527 );
8528 block_start = block_end;
8529 }
8530}
8531
8532#[doc(hidden)]
8534pub fn final_bwt_scan_right_to_left_8u(
8535 t: &[u8],
8536 sa: &mut [SaSint],
8537 induction_bucket: &mut [SaSint],
8538 omp_block_start: FastSint,
8539 omp_block_size: FastSint,
8540) -> SaSint {
8541 if omp_block_size <= 0 {
8542 return -1;
8543 }
8544
8545 let mut index = -1;
8546
8547 let start =
8548 usize::try_from(omp_block_start).expect("omp_block_start must be non-negative") as FastSint;
8549 let mut i = omp_block_start + omp_block_size - 1;
8550 let mut j = start + 1;
8551 while i >= j {
8552 let i0 = usize::try_from(i).expect("loop index must be non-negative");
8553 let i1 = usize::try_from(i - 1).expect("loop index must be non-negative");
8554
8555 let mut p0 = sa[i0];
8556 if p0 == 0 {
8557 index = i0 as SaSint;
8558 }
8559 sa[i0] = p0 & SAINT_MAX;
8560 if p0 > 0 {
8561 p0 -= 1;
8562 let p0_usize = usize::try_from(p0).expect("suffix index must be non-negative");
8563 let c0 = t[p0_usize - usize::from(p0 > 0)] as SaSint;
8564 let c1 = t[p0_usize] as SaSint;
8565 sa[i0] = c1;
8566 induction_bucket[c1 as usize] -= 1;
8567 let slot = usize::try_from(induction_bucket[c1 as usize])
8568 .expect("bucket slot must be non-negative");
8569 let marked = c0 | SAINT_MIN;
8570 sa[slot] = if c0 <= c1 { p0 } else { marked };
8571 }
8572
8573 let mut p1 = sa[i1];
8574 if p1 == 0 {
8575 index = i1 as SaSint;
8576 }
8577 sa[i1] = p1 & SAINT_MAX;
8578 if p1 > 0 {
8579 p1 -= 1;
8580 let p1_usize = usize::try_from(p1).expect("suffix index must be non-negative");
8581 let c0 = t[p1_usize - usize::from(p1 > 0)] as SaSint;
8582 let c1 = t[p1_usize] as SaSint;
8583 sa[i1] = c1;
8584 induction_bucket[c1 as usize] -= 1;
8585 let slot = usize::try_from(induction_bucket[c1 as usize])
8586 .expect("bucket slot must be non-negative");
8587 let marked = c0 | SAINT_MIN;
8588 sa[slot] = if c0 <= c1 { p1 } else { marked };
8589 }
8590
8591 i -= 2;
8592 }
8593
8594 j -= 1;
8595 while i >= j {
8596 let idx = usize::try_from(i).expect("loop index must be non-negative");
8597 let mut p = sa[idx];
8598 if p == 0 {
8599 index = idx as SaSint;
8600 }
8601 sa[idx] = p & SAINT_MAX;
8602 if p > 0 {
8603 p -= 1;
8604 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
8605 let c0 = t[p_usize - usize::from(p > 0)] as SaSint;
8606 let c1 = t[p_usize] as SaSint;
8607 sa[idx] = c1;
8608 induction_bucket[c1 as usize] -= 1;
8609 let slot = usize::try_from(induction_bucket[c1 as usize])
8610 .expect("bucket slot must be non-negative");
8611 let marked = c0 | SAINT_MIN;
8612 sa[slot] = if c0 <= c1 { p } else { marked };
8613 }
8614
8615 i -= 1;
8616 }
8617
8618 index
8619}
8620
8621#[doc(hidden)]
8623pub fn final_bwt_aux_scan_right_to_left_8u(
8624 t: &[u8],
8625 sa: &mut [SaSint],
8626 rm: SaSint,
8627 i_out: &mut [SaSint],
8628 induction_bucket: &mut [SaSint],
8629 omp_block_start: FastSint,
8630 omp_block_size: FastSint,
8631) {
8632 if omp_block_size <= 0 {
8633 return;
8634 }
8635
8636 let start =
8637 usize::try_from(omp_block_start).expect("omp_block_start must be non-negative") as FastSint;
8638 let mut i = omp_block_start + omp_block_size - 1;
8639 let mut j = start + 1;
8640 while i >= j {
8641 let i0 = usize::try_from(i).expect("loop index must be non-negative");
8642 let i1 = usize::try_from(i - 1).expect("loop index must be non-negative");
8643
8644 let mut p0 = sa[i0];
8645 sa[i0] = p0 & SAINT_MAX;
8646 if p0 > 0 {
8647 p0 -= 1;
8648 let p0_usize = usize::try_from(p0).expect("suffix index must be non-negative");
8649 let c0 = t[p0_usize - usize::from(p0 > 0)] as SaSint;
8650 let c1 = t[p0_usize] as SaSint;
8651 sa[i0] = c1;
8652 induction_bucket[c1 as usize] -= 1;
8653 let slot = usize::try_from(induction_bucket[c1 as usize])
8654 .expect("bucket slot must be non-negative");
8655 let marked = c0 | SAINT_MIN;
8656 sa[slot] = if c0 <= c1 { p0 } else { marked };
8657 if (p0 & rm) == 0 {
8658 let out_idx =
8659 usize::try_from(p0 / (rm + 1)).expect("sample index must be non-negative");
8660 i_out[out_idx] = induction_bucket[t[p0_usize] as usize] + 1;
8661 }
8662 }
8663
8664 let mut p1 = sa[i1];
8665 sa[i1] = p1 & SAINT_MAX;
8666 if p1 > 0 {
8667 p1 -= 1;
8668 let p1_usize = usize::try_from(p1).expect("suffix index must be non-negative");
8669 let c0 = t[p1_usize - usize::from(p1 > 0)] as SaSint;
8670 let c1 = t[p1_usize] as SaSint;
8671 sa[i1] = c1;
8672 induction_bucket[c1 as usize] -= 1;
8673 let slot = usize::try_from(induction_bucket[c1 as usize])
8674 .expect("bucket slot must be non-negative");
8675 let marked = c0 | SAINT_MIN;
8676 sa[slot] = if c0 <= c1 { p1 } else { marked };
8677 if (p1 & rm) == 0 {
8678 let out_idx =
8679 usize::try_from(p1 / (rm + 1)).expect("sample index must be non-negative");
8680 i_out[out_idx] = induction_bucket[t[p1_usize] as usize] + 1;
8681 }
8682 }
8683
8684 i -= 2;
8685 }
8686
8687 j -= 1;
8688 while i >= j {
8689 let idx = usize::try_from(i).expect("loop index must be non-negative");
8690 let mut p = sa[idx];
8691 sa[idx] = p & SAINT_MAX;
8692 if p > 0 {
8693 p -= 1;
8694 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
8695 let c0 = t[p_usize - usize::from(p > 0)] as SaSint;
8696 let c1 = t[p_usize] as SaSint;
8697 sa[idx] = c1;
8698 induction_bucket[c1 as usize] -= 1;
8699 let slot = usize::try_from(induction_bucket[c1 as usize])
8700 .expect("bucket slot must be non-negative");
8701 let marked = c0 | SAINT_MIN;
8702 sa[slot] = if c0 <= c1 { p } else { marked };
8703 if (p & rm) == 0 {
8704 let out_idx =
8705 usize::try_from(p / (rm + 1)).expect("sample index must be non-negative");
8706 i_out[out_idx] = induction_bucket[t[p_usize] as usize] + 1;
8707 }
8708 }
8709
8710 i -= 1;
8711 }
8712}
8713
8714#[doc(hidden)]
8716pub fn final_sorting_scan_right_to_left_8u(
8717 t: &[u8],
8718 sa: &mut [SaSint],
8719 induction_bucket: &mut [SaSint],
8720 omp_block_start: FastSint,
8721 omp_block_size: FastSint,
8722) {
8723 if omp_block_size <= 0 {
8724 return;
8725 }
8726
8727 let prefetch_distance = 64usize;
8728 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
8729 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
8730 let mut i = start + size - 1;
8731 let mut j = start + prefetch_distance + 1;
8732
8733 while i >= j {
8734 let mut p0 = sa[i];
8735 sa[i] = p0 & SAINT_MAX;
8736 if p0 > 0 {
8737 p0 -= 1;
8738 let p0_usize = p0 as usize;
8739 let bucket0 = t[p0_usize] as usize;
8740 induction_bucket[bucket0] -= 1;
8741 let slot0 = induction_bucket[bucket0] as usize;
8742 sa[slot0] = p0
8743 | ((usize::from(t[p0_usize - usize::from(p0 > 0)] > t[p0_usize]) as SaSint)
8744 << (SAINT_BIT - 1));
8745 }
8746
8747 let mut p1 = sa[i - 1];
8748 sa[i - 1] = p1 & SAINT_MAX;
8749 if p1 > 0 {
8750 p1 -= 1;
8751 let p1_usize = p1 as usize;
8752 let bucket1 = t[p1_usize] as usize;
8753 induction_bucket[bucket1] -= 1;
8754 let slot1 = induction_bucket[bucket1] as usize;
8755 sa[slot1] = p1
8756 | ((usize::from(t[p1_usize - usize::from(p1 > 0)] > t[p1_usize]) as SaSint)
8757 << (SAINT_BIT - 1));
8758 }
8759
8760 i -= 2;
8761 }
8762
8763 j -= prefetch_distance + 1;
8764 while i >= j {
8765 let mut p = sa[i];
8766 sa[i] = p & SAINT_MAX;
8767 if p > 0 {
8768 p -= 1;
8769 let p_usize = p as usize;
8770 let bucket = t[p_usize] as usize;
8771 induction_bucket[bucket] -= 1;
8772 let slot = induction_bucket[bucket] as usize;
8773 sa[slot] = p
8774 | ((usize::from(t[p_usize - usize::from(p > 0)] > t[p_usize]) as SaSint)
8775 << (SAINT_BIT - 1));
8776 }
8777
8778 if i == 0 {
8779 break;
8780 }
8781 i -= 1;
8782 }
8783}
8784
8785#[doc(hidden)]
8787pub fn final_gsa_scan_right_to_left_8u(
8788 t: &[u8],
8789 sa: &mut [SaSint],
8790 induction_bucket: &mut [SaSint],
8791 omp_block_start: FastSint,
8792 omp_block_size: FastSint,
8793) {
8794 if omp_block_size <= 0 {
8795 return;
8796 }
8797
8798 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
8799 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
8800 let mut i = start + size;
8801 while i > start {
8802 i -= 1;
8803 let mut p = sa[i];
8804 sa[i] = p & SAINT_MAX;
8805 if p > 0 {
8806 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
8807 if t[p_usize - 1] > 0 {
8808 p -= 1;
8809 let bucket =
8810 t[usize::try_from(p).expect("suffix index must be non-negative")] as usize;
8811 induction_bucket[bucket] -= 1;
8812 let slot = usize::try_from(induction_bucket[bucket])
8813 .expect("bucket slot must be non-negative");
8814 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
8815 sa[slot] = p
8816 | ((usize::from(t[p_usize - usize::from(p > 0)] > t[p_usize]) as SaSint)
8817 << (SAINT_BIT - 1));
8818 }
8819 }
8820 }
8821}
8822
8823#[doc(hidden)]
8825pub fn final_sorting_scan_right_to_left_32s(
8826 t: &[SaSint],
8827 sa: &mut [SaSint],
8828 induction_bucket: &mut [SaSint],
8829 omp_block_start: FastSint,
8830 omp_block_size: FastSint,
8831) {
8832 if omp_block_size <= 0 {
8833 return;
8834 }
8835
8836 let prefetch_distance: FastSint = 64;
8837 let mut i = omp_block_start + omp_block_size - 1;
8838 let mut j = omp_block_start + 2 * prefetch_distance + 1;
8839
8840 while i >= j {
8841 let i0 = i as usize;
8842 let mut p0 = sa[i0];
8843 sa[i0] = p0 & SAINT_MAX;
8844 if p0 > 0 {
8845 p0 -= 1;
8846 let p0u = p0 as usize;
8847 let bucket0 = t[p0u] as usize;
8848 induction_bucket[bucket0] -= 1;
8849 let slot0 = induction_bucket[bucket0] as usize;
8850 sa[slot0] = p0
8851 | ((usize::from(t[p0u - usize::from(p0 > 0)] > t[p0u]) as SaSint)
8852 << (SAINT_BIT - 1));
8853 }
8854
8855 let i1 = (i - 1) as usize;
8856 let mut p1 = sa[i1];
8857 sa[i1] = p1 & SAINT_MAX;
8858 if p1 > 0 {
8859 p1 -= 1;
8860 let p1u = p1 as usize;
8861 let bucket1 = t[p1u] as usize;
8862 induction_bucket[bucket1] -= 1;
8863 let slot1 = induction_bucket[bucket1] as usize;
8864 sa[slot1] = p1
8865 | ((usize::from(t[p1u - usize::from(p1 > 0)] > t[p1u]) as SaSint)
8866 << (SAINT_BIT - 1));
8867 }
8868 i -= 2;
8869 }
8870
8871 j -= 2 * prefetch_distance + 1;
8872 while i >= j {
8873 let iu = i as usize;
8874 let mut p = sa[iu];
8875 sa[iu] = p & SAINT_MAX;
8876 if p > 0 {
8877 p -= 1;
8878 let pu = p as usize;
8879 let bucket = t[pu] as usize;
8880 induction_bucket[bucket] -= 1;
8881 let slot = induction_bucket[bucket] as usize;
8882 sa[slot] = p
8883 | ((usize::from(t[pu - usize::from(p > 0)] > t[pu]) as SaSint) << (SAINT_BIT - 1));
8884 }
8885 i -= 1;
8886 }
8887}
8888
8889#[doc(hidden)]
8891pub fn final_bwt_scan_right_to_left_8u_block_prepare(
8892 t: &[u8],
8893 sa: &mut [SaSint],
8894 k: SaSint,
8895 buckets: &mut [SaSint],
8896 cache: &mut [ThreadCache],
8897 omp_block_start: FastSint,
8898 omp_block_size: FastSint,
8899) -> FastSint {
8900 if omp_block_size <= 0 {
8901 return 0;
8902 }
8903 let k_usize = usize::try_from(k).expect("k must be non-negative");
8904 buckets[..k_usize].fill(0);
8905 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
8906 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
8907 let mut count = 0usize;
8908 let mut i = start + size;
8909 while i > start {
8910 i -= 1;
8911 let mut p = sa[i];
8912 sa[i] = p & SAINT_MAX;
8913 if p > 0 {
8914 p -= 1;
8915 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
8916 let c0 = t[p_usize - usize::from(p > 0)] as SaSint;
8917 let c1 = t[p_usize] as SaSint;
8918 sa[i] = c1;
8919 buckets[c1 as usize] += 1;
8920 cache[count].symbol = c1;
8921 cache[count].index = if c0 <= c1 { p } else { c0 | SAINT_MIN };
8922 count += 1;
8923 }
8924 }
8925 count as FastSint
8926}
8927
8928#[doc(hidden)]
8930pub fn final_bwt_aux_scan_right_to_left_8u_block_prepare(
8931 t: &[u8],
8932 sa: &mut [SaSint],
8933 k: SaSint,
8934 buckets: &mut [SaSint],
8935 cache: &mut [ThreadCache],
8936 omp_block_start: FastSint,
8937 omp_block_size: FastSint,
8938) -> FastSint {
8939 if omp_block_size <= 0 {
8940 return 0;
8941 }
8942 let k_usize = usize::try_from(k).expect("k must be non-negative");
8943 buckets[..k_usize].fill(0);
8944 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
8945 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
8946 let mut count = 0usize;
8947 let mut i = start + size;
8948 while i > start {
8949 i -= 1;
8950 let mut p = sa[i];
8951 sa[i] = p & SAINT_MAX;
8952 if p > 0 {
8953 p -= 1;
8954 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
8955 let c0 = t[p_usize - usize::from(p > 0)] as SaSint;
8956 let c1 = t[p_usize] as SaSint;
8957 sa[i] = c1;
8958 buckets[c1 as usize] += 1;
8959 cache[count].symbol = c1;
8960 cache[count].index = if c0 <= c1 { p } else { c0 | SAINT_MIN };
8961 cache[count + 1].index = p;
8962 count += 2;
8963 }
8964 }
8965 count as FastSint
8966}
8967
8968#[doc(hidden)]
8970pub fn final_sorting_scan_right_to_left_8u_block_prepare(
8971 t: &[u8],
8972 sa: &mut [SaSint],
8973 k: SaSint,
8974 buckets: &mut [SaSint],
8975 cache: &mut [ThreadCache],
8976 omp_block_start: FastSint,
8977 omp_block_size: FastSint,
8978) -> FastSint {
8979 if omp_block_size <= 0 {
8980 return 0;
8981 }
8982
8983 let k_usize = usize::try_from(k).expect("k must be non-negative");
8984 buckets[..k_usize].fill(0);
8985
8986 let start =
8987 usize::try_from(omp_block_start).expect("omp_block_start must be non-negative") as FastSint;
8988 let mut i = omp_block_start + omp_block_size - 1;
8989 let mut j = start + 1;
8990 let mut count = 0usize;
8991
8992 while i >= j {
8993 let i0 = usize::try_from(i).expect("loop index must be non-negative");
8994 let i1 = usize::try_from(i - 1).expect("loop index must be non-negative");
8995
8996 let mut p0 = sa[i0];
8997 sa[i0] = p0 & SAINT_MAX;
8998 if p0 > 0 {
8999 p0 -= 1;
9000 let p0_usize = usize::try_from(p0).expect("suffix index must be non-negative");
9001 let c0 = t[p0_usize] as SaSint;
9002 buckets[c0 as usize] += 1;
9003 cache[count].symbol = c0;
9004 cache[count].index = p0
9005 | ((usize::from(t[p0_usize - usize::from(p0 > 0)] > t[p0_usize]) as SaSint)
9006 << (SAINT_BIT - 1));
9007 count += 1;
9008 }
9009
9010 let mut p1 = sa[i1];
9011 sa[i1] = p1 & SAINT_MAX;
9012 if p1 > 0 {
9013 p1 -= 1;
9014 let p1_usize = usize::try_from(p1).expect("suffix index must be non-negative");
9015 let c1 = t[p1_usize] as SaSint;
9016 buckets[c1 as usize] += 1;
9017 cache[count].symbol = c1;
9018 cache[count].index = p1
9019 | ((usize::from(t[p1_usize - usize::from(p1 > 0)] > t[p1_usize]) as SaSint)
9020 << (SAINT_BIT - 1));
9021 count += 1;
9022 }
9023
9024 i -= 2;
9025 }
9026
9027 j -= 1;
9028 while i >= j {
9029 let idx = usize::try_from(i).expect("loop index must be non-negative");
9030 let mut p = sa[idx];
9031 sa[idx] = p & SAINT_MAX;
9032 if p > 0 {
9033 p -= 1;
9034 let p_usize = usize::try_from(p).expect("suffix index must be non-negative");
9035 let c = t[p_usize] as SaSint;
9036 buckets[c as usize] += 1;
9037 cache[count].symbol = c;
9038 cache[count].index = p
9039 | ((usize::from(t[p_usize - usize::from(p > 0)] > t[p_usize]) as SaSint)
9040 << (SAINT_BIT - 1));
9041 count += 1;
9042 }
9043
9044 i -= 1;
9045 }
9046
9047 count as FastSint
9048}
9049
9050#[doc(hidden)]
9052pub fn final_order_scan_right_to_left_8u_block_place(
9053 sa: &mut [SaSint],
9054 buckets: &mut [SaSint],
9055 cache: &[ThreadCache],
9056 count: FastSint,
9057) {
9058 if count <= 0 {
9059 return;
9060 }
9061 let count_usize = usize::try_from(count).expect("count must be non-negative");
9062 for entry in &cache[..count_usize] {
9063 let symbol = usize::try_from(entry.symbol).expect("cache symbol must be non-negative");
9064 buckets[symbol] -= 1;
9065 let slot = usize::try_from(buckets[symbol]).expect("bucket slot must be non-negative");
9066 sa[slot] = entry.index;
9067 }
9068}
9069
9070#[doc(hidden)]
9072pub fn final_gsa_scan_right_to_left_8u_block_place(
9073 sa: &mut [SaSint],
9074 buckets: &mut [SaSint],
9075 cache: &[ThreadCache],
9076 count: FastSint,
9077) {
9078 if count <= 0 {
9079 return;
9080 }
9081 let count_usize = usize::try_from(count).expect("count must be non-negative");
9082 for entry in &cache[..count_usize] {
9083 if entry.symbol > 0 {
9084 let symbol = usize::try_from(entry.symbol).expect("cache symbol must be non-negative");
9085 buckets[symbol] -= 1;
9086 let slot = usize::try_from(buckets[symbol]).expect("bucket slot must be non-negative");
9087 sa[slot] = entry.index;
9088 }
9089 }
9090}
9091
9092#[doc(hidden)]
9094pub fn final_bwt_aux_scan_right_to_left_8u_block_place(
9095 sa: &mut [SaSint],
9096 rm: SaSint,
9097 i_out: &mut [SaSint],
9098 buckets: &mut [SaSint],
9099 cache: &[ThreadCache],
9100 count: FastSint,
9101) {
9102 if count <= 0 {
9103 return;
9104 }
9105 let count_usize = usize::try_from(count).expect("count must be non-negative");
9106 let mut i = 0usize;
9107 while i < count_usize {
9108 let symbol = usize::try_from(cache[i].symbol).expect("cache symbol must be non-negative");
9109 buckets[symbol] -= 1;
9110 let slot = usize::try_from(buckets[symbol]).expect("bucket slot must be non-negative");
9111 sa[slot] = cache[i].index;
9112 if (cache[i + 1].index & rm) == 0 {
9113 let sample_index = usize::try_from((cache[i + 1].index & SAINT_MAX) / (rm + 1))
9114 .expect("sample index must be non-negative");
9115 i_out[sample_index] = buckets[symbol] + 1;
9116 }
9117 i += 2;
9118 }
9119}
9120
9121#[doc(hidden)]
9123pub fn final_sorting_scan_right_to_left_32s_block_gather(
9124 t: &[SaSint],
9125 sa: &mut [SaSint],
9126 cache: &mut [ThreadCache],
9127 omp_block_start: FastSint,
9128 omp_block_size: FastSint,
9129) {
9130 if omp_block_size <= 0 {
9131 return;
9132 }
9133 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
9134 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
9135 for offset in 0..size {
9136 let i = start + offset;
9137 let mut symbol = SAINT_MIN;
9138 let mut p = sa[i];
9139 sa[i] = p & SAINT_MAX;
9140 if p > 0 {
9141 p -= 1;
9142 let p_usize = p as usize;
9143 cache[offset].index = p
9144 | ((usize::from(t[p_usize - usize::from(p > 0)] > t[p_usize]) as SaSint)
9145 << (SAINT_BIT - 1));
9146 symbol = t[p_usize];
9147 }
9148 cache[offset].symbol = symbol;
9149 }
9150}
9151
9152#[doc(hidden)]
9154pub fn final_sorting_scan_right_to_left_32s_block_sort(
9155 t: &[SaSint],
9156 induction_bucket: &mut [SaSint],
9157 cache: &mut [ThreadCache],
9158 omp_block_start: FastSint,
9159 omp_block_size: FastSint,
9160) {
9161 if omp_block_size <= 0 {
9162 return;
9163 }
9164 let prefetch_distance = 64usize;
9165 let start = omp_block_start as usize;
9166 let mut i = start + omp_block_size as usize - 1;
9167 let mut j = start + prefetch_distance + 1;
9168
9169 while i >= j {
9170 let ci = i - start;
9171 let v0 = cache[ci].symbol;
9172 if v0 >= 0 {
9173 let bucket_index0 = v0 as usize;
9174 induction_bucket[bucket_index0] -= 1;
9175 cache[ci].symbol = induction_bucket[bucket_index0];
9176 if cache[ci].symbol >= omp_block_start as SaSint {
9177 let ni = cache[ci].symbol as usize;
9178 let cni = ni - start;
9179 let mut np = cache[ci].index;
9180 cache[ci].index = np & SAINT_MAX;
9181 if np > 0 {
9182 np -= 1;
9183 let np_usize = np as usize;
9184 cache[cni].index = np
9185 | ((usize::from(t[np_usize - usize::from(np > 0)] > t[np_usize])
9186 as SaSint)
9187 << (SAINT_BIT - 1));
9188 cache[cni].symbol = t[np_usize];
9189 }
9190 }
9191 }
9192
9193 let i1 = i - 1;
9194 let ci1 = i1 - start;
9195 let v1 = cache[ci1].symbol;
9196 if v1 >= 0 {
9197 let bucket_index1 = v1 as usize;
9198 induction_bucket[bucket_index1] -= 1;
9199 cache[ci1].symbol = induction_bucket[bucket_index1];
9200 if cache[ci1].symbol >= omp_block_start as SaSint {
9201 let ni = cache[ci1].symbol as usize;
9202 let cni = ni - start;
9203 let mut np = cache[ci1].index;
9204 cache[ci1].index = np & SAINT_MAX;
9205 if np > 0 {
9206 np -= 1;
9207 let np_usize = np as usize;
9208 cache[cni].index = np
9209 | ((usize::from(t[np_usize - usize::from(np > 0)] > t[np_usize])
9210 as SaSint)
9211 << (SAINT_BIT - 1));
9212 cache[cni].symbol = t[np_usize];
9213 }
9214 }
9215 }
9216
9217 i -= 2;
9218 }
9219
9220 j -= prefetch_distance + 1;
9221 while i >= j {
9222 let ci = i - start;
9223 let v = cache[ci].symbol;
9224 if v >= 0 {
9225 let bucket_index = v as usize;
9226 induction_bucket[bucket_index] -= 1;
9227 cache[ci].symbol = induction_bucket[bucket_index];
9228 if cache[ci].symbol >= omp_block_start as SaSint {
9229 let ni = cache[ci].symbol as usize;
9230 let cni = ni - start;
9231 let mut np = cache[ci].index;
9232 cache[ci].index = np & SAINT_MAX;
9233 if np > 0 {
9234 np -= 1;
9235 let np_usize = np as usize;
9236 cache[cni].index = np
9237 | ((usize::from(t[np_usize - usize::from(np > 0)] > t[np_usize])
9238 as SaSint)
9239 << (SAINT_BIT - 1));
9240 cache[cni].symbol = t[np_usize];
9241 }
9242 }
9243 }
9244
9245 if i == 0 {
9246 break;
9247 }
9248 i -= 1;
9249 }
9250}
9251
9252#[doc(hidden)]
9254pub fn final_bwt_scan_right_to_left_8u_block_omp(
9255 t: &[u8],
9256 sa: &mut [SaSint],
9257 k: SaSint,
9258 induction_bucket: &mut [SaSint],
9259 block_start: FastSint,
9260 block_size: FastSint,
9261 threads: SaSint,
9262 thread_state: &mut [ThreadState],
9263) {
9264 if block_size <= 0 {
9265 return;
9266 }
9267 let k_usize = usize::try_from(k).expect("k must be non-negative");
9268 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
9269 let threads_usize = usize::try_from(threads.max(1)).expect("threads must be positive");
9270 let omp_num_threads = threads_usize.min(thread_state.len()).min(block_size_usize);
9271 if omp_num_threads <= 1 || block_size < 64 * k.max(256) as FastSint {
9272 let _ = final_bwt_scan_right_to_left_8u(t, sa, induction_bucket, block_start, block_size);
9273 return;
9274 }
9275
9276 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
9277 for (omp_thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
9278 let omp_block_start = omp_thread_num * omp_block_stride;
9279 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
9280 omp_block_stride
9281 } else {
9282 block_size_usize - omp_block_start
9283 };
9284 state.count = final_bwt_scan_right_to_left_8u_block_prepare(
9285 t,
9286 sa,
9287 k,
9288 &mut state.buckets,
9289 &mut state.cache,
9290 block_start + omp_block_start as FastSint,
9291 omp_block_size as FastSint,
9292 );
9293 }
9294 for state in thread_state.iter_mut().take(omp_num_threads).rev() {
9295 for c in 0..k_usize {
9296 let a = induction_bucket[c];
9297 let b = state.buckets[c];
9298 induction_bucket[c] = a - b;
9299 state.buckets[c] = a;
9300 }
9301 }
9302 for state in thread_state.iter_mut().take(omp_num_threads) {
9303 final_order_scan_right_to_left_8u_block_place(
9304 sa,
9305 &mut state.buckets,
9306 &state.cache,
9307 state.count,
9308 );
9309 }
9310}
9311
9312#[doc(hidden)]
9314pub fn final_bwt_aux_scan_right_to_left_8u_block_omp(
9315 t: &[u8],
9316 sa: &mut [SaSint],
9317 k: SaSint,
9318 rm: SaSint,
9319 i_out: &mut [SaSint],
9320 induction_bucket: &mut [SaSint],
9321 block_start: FastSint,
9322 block_size: FastSint,
9323 threads: SaSint,
9324 thread_state: &mut [ThreadState],
9325) {
9326 if block_size <= 0 {
9327 return;
9328 }
9329 let k_usize = usize::try_from(k).expect("k must be non-negative");
9330 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
9331 let threads_usize = usize::try_from(threads.max(1)).expect("threads must be positive");
9332 let omp_num_threads = threads_usize.min(thread_state.len()).min(block_size_usize);
9333 if omp_num_threads <= 1 || block_size < 64 * k.max(256) as FastSint {
9334 final_bwt_aux_scan_right_to_left_8u(
9335 t,
9336 sa,
9337 rm,
9338 i_out,
9339 induction_bucket,
9340 block_start,
9341 block_size,
9342 );
9343 return;
9344 }
9345
9346 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
9347 for (omp_thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
9348 let omp_block_start = omp_thread_num * omp_block_stride;
9349 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
9350 omp_block_stride
9351 } else {
9352 block_size_usize - omp_block_start
9353 };
9354 state.count = final_bwt_aux_scan_right_to_left_8u_block_prepare(
9355 t,
9356 sa,
9357 k,
9358 &mut state.buckets,
9359 &mut state.cache,
9360 block_start + omp_block_start as FastSint,
9361 omp_block_size as FastSint,
9362 );
9363 }
9364 for state in thread_state.iter_mut().take(omp_num_threads).rev() {
9365 for c in 0..k_usize {
9366 let a = induction_bucket[c];
9367 let b = state.buckets[c];
9368 induction_bucket[c] = a - b;
9369 state.buckets[c] = a;
9370 }
9371 }
9372 for state in thread_state.iter_mut().take(omp_num_threads) {
9373 final_bwt_aux_scan_right_to_left_8u_block_place(
9374 sa,
9375 rm,
9376 i_out,
9377 &mut state.buckets,
9378 &state.cache,
9379 state.count,
9380 );
9381 }
9382}
9383
9384#[doc(hidden)]
9386pub fn final_sorting_scan_right_to_left_8u_block_omp(
9387 t: &[u8],
9388 sa: &mut [SaSint],
9389 k: SaSint,
9390 induction_bucket: &mut [SaSint],
9391 block_start: FastSint,
9392 block_size: FastSint,
9393 threads: SaSint,
9394 thread_state: &mut [ThreadState],
9395) {
9396 if block_size <= 0 {
9397 return;
9398 }
9399 let k_usize = usize::try_from(k).expect("k must be non-negative");
9400 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
9401 let threads_usize = usize::try_from(threads.max(1)).expect("threads must be positive");
9402 let omp_num_threads = threads_usize.min(thread_state.len()).min(block_size_usize);
9403 if omp_num_threads <= 1 || block_size < 64 * k.max(256) as FastSint {
9404 final_sorting_scan_right_to_left_8u(t, sa, induction_bucket, block_start, block_size);
9405 return;
9406 }
9407
9408 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
9409 for (omp_thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
9410 let omp_block_start = omp_thread_num * omp_block_stride;
9411 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
9412 omp_block_stride
9413 } else {
9414 block_size_usize - omp_block_start
9415 };
9416 state.count = final_sorting_scan_right_to_left_8u_block_prepare(
9417 t,
9418 sa,
9419 k,
9420 &mut state.buckets,
9421 &mut state.cache,
9422 block_start + omp_block_start as FastSint,
9423 omp_block_size as FastSint,
9424 );
9425 }
9426 for state in thread_state.iter_mut().take(omp_num_threads).rev() {
9427 for c in 0..k_usize {
9428 let a = induction_bucket[c];
9429 let b = state.buckets[c];
9430 induction_bucket[c] = a - b;
9431 state.buckets[c] = a;
9432 }
9433 }
9434 for state in thread_state.iter_mut().take(omp_num_threads) {
9435 final_order_scan_right_to_left_8u_block_place(
9436 sa,
9437 &mut state.buckets,
9438 &state.cache,
9439 state.count,
9440 );
9441 }
9442}
9443
9444#[doc(hidden)]
9446pub fn final_gsa_scan_right_to_left_8u_block_omp(
9447 t: &[u8],
9448 sa: &mut [SaSint],
9449 k: SaSint,
9450 induction_bucket: &mut [SaSint],
9451 block_start: FastSint,
9452 block_size: FastSint,
9453 threads: SaSint,
9454 thread_state: &mut [ThreadState],
9455) {
9456 if block_size <= 0 {
9457 return;
9458 }
9459 let k_usize = usize::try_from(k).expect("k must be non-negative");
9460 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
9461 let threads_usize = usize::try_from(threads.max(1)).expect("threads must be positive");
9462 let omp_num_threads = threads_usize.min(thread_state.len()).min(block_size_usize);
9463 if omp_num_threads <= 1 || block_size < 64 * k.max(256) as FastSint {
9464 final_gsa_scan_right_to_left_8u(t, sa, induction_bucket, block_start, block_size);
9465 return;
9466 }
9467
9468 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
9469 for (omp_thread_num, state) in thread_state.iter_mut().take(omp_num_threads).enumerate() {
9470 let omp_block_start = omp_thread_num * omp_block_stride;
9471 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
9472 omp_block_stride
9473 } else {
9474 block_size_usize - omp_block_start
9475 };
9476 state.count = final_sorting_scan_right_to_left_8u_block_prepare(
9477 t,
9478 sa,
9479 k,
9480 &mut state.buckets,
9481 &mut state.cache,
9482 block_start + omp_block_start as FastSint,
9483 omp_block_size as FastSint,
9484 );
9485 }
9486 for state in thread_state.iter_mut().take(omp_num_threads).rev() {
9487 for c in 0..k_usize {
9488 let a = induction_bucket[c];
9489 let b = state.buckets[c];
9490 induction_bucket[c] = a - b;
9491 state.buckets[c] = a;
9492 }
9493 }
9494 for state in thread_state.iter_mut().take(omp_num_threads) {
9495 final_gsa_scan_right_to_left_8u_block_place(
9496 sa,
9497 &mut state.buckets,
9498 &state.cache,
9499 state.count,
9500 );
9501 }
9502}
9503
9504#[doc(hidden)]
9506pub fn final_sorting_scan_right_to_left_32s_block_omp(
9507 t: &[SaSint],
9508 sa: &mut [SaSint],
9509 buckets: &mut [SaSint],
9510 cache: &mut [ThreadCache],
9511 block_start: FastSint,
9512 block_size: FastSint,
9513 threads: SaSint,
9514) {
9515 if threads <= 1 || block_size < 16_384 {
9516 final_sorting_scan_right_to_left_32s(t, sa, buckets, block_start, block_size);
9517 return;
9518 }
9519
9520 final_sorting_scan_right_to_left_32s_block_gather(t, sa, cache, block_start, block_size);
9521 final_sorting_scan_right_to_left_32s_block_sort(t, buckets, cache, block_start, block_size);
9522 let block_size_usize = usize::try_from(block_size).expect("block_size must be non-negative");
9523 let threads_usize = usize::try_from(threads.max(1)).expect("threads must be positive");
9524 let omp_num_threads = threads_usize.min(block_size_usize);
9525 let omp_block_stride = (block_size_usize / omp_num_threads) & !15usize;
9526 {
9527 let sa_ptr = SyncMutPtr::new(sa);
9528 let cache_ptr = SyncMutPtr::new(cache);
9529 run_rayon_with_threads(omp_num_threads, || {
9530 (0..omp_num_threads)
9531 .into_par_iter()
9532 .for_each(|omp_thread_num| {
9533 let omp_block_start = omp_thread_num * omp_block_stride;
9534 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
9535 omp_block_stride
9536 } else {
9537 block_size_usize - omp_block_start
9538 };
9539 let sa = unsafe { sa_ptr.as_slice() };
9540 let cache = unsafe { cache_ptr.as_slice() };
9541 compact_and_place_cached_suffixes(
9542 sa,
9543 cache,
9544 omp_block_start as FastSint,
9545 omp_block_size as FastSint,
9546 );
9547 });
9548 });
9549 }
9550}
9551
9552#[doc(hidden)]
9554pub fn final_bwt_scan_right_to_left_8u_omp(
9555 t: &[u8],
9556 sa: &mut [SaSint],
9557 n: SaSint,
9558 k: SaSint,
9559 induction_bucket: &mut [SaSint],
9560 threads: SaSint,
9561 thread_state: &mut [ThreadState],
9562) -> SaSint {
9563 if threads == 1 || n < 65_536 {
9564 return final_bwt_scan_right_to_left_8u(t, sa, induction_bucket, 0, n as FastSint);
9565 }
9566 let mut index = -1;
9567 let mut block_start = usize::try_from(n).expect("n must be non-negative");
9568 while block_start > 0 {
9569 block_start -= 1;
9570 if sa[block_start] == 0 {
9571 index = block_start as SaSint;
9572 } else {
9573 let threads_usize = usize::try_from(threads)
9574 .expect("threads must be non-negative")
9575 .min(thread_state.len())
9576 .max(1);
9577 let max_back =
9578 threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE.saturating_sub(16 * threads_usize);
9579 let block_max_end = block_start.saturating_sub(max_back);
9580 let mut block_end = block_start;
9581 while block_end > block_max_end && sa[block_end - 1] != 0 {
9582 block_end -= 1;
9583 }
9584 let size = block_start - block_end + 1;
9585 if size < 32 {
9586 let res = final_bwt_scan_right_to_left_8u(
9587 t,
9588 sa,
9589 induction_bucket,
9590 block_end as FastSint,
9591 size as FastSint,
9592 );
9593 if res >= 0 {
9594 index = res;
9595 }
9596 } else {
9597 final_bwt_scan_right_to_left_8u_block_omp(
9598 t,
9599 sa,
9600 k,
9601 induction_bucket,
9602 block_end as FastSint,
9603 size as FastSint,
9604 threads,
9605 thread_state,
9606 );
9607 }
9608 block_start = block_end;
9609 }
9610 }
9611 index
9612}
9613
9614#[doc(hidden)]
9616pub fn final_bwt_aux_scan_right_to_left_8u_omp(
9617 t: &[u8],
9618 sa: &mut [SaSint],
9619 n: SaSint,
9620 k: SaSint,
9621 rm: SaSint,
9622 i_out: &mut [SaSint],
9623 induction_bucket: &mut [SaSint],
9624 threads: SaSint,
9625 thread_state: &mut [ThreadState],
9626) {
9627 if threads == 1 || n < 65_536 {
9628 final_bwt_aux_scan_right_to_left_8u(t, sa, rm, i_out, induction_bucket, 0, n as FastSint);
9629 return;
9630 }
9631 let mut block_start = usize::try_from(n).expect("n must be non-negative");
9632 while block_start > 0 {
9633 block_start -= 1;
9634 if sa[block_start] != 0 {
9635 let threads_usize = usize::try_from(threads)
9636 .expect("threads must be non-negative")
9637 .min(thread_state.len())
9638 .max(1);
9639 let max_back = threads_usize
9640 * (LIBSAIS_PER_THREAD_CACHE_SIZE.saturating_sub(16 * threads_usize) / 2);
9641 let block_max_end = block_start.saturating_sub(max_back);
9642 let mut block_end = block_start;
9643 while block_end > block_max_end && sa[block_end - 1] != 0 {
9644 block_end -= 1;
9645 }
9646 let size = block_start - block_end + 1;
9647 if size < 32 {
9648 final_bwt_aux_scan_right_to_left_8u(
9649 t,
9650 sa,
9651 rm,
9652 i_out,
9653 induction_bucket,
9654 block_end as FastSint,
9655 size as FastSint,
9656 );
9657 } else {
9658 final_bwt_aux_scan_right_to_left_8u_block_omp(
9659 t,
9660 sa,
9661 k,
9662 rm,
9663 i_out,
9664 induction_bucket,
9665 block_end as FastSint,
9666 size as FastSint,
9667 threads,
9668 thread_state,
9669 );
9670 }
9671 block_start = block_end;
9672 }
9673 }
9674}
9675
9676#[doc(hidden)]
9678pub fn final_sorting_scan_right_to_left_8u_omp(
9679 t: &[u8],
9680 sa: &mut [SaSint],
9681 omp_block_start: FastSint,
9682 omp_block_size: FastSint,
9683 k: SaSint,
9684 induction_bucket: &mut [SaSint],
9685 threads: SaSint,
9686 thread_state: &mut [ThreadState],
9687) {
9688 if threads == 1 || omp_block_size < 65_536 {
9689 final_sorting_scan_right_to_left_8u(
9690 t,
9691 sa,
9692 induction_bucket,
9693 omp_block_start,
9694 omp_block_size,
9695 );
9696 return;
9697 }
9698 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
9699 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
9700 let mut block_start = start + size;
9701 while block_start > start {
9702 block_start -= 1;
9703 if sa[block_start] != 0 {
9704 let threads_usize = usize::try_from(threads)
9705 .expect("threads must be non-negative")
9706 .min(thread_state.len())
9707 .max(1);
9708 let max_back =
9709 threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE.saturating_sub(16 * threads_usize);
9710 let block_max_end = block_start.saturating_sub(max_back).max(start);
9711 let mut block_end = block_start;
9712 while block_end > block_max_end && sa[block_end - 1] != 0 {
9713 block_end -= 1;
9714 }
9715 let span = block_start - block_end + 1;
9716 if span < 32 {
9717 final_sorting_scan_right_to_left_8u(
9718 t,
9719 sa,
9720 induction_bucket,
9721 block_end as FastSint,
9722 span as FastSint,
9723 );
9724 } else {
9725 final_sorting_scan_right_to_left_8u_block_omp(
9726 t,
9727 sa,
9728 k,
9729 induction_bucket,
9730 block_end as FastSint,
9731 span as FastSint,
9732 threads,
9733 thread_state,
9734 );
9735 }
9736 block_start = block_end;
9737 }
9738 }
9739}
9740
9741#[doc(hidden)]
9743pub fn final_gsa_scan_right_to_left_8u_omp(
9744 t: &[u8],
9745 sa: &mut [SaSint],
9746 omp_block_start: FastSint,
9747 omp_block_size: FastSint,
9748 k: SaSint,
9749 induction_bucket: &mut [SaSint],
9750 threads: SaSint,
9751 thread_state: &mut [ThreadState],
9752) {
9753 if threads == 1 || omp_block_size < 65_536 {
9754 final_gsa_scan_right_to_left_8u(t, sa, induction_bucket, omp_block_start, omp_block_size);
9755 return;
9756 }
9757 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
9758 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
9759 let mut block_start = start + size;
9760 while block_start > start {
9761 block_start -= 1;
9762 if sa[block_start] != 0 {
9763 let threads_usize = usize::try_from(threads)
9764 .expect("threads must be non-negative")
9765 .min(thread_state.len())
9766 .max(1);
9767 let max_back =
9768 threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE.saturating_sub(16 * threads_usize);
9769 let block_max_end = block_start.saturating_sub(max_back).max(start);
9770 let mut block_end = block_start;
9771 while block_end > block_max_end && sa[block_end - 1] != 0 {
9772 block_end -= 1;
9773 }
9774 let span = block_start - block_end + 1;
9775 if span < 32 {
9776 final_gsa_scan_right_to_left_8u(
9777 t,
9778 sa,
9779 induction_bucket,
9780 block_end as FastSint,
9781 span as FastSint,
9782 );
9783 } else {
9784 final_gsa_scan_right_to_left_8u_block_omp(
9785 t,
9786 sa,
9787 k,
9788 induction_bucket,
9789 block_end as FastSint,
9790 span as FastSint,
9791 threads,
9792 thread_state,
9793 );
9794 }
9795 block_start = block_end;
9796 }
9797 }
9798}
9799
9800#[doc(hidden)]
9802pub fn final_sorting_scan_right_to_left_32s_omp(
9803 t: &[SaSint],
9804 sa: &mut [SaSint],
9805 n: SaSint,
9806 induction_bucket: &mut [SaSint],
9807 threads: SaSint,
9808 thread_state: &mut [ThreadState],
9809) {
9810 if threads == 1 || n < 65_536 {
9811 final_sorting_scan_right_to_left_32s(t, sa, induction_bucket, 0, n as FastSint);
9812 return;
9813 }
9814 if thread_state.is_empty() {
9815 final_sorting_scan_right_to_left_32s(t, sa, induction_bucket, 0, n as FastSint);
9816 return;
9817 }
9818 let threads_usize = usize::try_from(threads)
9819 .expect("threads must be non-negative")
9820 .max(1);
9821 let mut cache = vec![ThreadCache::default(); threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE];
9822 let mut block_start = isize::try_from(n).expect("n must fit isize") - 1;
9823 while block_start >= 0 {
9824 let block_end = (block_start
9825 - isize::try_from(threads_usize * LIBSAIS_PER_THREAD_CACHE_SIZE)
9826 .expect("block span must fit isize"))
9827 .max(-1);
9828 final_sorting_scan_right_to_left_32s_block_omp(
9829 t,
9830 sa,
9831 induction_bucket,
9832 &mut cache,
9833 (block_end + 1) as FastSint,
9834 (block_start - block_end) as FastSint,
9835 threads,
9836 );
9837 block_start = block_end;
9838 }
9839}
9840
9841#[doc(hidden)]
9843pub fn clear_lms_suffixes_omp(
9844 sa: &mut [SaSint],
9845 n: SaSint,
9846 k: SaSint,
9847 bucket_start: &[SaSint],
9848 bucket_end: &[SaSint],
9849 threads: SaSint,
9850) {
9851 let k_usize = usize::try_from(k).expect("k must be non-negative");
9852 let thread_count = if threads > 1 && n >= 65536 {
9853 usize::try_from(threads).expect("threads must be positive")
9854 } else {
9855 1
9856 };
9857 {
9858 let sa_ptr = SyncMutPtr::new(sa);
9859 let bucket_start_ref: &[SaSint] = bucket_start;
9860 let bucket_end_ref: &[SaSint] = bucket_end;
9861 run_rayon_with_threads(thread_count, || {
9862 (0..thread_count).into_par_iter().for_each(|t| {
9863 let mut c = t;
9864 let sa = unsafe { sa_ptr.as_slice() };
9865 while c < k_usize {
9866 if bucket_end_ref[c] > bucket_start_ref[c] {
9867 let start = usize::try_from(bucket_start_ref[c])
9868 .expect("bucket start must be non-negative");
9869 let end = usize::try_from(bucket_end_ref[c])
9870 .expect("bucket end must be non-negative");
9871 sa[start..end].fill(0);
9872 }
9873 c += thread_count;
9874 }
9875 });
9876 });
9877 }
9878}
9879
9880#[doc(hidden)]
9882pub fn induce_final_order_8u_omp(
9883 t: &[u8],
9884 sa: &mut [SaSint],
9885 n: SaSint,
9886 k: SaSint,
9887 flags: SaSint,
9888 r: SaSint,
9889 i_out: Option<&mut [SaSint]>,
9890 buckets: &mut [SaSint],
9891 threads: SaSint,
9892 thread_state: &mut [ThreadState],
9893) -> SaSint {
9894 if (flags & LIBSAIS_FLAGS_BWT) == 0 {
9895 if (flags & LIBSAIS_FLAGS_GSA) != 0 {
9896 buckets[6 * ALPHABET_SIZE] = buckets[7 * ALPHABET_SIZE] - 1;
9897 }
9898
9899 let (left_buckets, right_tail) = buckets.split_at_mut(7 * ALPHABET_SIZE);
9900 let bucket_start = &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE];
9901 let bucket_end = &mut right_tail[..ALPHABET_SIZE];
9902
9903 final_sorting_scan_left_to_right_8u_omp(
9904 t,
9905 sa,
9906 n as FastSint,
9907 k,
9908 bucket_start,
9909 threads,
9910 thread_state,
9911 );
9912 if threads > 1 && n >= 65_536 {
9913 clear_lms_suffixes_omp(
9914 sa,
9915 n,
9916 ALPHABET_SIZE as SaSint,
9917 bucket_start,
9918 bucket_end,
9919 threads,
9920 );
9921 }
9922
9923 if (flags & LIBSAIS_FLAGS_GSA) != 0 {
9924 flip_suffix_markers_omp(sa, bucket_end[0], threads);
9925 final_gsa_scan_right_to_left_8u_omp(
9926 t,
9927 sa,
9928 bucket_end[0] as FastSint,
9929 n as FastSint - bucket_end[0] as FastSint,
9930 k,
9931 bucket_end,
9932 threads,
9933 thread_state,
9934 );
9935 } else {
9936 final_sorting_scan_right_to_left_8u_omp(
9937 t,
9938 sa,
9939 0,
9940 n as FastSint,
9941 k,
9942 bucket_end,
9943 threads,
9944 thread_state,
9945 );
9946 }
9947
9948 0
9949 } else if let Some(i_out) = i_out {
9950 let (left_buckets, right_tail) = buckets.split_at_mut(7 * ALPHABET_SIZE);
9951 let bucket_start = &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE];
9952 let bucket_end = &mut right_tail[..ALPHABET_SIZE];
9953
9954 final_bwt_aux_scan_left_to_right_8u_omp(
9955 t,
9956 sa,
9957 n as FastSint,
9958 k,
9959 r - 1,
9960 i_out,
9961 bucket_start,
9962 threads,
9963 thread_state,
9964 );
9965 if threads > 1 && n >= 65_536 {
9966 clear_lms_suffixes_omp(
9967 sa,
9968 n,
9969 ALPHABET_SIZE as SaSint,
9970 bucket_start,
9971 bucket_end,
9972 threads,
9973 );
9974 }
9975 final_bwt_aux_scan_right_to_left_8u_omp(
9976 t,
9977 sa,
9978 n,
9979 k,
9980 r - 1,
9981 i_out,
9982 bucket_end,
9983 threads,
9984 thread_state,
9985 );
9986 0
9987 } else {
9988 let (left_buckets, right_tail) = buckets.split_at_mut(7 * ALPHABET_SIZE);
9989 let bucket_start = &mut left_buckets[6 * ALPHABET_SIZE..7 * ALPHABET_SIZE];
9990 let bucket_end = &mut right_tail[..ALPHABET_SIZE];
9991
9992 final_bwt_scan_left_to_right_8u_omp(
9993 t,
9994 sa,
9995 n as FastSint,
9996 k,
9997 bucket_start,
9998 threads,
9999 thread_state,
10000 );
10001 if threads > 1 && n >= 65_536 {
10002 clear_lms_suffixes_omp(
10003 sa,
10004 n,
10005 ALPHABET_SIZE as SaSint,
10006 bucket_start,
10007 bucket_end,
10008 threads,
10009 );
10010 }
10011 final_bwt_scan_right_to_left_8u_omp(t, sa, n, k, bucket_end, threads, thread_state)
10012 }
10013}
10014
10015#[doc(hidden)]
10017pub fn induce_final_order_32s_6k(
10018 t: &[SaSint],
10019 sa: &mut [SaSint],
10020 n: SaSint,
10021 k: SaSint,
10022 buckets: &mut [SaSint],
10023 threads: SaSint,
10024 thread_state: &mut [ThreadState],
10025) {
10026 let k_usize = usize::try_from(k).expect("k must be non-negative");
10027 let (_head, tail) = buckets.split_at_mut(4 * k_usize);
10028 let (left, right) = tail.split_at_mut(k_usize);
10029 final_sorting_scan_left_to_right_32s_omp(t, sa, n, left, threads, thread_state);
10030 final_sorting_scan_right_to_left_32s_omp(t, sa, n, right, threads, thread_state);
10031}
10032
10033#[doc(hidden)]
10035pub fn induce_final_order_32s_4k(
10036 t: &[SaSint],
10037 sa: &mut [SaSint],
10038 n: SaSint,
10039 k: SaSint,
10040 buckets: &mut [SaSint],
10041 threads: SaSint,
10042 thread_state: &mut [ThreadState],
10043) {
10044 let k_usize = usize::try_from(k).expect("k must be non-negative");
10045 let (_head, tail) = buckets.split_at_mut(2 * k_usize);
10046 let (left, right) = tail.split_at_mut(k_usize);
10047 final_sorting_scan_left_to_right_32s_omp(t, sa, n, left, threads, thread_state);
10048 final_sorting_scan_right_to_left_32s_omp(t, sa, n, right, threads, thread_state);
10049}
10050
10051#[doc(hidden)]
10053pub fn induce_final_order_32s_2k(
10054 t: &[SaSint],
10055 sa: &mut [SaSint],
10056 n: SaSint,
10057 k: SaSint,
10058 buckets: &mut [SaSint],
10059 threads: SaSint,
10060 thread_state: &mut [ThreadState],
10061) {
10062 let k_usize = usize::try_from(k).expect("k must be non-negative");
10063 let (right, left) = buckets.split_at_mut(k_usize);
10064 final_sorting_scan_left_to_right_32s_omp(t, sa, n, left, threads, thread_state);
10065 final_sorting_scan_right_to_left_32s_omp(t, sa, n, right, threads, thread_state);
10066}
10067
10068#[doc(hidden)]
10070pub fn induce_final_order_32s_1k(
10071 t: &[SaSint],
10072 sa: &mut [SaSint],
10073 n: SaSint,
10074 k: SaSint,
10075 buckets: &mut [SaSint],
10076 threads: SaSint,
10077 thread_state: &mut [ThreadState],
10078) {
10079 count_suffixes_32s(t, n, k, buckets);
10080 initialize_buckets_start_32s_1k(k, buckets);
10081 final_sorting_scan_left_to_right_32s_omp(t, sa, n, buckets, threads, thread_state);
10082
10083 count_suffixes_32s(t, n, k, buckets);
10084 initialize_buckets_end_32s_1k(k, buckets);
10085 final_sorting_scan_right_to_left_32s_omp(t, sa, n, buckets, threads, thread_state);
10086}
10087
10088#[doc(hidden)]
10090pub fn renumber_unique_and_nonunique_lms_suffixes_32s(
10091 t: &mut [SaSint],
10092 sa: &mut [SaSint],
10093 m: SaSint,
10094 mut f: SaSint,
10095 omp_block_start: FastSint,
10096 omp_block_size: FastSint,
10097) -> SaSint {
10098 if omp_block_size <= 0 {
10099 return f;
10100 }
10101
10102 let prefetch_distance = 64 as SaSint;
10103 let m_usize = usize::try_from(m).expect("m must be non-negative");
10104 let (sa_head, sam) = sa.split_at_mut(m_usize);
10105 let mut i = omp_block_start as SaSint;
10106 let mut j = omp_block_start as SaSint + omp_block_size as SaSint - 2 * prefetch_distance - 3;
10107
10108 while i < j {
10109 let p0 = sa_head[i as usize] as SaUint;
10110 let p0_half = (p0 >> 1) as usize;
10111 let mut s0 = sam[p0_half];
10112 if s0 < 0 {
10113 t[p0 as usize] |= SAINT_MIN;
10114 f += 1;
10115 s0 = i + SAINT_MIN + f;
10116 }
10117 sam[p0_half] = s0 - f;
10118
10119 let p1 = sa_head[(i + 1) as usize] as SaUint;
10120 let p1_half = (p1 >> 1) as usize;
10121 let mut s1 = sam[p1_half];
10122 if s1 < 0 {
10123 t[p1 as usize] |= SAINT_MIN;
10124 f += 1;
10125 s1 = i + 1 + SAINT_MIN + f;
10126 }
10127 sam[p1_half] = s1 - f;
10128
10129 let p2 = sa_head[(i + 2) as usize] as SaUint;
10130 let p2_half = (p2 >> 1) as usize;
10131 let mut s2 = sam[p2_half];
10132 if s2 < 0 {
10133 t[p2 as usize] |= SAINT_MIN;
10134 f += 1;
10135 s2 = i + 2 + SAINT_MIN + f;
10136 }
10137 sam[p2_half] = s2 - f;
10138
10139 let p3 = sa_head[(i + 3) as usize] as SaUint;
10140 let p3_half = (p3 >> 1) as usize;
10141 let mut s3 = sam[p3_half];
10142 if s3 < 0 {
10143 t[p3 as usize] |= SAINT_MIN;
10144 f += 1;
10145 s3 = i + 3 + SAINT_MIN + f;
10146 }
10147 sam[p3_half] = s3 - f;
10148
10149 i += 4;
10150 }
10151
10152 j += 2 * prefetch_distance + 3;
10153 while i < j {
10154 let p = sa_head[i as usize] as SaUint;
10155 let p_half = (p >> 1) as usize;
10156 let mut s = sam[p_half];
10157 if s < 0 {
10158 t[p as usize] |= SAINT_MIN;
10159 f += 1;
10160 s = i + SAINT_MIN + f;
10161 }
10162 sam[p_half] = s - f;
10163 i += 1;
10164 }
10165
10166 f
10167}
10168
10169#[doc(hidden)]
10171pub fn compact_unique_and_nonunique_lms_suffixes_32s(
10172 sa: &mut [SaSint],
10173 m: SaSint,
10174 pl: &mut FastSint,
10175 pr: &mut FastSint,
10176 omp_block_start: FastSint,
10177 omp_block_size: FastSint,
10178) {
10179 if omp_block_size <= 0 {
10180 return;
10181 }
10182
10183 let m_usize = usize::try_from(m).expect("m must be non-negative");
10184 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
10185 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
10186
10187 let source: Vec<SaSint> = sa[m_usize + start..m_usize + start + size].to_vec();
10188 let mut l = usize::try_from(*pl - 1).expect("left position must be positive");
10189 let mut r = usize::try_from(*pr - 1).expect("right position must be positive");
10190
10191 for &p in source.iter().rev() {
10192 let pu = p as SaUint;
10193 sa[l] = (pu & SAINT_MAX as SaUint) as SaSint;
10194 l = l.saturating_sub(usize::from((pu as SaSint) < 0));
10195
10196 sa[r] = pu.wrapping_sub(1) as SaSint;
10197 r = r.saturating_sub(usize::from((pu as SaSint) > 0));
10198 }
10199
10200 *pl = l as FastSint + 1;
10201 *pr = r as FastSint + 1;
10202}
10203
10204#[doc(hidden)]
10206pub fn count_unique_suffixes(
10207 sa: &[SaSint],
10208 m: SaSint,
10209 omp_block_start: FastSint,
10210 omp_block_size: FastSint,
10211) -> SaSint {
10212 if omp_block_size <= 0 {
10213 return 0;
10214 }
10215
10216 let m_usize = usize::try_from(m).expect("m must be non-negative");
10217 let sam = &sa[m_usize..];
10218 let mut i = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
10219 let block_end =
10220 i + usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
10221 let j = block_end.saturating_sub(67);
10222 let mut f0 = 0;
10223 let mut f1 = 0;
10224 let mut f2 = 0;
10225 let mut f3 = 0;
10226
10227 while i < j {
10228 f0 += SaSint::from(
10229 sam[usize::try_from((sa[i] as SaUint) >> 1).expect("name slot must fit usize")] < 0,
10230 );
10231 f1 += SaSint::from(
10232 sam[usize::try_from((sa[i + 1] as SaUint) >> 1).expect("name slot must fit usize")] < 0,
10233 );
10234 f2 += SaSint::from(
10235 sam[usize::try_from((sa[i + 2] as SaUint) >> 1).expect("name slot must fit usize")] < 0,
10236 );
10237 f3 += SaSint::from(
10238 sam[usize::try_from((sa[i + 3] as SaUint) >> 1).expect("name slot must fit usize")] < 0,
10239 );
10240 i += 4;
10241 }
10242
10243 while i < block_end {
10244 f0 += SaSint::from(
10245 sam[usize::try_from((sa[i] as SaUint) >> 1).expect("name slot must fit usize")] < 0,
10246 );
10247 i += 1;
10248 }
10249
10250 f0 + f1 + f2 + f3
10251}
10252
10253#[doc(hidden)]
10255pub fn renumber_unique_and_nonunique_lms_suffixes_32s_omp(
10256 t: &mut [SaSint],
10257 sa: &mut [SaSint],
10258 m: SaSint,
10259 threads: SaSint,
10260 thread_state: &mut [ThreadState],
10261) -> SaSint {
10262 let f = if threads == 1 || m < 65_536 {
10263 renumber_unique_and_nonunique_lms_suffixes_32s(t, sa, m, 0, 0, m as FastSint)
10264 } else {
10265 let threads_usize = usize::try_from(threads)
10266 .expect("threads must be non-negative")
10267 .max(1);
10268 let m_usize = usize::try_from(m).expect("m must be non-negative");
10269 let omp_num_threads = threads_usize.min(m_usize.max(1));
10270 let omp_block_stride = (m_usize / omp_num_threads) & !15usize;
10271
10272 {
10273 let sa_ro: &[SaSint] = sa;
10274 run_rayon_with_threads(omp_num_threads, || {
10275 thread_state[..omp_num_threads]
10276 .par_iter_mut()
10277 .enumerate()
10278 .for_each(|(omp_thread_num, state)| {
10279 let omp_block_start = omp_thread_num * omp_block_stride;
10280 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
10281 omp_block_stride
10282 } else {
10283 m_usize - omp_block_start
10284 };
10285 state.count = count_unique_suffixes(
10286 sa_ro,
10287 m,
10288 omp_block_start as FastSint,
10289 omp_block_size as FastSint,
10290 ) as FastSint;
10291 });
10292 });
10293 }
10294
10295 let counts: Vec<FastSint> = thread_state[..omp_num_threads]
10296 .iter()
10297 .map(|s| s.count)
10298 .collect();
10299 let f = counts.iter().sum::<FastSint>() as SaSint;
10300
10301 {
10302 let sa_ptr = SyncMutPtr::new(sa);
10303 let t_ptr = SyncMutPtr::new(t);
10304 let counts_ref: &[FastSint] = &counts;
10305 run_rayon_with_threads(omp_num_threads, || {
10306 (0..omp_num_threads)
10307 .into_par_iter()
10308 .for_each(|omp_thread_num| {
10309 let omp_block_start = omp_thread_num * omp_block_stride;
10310 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
10311 omp_block_stride
10312 } else {
10313 m_usize - omp_block_start
10314 };
10315
10316 let mut count: FastSint = 0;
10317 for tt in 0..omp_thread_num {
10318 count += counts_ref[tt];
10319 }
10320
10321 let sa = unsafe { sa_ptr.as_slice() };
10322 let t = unsafe { t_ptr.as_slice() };
10323 renumber_unique_and_nonunique_lms_suffixes_32s(
10324 t,
10325 sa,
10326 m,
10327 count as SaSint,
10328 omp_block_start as FastSint,
10329 omp_block_size as FastSint,
10330 );
10331 });
10332 });
10333 }
10334 f
10335 };
10336
10337 f
10338}
10339
10340#[doc(hidden)]
10342pub fn compact_unique_and_nonunique_lms_suffixes_32s_omp(
10343 sa: &mut [SaSint],
10344 n: SaSint,
10345 m: SaSint,
10346 fs: SaSint,
10347 f: SaSint,
10348 threads: SaSint,
10349 thread_state: &mut [ThreadState],
10350) {
10351 let half_n = (n as FastSint) >> 1;
10352 if threads == 1 || n < 131_072 || m >= fs {
10353 let mut l = m as FastSint;
10354 let mut r = n as FastSint + fs as FastSint;
10355 compact_unique_and_nonunique_lms_suffixes_32s(sa, m, &mut l, &mut r, 0, half_n);
10356 } else {
10357 let threads_usize = usize::try_from(threads)
10358 .expect("threads must be non-negative")
10359 .max(1);
10360 let half_n_usize = usize::try_from(half_n).expect("half_n must be non-negative");
10361 let omp_num_threads = threads_usize.min(half_n_usize.max(1));
10362 let omp_block_stride = (half_n_usize / omp_num_threads) & !15usize;
10363
10364 {
10365 let sa_ptr = SyncMutPtr::new(sa);
10366 run_rayon_with_threads(omp_num_threads, || {
10367 thread_state[..omp_num_threads]
10368 .par_iter_mut()
10369 .enumerate()
10370 .for_each(|(omp_thread_num, state)| {
10371 let omp_block_start = omp_thread_num * omp_block_stride;
10372 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
10373 omp_block_stride
10374 } else {
10375 half_n_usize - omp_block_start
10376 };
10377
10378 let mut position = m as FastSint
10379 + half_n
10380 + omp_block_start as FastSint
10381 + omp_block_size as FastSint;
10382 let mut count = m as FastSint
10383 + omp_block_start as FastSint
10384 + omp_block_size as FastSint;
10385
10386 let sa = unsafe { sa_ptr.as_slice() };
10387 compact_unique_and_nonunique_lms_suffixes_32s(
10388 sa,
10389 m,
10390 &mut position,
10391 &mut count,
10392 omp_block_start as FastSint,
10393 omp_block_size as FastSint,
10394 );
10395 state.position = position;
10396 state.count = count;
10397 });
10398 });
10399 }
10400
10401 let mut position = m as FastSint;
10402 for t in (0..omp_num_threads).rev() {
10403 let omp_block_end = if t + 1 < omp_num_threads {
10404 omp_block_stride * (t + 1)
10405 } else {
10406 half_n_usize
10407 };
10408 let count =
10409 m as FastSint + half_n + omp_block_end as FastSint - thread_state[t].position;
10410 if count > 0 {
10411 position -= count;
10412 let dst = usize::try_from(position).expect("destination must be non-negative");
10413 let src =
10414 usize::try_from(thread_state[t].position).expect("source must be non-negative");
10415 let len = usize::try_from(count).expect("length must be non-negative");
10416 sa.copy_within(src..src + len, dst);
10417 }
10418 }
10419
10420 let mut position = n as FastSint + fs as FastSint;
10421 for t in (0..omp_num_threads).rev() {
10422 let omp_block_end = if t + 1 < omp_num_threads {
10423 omp_block_stride * (t + 1)
10424 } else {
10425 half_n_usize
10426 };
10427 let count = m as FastSint + omp_block_end as FastSint - thread_state[t].count;
10428 if count > 0 {
10429 position -= count;
10430 let dst = usize::try_from(position).expect("destination must be non-negative");
10431 let src =
10432 usize::try_from(thread_state[t].count).expect("source must be non-negative");
10433 let len = usize::try_from(count).expect("length must be non-negative");
10434 sa.copy_within(src..src + len, dst);
10435 }
10436 }
10437 }
10438
10439 let copy_dst = usize::try_from(n + fs - m).expect("copy destination must be non-negative");
10440 let copy_src = usize::try_from(m - f).expect("copy source must be non-negative");
10441 let copy_len = usize::try_from(f).expect("copy length must be non-negative");
10442 sa.copy_within(copy_src..copy_src + copy_len, copy_dst);
10443}
10444
10445#[doc(hidden)]
10447pub fn compact_lms_suffixes_32s_omp(
10448 t: &mut [SaSint],
10449 sa: &mut [SaSint],
10450 n: SaSint,
10451 m: SaSint,
10452 fs: SaSint,
10453 threads: SaSint,
10454 thread_state: &mut [ThreadState],
10455) -> SaSint {
10456 let f = renumber_unique_and_nonunique_lms_suffixes_32s_omp(t, sa, m, threads, thread_state);
10457 compact_unique_and_nonunique_lms_suffixes_32s_omp(sa, n, m, fs, f, threads, thread_state);
10458 f
10459}
10460
10461#[doc(hidden)]
10463pub fn merge_unique_lms_suffixes_32s(
10464 t: &mut [SaSint],
10465 sa: &mut [SaSint],
10466 n: SaSint,
10467 m: SaSint,
10468 l: FastSint,
10469 omp_block_start: FastSint,
10470 omp_block_size: FastSint,
10471) {
10472 if omp_block_size <= 0 {
10473 return;
10474 }
10475
10476 let n_usize = usize::try_from(n).expect("n must be non-negative");
10477 let m_usize = usize::try_from(m).expect("m must be non-negative");
10478 let mut src_index = n_usize - m_usize - 1 + usize::try_from(l).expect("l must be non-negative");
10479 let mut tmp = sa[src_index] as FastSint;
10480 src_index += 1;
10481
10482 let mut i = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
10483 let block_end =
10484 i + usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
10485 let j = block_end.saturating_sub(6);
10486 while i < j {
10487 let c0 = t[i];
10488 if c0 < 0 {
10489 t[i] = c0 & SAINT_MAX;
10490 sa[usize::try_from(tmp).expect("target slot must be non-negative")] = i as SaSint;
10491 i += 1;
10492 tmp = sa[src_index] as FastSint;
10493 src_index += 1;
10494 }
10495
10496 let c1 = t[i + 1];
10497 if c1 < 0 {
10498 t[i + 1] = c1 & SAINT_MAX;
10499 sa[usize::try_from(tmp).expect("target slot must be non-negative")] = i as SaSint + 1;
10500 i += 1;
10501 tmp = sa[src_index] as FastSint;
10502 src_index += 1;
10503 }
10504
10505 let c2 = t[i + 2];
10506 if c2 < 0 {
10507 t[i + 2] = c2 & SAINT_MAX;
10508 sa[usize::try_from(tmp).expect("target slot must be non-negative")] = i as SaSint + 2;
10509 i += 1;
10510 tmp = sa[src_index] as FastSint;
10511 src_index += 1;
10512 }
10513
10514 let c3 = t[i + 3];
10515 if c3 < 0 {
10516 t[i + 3] = c3 & SAINT_MAX;
10517 sa[usize::try_from(tmp).expect("target slot must be non-negative")] = i as SaSint + 3;
10518 i += 1;
10519 tmp = sa[src_index] as FastSint;
10520 src_index += 1;
10521 }
10522
10523 i += 4;
10524 }
10525
10526 while i < block_end {
10527 let c = t[i];
10528 if c < 0 {
10529 t[i] = c & SAINT_MAX;
10530 sa[usize::try_from(tmp).expect("target slot must be non-negative")] = i as SaSint;
10531 i += 1;
10532 tmp = sa[src_index] as FastSint;
10533 src_index += 1;
10534 }
10535 i += 1;
10536 }
10537}
10538
10539#[doc(hidden)]
10541pub fn merge_nonunique_lms_suffixes_32s(
10542 sa: &mut [SaSint],
10543 n: SaSint,
10544 m: SaSint,
10545 l: FastSint,
10546 omp_block_start: FastSint,
10547 omp_block_size: FastSint,
10548) {
10549 if omp_block_size <= 0 {
10550 return;
10551 }
10552
10553 let n_usize = usize::try_from(n).expect("n must be non-negative");
10554 let m_usize = usize::try_from(m).expect("m must be non-negative");
10555 let mut src_index = n_usize - m_usize - 1 + usize::try_from(l).expect("l must be non-negative");
10556 let mut tmp = sa[src_index];
10557 src_index += 1;
10558
10559 let mut i = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
10560 let block_end =
10561 i + usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
10562 let j = block_end.saturating_sub(3);
10563 while i < j {
10564 if sa[i] == 0 {
10565 sa[i] = tmp;
10566 tmp = sa[src_index];
10567 src_index += 1;
10568 }
10569 if sa[i + 1] == 0 {
10570 sa[i + 1] = tmp;
10571 tmp = sa[src_index];
10572 src_index += 1;
10573 }
10574 if sa[i + 2] == 0 {
10575 sa[i + 2] = tmp;
10576 tmp = sa[src_index];
10577 src_index += 1;
10578 }
10579 if sa[i + 3] == 0 {
10580 sa[i + 3] = tmp;
10581 tmp = sa[src_index];
10582 src_index += 1;
10583 }
10584 i += 4;
10585 }
10586
10587 while i < block_end {
10588 if sa[i] == 0 {
10589 sa[i] = tmp;
10590 tmp = sa[src_index];
10591 src_index += 1;
10592 }
10593 i += 1;
10594 }
10595}
10596
10597#[doc(hidden)]
10599pub fn merge_unique_lms_suffixes_32s_omp(
10600 t: &mut [SaSint],
10601 sa: &mut [SaSint],
10602 n: SaSint,
10603 m: SaSint,
10604 threads: SaSint,
10605 thread_state: &mut [ThreadState],
10606) {
10607 if threads == 1 || n < 65_536 {
10608 merge_unique_lms_suffixes_32s(t, sa, n, m, 0, 0, n as FastSint);
10609 return;
10610 }
10611
10612 let threads_usize = usize::try_from(threads)
10613 .expect("threads must be non-negative")
10614 .max(1);
10615 let n_usize = usize::try_from(n).expect("n must be non-negative");
10616 let omp_num_threads = threads_usize.min(n_usize.max(1));
10617 let omp_block_stride = (n_usize / omp_num_threads) & !15usize;
10618
10619 {
10620 let t_ro: &[SaSint] = t;
10621 run_rayon_with_threads(omp_num_threads, || {
10622 thread_state[..omp_num_threads]
10623 .par_iter_mut()
10624 .enumerate()
10625 .for_each(|(omp_thread_num, state)| {
10626 let omp_block_start = omp_thread_num * omp_block_stride;
10627 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
10628 omp_block_stride
10629 } else {
10630 n_usize - omp_block_start
10631 };
10632 state.count = count_negative_marked_suffixes(
10633 t_ro,
10634 omp_block_start as FastSint,
10635 omp_block_size as FastSint,
10636 ) as FastSint;
10637 });
10638 });
10639 }
10640
10641 let counts: Vec<FastSint> = thread_state[..omp_num_threads]
10642 .iter()
10643 .map(|s| s.count)
10644 .collect();
10645
10646 {
10647 let sa_ptr = SyncMutPtr::new(sa);
10648 let t_ptr = SyncMutPtr::new(t);
10649 let counts_ref: &[FastSint] = &counts;
10650 run_rayon_with_threads(omp_num_threads, || {
10651 (0..omp_num_threads)
10652 .into_par_iter()
10653 .for_each(|omp_thread_num| {
10654 let omp_block_start = omp_thread_num * omp_block_stride;
10655 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
10656 omp_block_stride
10657 } else {
10658 n_usize - omp_block_start
10659 };
10660
10661 let mut count: FastSint = 0;
10662 for tt in 0..omp_thread_num {
10663 count += counts_ref[tt];
10664 }
10665
10666 let sa = unsafe { sa_ptr.as_slice() };
10667 let t = unsafe { t_ptr.as_slice() };
10668 merge_unique_lms_suffixes_32s(
10669 t,
10670 sa,
10671 n,
10672 m,
10673 count,
10674 omp_block_start as FastSint,
10675 omp_block_size as FastSint,
10676 );
10677 });
10678 });
10679 }
10680}
10681
10682#[doc(hidden)]
10684pub fn merge_nonunique_lms_suffixes_32s_omp(
10685 sa: &mut [SaSint],
10686 n: SaSint,
10687 m: SaSint,
10688 f: SaSint,
10689 threads: SaSint,
10690 thread_state: &mut [ThreadState],
10691) {
10692 if threads == 1 || m < 65_536 {
10693 merge_nonunique_lms_suffixes_32s(sa, n, m, f as FastSint, 0, m as FastSint);
10694 return;
10695 }
10696
10697 let threads_usize = usize::try_from(threads)
10698 .expect("threads must be non-negative")
10699 .max(1);
10700 let m_usize = usize::try_from(m).expect("m must be non-negative");
10701 let omp_num_threads = threads_usize.min(m_usize.max(1));
10702 let omp_block_stride = (m_usize / omp_num_threads) & !15usize;
10703
10704 {
10705 let sa_ro: &[SaSint] = sa;
10706 run_rayon_with_threads(omp_num_threads, || {
10707 thread_state[..omp_num_threads]
10708 .par_iter_mut()
10709 .enumerate()
10710 .for_each(|(omp_thread_num, state)| {
10711 let omp_block_start = omp_thread_num * omp_block_stride;
10712 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
10713 omp_block_stride
10714 } else {
10715 m_usize - omp_block_start
10716 };
10717 state.count = count_zero_marked_suffixes(
10718 sa_ro,
10719 omp_block_start as FastSint,
10720 omp_block_size as FastSint,
10721 ) as FastSint;
10722 });
10723 });
10724 }
10725
10726 let counts: Vec<FastSint> = thread_state[..omp_num_threads]
10727 .iter()
10728 .map(|s| s.count)
10729 .collect();
10730
10731 {
10732 let sa_ptr = SyncMutPtr::new(sa);
10733 let counts_ref: &[FastSint] = &counts;
10734 run_rayon_with_threads(omp_num_threads, || {
10735 (0..omp_num_threads)
10736 .into_par_iter()
10737 .for_each(|omp_thread_num| {
10738 let omp_block_start = omp_thread_num * omp_block_stride;
10739 let omp_block_size = if omp_thread_num + 1 < omp_num_threads {
10740 omp_block_stride
10741 } else {
10742 m_usize - omp_block_start
10743 };
10744
10745 let mut count: FastSint = f as FastSint;
10746 for tt in 0..omp_thread_num {
10747 count += counts_ref[tt];
10748 }
10749
10750 let sa = unsafe { sa_ptr.as_slice() };
10751 merge_nonunique_lms_suffixes_32s(
10752 sa,
10753 n,
10754 m,
10755 count,
10756 omp_block_start as FastSint,
10757 omp_block_size as FastSint,
10758 );
10759 });
10760 });
10761 }
10762}
10763
10764#[doc(hidden)]
10766pub fn merge_compacted_lms_suffixes_32s_omp(
10767 t: &mut [SaSint],
10768 sa: &mut [SaSint],
10769 n: SaSint,
10770 m: SaSint,
10771 f: SaSint,
10772 threads: SaSint,
10773 thread_state: &mut [ThreadState],
10774) {
10775 merge_unique_lms_suffixes_32s_omp(t, sa, n, m, threads, thread_state);
10776 merge_nonunique_lms_suffixes_32s_omp(sa, n, m, f, threads, thread_state);
10777}
10778
10779#[doc(hidden)]
10781pub fn reconstruct_compacted_lms_suffixes_32s_2k_omp(
10782 t: &mut [SaSint],
10783 sa: &mut [SaSint],
10784 n: SaSint,
10785 k: SaSint,
10786 m: SaSint,
10787 fs: SaSint,
10788 f: SaSint,
10789 buckets: &mut [SaSint],
10790 local_buckets: SaSint,
10791 threads: SaSint,
10792 thread_state: &mut [ThreadState],
10793) {
10794 if f > 0 {
10795 let dst = usize::try_from(n - m - 1).expect("destination must be non-negative");
10796 let src = usize::try_from(n + fs - m).expect("source must be non-negative");
10797 let len = usize::try_from(f).expect("length must be non-negative");
10798 sa.copy_within(src..src + len, dst);
10799
10800 let _ = count_and_gather_compacted_lms_suffixes_32s_2k_omp(
10801 t,
10802 sa,
10803 n,
10804 k,
10805 buckets,
10806 local_buckets,
10807 threads,
10808 thread_state,
10809 );
10810 reconstruct_lms_suffixes_omp(sa, n, m - f, threads);
10811
10812 let src_copy = 0usize;
10813 let dst_copy = usize::try_from(n - m - 1 + f).expect("destination must be non-negative");
10814 let copy_len = usize::try_from(m - f).expect("copy length must be non-negative");
10815 sa.copy_within(src_copy..src_copy + copy_len, dst_copy);
10816 sa[..usize::try_from(m).expect("m must be non-negative")].fill(0);
10817
10818 merge_compacted_lms_suffixes_32s_omp(t, sa, n, m, f, threads, thread_state);
10819 } else {
10820 let _ = count_and_gather_lms_suffixes_32s_2k(t, sa, n, k, buckets, 0, n as FastSint);
10821 reconstruct_lms_suffixes_omp(sa, n, m, threads);
10822 }
10823}
10824
10825#[doc(hidden)]
10827pub fn reconstruct_compacted_lms_suffixes_32s_1k_omp(
10828 t: &mut [SaSint],
10829 sa: &mut [SaSint],
10830 n: SaSint,
10831 m: SaSint,
10832 fs: SaSint,
10833 f: SaSint,
10834 threads: SaSint,
10835 thread_state: &mut [ThreadState],
10836) {
10837 if f > 0 {
10838 let dst = usize::try_from(n - m - 1).expect("destination must be non-negative");
10839 let src = usize::try_from(n + fs - m).expect("source must be non-negative");
10840 let len = usize::try_from(f).expect("length must be non-negative");
10841 sa.copy_within(src..src + len, dst);
10842
10843 let _ = gather_compacted_lms_suffixes_32s(t, sa, n);
10844 reconstruct_lms_suffixes_omp(sa, n, m - f, threads);
10845
10846 let dst_copy = usize::try_from(n - m - 1 + f).expect("destination must be non-negative");
10847 let copy_len = usize::try_from(m - f).expect("copy length must be non-negative");
10848 sa.copy_within(0..copy_len, dst_copy);
10849 sa[..usize::try_from(m).expect("m must be non-negative")].fill(0);
10850
10851 merge_compacted_lms_suffixes_32s_omp(t, sa, n, m, f, threads, thread_state);
10852 } else {
10853 let _ = gather_lms_suffixes_32s(t, sa, n);
10854 reconstruct_lms_suffixes_omp(sa, n, m, threads);
10855 }
10856}
10857
10858fn normalize_omp_threads(threads: SaSint) -> SaSint {
10859 if threads > 0 {
10860 threads
10861 } else {
10862 std::thread::available_parallelism()
10863 .map(|value| value.get() as SaSint)
10864 .unwrap_or(1)
10865 .max(1)
10866 }
10867}
10868
10869fn libsais64_main_32s_recursion(
10870 t_ptr: *mut SaSint,
10871 sa_ptr: *mut SaSint,
10872 sa_capacity: usize,
10873 n: SaSint,
10874 k: SaSint,
10875 fs: SaSint,
10876 threads: SaSint,
10877 thread_state: &mut [ThreadState],
10878 _local_buffer: &mut [SaSint],
10879) -> SaSint {
10880 let fs = fs.min(SAINT_MAX - n);
10881 let local_buffer_size = SaSint::try_from(LIBSAIS_LOCAL_BUFFER_SIZE).expect("fits");
10882 let n_usize = usize::try_from(n).expect("n must be non-negative");
10883 let fs_usize = usize::try_from(fs).expect("fs must be non-negative");
10884 let total_len = n_usize + fs_usize;
10885 assert!(total_len <= sa_capacity);
10886
10887 if k > 0 && n <= i32::MAX as SaSint {
10888 let int32_max = i32::MAX as SaSint;
10889 let expanded_space = fs as i128 + fs as i128 + n as i128 + n as i128;
10890 let new_fs = if expanded_space <= int32_max as i128 {
10891 fs + fs + n
10892 } else {
10893 int32_max - n
10894 };
10895
10896 if (new_fs / k >= 6)
10897 || (new_fs / k >= 4 && n <= int32_max / 2)
10898 || (new_fs / k < 4 && new_fs >= fs)
10899 {
10900 let mut t32 = unsafe { std::slice::from_raw_parts(t_ptr, n_usize) }
10901 .iter()
10902 .map(|&value| (value as u64 as u32) as i32)
10903 .collect::<Vec<_>>();
10904 let mut sa32 = vec![0i32; n_usize + new_fs as usize];
10905
10906 let index = crate::libsais_int_omp(
10907 &mut t32,
10908 &mut sa32,
10909 k as i32,
10910 new_fs as i32,
10911 threads as i32,
10912 );
10913 if index >= 0 {
10914 unsafe {
10915 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
10916 for (dst, src) in t.iter_mut().zip(t32.iter()) {
10917 *dst = (*src as u32) as SaSint;
10918 }
10919
10920 let sa = std::slice::from_raw_parts_mut(sa_ptr, n_usize);
10921 for (dst, src) in sa.iter_mut().zip(sa32.iter()) {
10922 *dst = (*src as u32) as SaSint;
10923 }
10924 }
10925 }
10926
10927 return index as SaSint;
10928 }
10929 }
10930
10931 if k > 0 && ((fs / k) >= 6 || (local_buffer_size / k) >= 6) {
10932 let k_usize = usize::try_from(k).expect("k must be non-negative");
10933 let alignment = if fs >= 1024 && ((fs - 1024) / k) >= 6 {
10934 1024usize
10935 } else {
10936 16usize
10937 };
10938 let need = 6 * k_usize;
10939 let use_local_buffer = local_buffer_size > fs;
10940 let buckets_ptr = if use_local_buffer {
10941 _local_buffer.as_mut_ptr()
10942 } else {
10943 unsafe {
10944 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
10945 let start =
10946 if fs_usize >= need + alignment && ((fs_usize - alignment) / k_usize) >= 6 {
10947 let byte_ptr = sa[total_len - need - alignment..].as_mut_ptr() as usize;
10948 let aligned = align_up(byte_ptr, alignment * mem::size_of::<SaSint>());
10949 (aligned - sa_ptr as usize) / mem::size_of::<SaSint>()
10950 } else {
10951 total_len - need
10952 };
10953 sa[start..].as_mut_ptr()
10954 }
10955 };
10956
10957 let m = unsafe {
10958 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
10959 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
10960 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
10961 count_and_gather_lms_suffixes_32s_4k_omp(
10962 t,
10963 sa,
10964 n,
10965 k,
10966 buckets,
10967 SaSint::from(use_local_buffer),
10968 threads,
10969 thread_state,
10970 )
10971 };
10972 if m > 1 {
10973 let m_usize = usize::try_from(m).expect("m must be non-negative");
10974 unsafe {
10975 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
10976 sa[..n_usize - m_usize].fill(0);
10977 }
10978
10979 let first_lms_suffix = unsafe {
10980 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
10981 sa[n_usize - m_usize]
10982 };
10983 let left_suffixes_count = unsafe {
10984 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
10985 initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(
10986 std::slice::from_raw_parts_mut(t_ptr, n_usize),
10987 k,
10988 buckets,
10989 first_lms_suffix,
10990 )
10991 };
10992
10993 unsafe {
10994 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
10995 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
10996 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
10997 let (_, induction_bucket) = buckets.split_at_mut(4 * k_usize);
10998 radix_sort_lms_suffixes_32s_6k_omp(
10999 t,
11000 sa,
11001 n,
11002 m,
11003 induction_bucket,
11004 threads,
11005 thread_state,
11006 );
11007 if (n / 8192) < k {
11008 radix_sort_set_markers_32s_6k_omp(sa, k, induction_bucket, threads);
11009 }
11010 if threads > 1 && n >= 65_536 {
11011 sa[n_usize - m_usize..n_usize].fill(0);
11012 }
11013 initialize_buckets_for_partial_sorting_32s_6k(
11014 t,
11015 k,
11016 buckets,
11017 first_lms_suffix,
11018 left_suffixes_count,
11019 );
11020 induce_partial_order_32s_6k_omp(
11021 t,
11022 sa,
11023 n,
11024 k,
11025 buckets,
11026 first_lms_suffix,
11027 left_suffixes_count,
11028 threads,
11029 thread_state,
11030 );
11031 }
11032
11033 let names = unsafe {
11034 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11035 if (n / 8192) < k {
11036 renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
11037 sa,
11038 n,
11039 m,
11040 threads,
11041 thread_state,
11042 )
11043 } else {
11044 renumber_and_gather_lms_suffixes_omp(sa, n, m, fs, threads, thread_state)
11045 }
11046 };
11047
11048 if names < m {
11049 let f = if (n / 8192) < k {
11050 unsafe {
11051 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11052 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11053 compact_lms_suffixes_32s_omp(t, sa, n, m, fs, threads, thread_state)
11054 }
11055 } else {
11056 0
11057 };
11058
11059 let new_t_start =
11060 total_len - usize::try_from(m - f).expect("m - f must be non-negative");
11061 if libsais64_main_32s_recursion(
11062 unsafe {
11063 std::slice::from_raw_parts_mut(sa_ptr, total_len)[new_t_start..]
11064 .as_mut_ptr()
11065 },
11066 sa_ptr,
11067 sa_capacity,
11068 m - f,
11069 names - f,
11070 fs + n - 2 * m + f,
11071 threads,
11072 thread_state,
11073 _local_buffer,
11074 ) != 0
11075 {
11076 return -2;
11077 }
11078
11079 unsafe {
11080 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11081 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11082 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11083 reconstruct_compacted_lms_suffixes_32s_2k_omp(
11084 t,
11085 sa,
11086 n,
11087 k,
11088 m,
11089 fs,
11090 f,
11091 buckets,
11092 SaSint::from(use_local_buffer),
11093 threads,
11094 thread_state,
11095 );
11096 }
11097 } else {
11098 unsafe {
11099 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11100 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11101 count_lms_suffixes_32s_2k(t, n, k, buckets);
11102 }
11103 }
11104
11105 unsafe {
11106 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11107 initialize_buckets_start_and_end_32s_4k(k, buckets);
11108 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11109 place_lms_suffixes_histogram_32s_4k(sa, n, k, m, buckets);
11110 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11111 induce_final_order_32s_4k(t, sa, n, k, buckets, threads, thread_state);
11112 }
11113 } else {
11114 unsafe {
11115 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11116 sa[0] = sa[n_usize - 1];
11117 }
11118
11119 unsafe {
11120 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11121 initialize_buckets_start_and_end_32s_6k(k, buckets);
11122 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11123 place_lms_suffixes_histogram_32s_6k(sa, n, k, m, buckets);
11124 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11125 induce_final_order_32s_6k(t, sa, n, k, buckets, threads, thread_state);
11126 }
11127 }
11128
11129 return 0;
11130 } else if k > 0 && n <= SAINT_MAX / 2 && ((fs / k) >= 4 || (local_buffer_size / k) >= 4) {
11131 let k_usize = usize::try_from(k).expect("k must be non-negative");
11132 let alignment = if fs >= 1024 && ((fs - 1024) / k) >= 4 {
11133 1024usize
11134 } else {
11135 16usize
11136 };
11137 let need = 4 * k_usize;
11138 let use_local_buffer = local_buffer_size > fs;
11139 let buckets_ptr = if use_local_buffer {
11140 _local_buffer.as_mut_ptr()
11141 } else {
11142 unsafe {
11143 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11144 let start =
11145 if fs_usize >= need + alignment && ((fs_usize - alignment) / k_usize) >= 4 {
11146 let byte_ptr = sa[total_len - need - alignment..].as_mut_ptr() as usize;
11147 let aligned = align_up(byte_ptr, alignment * mem::size_of::<SaSint>());
11148 (aligned - sa_ptr as usize) / mem::size_of::<SaSint>()
11149 } else {
11150 total_len - need
11151 };
11152 sa[start..].as_mut_ptr()
11153 }
11154 };
11155
11156 let m = unsafe {
11157 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11158 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11159 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11160 count_and_gather_lms_suffixes_32s_2k_omp(
11161 t,
11162 sa,
11163 n,
11164 k,
11165 buckets,
11166 SaSint::from(use_local_buffer),
11167 threads,
11168 thread_state,
11169 )
11170 };
11171 if m > 1 {
11172 let m_usize = usize::try_from(m).expect("m must be non-negative");
11173 unsafe {
11174 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11175 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11176 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11177 initialize_buckets_for_radix_and_partial_sorting_32s_4k(
11178 t,
11179 k,
11180 buckets,
11181 sa[n_usize - m_usize],
11182 );
11183 let (_, induction_bucket) = buckets.split_at_mut(1);
11184 radix_sort_lms_suffixes_32s_2k_omp(
11185 t,
11186 sa,
11187 n,
11188 m,
11189 induction_bucket,
11190 threads,
11191 thread_state,
11192 );
11193 radix_sort_set_markers_32s_4k_omp(sa, k, induction_bucket, threads);
11194 place_lms_suffixes_interval_32s_4k(sa, n, k, m - 1, buckets);
11195 induce_partial_order_32s_4k_omp(t, sa, n, k, buckets, threads, thread_state);
11196 }
11197
11198 let names = unsafe {
11199 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11200 renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(sa, n, m, threads, thread_state)
11201 };
11202 if names < m {
11203 let f = unsafe {
11204 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11205 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11206 compact_lms_suffixes_32s_omp(t, sa, n, m, fs, threads, thread_state)
11207 };
11208
11209 let new_t_start =
11210 total_len - usize::try_from(m - f).expect("m - f must be non-negative");
11211 if libsais64_main_32s_recursion(
11212 unsafe {
11213 std::slice::from_raw_parts_mut(sa_ptr, total_len)[new_t_start..]
11214 .as_mut_ptr()
11215 },
11216 sa_ptr,
11217 sa_capacity,
11218 m - f,
11219 names - f,
11220 fs + n - 2 * m + f,
11221 threads,
11222 thread_state,
11223 _local_buffer,
11224 ) != 0
11225 {
11226 return -2;
11227 }
11228
11229 unsafe {
11230 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11231 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11232 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11233 reconstruct_compacted_lms_suffixes_32s_2k_omp(
11234 t,
11235 sa,
11236 n,
11237 k,
11238 m,
11239 fs,
11240 f,
11241 buckets,
11242 SaSint::from(use_local_buffer),
11243 threads,
11244 thread_state,
11245 );
11246 }
11247 } else {
11248 unsafe {
11249 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11250 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11251 count_lms_suffixes_32s_2k(t, n, k, buckets);
11252 }
11253 }
11254 } else {
11255 unsafe {
11256 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11257 sa[0] = sa[n_usize - 1];
11258 }
11259 }
11260
11261 unsafe {
11262 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11263 initialize_buckets_start_and_end_32s_4k(k, buckets);
11264 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11265 place_lms_suffixes_histogram_32s_4k(sa, n, k, m, buckets);
11266 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11267 induce_final_order_32s_4k(t, sa, n, k, buckets, threads, thread_state);
11268 }
11269
11270 return 0;
11271 } else if k > 0 && ((fs / k) >= 2 || (local_buffer_size / k) >= 2) {
11272 let k_usize = usize::try_from(k).expect("k must be non-negative");
11273 let alignment = if fs >= 1024 && ((fs - 1024) / k) >= 2 {
11274 1024usize
11275 } else {
11276 16usize
11277 };
11278 let need = 2 * k_usize;
11279 let use_local_buffer = local_buffer_size > fs;
11280 let buckets_ptr = if use_local_buffer {
11281 _local_buffer.as_mut_ptr()
11282 } else {
11283 unsafe {
11284 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11285 let start =
11286 if fs_usize >= need + alignment && ((fs_usize - alignment) / k_usize) >= 2 {
11287 let byte_ptr = sa[total_len - need - alignment..].as_mut_ptr() as usize;
11288 let aligned = align_up(byte_ptr, alignment * mem::size_of::<SaSint>());
11289 (aligned - sa_ptr as usize) / mem::size_of::<SaSint>()
11290 } else {
11291 total_len - need
11292 };
11293 sa[start..].as_mut_ptr()
11294 }
11295 };
11296
11297 let m = unsafe {
11298 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11299 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11300 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11301 count_and_gather_lms_suffixes_32s_2k_omp(
11302 t,
11303 sa,
11304 n,
11305 k,
11306 buckets,
11307 SaSint::from(use_local_buffer),
11308 threads,
11309 thread_state,
11310 )
11311 };
11312 if m > 1 {
11313 let m_usize = usize::try_from(m).expect("m must be non-negative");
11314 unsafe {
11315 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11316 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11317 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11318 initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(
11319 t,
11320 k,
11321 buckets,
11322 sa[n_usize - m_usize],
11323 );
11324 let (_, induction_bucket) = buckets.split_at_mut(1);
11325 radix_sort_lms_suffixes_32s_2k_omp(
11326 t,
11327 sa,
11328 n,
11329 m,
11330 induction_bucket,
11331 threads,
11332 thread_state,
11333 );
11334 place_lms_suffixes_interval_32s_2k(sa, n, k, m - 1, buckets);
11335 }
11336
11337 unsafe {
11338 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11339 initialize_buckets_start_and_end_32s_2k(k, buckets);
11340 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11341 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11342 induce_partial_order_32s_2k_omp(t, sa, n, k, buckets, threads, thread_state);
11343 }
11344
11345 let names = unsafe {
11346 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11347 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11348 renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(t, sa, n, m, threads)
11349 };
11350 if names < m {
11351 let f = unsafe {
11352 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11353 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11354 compact_lms_suffixes_32s_omp(t, sa, n, m, fs, threads, thread_state)
11355 };
11356
11357 let new_t_start =
11358 total_len - usize::try_from(m - f).expect("m - f must be non-negative");
11359 if libsais64_main_32s_recursion(
11360 unsafe {
11361 std::slice::from_raw_parts_mut(sa_ptr, total_len)[new_t_start..]
11362 .as_mut_ptr()
11363 },
11364 sa_ptr,
11365 sa_capacity,
11366 m - f,
11367 names - f,
11368 fs + n - 2 * m + f,
11369 threads,
11370 thread_state,
11371 _local_buffer,
11372 ) != 0
11373 {
11374 return -2;
11375 }
11376
11377 unsafe {
11378 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11379 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11380 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11381 reconstruct_compacted_lms_suffixes_32s_2k_omp(
11382 t,
11383 sa,
11384 n,
11385 k,
11386 m,
11387 fs,
11388 f,
11389 buckets,
11390 SaSint::from(use_local_buffer),
11391 threads,
11392 thread_state,
11393 );
11394 }
11395 } else {
11396 unsafe {
11397 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11398 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11399 count_lms_suffixes_32s_2k(t, n, k, buckets);
11400 }
11401 }
11402 } else {
11403 unsafe {
11404 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11405 sa[0] = sa[n_usize - 1];
11406 }
11407 }
11408
11409 unsafe {
11410 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11411 initialize_buckets_end_32s_2k(k, buckets);
11412 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11413 place_lms_suffixes_histogram_32s_2k(sa, n, k, m, buckets);
11414 }
11415
11416 unsafe {
11417 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, need);
11418 initialize_buckets_start_and_end_32s_2k(k, buckets);
11419 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11420 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11421 induce_final_order_32s_2k(t, sa, n, k, buckets, threads, thread_state);
11422 }
11423
11424 return 0;
11425 } else {
11426 let k_usize = usize::try_from(k).expect("k must be non-negative");
11427 let mut heap_buckets = if fs < k { Some(vec![0; k_usize]) } else { None };
11428 let alignment = if fs >= 1024 && (fs - 1024) >= k {
11429 1024usize
11430 } else {
11431 16usize
11432 };
11433 let mut buckets_ptr = if let Some(ref mut heap) = heap_buckets {
11434 heap.as_mut_ptr()
11435 } else {
11436 unsafe {
11437 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11438 let start = if fs_usize >= k_usize + alignment {
11439 let byte_ptr = sa[total_len - k_usize - alignment..].as_mut_ptr() as usize;
11440 let aligned = align_up(byte_ptr, alignment * mem::size_of::<SaSint>());
11441 (aligned - sa_ptr as usize) / mem::size_of::<SaSint>()
11442 } else {
11443 total_len - k_usize
11444 };
11445 sa[start..].as_mut_ptr()
11446 }
11447 };
11448
11449 if buckets_ptr.is_null() {
11450 return -2;
11451 }
11452
11453 unsafe {
11454 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11455 sa[..n_usize].fill(0);
11456 }
11457
11458 unsafe {
11459 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11460 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
11461 count_suffixes_32s(t, n, k, buckets);
11462 }
11463 unsafe {
11464 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
11465 initialize_buckets_end_32s_1k(k, buckets);
11466 }
11467
11468 let m = unsafe {
11469 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11470 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11471 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
11472 radix_sort_lms_suffixes_32s_1k(t, sa, n, buckets)
11473 };
11474 if m > 1 {
11475 unsafe {
11476 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11477 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11478 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
11479 induce_partial_order_32s_1k_omp(t, sa, n, k, buckets, threads, thread_state);
11480 }
11481
11482 let names = unsafe {
11483 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11484 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11485 renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(t, sa, n, m, threads)
11486 };
11487 if names < m {
11488 if heap_buckets.is_some() {
11489 let _ = heap_buckets.take();
11490 buckets_ptr = std::ptr::null_mut();
11491 }
11492
11493 let f = unsafe {
11494 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11495 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11496 compact_lms_suffixes_32s_omp(t, sa, n, m, fs, threads, thread_state)
11497 };
11498
11499 let new_t_start =
11500 total_len - usize::try_from(m - f).expect("m - f must be non-negative");
11501 if libsais64_main_32s_recursion(
11502 unsafe {
11503 std::slice::from_raw_parts_mut(sa_ptr, total_len)[new_t_start..]
11504 .as_mut_ptr()
11505 },
11506 sa_ptr,
11507 sa_capacity,
11508 m - f,
11509 names - f,
11510 fs + n - 2 * m + f,
11511 threads,
11512 thread_state,
11513 _local_buffer,
11514 ) != 0
11515 {
11516 return -2;
11517 }
11518
11519 unsafe {
11520 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11521 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11522 reconstruct_compacted_lms_suffixes_32s_1k_omp(
11523 t,
11524 sa,
11525 n,
11526 m,
11527 fs,
11528 f,
11529 threads,
11530 thread_state,
11531 );
11532 }
11533
11534 if buckets_ptr.is_null() {
11535 heap_buckets = Some(vec![0; k_usize]);
11536 buckets_ptr = heap_buckets
11537 .as_mut()
11538 .expect("heap buckets must exist")
11539 .as_mut_ptr();
11540 if buckets_ptr.is_null() {
11541 return -2;
11542 }
11543 }
11544 }
11545
11546 unsafe {
11547 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11548 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
11549 count_suffixes_32s(t, n, k, buckets);
11550 }
11551 unsafe {
11552 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
11553 initialize_buckets_end_32s_1k(k, buckets);
11554 }
11555 unsafe {
11556 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11557 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11558 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
11559 place_lms_suffixes_interval_32s_1k(t, sa, k, m, buckets);
11560 }
11561 }
11562
11563 unsafe {
11564 let t = std::slice::from_raw_parts_mut(t_ptr, n_usize);
11565 let sa = std::slice::from_raw_parts_mut(sa_ptr, total_len);
11566 let buckets = std::slice::from_raw_parts_mut(buckets_ptr, k_usize);
11567 induce_final_order_32s_1k(t, sa, n, k, buckets, threads, thread_state);
11568 }
11569
11570 0
11571 }
11572}
11573
11574fn libsais64_main_32s_entry(
11575 t: &mut [SaSint],
11576 sa: &mut [SaSint],
11577 n: SaSint,
11578 k: SaSint,
11579 fs: SaSint,
11580 threads: SaSint,
11581 thread_state: &mut [ThreadState],
11582) -> SaSint {
11583 let mut local_buffer = [0; 2 * LIBSAIS_LOCAL_BUFFER_SIZE];
11584 libsais64_main_32s_recursion(
11585 t.as_mut_ptr(),
11586 sa.as_mut_ptr(),
11587 sa.len(),
11588 n,
11589 k,
11590 fs,
11591 threads,
11592 thread_state,
11593 &mut local_buffer[LIBSAIS_LOCAL_BUFFER_SIZE..],
11594 )
11595}
11596
11597fn libsais64_main_8u(
11598 t: &[u8],
11599 sa: &mut [SaSint],
11600 buckets: &mut [SaSint],
11601 flags: SaSint,
11602 r: SaSint,
11603 i: Option<&mut [SaSint]>,
11604 fs: SaSint,
11605 freq: Option<&mut [SaSint]>,
11606 threads: SaSint,
11607 thread_state: &mut [ThreadState],
11608) -> SaSint {
11609 let n = SaSint::try_from(t.len()).expect("input length must fit SaSint");
11610 let n_usize = usize::try_from(n).expect("n must be non-negative");
11611 let fs = fs.min(SAINT_MAX - n);
11612
11613 let m = count_and_gather_lms_suffixes_8u_omp(t, sa, n, buckets, threads, thread_state);
11614 let k = initialize_buckets_start_and_end_8u(buckets, freq);
11615
11616 if (flags & LIBSAIS_FLAGS_GSA) != 0 && (buckets[0] != 0 || buckets[2] != 0 || buckets[3] != 1) {
11617 return -1;
11618 }
11619
11620 if m > 0 {
11621 let m_usize = usize::try_from(m).expect("m must be non-negative");
11622 let first_lms_suffix = sa[n_usize - m_usize];
11623 let left_suffixes_count =
11624 initialize_buckets_for_lms_suffixes_radix_sort_8u(t, buckets, first_lms_suffix);
11625
11626 if threads > 1 && n >= 65_536 {
11627 sa[..n_usize - m_usize].fill(0);
11628 }
11629 radix_sort_lms_suffixes_8u_omp(t, sa, n, m, flags, buckets, threads, thread_state);
11630 if threads > 1 && n >= 65_536 {
11631 sa[n_usize - m_usize..n_usize].fill(0);
11632 }
11633
11634 initialize_buckets_for_partial_sorting_8u(
11635 t,
11636 buckets,
11637 first_lms_suffix,
11638 left_suffixes_count,
11639 );
11640 induce_partial_order_8u_omp(
11641 t,
11642 sa,
11643 n,
11644 k,
11645 flags,
11646 buckets,
11647 first_lms_suffix,
11648 left_suffixes_count,
11649 threads,
11650 thread_state,
11651 );
11652
11653 let names = renumber_and_gather_lms_suffixes_omp(sa, n, m, fs, threads, thread_state);
11654 if names < m {
11655 if libsais64_main_32s_entry(
11656 unsafe {
11657 std::slice::from_raw_parts_mut(
11658 sa[n_usize + usize::try_from(fs).expect("fs must be non-negative")
11659 - m_usize..]
11660 .as_mut_ptr(),
11661 m_usize,
11662 )
11663 },
11664 sa,
11665 m,
11666 names,
11667 fs + n - 2 * m,
11668 threads,
11669 thread_state,
11670 ) != 0
11671 {
11672 return -2;
11673 }
11674
11675 gather_lms_suffixes_8u_omp(t, sa, n, threads, thread_state);
11676 reconstruct_lms_suffixes_omp(sa, n, m, threads);
11677 }
11678
11679 place_lms_suffixes_interval_8u(sa, n, m, flags, buckets);
11680 } else {
11681 sa[..n_usize].fill(0);
11682 }
11683
11684 induce_final_order_8u_omp(t, sa, n, k, flags, r, i, buckets, threads, thread_state)
11685}
11686
11687fn libsais64_main(
11688 t: &[u8],
11689 sa: &mut [SaSint],
11690 flags: SaSint,
11691 r: SaSint,
11692 i: Option<&mut [SaSint]>,
11693 fs: SaSint,
11694 freq: Option<&mut [SaSint]>,
11695 threads: SaSint,
11696) -> SaSint {
11697 let threads = normalize_omp_threads(threads);
11698 if threads > 1 {
11699 let mut thread_state = match alloc_thread_state(threads) {
11700 Some(thread_state) => thread_state,
11701 None => return -2,
11702 };
11703 let mut buckets = vec![0; 8 * ALPHABET_SIZE];
11704
11705 libsais64_main_8u(
11706 t,
11707 sa,
11708 &mut buckets,
11709 flags,
11710 r,
11711 i,
11712 fs,
11713 freq,
11714 threads,
11715 &mut thread_state,
11716 )
11717 } else {
11718 let mut thread_state = [];
11719 let mut buckets = [0; 8 * ALPHABET_SIZE];
11720
11721 libsais64_main_8u(
11722 t,
11723 sa,
11724 &mut buckets,
11725 flags,
11726 r,
11727 i,
11728 fs,
11729 freq,
11730 threads,
11731 &mut thread_state,
11732 )
11733 }
11734}
11735
11736fn libsais64_main_int(
11737 t: &mut [SaSint],
11738 sa: &mut [SaSint],
11739 k: SaSint,
11740 fs: SaSint,
11741 threads: SaSint,
11742) -> SaSint {
11743 let threads = normalize_omp_threads(threads);
11744 let mut thread_state = if threads > 1 {
11745 match alloc_thread_state(threads) {
11746 Some(thread_state) => thread_state,
11747 None => return -2,
11748 }
11749 } else {
11750 Vec::new()
11751 };
11752
11753 libsais64_main_32s_entry(
11754 t,
11755 sa,
11756 SaSint::try_from(t.len()).expect("input length must fit SaSint"),
11757 k,
11758 fs,
11759 threads,
11760 &mut thread_state,
11761 )
11762}
11763
11764#[allow(dead_code)]
11765fn convert_32u_to_64u(s: &[u32], d: &mut [u64], block_start: usize, block_size: usize) {
11766 for i in block_start..block_start + block_size {
11767 d[i] = s[i] as u64;
11768 }
11769}
11770
11771#[allow(dead_code)]
11772fn convert_inplace_32u_to_64u(v: &mut [u32], block_start: usize, block_size: usize) {
11773 for i in (block_start..block_start + block_size).rev() {
11774 let value = v[i];
11775 v[2 * i] = value;
11776 v[2 * i + 1] = 0;
11777 }
11778}
11779
11780#[allow(dead_code)]
11781fn convert_inplace_64u_to_32u(v: &mut [u32], block_start: usize, block_size: usize) {
11782 for i in block_start..block_start + block_size {
11783 v[i] = v[2 * i];
11784 }
11785}
11786
11787#[allow(dead_code)]
11788fn convert_inplace_32u_to_64u_omp(v: &mut [u32], n: SaSint, threads: SaSint) {
11789 let mut n = usize::try_from(n).expect("n must be non-negative");
11790 let threads = usize::try_from(threads.max(1)).expect("threads must be non-negative");
11791
11792 while n >= 65_536 {
11793 let block_size = n >> 1;
11794 n -= block_size;
11795
11796 let omp_block_stride = (block_size / threads) & !15usize;
11797 for thread in 0..threads {
11798 let block_start = thread * omp_block_stride;
11799 let size = if thread + 1 < threads {
11800 omp_block_stride
11801 } else {
11802 block_size - block_start
11803 };
11804 convert_inplace_32u_to_64u(v, n + block_start, size);
11805 }
11806 }
11807
11808 convert_inplace_32u_to_64u(v, 0, n);
11809}
11810
11811fn libsais64_main_ctx(
11812 ctx: &mut Context,
11813 t: &[u8],
11814 sa: &mut [SaSint],
11815 flags: SaSint,
11816 r: SaSint,
11817 i: Option<&mut [SaSint]>,
11818 fs: SaSint,
11819 freq: Option<&mut [SaSint]>,
11820) -> SaSint {
11821 if ctx.threads <= 0 || ctx.buckets.len() != 8 * ALPHABET_SIZE {
11822 return -2;
11823 }
11824
11825 let mut empty_thread_state = [];
11826 let thread_state = if ctx.threads > 1 {
11827 match ctx.thread_state.as_deref_mut() {
11828 Some(thread_state) if thread_state.len() >= ctx.threads as usize => thread_state,
11829 None => return -2,
11830 Some(_) => return -2,
11831 }
11832 } else {
11833 &mut empty_thread_state
11834 };
11835
11836 libsais64_main_8u(
11837 t,
11838 sa,
11839 &mut ctx.buckets,
11840 flags,
11841 r,
11842 i,
11843 fs,
11844 freq,
11845 ctx.threads as SaSint,
11846 thread_state,
11847 )
11848}
11849
11850pub fn libsais64(t: &[u8], sa: &mut [SaSint], fs: SaSint, freq: Option<&mut [SaSint]>) -> SaSint {
11861 if fs < 0
11862 || sa.len()
11863 < t.len()
11864 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
11865 {
11866 return -1;
11867 }
11868 if let Some(freq) = freq.as_ref() {
11869 if freq.len() < ALPHABET_SIZE {
11870 return -1;
11871 }
11872 }
11873
11874 let n = t.len();
11875 if n <= 1 {
11876 if let Some(freq) = freq {
11877 freq[..ALPHABET_SIZE].fill(0);
11878 if n == 1 {
11879 freq[t[0] as usize] += 1;
11880 }
11881 }
11882 if n == 1 {
11883 sa[0] = 0;
11884 }
11885 return 0;
11886 }
11887
11888 if n <= i32::MAX as usize {
11889 return libsais64_run_32bit_omp(t, sa, fs, freq, 1, false)
11890 .expect("n <= INT32_MAX must have 32-bit workspace");
11891 }
11892
11893 libsais64_main(t, sa, LIBSAIS_FLAGS_NONE, 0, None, fs, freq, 1)
11894}
11895
11896#[cfg(feature = "upstream-c")]
11897unsafe extern "C" {
11898 fn probe_public_libsais64_omp_freq(
11899 t: *const u8,
11900 sa: *mut SaSint,
11901 n: SaSint,
11902 fs: SaSint,
11903 freq: *mut SaSint,
11904 threads: SaSint,
11905 ) -> SaSint;
11906}
11907
11908#[cfg(feature = "upstream-c")]
11920pub fn libsais64_upstream_c_omp(
11921 t: &[u8],
11922 sa: &mut [SaSint],
11923 fs: SaSint,
11924 freq: Option<&mut [SaSint]>,
11925 threads: SaSint,
11926) -> SaSint {
11927 if threads < 0 {
11928 return -1;
11929 }
11930 if fs < 0
11931 || t.len() > SaSint::MAX as usize
11932 || sa.len()
11933 < t.len()
11934 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
11935 {
11936 return -1;
11937 }
11938 if let Some(freq) = freq.as_ref() {
11939 if freq.len() < ALPHABET_SIZE {
11940 return -1;
11941 }
11942 }
11943
11944 let n = t.len() as SaSint;
11945 let freq_ptr = freq.map_or(std::ptr::null_mut(), |freq| freq.as_mut_ptr());
11946 unsafe {
11947 probe_public_libsais64_omp_freq(
11948 t.as_ptr(),
11949 sa.as_mut_ptr(),
11950 n,
11951 fs,
11952 freq_ptr,
11953 threads.max(1),
11954 )
11955 }
11956}
11957
11958#[cfg(feature = "upstream-c")]
11962pub fn libsais64_upstream_c_omp_uninit(
11963 t: &[u8],
11964 sa: &mut [MaybeUninit<SaSint>],
11965 fs: SaSint,
11966 freq: Option<&mut [SaSint]>,
11967 threads: SaSint,
11968) -> SaSint {
11969 if threads < 0 {
11970 return -1;
11971 }
11972 if fs < 0
11973 || t.len() > SaSint::MAX as usize
11974 || sa.len()
11975 < t.len()
11976 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
11977 {
11978 return -1;
11979 }
11980 if let Some(freq) = freq.as_ref() {
11981 if freq.len() < ALPHABET_SIZE {
11982 return -1;
11983 }
11984 }
11985
11986 let n = t.len() as SaSint;
11987 let freq_ptr = freq.map_or(std::ptr::null_mut(), |freq| freq.as_mut_ptr());
11988 unsafe {
11989 probe_public_libsais64_omp_freq(
11990 t.as_ptr(),
11991 sa.as_mut_ptr().cast::<SaSint>(),
11992 n,
11993 fs,
11994 freq_ptr,
11995 threads.max(1),
11996 )
11997 }
11998}
11999
12000pub fn libsais64_gsa(
12011 t: &[u8],
12012 sa: &mut [SaSint],
12013 fs: SaSint,
12014 freq: Option<&mut [SaSint]>,
12015) -> SaSint {
12016 if fs < 0
12017 || sa.len()
12018 < t.len()
12019 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12020 {
12021 return -1;
12022 }
12023 if let Some(freq) = freq.as_ref() {
12024 if freq.len() < ALPHABET_SIZE {
12025 return -1;
12026 }
12027 }
12028
12029 let n = t.len();
12030 if n > 0 && t[n - 1] != 0 {
12031 return -1;
12032 }
12033
12034 if n <= 1 {
12035 if let Some(freq) = freq {
12036 freq[..ALPHABET_SIZE].fill(0);
12037 if n == 1 {
12038 freq[t[0] as usize] += 1;
12039 }
12040 }
12041 if n == 1 {
12042 sa[0] = 0;
12043 }
12044 return 0;
12045 }
12046
12047 if n <= i32::MAX as usize {
12048 return libsais64_run_32bit_omp(t, sa, fs, freq, 1, true)
12049 .expect("n <= INT32_MAX must have 32-bit workspace");
12050 }
12051
12052 libsais64_main(t, sa, LIBSAIS_FLAGS_GSA, 0, None, fs, freq, 1)
12053}
12054
12055pub fn libsais64_int(t: &mut [SaSint], sa: &mut [SaSint], k: SaSint, fs: SaSint) -> SaSint {
12057 if fs < 0
12058 || sa.len()
12059 < t.len()
12060 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12061 {
12062 return -1;
12063 }
12064
12065 if t.len() <= 1 {
12066 if t.len() == 1 {
12067 sa[0] = 0;
12068 }
12069 return 0;
12070 }
12071
12072 libsais64_main_int(t, sa, k, fs, 1)
12073}
12074
12075pub fn libsais64_long(t: &mut [SaSint], sa: &mut [SaSint], k: SaSint, fs: SaSint) -> SaSint {
12088 libsais64_int(t, sa, k, fs)
12089}
12090
12091pub fn libsais64_ctx(
12101 ctx: &mut Context,
12102 t: &[u8],
12103 sa: &mut [SaSint],
12104 fs: SaSint,
12105 freq: Option<&mut [SaSint]>,
12106) -> SaSint {
12107 if fs < 0
12108 || sa.len()
12109 < t.len()
12110 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12111 {
12112 return -1;
12113 }
12114 if let Some(freq) = freq.as_ref() {
12115 if freq.len() < ALPHABET_SIZE {
12116 return -1;
12117 }
12118 }
12119
12120 let n = t.len();
12121 if n <= 1 {
12122 if let Some(freq) = freq {
12123 freq[..ALPHABET_SIZE].fill(0);
12124 if n == 1 {
12125 freq[t[0] as usize] += 1;
12126 }
12127 }
12128 if n == 1 {
12129 sa[0] = 0;
12130 }
12131 return 0;
12132 }
12133
12134 libsais64_main_ctx(ctx, t, sa, LIBSAIS_FLAGS_NONE, 0, None, fs, freq)
12135}
12136
12137pub fn libsais64_gsa_ctx(
12147 ctx: &mut Context,
12148 t: &[u8],
12149 sa: &mut [SaSint],
12150 fs: SaSint,
12151 freq: Option<&mut [SaSint]>,
12152) -> SaSint {
12153 if fs < 0
12154 || sa.len()
12155 < t.len()
12156 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12157 {
12158 return -1;
12159 }
12160 if let Some(freq) = freq.as_ref() {
12161 if freq.len() < ALPHABET_SIZE {
12162 return -1;
12163 }
12164 }
12165
12166 let n = t.len();
12167 if n > 0 && t[n - 1] != 0 {
12168 return -1;
12169 }
12170
12171 if n <= 1 {
12172 if let Some(freq) = freq {
12173 freq[..ALPHABET_SIZE].fill(0);
12174 if n == 1 {
12175 freq[t[0] as usize] += 1;
12176 }
12177 }
12178 if n == 1 {
12179 sa[0] = 0;
12180 }
12181 return 0;
12182 }
12183
12184 libsais64_main_ctx(ctx, t, sa, LIBSAIS_FLAGS_GSA, 0, None, fs, freq)
12185}
12186
12187pub fn libsais64_bwt(
12199 t: &[u8],
12200 u: &mut [u8],
12201 a: &mut [SaSint],
12202 fs: SaSint,
12203 freq: Option<&mut [SaSint]>,
12204) -> SaSint {
12205 if fs < 0
12206 || u.len() < t.len()
12207 || a.len()
12208 < t.len()
12209 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12210 {
12211 return -1;
12212 }
12213 if let Some(freq) = freq.as_ref() {
12214 if freq.len() < ALPHABET_SIZE {
12215 return -1;
12216 }
12217 }
12218
12219 let n = t.len();
12220 if n <= 1 {
12221 if let Some(freq) = freq {
12222 freq[..ALPHABET_SIZE].fill(0);
12223 if n == 1 {
12224 u[0] = t[0];
12225 freq[t[0] as usize] += 1;
12226 }
12227 } else if n == 1 {
12228 u[0] = t[0];
12229 }
12230 return n as SaSint;
12231 }
12232
12233 if n <= i32::MAX as usize {
12234 return libsais64_bwt_run_32bit_omp(t, u, fs, freq, 1)
12235 .expect("n <= INT32_MAX must have 32-bit workspace");
12236 }
12237
12238 let mut index = libsais64_main(t, a, LIBSAIS_FLAGS_BWT, 0, None, fs, freq, 1);
12239 if index >= 0 {
12240 index += 1;
12241 let split = usize::try_from(index).expect("index must be non-negative");
12242 u[0] = t[n - 1];
12243 bwt_copy_8u_omp(&mut u[1..split], &a[..split - 1], index - 1, 1);
12244 bwt_copy_8u_omp(
12245 &mut u[split..n],
12246 &a[split..n],
12247 SaSint::try_from(n - split).expect("fits"),
12248 1,
12249 );
12250 }
12251 index
12252}
12253
12254pub fn libsais64_bwt_aux(
12268 t: &[u8],
12269 u: &mut [u8],
12270 a: &mut [SaSint],
12271 fs: SaSint,
12272 freq: Option<&mut [SaSint]>,
12273 r: SaSint,
12274 i: &mut [SaSint],
12275) -> SaSint {
12276 let n = t.len();
12277 if fs < 0
12278 || r < 2
12279 || (r & (r - 1)) != 0
12280 || u.len() < n
12281 || a.len() < n.saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12282 || freq.as_ref().is_some_and(|freq| freq.len() < ALPHABET_SIZE)
12283 {
12284 return -1;
12285 }
12286 let sample_count = if n == 0 {
12287 1
12288 } else {
12289 usize::try_from((SaSint::try_from(n).expect("input length must fit SaSint") - 1) / r)
12290 .expect("sample count must be non-negative")
12291 + 1
12292 };
12293 if i.len() < sample_count {
12294 return -1;
12295 }
12296
12297 if n <= 1 {
12298 if let Some(freq) = freq {
12299 freq[..ALPHABET_SIZE].fill(0);
12300 if n == 1 {
12301 u[0] = t[0];
12302 freq[t[0] as usize] += 1;
12303 }
12304 } else if n == 1 {
12305 u[0] = t[0];
12306 }
12307 i[0] = n as SaSint;
12308 return 0;
12309 }
12310
12311 if n <= i32::MAX as usize && r <= i32::MAX as SaSint {
12312 return libsais64_bwt_aux_run_32bit_omp(t, u, fs, freq, r, i, 1)
12313 .expect("n/r <= INT32_MAX must have 32-bit workspace");
12314 }
12315
12316 let index = libsais64_main(t, a, LIBSAIS_FLAGS_BWT, r, Some(i), fs, freq, 1);
12317 if index == 0 {
12318 let split = usize::try_from(i[0]).expect("primary index must be non-negative");
12319 u[0] = t[n - 1];
12320 bwt_copy_8u_omp(&mut u[1..split], &a[..split - 1], i[0] - 1, 1);
12321 bwt_copy_8u_omp(
12322 &mut u[split..n],
12323 &a[split..n],
12324 SaSint::try_from(n - split).expect("fits"),
12325 1,
12326 );
12327 }
12328 index
12329}
12330
12331pub fn libsais64_bwt_ctx(
12342 ctx: &mut Context,
12343 t: &[u8],
12344 u: &mut [u8],
12345 a: &mut [SaSint],
12346 fs: SaSint,
12347 freq: Option<&mut [SaSint]>,
12348) -> SaSint {
12349 if fs < 0
12350 || u.len() < t.len()
12351 || a.len()
12352 < t.len()
12353 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12354 {
12355 return -1;
12356 }
12357 if let Some(freq) = freq.as_ref() {
12358 if freq.len() < ALPHABET_SIZE {
12359 return -1;
12360 }
12361 }
12362
12363 let n = t.len();
12364 if n <= 1 {
12365 if let Some(freq) = freq {
12366 freq[..ALPHABET_SIZE].fill(0);
12367 if n == 1 {
12368 u[0] = t[0];
12369 freq[t[0] as usize] += 1;
12370 }
12371 } else if n == 1 {
12372 u[0] = t[0];
12373 }
12374 return n as SaSint;
12375 }
12376
12377 let mut index = libsais64_main_ctx(ctx, t, a, LIBSAIS_FLAGS_BWT, 0, None, fs, freq);
12378 if index >= 0 {
12379 index += 1;
12380 let split = usize::try_from(index).expect("index must be non-negative");
12381 u[0] = t[n - 1];
12382 bwt_copy_8u_omp(
12383 &mut u[1..split],
12384 &a[..split - 1],
12385 index - 1,
12386 ctx.threads as SaSint,
12387 );
12388 bwt_copy_8u_omp(
12389 &mut u[split..n],
12390 &a[split..n],
12391 SaSint::try_from(n - split).expect("fits"),
12392 ctx.threads as SaSint,
12393 );
12394 }
12395 index
12396}
12397
12398pub fn libsais64_bwt_aux_ctx(
12411 ctx: &mut Context,
12412 t: &[u8],
12413 u: &mut [u8],
12414 a: &mut [SaSint],
12415 fs: SaSint,
12416 freq: Option<&mut [SaSint]>,
12417 r: SaSint,
12418 i: &mut [SaSint],
12419) -> SaSint {
12420 let n = t.len();
12421 if fs < 0
12422 || r < 2
12423 || (r & (r - 1)) != 0
12424 || u.len() < n
12425 || a.len() < n.saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12426 {
12427 return -1;
12428 }
12429 if let Some(freq) = freq.as_ref() {
12430 if freq.len() < ALPHABET_SIZE {
12431 return -1;
12432 }
12433 }
12434 let sample_count = if n == 0 {
12435 1
12436 } else {
12437 usize::try_from((SaSint::try_from(n).expect("input length must fit SaSint") - 1) / r)
12438 .expect("sample count must be non-negative")
12439 + 1
12440 };
12441 if i.len() < sample_count {
12442 return -1;
12443 }
12444
12445 if n <= 1 {
12446 if let Some(freq) = freq {
12447 freq[..ALPHABET_SIZE].fill(0);
12448 if n == 1 {
12449 u[0] = t[0];
12450 freq[t[0] as usize] += 1;
12451 }
12452 } else if n == 1 {
12453 u[0] = t[0];
12454 }
12455 i[0] = n as SaSint;
12456 return 0;
12457 }
12458
12459 let index = libsais64_main_ctx(ctx, t, a, LIBSAIS_FLAGS_BWT, r, Some(i), fs, freq);
12460 if index == 0 {
12461 let split = usize::try_from(i[0]).expect("primary index must be non-negative");
12462 u[0] = t[n - 1];
12463 bwt_copy_8u_omp(
12464 &mut u[1..split],
12465 &a[..split - 1],
12466 i[0] - 1,
12467 ctx.threads as SaSint,
12468 );
12469 bwt_copy_8u_omp(
12470 &mut u[split..n],
12471 &a[split..n],
12472 SaSint::try_from(n - split).expect("fits"),
12473 ctx.threads as SaSint,
12474 );
12475 }
12476 index
12477}
12478
12479pub fn create_ctx_omp(threads: SaSint) -> Option<Context> {
12487 if threads < 0 {
12488 return None;
12489 }
12490
12491 create_ctx_main(normalize_omp_threads(threads))
12492}
12493
12494fn libsais64_new_32bit_fs(n: usize, fs: SaSint) -> Option<i32> {
12495 if n > i32::MAX as usize {
12496 return None;
12497 }
12498
12499 let n = n as SaSint;
12500 let int32_max = i32::MAX as SaSint;
12501 let expanded_space = fs as i128 + fs as i128 + n as i128 + n as i128;
12502 let new_fs = if expanded_space <= int32_max as i128 {
12503 fs + fs + n
12504 } else {
12505 int32_max - n
12506 };
12507
12508 i32::try_from(new_fs).ok()
12509}
12510
12511fn libsais64_run_32bit_omp(
12512 t: &[u8],
12513 sa: &mut [SaSint],
12514 fs: SaSint,
12515 freq: Option<&mut [SaSint]>,
12516 threads: SaSint,
12517 gsa: bool,
12518) -> Option<SaSint> {
12519 let new_fs = libsais64_new_32bit_fs(t.len(), fs)?;
12520 let mut sa32 = vec![0i32; t.len() + usize::try_from(new_fs).expect("new_fs is non-negative")];
12521
12522 let index = if let Some(freq) = freq {
12523 let mut freq32 = vec![0i32; ALPHABET_SIZE];
12524 let index = if gsa {
12525 crate::libsais_gsa_omp(t, &mut sa32, new_fs, Some(&mut freq32), threads as i32)
12526 } else {
12527 crate::libsais_omp(t, &mut sa32, new_fs, Some(&mut freq32), threads as i32)
12528 };
12529 if index >= 0 {
12530 for (dst, src) in freq.iter_mut().zip(freq32.iter()) {
12531 *dst = SaSint::from(*src);
12532 }
12533 }
12534 index
12535 } else if gsa {
12536 crate::libsais_gsa_omp(t, &mut sa32, new_fs, None, threads as i32)
12537 } else {
12538 crate::libsais_omp(t, &mut sa32, new_fs, None, threads as i32)
12539 };
12540
12541 if index >= 0 {
12542 for (dst, src) in sa.iter_mut().zip(sa32.iter()).take(t.len()) {
12543 *dst = SaSint::from(*src as u32);
12544 }
12545 }
12546
12547 Some(SaSint::from(index))
12548}
12549
12550fn copy_freq32_to_64(freq: &mut [SaSint], freq32: &[i32]) {
12551 for (dst, src) in freq.iter_mut().zip(freq32.iter()).take(ALPHABET_SIZE) {
12552 *dst = SaSint::from(*src);
12553 }
12554}
12555
12556fn libsais64_bwt_run_32bit_omp(
12557 t: &[u8],
12558 u: &mut [u8],
12559 fs: SaSint,
12560 freq: Option<&mut [SaSint]>,
12561 threads: SaSint,
12562) -> Option<SaSint> {
12563 let new_fs = libsais64_new_32bit_fs(t.len(), fs)?;
12564 let mut a32 = vec![0i32; t.len() + usize::try_from(new_fs).expect("new_fs is non-negative")];
12565
12566 let index = if let Some(freq) = freq {
12567 let mut freq32 = vec![0i32; ALPHABET_SIZE];
12568 let index =
12569 crate::libsais_bwt_omp(t, u, &mut a32, new_fs, Some(&mut freq32), threads as i32);
12570 if index >= 0 {
12571 copy_freq32_to_64(freq, &freq32);
12572 }
12573 index
12574 } else {
12575 crate::libsais_bwt_omp(t, u, &mut a32, new_fs, None, threads as i32)
12576 };
12577
12578 Some(SaSint::from(index))
12579}
12580
12581fn libsais64_bwt_aux_run_32bit_omp(
12582 t: &[u8],
12583 u: &mut [u8],
12584 fs: SaSint,
12585 freq: Option<&mut [SaSint]>,
12586 r: SaSint,
12587 i: &mut [SaSint],
12588 threads: SaSint,
12589) -> Option<SaSint> {
12590 if r > i32::MAX as SaSint {
12591 return None;
12592 }
12593
12594 let new_fs = libsais64_new_32bit_fs(t.len(), fs)?;
12595 let mut a32 = vec![0i32; t.len() + usize::try_from(new_fs).expect("new_fs is non-negative")];
12596 let sample_count = if t.is_empty() {
12597 1
12598 } else {
12599 (t.len() - 1) / usize::try_from(r).expect("r must be positive") + 1
12600 };
12601 let mut i32_out = vec![0i32; sample_count];
12602
12603 let index = if let Some(freq) = freq {
12604 let mut freq32 = vec![0i32; ALPHABET_SIZE];
12605 let index = crate::libsais_bwt_aux_omp(
12606 t,
12607 u,
12608 &mut a32,
12609 new_fs,
12610 Some(&mut freq32),
12611 r as i32,
12612 &mut i32_out,
12613 threads as i32,
12614 );
12615 if index >= 0 {
12616 copy_freq32_to_64(freq, &freq32);
12617 }
12618 index
12619 } else {
12620 crate::libsais_bwt_aux_omp(
12621 t,
12622 u,
12623 &mut a32,
12624 new_fs,
12625 None,
12626 r as i32,
12627 &mut i32_out,
12628 threads as i32,
12629 )
12630 };
12631
12632 if index >= 0 {
12633 for (dst, src) in i.iter_mut().zip(i32_out.iter()).take(sample_count) {
12634 *dst = SaSint::from(*src);
12635 }
12636 }
12637
12638 Some(SaSint::from(index))
12639}
12640
12641pub fn libsais64_omp(
12653 t: &[u8],
12654 sa: &mut [SaSint],
12655 fs: SaSint,
12656 freq: Option<&mut [SaSint]>,
12657 threads: SaSint,
12658) -> SaSint {
12659 if threads < 0 {
12660 return -1;
12661 }
12662 if fs < 0
12663 || sa.len()
12664 < t.len()
12665 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12666 {
12667 return -1;
12668 }
12669 if let Some(freq) = freq.as_ref() {
12670 if freq.len() < ALPHABET_SIZE {
12671 return -1;
12672 }
12673 }
12674 let n = t.len();
12675 if n <= 1 {
12676 if let Some(freq) = freq {
12677 freq[..ALPHABET_SIZE].fill(0);
12678 if n == 1 {
12679 sa[0] = 0;
12680 freq[t[0] as usize] += 1;
12681 }
12682 } else if n == 1 {
12683 sa[0] = 0;
12684 }
12685 return 0;
12686 }
12687
12688 let threads = normalize_omp_threads(threads);
12689 if n <= i32::MAX as usize {
12690 return libsais64_run_32bit_omp(t, sa, fs, freq, threads, false)
12691 .expect("n <= INT32_MAX must have 32-bit workspace");
12692 }
12693
12694 libsais64_main(t, sa, LIBSAIS_FLAGS_NONE, 0, None, fs, freq, threads)
12695}
12696
12697pub fn libsais64_gsa_omp(
12709 t: &[u8],
12710 sa: &mut [SaSint],
12711 fs: SaSint,
12712 freq: Option<&mut [SaSint]>,
12713 threads: SaSint,
12714) -> SaSint {
12715 if threads < 0
12716 || t.last().copied().unwrap_or(0) != 0
12717 || fs < 0
12718 || sa.len()
12719 < t.len()
12720 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12721 {
12722 return -1;
12723 }
12724 if let Some(freq) = freq.as_ref() {
12725 if freq.len() < ALPHABET_SIZE {
12726 return -1;
12727 }
12728 }
12729 let n = t.len();
12730 if n <= 1 {
12731 if let Some(freq) = freq {
12732 freq[..ALPHABET_SIZE].fill(0);
12733 if n == 1 {
12734 sa[0] = 0;
12735 freq[t[0] as usize] += 1;
12736 }
12737 } else if n == 1 {
12738 sa[0] = 0;
12739 }
12740 return 0;
12741 }
12742
12743 let threads = normalize_omp_threads(threads);
12744 if n <= i32::MAX as usize {
12745 return libsais64_run_32bit_omp(t, sa, fs, freq, threads, true)
12746 .expect("n <= INT32_MAX must have 32-bit workspace");
12747 }
12748
12749 libsais64_main(t, sa, LIBSAIS_FLAGS_GSA, 0, None, fs, freq, threads)
12750}
12751
12752pub fn libsais64_int_omp(
12754 t: &mut [SaSint],
12755 sa: &mut [SaSint],
12756 k: SaSint,
12757 fs: SaSint,
12758 threads: SaSint,
12759) -> SaSint {
12760 if threads < 0 {
12761 return -1;
12762 }
12763 if fs < 0
12764 || sa.len()
12765 < t.len()
12766 .saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12767 {
12768 return -1;
12769 }
12770 if t.len() <= 1 {
12771 if t.len() == 1 {
12772 sa[0] = 0;
12773 }
12774 return 0;
12775 }
12776
12777 libsais64_main_int(t, sa, k, fs, normalize_omp_threads(threads))
12778}
12779
12780pub fn libsais64_long_omp(
12794 t: &mut [SaSint],
12795 sa: &mut [SaSint],
12796 k: SaSint,
12797 fs: SaSint,
12798 threads: SaSint,
12799) -> SaSint {
12800 libsais64_int_omp(t, sa, k, fs, threads)
12801}
12802
12803pub fn libsais64_bwt_omp(
12816 t: &[u8],
12817 u: &mut [u8],
12818 a: &mut [SaSint],
12819 fs: SaSint,
12820 freq: Option<&mut [SaSint]>,
12821 threads: SaSint,
12822) -> SaSint {
12823 let n = t.len();
12824 if threads < 0
12825 || fs < 0
12826 || u.len() < n
12827 || a.len() < n.saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12828 || freq.as_ref().is_some_and(|freq| freq.len() < ALPHABET_SIZE)
12829 {
12830 return -1;
12831 }
12832
12833 if n <= 1 {
12834 if let Some(freq) = freq {
12835 freq[..ALPHABET_SIZE].fill(0);
12836 if n == 1 {
12837 u[0] = t[0];
12838 freq[t[0] as usize] += 1;
12839 }
12840 } else if n == 1 {
12841 u[0] = t[0];
12842 }
12843 return n as SaSint;
12844 }
12845
12846 let threads = normalize_omp_threads(threads);
12847 if n <= i32::MAX as usize {
12848 return libsais64_bwt_run_32bit_omp(t, u, fs, freq, threads)
12849 .expect("n <= INT32_MAX must have 32-bit workspace");
12850 }
12851
12852 let mut index = libsais64_main(t, a, LIBSAIS_FLAGS_BWT, 0, None, fs, freq, threads);
12853 if index >= 0 {
12854 index += 1;
12855 let index_usize = usize::try_from(index).expect("index must be non-negative");
12856 u[0] = t[n - 1];
12857 bwt_copy_8u_omp(
12858 &mut u[1..index_usize],
12859 &a[..index_usize - 1],
12860 index - 1,
12861 threads,
12862 );
12863 bwt_copy_8u_omp(
12864 &mut u[index_usize..n],
12865 &a[index_usize..n],
12866 SaSint::try_from(n - index_usize).expect("fits"),
12867 threads,
12868 );
12869 }
12870 index
12871}
12872
12873pub fn libsais64_bwt_aux_omp(
12888 t: &[u8],
12889 u: &mut [u8],
12890 a: &mut [SaSint],
12891 fs: SaSint,
12892 freq: Option<&mut [SaSint]>,
12893 r: SaSint,
12894 i: &mut [SaSint],
12895 threads: SaSint,
12896) -> SaSint {
12897 let n = t.len();
12898 if threads < 0
12899 || fs < 0
12900 || r < 2
12901 || (r & (r - 1)) != 0
12902 || u.len() < n
12903 || a.len() < n.saturating_add(usize::try_from(fs).unwrap_or(usize::MAX))
12904 {
12905 return -1;
12906 }
12907 if let Some(freq) = freq.as_ref() {
12908 if freq.len() < ALPHABET_SIZE {
12909 return -1;
12910 }
12911 }
12912 let sample_count = if n == 0 {
12913 1
12914 } else {
12915 usize::try_from((SaSint::try_from(n).expect("input length must fit SaSint") - 1) / r)
12916 .expect("sample count must be non-negative")
12917 + 1
12918 };
12919 if i.len() < sample_count {
12920 return -1;
12921 }
12922 if n <= 1 {
12923 if let Some(freq) = freq {
12924 freq[..ALPHABET_SIZE].fill(0);
12925 if n == 1 {
12926 u[0] = t[0];
12927 freq[t[0] as usize] += 1;
12928 }
12929 } else if n == 1 {
12930 u[0] = t[0];
12931 }
12932 i[0] = n as SaSint;
12933 return 0;
12934 }
12935
12936 let threads = normalize_omp_threads(threads);
12937 if n <= i32::MAX as usize && r <= i32::MAX as SaSint {
12938 return libsais64_bwt_aux_run_32bit_omp(t, u, fs, freq, r, i, threads)
12939 .expect("n/r <= INT32_MAX must have 32-bit workspace");
12940 }
12941
12942 let index = libsais64_main(t, a, LIBSAIS_FLAGS_BWT, r, Some(i), fs, freq, threads);
12943 if index == 0 {
12944 let split = usize::try_from(i[0]).expect("primary index must be non-negative");
12945 u[0] = t[n - 1];
12946 bwt_copy_8u_omp(&mut u[1..split], &a[..split - 1], i[0] - 1, threads);
12947 bwt_copy_8u_omp(
12948 &mut u[split..n],
12949 &a[split..n],
12950 SaSint::try_from(n - split).expect("fits"),
12951 threads,
12952 );
12953 }
12954 index
12955}
12956
12957#[doc(hidden)]
12959pub fn compute_phi(
12960 sa: &[SaSint],
12961 plcp: &mut [SaSint],
12962 n: SaSint,
12963 omp_block_start: FastSint,
12964 omp_block_size: FastSint,
12965) {
12966 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
12967 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
12968 let end = start + size;
12969 let n_usize = usize::try_from(n).expect("n must be non-negative");
12970 let mut i = start;
12971 let mut k = if omp_block_start > 0 {
12972 sa[start - 1]
12973 } else {
12974 n
12975 };
12976
12977 let fast_end = omp_block_start + omp_block_size - 64 - 3;
12978 while (i as FastSint) < fast_end {
12979 plcp[usize::try_from(sa[i]).expect("suffix index must be non-negative")] = k;
12980 k = sa[i];
12981 plcp[usize::try_from(sa[i + 1]).expect("suffix index must be non-negative")] = k;
12982 k = sa[i + 1];
12983 plcp[usize::try_from(sa[i + 2]).expect("suffix index must be non-negative")] = k;
12984 k = sa[i + 2];
12985 plcp[usize::try_from(sa[i + 3]).expect("suffix index must be non-negative")] = k;
12986 k = sa[i + 3];
12987 i += 4;
12988 }
12989
12990 while i < end.min(n_usize) {
12991 plcp[usize::try_from(sa[i]).expect("suffix index must be non-negative")] = k;
12992 k = sa[i];
12993 i += 1;
12994 }
12995}
12996
12997#[doc(hidden)]
12999pub fn compute_phi_omp(sa: &[SaSint], plcp: &mut [SaSint], n: SaSint, threads: SaSint) {
13000 if threads == 1 || n < 65_536 {
13001 compute_phi(sa, plcp, n, 0, n as FastSint);
13002 return;
13003 }
13004
13005 let threads_usize = usize::try_from(threads).expect("threads must be non-negative");
13006 let block_stride = ((n as FastSint) / (threads as FastSint)) & !15;
13007 let plcp_addr = plcp.as_mut_ptr() as usize;
13008 let n_usize = usize::try_from(n).expect("n must be non-negative");
13009
13010 run_rayon_with_threads(threads_usize, || {
13011 (0..threads_usize).into_par_iter().for_each(|thread| {
13012 let block_start = thread as FastSint * block_stride;
13013 let block_size = if thread + 1 < threads_usize {
13014 block_stride
13015 } else {
13016 n as FastSint - block_start
13017 };
13018 let start = usize::try_from(block_start).expect("omp_block_start must be non-negative");
13019 let size = usize::try_from(block_size).expect("omp_block_size must be non-negative");
13020 let end = start + size;
13021 let mut i = start;
13022 let mut k = if block_start > 0 { sa[start - 1] } else { n };
13023 let plcp_ptr = plcp_addr as *mut SaSint;
13024
13025 let fast_end = block_start + block_size - 64 - 3;
13026 while (i as FastSint) < fast_end {
13027 unsafe {
13028 *plcp_ptr
13030 .add(usize::try_from(sa[i]).expect("suffix index must be non-negative")) =
13031 k;
13032 k = sa[i];
13033 *plcp_ptr.add(
13034 usize::try_from(sa[i + 1]).expect("suffix index must be non-negative"),
13035 ) = k;
13036 k = sa[i + 1];
13037 *plcp_ptr.add(
13038 usize::try_from(sa[i + 2]).expect("suffix index must be non-negative"),
13039 ) = k;
13040 k = sa[i + 2];
13041 *plcp_ptr.add(
13042 usize::try_from(sa[i + 3]).expect("suffix index must be non-negative"),
13043 ) = k;
13044 k = sa[i + 3];
13045 }
13046 i += 4;
13047 }
13048
13049 while i < end.min(n_usize) {
13050 unsafe {
13051 *plcp_ptr
13053 .add(usize::try_from(sa[i]).expect("suffix index must be non-negative")) =
13054 k;
13055 }
13056 k = sa[i];
13057 i += 1;
13058 }
13059 });
13060 });
13061}
13062
13063#[doc(hidden)]
13065pub fn compute_plcp(
13066 t: &[u8],
13067 plcp: &mut [SaSint],
13068 n: FastSint,
13069 omp_block_start: FastSint,
13070 omp_block_size: FastSint,
13071) {
13072 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
13073 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
13074 let end = start + size;
13075 let n_usize = usize::try_from(n).expect("n must be non-negative");
13076 let mut l = 0usize;
13077
13078 for i in start..end.min(n_usize) {
13079 let k = usize::try_from(plcp[i]).expect("phi entry must be non-negative");
13080 let m = n_usize - i.max(k);
13081 while l < m && t[i + l] == t[k + l] {
13082 l += 1;
13083 }
13084 plcp[i] = SaSint::try_from(l).expect("LCP length must fit SaSint");
13085 l = l.saturating_sub(1);
13086 }
13087}
13088
13089#[doc(hidden)]
13091pub fn compute_plcp_omp(t: &[u8], plcp: &mut [SaSint], n: SaSint, threads: SaSint) {
13092 if threads == 1 || n < 65_536 {
13093 compute_plcp(t, plcp, n as FastSint, 0, n as FastSint);
13094 return;
13095 }
13096
13097 let n_usize = usize::try_from(n).expect("n must be non-negative");
13098 let threads_usize = usize::try_from(threads).expect("threads must be non-negative");
13099 let chunk_size = ((n_usize / threads_usize) & !15usize).max(16);
13100 run_rayon_with_threads(threads_usize, || {
13101 plcp[..n_usize]
13102 .par_chunks_mut(chunk_size)
13103 .enumerate()
13104 .for_each(|(chunk_index, chunk)| {
13105 let start = chunk_index * chunk_size;
13106 let mut l = 0usize;
13107 for (offset, value) in chunk.iter_mut().enumerate() {
13108 let i = start + offset;
13109 let k = usize::try_from(*value).expect("phi entry must be non-negative");
13110 let m = n_usize - i.max(k);
13111 while l < m && t[i + l] == t[k + l] {
13112 l += 1;
13113 }
13114 *value = SaSint::try_from(l).expect("LCP length must fit SaSint");
13115 l = l.saturating_sub(1);
13116 }
13117 });
13118 });
13119}
13120
13121#[doc(hidden)]
13123pub fn compute_plcp_gsa(
13124 t: &[u8],
13125 plcp: &mut [SaSint],
13126 omp_block_start: FastSint,
13127 omp_block_size: FastSint,
13128) {
13129 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
13130 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
13131 let end = start + size;
13132 let mut l = 0usize;
13133
13134 for i in start..end.min(t.len()) {
13135 let k = usize::try_from(plcp[i]).expect("phi entry must be non-negative");
13136 while t[i + l] > 0 && t[i + l] == t[k + l] {
13137 l += 1;
13138 }
13139 plcp[i] = SaSint::try_from(l).expect("LCP length must fit SaSint");
13140 l = l.saturating_sub(1);
13141 }
13142}
13143
13144#[doc(hidden)]
13146pub fn compute_plcp_gsa_omp(t: &[u8], plcp: &mut [SaSint], n: SaSint, threads: SaSint) {
13147 if threads == 1 || n < 65_536 {
13148 compute_plcp_gsa(t, plcp, 0, n as FastSint);
13149 return;
13150 }
13151
13152 let n_usize = usize::try_from(n).expect("n must be non-negative");
13153 let threads_usize = usize::try_from(threads).expect("threads must be non-negative");
13154 let chunk_size = ((n_usize / threads_usize) & !15usize).max(16);
13155 run_rayon_with_threads(threads_usize, || {
13156 plcp[..n_usize]
13157 .par_chunks_mut(chunk_size)
13158 .enumerate()
13159 .for_each(|(chunk_index, chunk)| {
13160 let start = chunk_index * chunk_size;
13161 let mut l = 0usize;
13162 for (offset, value) in chunk.iter_mut().enumerate() {
13163 let i = start + offset;
13164 let k = usize::try_from(*value).expect("phi entry must be non-negative");
13165 while t[i + l] > 0 && t[i + l] == t[k + l] {
13166 l += 1;
13167 }
13168 *value = SaSint::try_from(l).expect("LCP length must fit SaSint");
13169 l = l.saturating_sub(1);
13170 }
13171 });
13172 });
13173}
13174
13175#[doc(hidden)]
13177pub fn compute_plcp_int(
13178 t: &[SaSint],
13179 plcp: &mut [SaSint],
13180 n: FastSint,
13181 omp_block_start: FastSint,
13182 omp_block_size: FastSint,
13183) {
13184 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
13185 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
13186 let end = start + size;
13187 let n_usize = usize::try_from(n).expect("n must be non-negative");
13188 let mut l = 0usize;
13189
13190 for i in start..end.min(n_usize) {
13191 let k = usize::try_from(plcp[i]).expect("phi entry must be non-negative");
13192 let m = n_usize - i.max(k);
13193 while l < m && t[i + l] == t[k + l] {
13194 l += 1;
13195 }
13196 plcp[i] = SaSint::try_from(l).expect("LCP length must fit SaSint");
13197 l = l.saturating_sub(1);
13198 }
13199}
13200
13201#[doc(hidden)]
13203pub fn compute_plcp_int_omp(t: &[SaSint], plcp: &mut [SaSint], n: SaSint, threads: SaSint) {
13204 if threads == 1 || n < 65_536 {
13205 compute_plcp_int(t, plcp, n as FastSint, 0, n as FastSint);
13206 return;
13207 }
13208
13209 let n_usize = usize::try_from(n).expect("n must be non-negative");
13210 let threads_usize = usize::try_from(threads).expect("threads must be non-negative");
13211 let chunk_size = ((n_usize / threads_usize) & !15usize).max(16);
13212 run_rayon_with_threads(threads_usize, || {
13213 plcp[..n_usize]
13214 .par_chunks_mut(chunk_size)
13215 .enumerate()
13216 .for_each(|(chunk_index, chunk)| {
13217 let start = chunk_index * chunk_size;
13218 let mut l = 0usize;
13219 for (offset, value) in chunk.iter_mut().enumerate() {
13220 let i = start + offset;
13221 let k = usize::try_from(*value).expect("phi entry must be non-negative");
13222 let m = n_usize - i.max(k);
13223 while l < m && t[i + l] == t[k + l] {
13224 l += 1;
13225 }
13226 *value = SaSint::try_from(l).expect("LCP length must fit SaSint");
13227 l = l.saturating_sub(1);
13228 }
13229 });
13230 });
13231}
13232
13233#[doc(hidden)]
13235pub fn compute_lcp(
13236 plcp: &[SaSint],
13237 sa: &[SaSint],
13238 lcp: &mut [SaSint],
13239 omp_block_start: FastSint,
13240 omp_block_size: FastSint,
13241) {
13242 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
13243 let size = usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
13244 let end = start + size;
13245
13246 for i in start..end.min(sa.len()) {
13247 lcp[i] = plcp[usize::try_from(sa[i]).expect("suffix index must be non-negative")];
13248 }
13249}
13250
13251#[doc(hidden)]
13253pub fn compute_lcp_omp(
13254 plcp: &[SaSint],
13255 sa: &[SaSint],
13256 lcp: &mut [SaSint],
13257 n: SaSint,
13258 threads: SaSint,
13259) {
13260 if threads == 1 || n < 65_536 {
13261 compute_lcp(plcp, sa, lcp, 0, n as FastSint);
13262 return;
13263 }
13264
13265 let n_usize = usize::try_from(n).expect("n must be non-negative");
13266 assert!(plcp.len() >= n_usize);
13267 assert!(sa.len() >= n_usize);
13268 assert!(lcp.len() >= n_usize);
13269 let threads_usize = usize::try_from(threads).expect("threads must be non-negative");
13270 let chunk_size = ((n_usize / threads_usize) & !15usize).max(16);
13271 let plcp_ptr = plcp.as_ptr() as usize;
13272 let sa_ptr = sa.as_ptr() as usize;
13273 run_rayon_with_threads(threads_usize, || {
13274 lcp[..n_usize]
13275 .par_chunks_mut(chunk_size)
13276 .enumerate()
13277 .for_each(|(chunk_index, chunk)| {
13278 let start = chunk_index * chunk_size;
13279 let dst_ptr = chunk.as_mut_ptr();
13280 let sa_ptr = sa_ptr as *const SaSint;
13281 let plcp_ptr = plcp_ptr as *const SaSint;
13282 for offset in 0..chunk.len() {
13283 let i = start + offset;
13284 let suffix = unsafe { *sa_ptr.add(i) };
13285 let suffix =
13286 usize::try_from(suffix).expect("suffix index must be non-negative");
13287 assert!(suffix < plcp.len());
13288 unsafe {
13289 *dst_ptr.add(offset) = *plcp_ptr.add(suffix);
13290 }
13291 }
13292 });
13293 });
13294}
13295
13296pub fn libsais64_plcp(t: &[u8], sa: &[SaSint], plcp: &mut [SaSint]) -> SaSint {
13306 if sa.len() != t.len() || plcp.len() != t.len() {
13307 return -1;
13308 }
13309 if !suffix_entries_in_bounds(sa, t.len()) {
13310 return -1;
13311 }
13312 if t.len() <= 1 {
13313 if t.len() == 1 {
13314 plcp[0] = 0;
13315 }
13316 return 0;
13317 }
13318
13319 let n = SaSint::try_from(t.len()).expect("input length must fit SaSint");
13320 compute_phi_omp(sa, plcp, n, 1);
13321 compute_plcp_omp(t, plcp, n, 1);
13322 0
13323}
13324
13325pub fn libsais64_plcp_gsa(t: &[u8], sa: &[SaSint], plcp: &mut [SaSint]) -> SaSint {
13335 if t.last().copied().unwrap_or(0) != 0 {
13336 return -1;
13337 }
13338 if sa.len() != t.len() || plcp.len() != t.len() {
13339 return -1;
13340 }
13341 if !suffix_entries_in_bounds(sa, t.len()) {
13342 return -1;
13343 }
13344 if t.len() <= 1 {
13345 if t.len() == 1 {
13346 plcp[0] = 0;
13347 }
13348 return 0;
13349 }
13350
13351 let n = SaSint::try_from(t.len()).expect("input length must fit SaSint");
13352 compute_phi_omp(sa, plcp, n, 1);
13353 compute_plcp_gsa_omp(t, plcp, n, 1);
13354 0
13355}
13356
13357pub fn libsais64_plcp_int(t: &[SaSint], sa: &[SaSint], plcp: &mut [SaSint]) -> SaSint {
13365 if sa.len() != t.len() || plcp.len() != t.len() {
13366 return -1;
13367 }
13368 if !suffix_entries_in_bounds(sa, t.len()) {
13369 return -1;
13370 }
13371 if t.len() <= 1 {
13372 if t.len() == 1 {
13373 plcp[0] = 0;
13374 }
13375 return 0;
13376 }
13377
13378 let n = SaSint::try_from(t.len()).expect("input length must fit SaSint");
13379 compute_phi_omp(sa, plcp, n, 1);
13380 compute_plcp_int_omp(t, plcp, n, 1);
13381 0
13382}
13383
13384pub fn libsais64_lcp(plcp: &[SaSint], sa: &[SaSint], lcp: &mut [SaSint]) -> SaSint {
13394 if plcp.len() != sa.len() || lcp.len() != sa.len() {
13395 return -1;
13396 }
13397 if !suffix_entries_in_bounds(sa, plcp.len()) {
13398 return -1;
13399 }
13400 if sa.len() <= 1 {
13401 if sa.len() == 1 {
13402 lcp[0] = plcp[usize::try_from(sa[0]).expect("suffix index must be non-negative")];
13403 }
13404 return 0;
13405 }
13406
13407 compute_lcp_omp(
13408 plcp,
13409 sa,
13410 lcp,
13411 SaSint::try_from(sa.len()).expect("suffix array length must fit SaSint"),
13412 1,
13413 );
13414 0
13415}
13416
13417pub fn libsais64_plcp_omp(t: &[u8], sa: &[SaSint], plcp: &mut [SaSint], threads: SaSint) -> SaSint {
13428 if threads < 0 {
13429 return -1;
13430 }
13431 if sa.len() != t.len() || plcp.len() != t.len() {
13432 return -1;
13433 }
13434 if !suffix_entries_in_bounds(sa, t.len()) {
13435 return -1;
13436 }
13437 if t.len() <= 1 {
13438 if t.len() == 1 {
13439 plcp[0] = 0;
13440 }
13441 return 0;
13442 }
13443
13444 let n = SaSint::try_from(t.len()).expect("input length must fit SaSint");
13445 let threads = normalize_omp_threads(threads);
13446 compute_phi_omp(sa, plcp, n, threads);
13447 compute_plcp_omp(t, plcp, n, threads);
13448 0
13449}
13450
13451pub fn libsais64_plcp_gsa_omp(
13462 t: &[u8],
13463 sa: &[SaSint],
13464 plcp: &mut [SaSint],
13465 threads: SaSint,
13466) -> SaSint {
13467 if threads < 0 || t.last().copied().unwrap_or(0) != 0 {
13468 return -1;
13469 }
13470 if sa.len() != t.len() || plcp.len() != t.len() {
13471 return -1;
13472 }
13473 if !suffix_entries_in_bounds(sa, t.len()) {
13474 return -1;
13475 }
13476 if t.len() <= 1 {
13477 if t.len() == 1 {
13478 plcp[0] = 0;
13479 }
13480 return 0;
13481 }
13482
13483 let n = SaSint::try_from(t.len()).expect("input length must fit SaSint");
13484 let threads = normalize_omp_threads(threads);
13485 compute_phi_omp(sa, plcp, n, threads);
13486 compute_plcp_gsa_omp(t, plcp, n, threads);
13487 0
13488}
13489
13490pub fn libsais64_plcp_int_omp(
13499 t: &[SaSint],
13500 sa: &[SaSint],
13501 plcp: &mut [SaSint],
13502 threads: SaSint,
13503) -> SaSint {
13504 if threads < 0 {
13505 return -1;
13506 }
13507 if sa.len() != t.len() || plcp.len() != t.len() {
13508 return -1;
13509 }
13510 if !suffix_entries_in_bounds(sa, t.len()) {
13511 return -1;
13512 }
13513 if t.len() <= 1 {
13514 if t.len() == 1 {
13515 plcp[0] = 0;
13516 }
13517 return 0;
13518 }
13519
13520 let n = SaSint::try_from(t.len()).expect("input length must fit SaSint");
13521 let threads = normalize_omp_threads(threads);
13522 compute_phi_omp(sa, plcp, n, threads);
13523 compute_plcp_int_omp(t, plcp, n, threads);
13524 0
13525}
13526
13527pub fn libsais64_lcp_omp(
13538 plcp: &[SaSint],
13539 sa: &[SaSint],
13540 lcp: &mut [SaSint],
13541 threads: SaSint,
13542) -> SaSint {
13543 if threads < 0 {
13544 return -1;
13545 }
13546 if plcp.len() != sa.len() || lcp.len() != sa.len() {
13547 return -1;
13548 }
13549 if !suffix_entries_in_bounds(sa, plcp.len()) {
13550 return -1;
13551 }
13552 if sa.len() <= 1 {
13553 if sa.len() == 1 {
13554 lcp[0] = plcp[usize::try_from(sa[0]).expect("suffix index must be non-negative")];
13555 }
13556 return 0;
13557 }
13558
13559 compute_lcp_omp(
13560 plcp,
13561 sa,
13562 lcp,
13563 SaSint::try_from(sa.len()).expect("suffix array length must fit SaSint"),
13564 normalize_omp_threads(threads),
13565 );
13566 0
13567}
13568
13569fn suffix_entries_in_bounds(sa: &[SaSint], len: usize) -> bool {
13570 sa.iter()
13571 .all(|&value| usize::try_from(value).is_ok_and(|index| index < len))
13572}
13573
13574#[doc(hidden)]
13576pub fn unbwt_compute_histogram(t: &[u8], n: FastSint, count: &mut [SaUint]) {
13577 let n = usize::try_from(n).expect("n must be non-negative");
13578 assert!(count.len() >= ALPHABET_SIZE);
13579 for &byte in &t[..n] {
13580 count[byte as usize] += 1;
13581 }
13582}
13583
13584#[doc(hidden)]
13586pub fn unbwt_transpose_bucket2(bucket2: &mut [SaUint]) {
13587 assert!(bucket2.len() >= ALPHABET_SIZE * ALPHABET_SIZE);
13588 for x in 0..ALPHABET_SIZE {
13589 for y in x + 1..ALPHABET_SIZE {
13590 bucket2.swap((y << 8) + x, (x << 8) + y);
13591 }
13592 }
13593}
13594
13595#[doc(hidden)]
13597pub fn unbwt_compute_bigram_histogram_single(
13598 t: &[u8],
13599 bucket1: &mut [SaUint],
13600 bucket2: &mut [SaUint],
13601 index: FastUint,
13602) {
13603 let mut sum = 1usize;
13604 for c in 0..ALPHABET_SIZE {
13605 let prev = sum;
13606 sum += bucket1[c] as usize;
13607 bucket1[c] = prev as SaUint;
13608 if prev != sum {
13609 let bucket2_p = &mut bucket2[c << 8..(c + 1) << 8];
13610
13611 let hi = sum.min(index);
13612 if hi > prev {
13613 unbwt_compute_histogram(&t[prev..], (hi - prev) as FastSint, bucket2_p);
13614 }
13615
13616 let lo = prev.max(index + 1);
13617 if sum > lo {
13618 unbwt_compute_histogram(&t[lo - 1..], (sum - lo) as FastSint, bucket2_p);
13619 }
13620 }
13621 }
13622
13623 unbwt_transpose_bucket2(bucket2);
13624}
13625
13626#[doc(hidden)]
13628pub fn unbwt_calculate_fastbits(
13629 bucket2: &mut [SaUint],
13630 fastbits: &mut [u16],
13631 lastc: FastUint,
13632 shift: FastUint,
13633) {
13634 let mut v = 0usize;
13635 let mut w = 0usize;
13636 let mut sum = 1usize;
13637
13638 for c in 0..ALPHABET_SIZE {
13639 if c == lastc {
13640 sum += 1;
13641 }
13642
13643 for _d in 0..ALPHABET_SIZE {
13644 let prev = sum;
13645 sum += bucket2[w] as usize;
13646 bucket2[w] = prev as SaUint;
13647 if prev != sum {
13648 while v <= ((sum - 1) >> shift) {
13649 fastbits[v] = w as u16;
13650 v += 1;
13651 }
13652 }
13653 w += 1;
13654 }
13655 }
13656}
13657
13658#[doc(hidden)]
13660pub fn unbwt_calculate_bi_psi(
13661 t: &[u8],
13662 p: &mut [SaUint],
13663 bucket1: &mut [SaUint],
13664 bucket2: &mut [SaUint],
13665 index: FastUint,
13666 omp_block_start: FastSint,
13667 omp_block_end: FastSint,
13668) {
13669 let mut i = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
13670 let mut j = index;
13671 let block_end = usize::try_from(omp_block_end).expect("omp_block_end must be non-negative");
13672 if block_end < j {
13673 j = block_end;
13674 }
13675 while i < j {
13676 let c = t[i] as usize;
13677 let pidx = bucket1[c] as usize;
13678 bucket1[c] += 1;
13679 let tidx = index.wrapping_sub(pidx) as i64;
13680 if tidx != 0 {
13681 let src = pidx.wrapping_add((tidx >> 63) as usize);
13682 let w = ((t[src] as usize) << 8) + c;
13683 let dst = bucket2[w] as usize;
13684 p[dst] = i as SaUint;
13685 bucket2[w] += 1;
13686 }
13687 i += 1;
13688 }
13689
13690 let mut i = index;
13691 if usize::try_from(omp_block_start).expect("omp_block_start must be non-negative") > i {
13692 i = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
13693 }
13694 i += 1;
13695 while i <= block_end {
13696 let c = t[i - 1] as usize;
13697 let pidx = bucket1[c] as usize;
13698 bucket1[c] += 1;
13699 let tidx = index.wrapping_sub(pidx) as i64;
13700 if tidx != 0 {
13701 let src = pidx.wrapping_add((tidx >> 63) as usize);
13702 let w = ((t[src] as usize) << 8) + c;
13703 let dst = bucket2[w] as usize;
13704 p[dst] = i as SaUint;
13705 bucket2[w] += 1;
13706 }
13707 i += 1;
13708 }
13709}
13710
13711#[doc(hidden)]
13713#[allow(dead_code, non_snake_case)]
13714pub fn unbwt_calculate_biPSI(
13715 t: &[u8],
13716 p: &mut [SaUint],
13717 bucket1: &mut [SaUint],
13718 bucket2: &mut [SaUint],
13719 index: FastUint,
13720 omp_block_start: FastSint,
13721 omp_block_end: FastSint,
13722) {
13723 unbwt_calculate_bi_psi(
13724 t,
13725 p,
13726 bucket1,
13727 bucket2,
13728 index,
13729 omp_block_start,
13730 omp_block_end,
13731 );
13732}
13733
13734#[doc(hidden)]
13736pub fn unbwt_init_single(
13737 t: &[u8],
13738 p: &mut [SaUint],
13739 n: SaSint,
13740 freq: Option<&[SaSint]>,
13741 i: &[SaUint],
13742 bucket2: &mut [SaUint],
13743 fastbits: &mut [u16],
13744) {
13745 let mut bucket1 = vec![0u64; ALPHABET_SIZE];
13746 let index = i[0] as usize;
13747 let lastc = t[0] as usize;
13748 let mut shift = 0usize;
13749 while (usize::try_from(n).expect("n must be non-negative") >> shift)
13750 > (1usize << UNBWT_FASTBITS)
13751 {
13752 shift += 1;
13753 }
13754
13755 if let Some(freq) = freq {
13756 for c in 0..ALPHABET_SIZE {
13757 bucket1[c] = freq[c] as SaUint;
13758 }
13759 } else {
13760 unbwt_compute_histogram(t, n as FastSint, &mut bucket1);
13761 }
13762
13763 bucket2.fill(0);
13764 unbwt_compute_bigram_histogram_single(t, &mut bucket1, bucket2, index);
13765 unbwt_calculate_fastbits(bucket2, fastbits, lastc, shift);
13766 unbwt_calculate_bi_psi(t, p, &mut bucket1, bucket2, index, 0, n as FastSint);
13767}
13768
13769#[doc(hidden)]
13771pub fn unbwt_compute_bigram_histogram_parallel(
13772 t: &[u8],
13773 index: FastUint,
13774 bucket1: &mut [SaUint],
13775 bucket2: &mut [SaUint],
13776 omp_block_start: FastSint,
13777 omp_block_size: FastSint,
13778) {
13779 let start = usize::try_from(omp_block_start).expect("omp_block_start must be non-negative");
13780 let end = start + usize::try_from(omp_block_size).expect("omp_block_size must be non-negative");
13781 for &c_u8 in &t[start..end] {
13782 let c = c_u8 as usize;
13783 let p = bucket1[c] as usize;
13784 bucket1[c] += 1;
13785 let tidx = index.wrapping_sub(p) as i64;
13786 if tidx != 0 {
13787 let src = p.wrapping_add((tidx >> 63) as usize);
13788 let w = ((t[src] as usize) << 8) + c;
13789 bucket2[w] += 1;
13790 }
13791 }
13792}
13793
13794#[doc(hidden)]
13796pub fn unbwt_init_parallel(
13797 t: &[u8],
13798 p: &mut [SaUint],
13799 n: SaSint,
13800 freq: Option<&[SaSint]>,
13801 i: &[SaUint],
13802 bucket2: &mut [SaUint],
13803 fastbits: &mut [u16],
13804 buckets: Option<&mut [SaUint]>,
13805 threads: SaSint,
13806) {
13807 let num_threads = usize::try_from(threads.max(1)).expect("threads must be non-negative");
13808 if num_threads <= 1 || usize::try_from(n).expect("n must be non-negative") < 65_536 {
13809 unbwt_init_single(t, p, n, freq, i, bucket2, fastbits);
13810 return;
13811 }
13812
13813 let buckets = match buckets {
13814 Some(buckets) => buckets,
13815 None => {
13816 unbwt_init_single(t, p, n, freq, i, bucket2, fastbits);
13817 return;
13818 }
13819 };
13820
13821 let segment_len = ALPHABET_SIZE + ALPHABET_SIZE * ALPHABET_SIZE;
13822 assert!(buckets.len() >= num_threads * segment_len);
13823
13824 let index = i[0] as usize;
13825 let lastc = t[0] as usize;
13826 let mut shift = 0usize;
13827 while (usize::try_from(n).expect("n must be non-negative") >> shift)
13828 > (1usize << UNBWT_FASTBITS)
13829 {
13830 shift += 1;
13831 }
13832
13833 let mut bucket1 = vec![0u64; ALPHABET_SIZE];
13834 bucket2.fill(0);
13835
13836 let n_fast = n as FastSint;
13837 let block_stride = (n_fast / num_threads as FastSint) & (-16);
13838 let mut block_starts = vec![0usize; num_threads];
13839 let mut block_sizes = vec![0usize; num_threads];
13840
13841 for thread in 0..num_threads {
13842 let start = usize::try_from(thread as FastSint * block_stride)
13843 .expect("block start must be non-negative");
13844 let size = if thread + 1 < num_threads {
13845 usize::try_from(block_stride).expect("block stride must be non-negative")
13846 } else {
13847 usize::try_from(n_fast - thread as FastSint * block_stride)
13848 .expect("block size must be non-negative")
13849 };
13850 block_starts[thread] = start;
13851 block_sizes[thread] = size;
13852
13853 let segment = &mut buckets[thread * segment_len..(thread + 1) * segment_len];
13854 let (bucket1_local, _) = segment.split_at_mut(ALPHABET_SIZE);
13855 bucket1_local.fill(0);
13856 unbwt_compute_histogram(&t[start..], size as FastSint, bucket1_local);
13857 }
13858
13859 for thread in 0..num_threads {
13860 let segment = &mut buckets[thread * segment_len..(thread + 1) * segment_len];
13861 let (bucket1_temp, _) = segment.split_at_mut(ALPHABET_SIZE);
13862 for c in 0..ALPHABET_SIZE {
13863 let a = bucket1[c];
13864 let b = bucket1_temp[c];
13865 bucket1[c] = a + b;
13866 bucket1_temp[c] = a;
13867 }
13868 }
13869
13870 let mut sum = 1usize;
13871 for c in 0..ALPHABET_SIZE {
13872 let prev = sum;
13873 sum += bucket1[c] as usize;
13874 bucket1[c] = prev as SaUint;
13875 }
13876
13877 for thread in 0..num_threads {
13878 let start = block_starts[thread];
13879 let size = block_sizes[thread];
13880 let segment = &mut buckets[thread * segment_len..(thread + 1) * segment_len];
13881 let (bucket1_local, bucket2_local) = segment.split_at_mut(ALPHABET_SIZE);
13882 for c in 0..ALPHABET_SIZE {
13883 bucket1_local[c] += bucket1[c];
13884 }
13885 bucket2_local.fill(0);
13886 unbwt_compute_bigram_histogram_parallel(
13887 t,
13888 index,
13889 bucket1_local,
13890 bucket2_local,
13891 start as FastSint,
13892 size as FastSint,
13893 );
13894 }
13895
13896 for thread in 0..num_threads {
13897 let segment = &mut buckets[thread * segment_len..(thread + 1) * segment_len];
13898 let (_, bucket2_temp) = segment.split_at_mut(ALPHABET_SIZE);
13899 for c in 0..ALPHABET_SIZE * ALPHABET_SIZE {
13900 let a = bucket2[c];
13901 let b = bucket2_temp[c];
13902 bucket2[c] = a + b;
13903 bucket2_temp[c] = a;
13904 }
13905 }
13906
13907 unbwt_calculate_fastbits(bucket2, fastbits, lastc, shift);
13908
13909 for thread in (1..num_threads).rev() {
13910 let src_start = (thread - 1) * segment_len;
13911 let dst_start = thread * segment_len;
13912 let (head, tail) = buckets.split_at_mut(dst_start);
13913 let src = &head[src_start..src_start + ALPHABET_SIZE];
13914 let dst = &mut tail[..ALPHABET_SIZE];
13915 dst.copy_from_slice(src);
13916 }
13917 buckets[..ALPHABET_SIZE].copy_from_slice(&bucket1);
13918
13919 for thread in 0..num_threads {
13920 let start = block_starts[thread];
13921 let size = block_sizes[thread];
13922 let segment = &mut buckets[thread * segment_len..(thread + 1) * segment_len];
13923 let (bucket1_local, bucket2_local) = segment.split_at_mut(ALPHABET_SIZE);
13924 for c in 0..ALPHABET_SIZE * ALPHABET_SIZE {
13925 bucket2_local[c] += bucket2[c];
13926 }
13927 unbwt_calculate_bi_psi(
13928 t,
13929 p,
13930 bucket1_local,
13931 bucket2_local,
13932 index,
13933 start as FastSint,
13934 (start + size) as FastSint,
13935 );
13936 }
13937
13938 let last_segment = &buckets[(num_threads - 1) * segment_len..num_threads * segment_len];
13939 let (_, last_bucket2) = last_segment.split_at(ALPHABET_SIZE);
13940 bucket2.copy_from_slice(last_bucket2);
13941}
13942
13943fn bswap16(value: u16) -> u16 {
13944 value.swap_bytes()
13945}
13946
13947fn unbwt_resolve_symbol(bucket2: &[SaUint], fastbits: &[u16], shift: FastUint, p: SaUint) -> u16 {
13948 let mut c = fastbits[(p as usize) >> shift];
13949 while bucket2[c as usize] <= p {
13950 c += 1;
13951 }
13952 c
13953}
13954
13955#[doc(hidden)]
13957pub fn unbwt_decode_1(
13958 u: &mut [u8],
13959 p: &[SaUint],
13960 bucket2: &[SaUint],
13961 fastbits: &[u16],
13962 shift: FastUint,
13963 i0: &mut FastUint,
13964 k: FastUint,
13965) {
13966 let words = &mut u[..2 * k];
13967 let mut p0 = *i0 as SaUint;
13968
13969 for i in 0..k {
13970 let c0 = unbwt_resolve_symbol(bucket2, fastbits, shift, p0);
13971 p0 = p[p0 as usize];
13972 let bytes = bswap16(c0).to_ne_bytes();
13973 words[2 * i] = bytes[0];
13974 words[2 * i + 1] = bytes[1];
13975 }
13976
13977 *i0 = p0 as FastUint;
13978}
13979
13980#[doc(hidden)]
13982pub fn unbwt_decode_2(
13983 u: &mut [u8],
13984 p: &[SaUint],
13985 bucket2: &[SaUint],
13986 fastbits: &[u16],
13987 shift: FastUint,
13988 r: FastUint,
13989 i0: &mut FastUint,
13990 i1: &mut FastUint,
13991 k: FastUint,
13992) {
13993 let width = 2 * k;
13994 unbwt_decode_1(&mut u[0..width], p, bucket2, fastbits, shift, i0, k);
13995 unbwt_decode_1(&mut u[r..r + width], p, bucket2, fastbits, shift, i1, k);
13996}
13997
13998#[doc(hidden)]
14000pub fn unbwt_decode_3(
14001 u: &mut [u8],
14002 p: &[SaUint],
14003 bucket2: &[SaUint],
14004 fastbits: &[u16],
14005 shift: FastUint,
14006 r: FastUint,
14007 i0: &mut FastUint,
14008 i1: &mut FastUint,
14009 i2: &mut FastUint,
14010 k: FastUint,
14011) {
14012 let width = 2 * k;
14013 unbwt_decode_1(&mut u[0..width], p, bucket2, fastbits, shift, i0, k);
14014 unbwt_decode_1(&mut u[r..r + width], p, bucket2, fastbits, shift, i1, k);
14015 unbwt_decode_1(
14016 &mut u[2 * r..2 * r + width],
14017 p,
14018 bucket2,
14019 fastbits,
14020 shift,
14021 i2,
14022 k,
14023 );
14024}
14025
14026#[doc(hidden)]
14028pub fn unbwt_decode_4(
14029 u: &mut [u8],
14030 p: &[SaUint],
14031 bucket2: &[SaUint],
14032 fastbits: &[u16],
14033 shift: FastUint,
14034 r: FastUint,
14035 i0: &mut FastUint,
14036 i1: &mut FastUint,
14037 i2: &mut FastUint,
14038 i3: &mut FastUint,
14039 k: FastUint,
14040) {
14041 let width = 2 * k;
14042 unbwt_decode_1(&mut u[0..width], p, bucket2, fastbits, shift, i0, k);
14043 unbwt_decode_1(&mut u[r..r + width], p, bucket2, fastbits, shift, i1, k);
14044 unbwt_decode_1(
14045 &mut u[2 * r..2 * r + width],
14046 p,
14047 bucket2,
14048 fastbits,
14049 shift,
14050 i2,
14051 k,
14052 );
14053 unbwt_decode_1(
14054 &mut u[3 * r..3 * r + width],
14055 p,
14056 bucket2,
14057 fastbits,
14058 shift,
14059 i3,
14060 k,
14061 );
14062}
14063
14064#[doc(hidden)]
14066pub fn unbwt_decode_5(
14067 u: &mut [u8],
14068 p: &[SaUint],
14069 bucket2: &[SaUint],
14070 fastbits: &[u16],
14071 shift: FastUint,
14072 r: FastUint,
14073 i0: &mut FastUint,
14074 i1: &mut FastUint,
14075 i2: &mut FastUint,
14076 i3: &mut FastUint,
14077 i4: &mut FastUint,
14078 k: FastUint,
14079) {
14080 let width = 2 * k;
14081 unbwt_decode_1(&mut u[0..width], p, bucket2, fastbits, shift, i0, k);
14082 unbwt_decode_1(&mut u[r..r + width], p, bucket2, fastbits, shift, i1, k);
14083 unbwt_decode_1(
14084 &mut u[2 * r..2 * r + width],
14085 p,
14086 bucket2,
14087 fastbits,
14088 shift,
14089 i2,
14090 k,
14091 );
14092 unbwt_decode_1(
14093 &mut u[3 * r..3 * r + width],
14094 p,
14095 bucket2,
14096 fastbits,
14097 shift,
14098 i3,
14099 k,
14100 );
14101 unbwt_decode_1(
14102 &mut u[4 * r..4 * r + width],
14103 p,
14104 bucket2,
14105 fastbits,
14106 shift,
14107 i4,
14108 k,
14109 );
14110}
14111
14112#[doc(hidden)]
14114pub fn unbwt_decode_6(
14115 u: &mut [u8],
14116 p: &[SaUint],
14117 bucket2: &[SaUint],
14118 fastbits: &[u16],
14119 shift: FastUint,
14120 r: FastUint,
14121 i0: &mut FastUint,
14122 i1: &mut FastUint,
14123 i2: &mut FastUint,
14124 i3: &mut FastUint,
14125 i4: &mut FastUint,
14126 i5: &mut FastUint,
14127 k: FastUint,
14128) {
14129 let width = 2 * k;
14130 unbwt_decode_1(&mut u[0..width], p, bucket2, fastbits, shift, i0, k);
14131 unbwt_decode_1(&mut u[r..r + width], p, bucket2, fastbits, shift, i1, k);
14132 unbwt_decode_1(
14133 &mut u[2 * r..2 * r + width],
14134 p,
14135 bucket2,
14136 fastbits,
14137 shift,
14138 i2,
14139 k,
14140 );
14141 unbwt_decode_1(
14142 &mut u[3 * r..3 * r + width],
14143 p,
14144 bucket2,
14145 fastbits,
14146 shift,
14147 i3,
14148 k,
14149 );
14150 unbwt_decode_1(
14151 &mut u[4 * r..4 * r + width],
14152 p,
14153 bucket2,
14154 fastbits,
14155 shift,
14156 i4,
14157 k,
14158 );
14159 unbwt_decode_1(
14160 &mut u[5 * r..5 * r + width],
14161 p,
14162 bucket2,
14163 fastbits,
14164 shift,
14165 i5,
14166 k,
14167 );
14168}
14169
14170#[doc(hidden)]
14172pub fn unbwt_decode_7(
14173 u: &mut [u8],
14174 p: &[SaUint],
14175 bucket2: &[SaUint],
14176 fastbits: &[u16],
14177 shift: FastUint,
14178 r: FastUint,
14179 i0: &mut FastUint,
14180 i1: &mut FastUint,
14181 i2: &mut FastUint,
14182 i3: &mut FastUint,
14183 i4: &mut FastUint,
14184 i5: &mut FastUint,
14185 i6: &mut FastUint,
14186 k: FastUint,
14187) {
14188 let width = 2 * k;
14189 unbwt_decode_1(&mut u[0..width], p, bucket2, fastbits, shift, i0, k);
14190 unbwt_decode_1(&mut u[r..r + width], p, bucket2, fastbits, shift, i1, k);
14191 unbwt_decode_1(
14192 &mut u[2 * r..2 * r + width],
14193 p,
14194 bucket2,
14195 fastbits,
14196 shift,
14197 i2,
14198 k,
14199 );
14200 unbwt_decode_1(
14201 &mut u[3 * r..3 * r + width],
14202 p,
14203 bucket2,
14204 fastbits,
14205 shift,
14206 i3,
14207 k,
14208 );
14209 unbwt_decode_1(
14210 &mut u[4 * r..4 * r + width],
14211 p,
14212 bucket2,
14213 fastbits,
14214 shift,
14215 i4,
14216 k,
14217 );
14218 unbwt_decode_1(
14219 &mut u[5 * r..5 * r + width],
14220 p,
14221 bucket2,
14222 fastbits,
14223 shift,
14224 i5,
14225 k,
14226 );
14227 unbwt_decode_1(
14228 &mut u[6 * r..6 * r + width],
14229 p,
14230 bucket2,
14231 fastbits,
14232 shift,
14233 i6,
14234 k,
14235 );
14236}
14237
14238#[doc(hidden)]
14240pub fn unbwt_decode_8(
14241 u: &mut [u8],
14242 p: &[SaUint],
14243 bucket2: &[SaUint],
14244 fastbits: &[u16],
14245 shift: FastUint,
14246 r: FastUint,
14247 i0: &mut FastUint,
14248 i1: &mut FastUint,
14249 i2: &mut FastUint,
14250 i3: &mut FastUint,
14251 i4: &mut FastUint,
14252 i5: &mut FastUint,
14253 i6: &mut FastUint,
14254 i7: &mut FastUint,
14255 k: FastUint,
14256) {
14257 let width = 2 * k;
14258 unbwt_decode_1(&mut u[0..width], p, bucket2, fastbits, shift, i0, k);
14259 unbwt_decode_1(&mut u[r..r + width], p, bucket2, fastbits, shift, i1, k);
14260 unbwt_decode_1(
14261 &mut u[2 * r..2 * r + width],
14262 p,
14263 bucket2,
14264 fastbits,
14265 shift,
14266 i2,
14267 k,
14268 );
14269 unbwt_decode_1(
14270 &mut u[3 * r..3 * r + width],
14271 p,
14272 bucket2,
14273 fastbits,
14274 shift,
14275 i3,
14276 k,
14277 );
14278 unbwt_decode_1(
14279 &mut u[4 * r..4 * r + width],
14280 p,
14281 bucket2,
14282 fastbits,
14283 shift,
14284 i4,
14285 k,
14286 );
14287 unbwt_decode_1(
14288 &mut u[5 * r..5 * r + width],
14289 p,
14290 bucket2,
14291 fastbits,
14292 shift,
14293 i5,
14294 k,
14295 );
14296 unbwt_decode_1(
14297 &mut u[6 * r..6 * r + width],
14298 p,
14299 bucket2,
14300 fastbits,
14301 shift,
14302 i6,
14303 k,
14304 );
14305 unbwt_decode_1(
14306 &mut u[7 * r..7 * r + width],
14307 p,
14308 bucket2,
14309 fastbits,
14310 shift,
14311 i7,
14312 k,
14313 );
14314}
14315
14316#[doc(hidden)]
14318pub fn unbwt_decode(
14319 u: &mut [u8],
14320 p: &[SaUint],
14321 n: SaSint,
14322 r: SaSint,
14323 i: &[SaUint],
14324 bucket2: &[SaUint],
14325 fastbits: &[u16],
14326 mut blocks: FastSint,
14327 remainder: FastUint,
14328) {
14329 let mut shift = 0usize;
14330 while (usize::try_from(n).expect("n must be non-negative") >> shift)
14331 > (1usize << UNBWT_FASTBITS)
14332 {
14333 shift += 1;
14334 }
14335 let mut offset = 0usize;
14336 let mut i_index = 0usize;
14337 let r_usize = usize::try_from(r).expect("r must be non-negative");
14338
14339 while blocks > 8 {
14340 let mut i0 = i[i_index] as FastUint;
14341 let mut i1 = i[i_index + 1] as FastUint;
14342 let mut i2 = i[i_index + 2] as FastUint;
14343 let mut i3 = i[i_index + 3] as FastUint;
14344 let mut i4 = i[i_index + 4] as FastUint;
14345 let mut i5 = i[i_index + 5] as FastUint;
14346 let mut i6 = i[i_index + 6] as FastUint;
14347 let mut i7 = i[i_index + 7] as FastUint;
14348 unbwt_decode_8(
14349 &mut u[offset..],
14350 p,
14351 bucket2,
14352 fastbits,
14353 shift,
14354 r_usize,
14355 &mut i0,
14356 &mut i1,
14357 &mut i2,
14358 &mut i3,
14359 &mut i4,
14360 &mut i5,
14361 &mut i6,
14362 &mut i7,
14363 r_usize >> 1,
14364 );
14365 i_index += 8;
14366 blocks -= 8;
14367 offset += 8 * r_usize;
14368 }
14369
14370 match blocks {
14371 1 => {
14372 let mut i0 = i[i_index] as FastUint;
14373 unbwt_decode_1(
14374 &mut u[offset..],
14375 p,
14376 bucket2,
14377 fastbits,
14378 shift,
14379 &mut i0,
14380 remainder >> 1,
14381 );
14382 }
14383 2 => {
14384 let mut i0 = i[i_index] as FastUint;
14385 let mut i1 = i[i_index + 1] as FastUint;
14386 unbwt_decode_2(
14387 &mut u[offset..],
14388 p,
14389 bucket2,
14390 fastbits,
14391 shift,
14392 r_usize,
14393 &mut i0,
14394 &mut i1,
14395 remainder >> 1,
14396 );
14397 unbwt_decode_1(
14398 &mut u[offset + 2 * (remainder >> 1)..],
14399 p,
14400 bucket2,
14401 fastbits,
14402 shift,
14403 &mut i0,
14404 (r_usize >> 1) - (remainder >> 1),
14405 );
14406 }
14407 3 => {
14408 let mut i0 = i[i_index] as FastUint;
14409 let mut i1 = i[i_index + 1] as FastUint;
14410 let mut i2 = i[i_index + 2] as FastUint;
14411 unbwt_decode_3(
14412 &mut u[offset..],
14413 p,
14414 bucket2,
14415 fastbits,
14416 shift,
14417 r_usize,
14418 &mut i0,
14419 &mut i1,
14420 &mut i2,
14421 remainder >> 1,
14422 );
14423 unbwt_decode_2(
14424 &mut u[offset + 2 * (remainder >> 1)..],
14425 p,
14426 bucket2,
14427 fastbits,
14428 shift,
14429 r_usize,
14430 &mut i0,
14431 &mut i1,
14432 (r_usize >> 1) - (remainder >> 1),
14433 );
14434 }
14435 4 => {
14436 let mut i0 = i[i_index] as FastUint;
14437 let mut i1 = i[i_index + 1] as FastUint;
14438 let mut i2 = i[i_index + 2] as FastUint;
14439 let mut i3 = i[i_index + 3] as FastUint;
14440 unbwt_decode_4(
14441 &mut u[offset..],
14442 p,
14443 bucket2,
14444 fastbits,
14445 shift,
14446 r_usize,
14447 &mut i0,
14448 &mut i1,
14449 &mut i2,
14450 &mut i3,
14451 remainder >> 1,
14452 );
14453 unbwt_decode_3(
14454 &mut u[offset + 2 * (remainder >> 1)..],
14455 p,
14456 bucket2,
14457 fastbits,
14458 shift,
14459 r_usize,
14460 &mut i0,
14461 &mut i1,
14462 &mut i2,
14463 (r_usize >> 1) - (remainder >> 1),
14464 );
14465 }
14466 5 => {
14467 let mut i0 = i[i_index] as FastUint;
14468 let mut i1 = i[i_index + 1] as FastUint;
14469 let mut i2 = i[i_index + 2] as FastUint;
14470 let mut i3 = i[i_index + 3] as FastUint;
14471 let mut i4 = i[i_index + 4] as FastUint;
14472 unbwt_decode_5(
14473 &mut u[offset..],
14474 p,
14475 bucket2,
14476 fastbits,
14477 shift,
14478 r_usize,
14479 &mut i0,
14480 &mut i1,
14481 &mut i2,
14482 &mut i3,
14483 &mut i4,
14484 remainder >> 1,
14485 );
14486 unbwt_decode_4(
14487 &mut u[offset + 2 * (remainder >> 1)..],
14488 p,
14489 bucket2,
14490 fastbits,
14491 shift,
14492 r_usize,
14493 &mut i0,
14494 &mut i1,
14495 &mut i2,
14496 &mut i3,
14497 (r_usize >> 1) - (remainder >> 1),
14498 );
14499 }
14500 6 => {
14501 let mut i0 = i[i_index] as FastUint;
14502 let mut i1 = i[i_index + 1] as FastUint;
14503 let mut i2 = i[i_index + 2] as FastUint;
14504 let mut i3 = i[i_index + 3] as FastUint;
14505 let mut i4 = i[i_index + 4] as FastUint;
14506 let mut i5 = i[i_index + 5] as FastUint;
14507 unbwt_decode_6(
14508 &mut u[offset..],
14509 p,
14510 bucket2,
14511 fastbits,
14512 shift,
14513 r_usize,
14514 &mut i0,
14515 &mut i1,
14516 &mut i2,
14517 &mut i3,
14518 &mut i4,
14519 &mut i5,
14520 remainder >> 1,
14521 );
14522 unbwt_decode_5(
14523 &mut u[offset + 2 * (remainder >> 1)..],
14524 p,
14525 bucket2,
14526 fastbits,
14527 shift,
14528 r_usize,
14529 &mut i0,
14530 &mut i1,
14531 &mut i2,
14532 &mut i3,
14533 &mut i4,
14534 (r_usize >> 1) - (remainder >> 1),
14535 );
14536 }
14537 7 => {
14538 let mut i0 = i[i_index] as FastUint;
14539 let mut i1 = i[i_index + 1] as FastUint;
14540 let mut i2 = i[i_index + 2] as FastUint;
14541 let mut i3 = i[i_index + 3] as FastUint;
14542 let mut i4 = i[i_index + 4] as FastUint;
14543 let mut i5 = i[i_index + 5] as FastUint;
14544 let mut i6 = i[i_index + 6] as FastUint;
14545 unbwt_decode_7(
14546 &mut u[offset..],
14547 p,
14548 bucket2,
14549 fastbits,
14550 shift,
14551 r_usize,
14552 &mut i0,
14553 &mut i1,
14554 &mut i2,
14555 &mut i3,
14556 &mut i4,
14557 &mut i5,
14558 &mut i6,
14559 remainder >> 1,
14560 );
14561 unbwt_decode_6(
14562 &mut u[offset + 2 * (remainder >> 1)..],
14563 p,
14564 bucket2,
14565 fastbits,
14566 shift,
14567 r_usize,
14568 &mut i0,
14569 &mut i1,
14570 &mut i2,
14571 &mut i3,
14572 &mut i4,
14573 &mut i5,
14574 (r_usize >> 1) - (remainder >> 1),
14575 );
14576 }
14577 8 => {
14578 let mut i0 = i[i_index] as FastUint;
14579 let mut i1 = i[i_index + 1] as FastUint;
14580 let mut i2 = i[i_index + 2] as FastUint;
14581 let mut i3 = i[i_index + 3] as FastUint;
14582 let mut i4 = i[i_index + 4] as FastUint;
14583 let mut i5 = i[i_index + 5] as FastUint;
14584 let mut i6 = i[i_index + 6] as FastUint;
14585 let mut i7 = i[i_index + 7] as FastUint;
14586 unbwt_decode_8(
14587 &mut u[offset..],
14588 p,
14589 bucket2,
14590 fastbits,
14591 shift,
14592 r_usize,
14593 &mut i0,
14594 &mut i1,
14595 &mut i2,
14596 &mut i3,
14597 &mut i4,
14598 &mut i5,
14599 &mut i6,
14600 &mut i7,
14601 remainder >> 1,
14602 );
14603 unbwt_decode_7(
14604 &mut u[offset + 2 * (remainder >> 1)..],
14605 p,
14606 bucket2,
14607 fastbits,
14608 shift,
14609 r_usize,
14610 &mut i0,
14611 &mut i1,
14612 &mut i2,
14613 &mut i3,
14614 &mut i4,
14615 &mut i5,
14616 &mut i6,
14617 (r_usize >> 1) - (remainder >> 1),
14618 );
14619 }
14620 _ => {}
14621 }
14622}
14623
14624#[doc(hidden)]
14626pub fn unbwt_decode_omp(
14627 t: &[u8],
14628 u: &mut [u8],
14629 p: &[SaUint],
14630 n: SaSint,
14631 r: SaSint,
14632 i: &[SaUint],
14633 bucket2: &[SaUint],
14634 fastbits: &[u16],
14635 threads: SaSint,
14636) {
14637 let lastc = t[0];
14638 let blocks = 1 + ((n as FastSint - 1) / r as FastSint);
14639 let remainder = usize::try_from(n).expect("n must be non-negative")
14640 - usize::try_from(r).expect("r must be non-negative")
14641 * (usize::try_from(blocks).expect("blocks") - 1);
14642 let max_threads = usize::try_from(blocks.min(threads.max(1) as FastSint))
14643 .expect("thread count must fit usize");
14644 let block_stride = usize::try_from(blocks).expect("blocks must be non-negative") / max_threads;
14645 let block_remainder =
14646 usize::try_from(blocks).expect("blocks must be non-negative") % max_threads;
14647 let r_usize = usize::try_from(r).expect("r must be non-negative");
14648
14649 let u_ptr = SyncMutPtr::new(u);
14650 run_rayon_with_threads(max_threads, || {
14651 (0..max_threads).into_par_iter().for_each(|thread| {
14652 let block_size = block_stride + usize::from(thread < block_remainder);
14653 let block_start = block_stride * thread + thread.min(block_remainder);
14654 let u = unsafe { u_ptr.as_slice() };
14655 unbwt_decode(
14656 &mut u[r_usize * block_start..],
14657 p,
14658 n,
14659 r,
14660 &i[block_start..],
14661 bucket2,
14662 fastbits,
14663 block_size as FastSint,
14664 if thread + 1 < max_threads {
14665 r_usize
14666 } else {
14667 remainder
14668 },
14669 );
14670 });
14671 });
14672 u[usize::try_from(n).expect("n must be non-negative") - 1] = lastc;
14673}
14674
14675#[doc(hidden)]
14677pub fn unbwt_core(
14678 t: &[u8],
14679 u: &mut [u8],
14680 p: &mut [SaUint],
14681 n: SaSint,
14682 freq: Option<&[SaSint]>,
14683 r: SaSint,
14684 i: &[SaUint],
14685 bucket2: &mut [SaUint],
14686 fastbits: &mut [u16],
14687 buckets: Option<&mut [SaUint]>,
14688 threads: SaSint,
14689) -> SaSint {
14690 if threads > 1 && n >= 262_144 {
14691 unbwt_init_parallel(t, p, n, freq, i, bucket2, fastbits, buckets, threads);
14692 } else {
14693 unbwt_init_single(t, p, n, freq, i, bucket2, fastbits);
14694 }
14695
14696 unbwt_decode_omp(t, u, p, n, r, i, bucket2, fastbits, threads);
14697 0
14698}
14699
14700#[doc(hidden)]
14702pub fn unbwt_main(
14703 t: &[u8],
14704 u: &mut [u8],
14705 p: &mut [SaUint],
14706 n: SaSint,
14707 freq: Option<&[SaSint]>,
14708 r: SaSint,
14709 i: &[SaUint],
14710 threads: SaSint,
14711) -> SaSint {
14712 let mut shift = 0usize;
14713 while (usize::try_from(n).expect("n must be non-negative") >> shift)
14714 > (1usize << UNBWT_FASTBITS)
14715 {
14716 shift += 1;
14717 }
14718
14719 let mut bucket2 = vec![0u64; ALPHABET_SIZE * ALPHABET_SIZE];
14720 let mut fastbits =
14721 vec![0u16; 1 + (usize::try_from(n).expect("n must be non-negative") >> shift)];
14722 let mut buckets = if threads > 1 && n >= 262_144 {
14723 Some(vec![
14724 0u64;
14725 usize::try_from(threads)
14726 .expect("threads must be non-negative")
14727 * (ALPHABET_SIZE + ALPHABET_SIZE * ALPHABET_SIZE)
14728 ])
14729 } else {
14730 None
14731 };
14732
14733 unbwt_core(
14734 t,
14735 u,
14736 p,
14737 n,
14738 freq,
14739 r,
14740 i,
14741 &mut bucket2,
14742 &mut fastbits,
14743 buckets.as_deref_mut(),
14744 threads,
14745 )
14746}
14747
14748#[doc(hidden)]
14750pub fn unbwt_main_ctx(
14751 ctx: &mut UnbwtContext,
14752 t: &[u8],
14753 u: &mut [u8],
14754 p: &mut [SaUint],
14755 n: SaSint,
14756 freq: Option<&[SaSint]>,
14757 r: SaSint,
14758 i: &[SaUint],
14759) -> SaSint {
14760 if ctx.threads <= 0 {
14761 return -2;
14762 }
14763 let mut shift = 0usize;
14764 while (usize::try_from(n).expect("n must be non-negative") >> shift)
14765 > (1usize << UNBWT_FASTBITS)
14766 {
14767 shift += 1;
14768 }
14769 let required_fastbits = 1 + (usize::try_from(n).expect("n must be non-negative") >> shift);
14770 if ctx.bucket2.len() < ALPHABET_SIZE * ALPHABET_SIZE
14771 || ctx.fastbits.len() < required_fastbits
14772 || (ctx.threads > 1 && ctx.buckets.is_none())
14773 {
14774 return -2;
14775 }
14776
14777 unbwt_core(
14778 t,
14779 u,
14780 p,
14781 n,
14782 freq,
14783 r,
14784 i,
14785 &mut ctx.bucket2,
14786 &mut ctx.fastbits,
14787 ctx.buckets.as_deref_mut(),
14788 ctx.threads as SaSint,
14789 )
14790}
14791
14792pub fn libsais64_unbwt(
14804 t: &[u8],
14805 u: &mut [u8],
14806 a: &mut [SaSint],
14807 freq: Option<&[SaSint]>,
14808 i: SaSint,
14809) -> SaSint {
14810 libsais64_unbwt_aux(
14811 t,
14812 u,
14813 a,
14814 freq,
14815 SaSint::try_from(t.len()).expect("input length must fit SaSint"),
14816 &[i],
14817 )
14818}
14819
14820pub fn libsais64_unbwt_ctx(
14831 ctx: &mut UnbwtContext,
14832 t: &[u8],
14833 u: &mut [u8],
14834 a: &mut [SaSint],
14835 freq: Option<&[SaSint]>,
14836 i: SaSint,
14837) -> SaSint {
14838 libsais64_unbwt_aux_ctx(
14839 ctx,
14840 t,
14841 u,
14842 a,
14843 freq,
14844 SaSint::try_from(t.len()).expect("input length must fit SaSint"),
14845 &[i],
14846 )
14847}
14848
14849pub fn libsais64_unbwt_aux(
14862 t: &[u8],
14863 u: &mut [u8],
14864 a: &mut [SaSint],
14865 freq: Option<&[SaSint]>,
14866 r: SaSint,
14867 i: &[SaSint],
14868) -> SaSint {
14869 let t_len = t.len();
14870 let n = SaSint::try_from(t_len).expect("input length must fit SaSint");
14871 if u.len() < t_len
14872 || a.len() < t_len
14873 || freq.is_some_and(|freq| freq.len() < ALPHABET_SIZE)
14874 || (r != n && (r < 2 || (r & (r - 1)) != 0))
14875 {
14876 return -1;
14877 }
14878 let sample_count = if n == 0 {
14879 1
14880 } else {
14881 ((n - 1) / r + 1) as usize
14882 };
14883 if i.len() < sample_count {
14884 return -1;
14885 }
14886
14887 if n <= 1 {
14888 if i[0] != n {
14889 return -1;
14890 }
14891 if n == 1 {
14892 u[0] = t[0];
14893 }
14894 return 0;
14895 }
14896
14897 for t in 0..sample_count {
14898 let sample = i[t];
14899 if sample <= 0 || sample > n {
14900 return -1;
14901 }
14902 }
14903
14904 let i_uint: Vec<SaUint> = i
14905 .iter()
14906 .take(sample_count)
14907 .map(|&sample| SaUint::try_from(sample).expect("sample was validated positive"))
14908 .collect();
14909 let mut p = vec![0u64; t_len + 1];
14910 let result = unbwt_main(t, u, &mut p, n, freq, r, &i_uint, 1);
14911 for t in 0..t_len {
14912 a[t] = p[t] as SaSint;
14913 }
14914 result
14915}
14916
14917pub fn libsais64_unbwt_aux_ctx(
14929 ctx: &mut UnbwtContext,
14930 t: &[u8],
14931 u: &mut [u8],
14932 a: &mut [SaSint],
14933 freq: Option<&[SaSint]>,
14934 r: SaSint,
14935 i: &[SaSint],
14936) -> SaSint {
14937 let t_len = t.len();
14938 let n = SaSint::try_from(t_len).expect("input length must fit SaSint");
14939 if u.len() < t_len
14940 || a.len() < t_len
14941 || freq.is_some_and(|freq| freq.len() < ALPHABET_SIZE)
14942 || (r != n && (r < 2 || (r & (r - 1)) != 0))
14943 {
14944 return -1;
14945 }
14946 let sample_count = if n == 0 {
14947 1
14948 } else {
14949 ((n - 1) / r + 1) as usize
14950 };
14951 if i.len() < sample_count {
14952 return -1;
14953 }
14954
14955 if n <= 1 {
14956 if i[0] != n {
14957 return -1;
14958 }
14959 if n == 1 {
14960 u[0] = t[0];
14961 }
14962 return 0;
14963 }
14964
14965 for t in 0..sample_count {
14966 let sample = i[t];
14967 if sample <= 0 || sample > n {
14968 return -1;
14969 }
14970 }
14971
14972 let i_uint: Vec<SaUint> = i
14973 .iter()
14974 .take(sample_count)
14975 .map(|&sample| SaUint::try_from(sample).expect("sample was validated positive"))
14976 .collect();
14977 let mut p = vec![0u64; t_len + 1];
14978 let result = unbwt_main_ctx(ctx, t, u, &mut p, n, freq, r, &i_uint);
14979 for t in 0..t_len {
14980 a[t] = p[t] as SaSint;
14981 }
14982 result
14983}
14984
14985pub fn unbwt_create_ctx_omp(threads: SaSint) -> Option<UnbwtContext> {
14993 if threads < 0 {
14994 return None;
14995 }
14996 unbwt_create_ctx_main(normalize_omp_threads(threads))
14997}
14998
14999pub fn libsais64_unbwt_omp(
15012 t: &[u8],
15013 u: &mut [u8],
15014 a: &mut [SaSint],
15015 freq: Option<&[SaSint]>,
15016 i: SaSint,
15017 threads: SaSint,
15018) -> SaSint {
15019 libsais64_unbwt_aux_omp(
15020 t,
15021 u,
15022 a,
15023 freq,
15024 SaSint::try_from(t.len()).expect("input length must fit SaSint"),
15025 &[i],
15026 threads,
15027 )
15028}
15029
15030pub fn libsais64_unbwt_aux_omp(
15044 t: &[u8],
15045 u: &mut [u8],
15046 a: &mut [SaSint],
15047 freq: Option<&[SaSint]>,
15048 r: SaSint,
15049 i: &[SaSint],
15050 threads: SaSint,
15051) -> SaSint {
15052 let t_len = t.len();
15053 let n = SaSint::try_from(t_len).expect("input length must fit SaSint");
15054 if threads < 0
15055 || u.len() < t_len
15056 || a.len() < t_len
15057 || freq.is_some_and(|freq| freq.len() < ALPHABET_SIZE)
15058 || (r != n && (r < 2 || (r & (r - 1)) != 0))
15059 {
15060 return -1;
15061 }
15062 let sample_count = if n == 0 {
15063 1
15064 } else {
15065 ((n - 1) / r + 1) as usize
15066 };
15067 if i.len() < sample_count {
15068 return -1;
15069 }
15070
15071 if n <= 1 {
15072 if i[0] != n {
15073 return -1;
15074 }
15075 if n == 1 {
15076 u[0] = t[0];
15077 }
15078 return 0;
15079 }
15080
15081 for sample in i.iter().take(sample_count) {
15082 let sample = *sample;
15083 if sample <= 0 || sample > n {
15084 return -1;
15085 }
15086 }
15087
15088 let threads = if threads > 0 { threads } else { 1 };
15089 let i_uint: Vec<SaUint> = i
15090 .iter()
15091 .take(sample_count)
15092 .map(|&sample| SaUint::try_from(sample).expect("sample was validated positive"))
15093 .collect();
15094 let mut p = vec![0u64; t_len + 1];
15095 let result = unbwt_main(t, u, &mut p, n, freq, r, &i_uint, threads);
15096 for idx in 0..t_len {
15097 a[idx] = p[idx] as SaSint;
15098 }
15099 result
15100}
15101
15102#[doc(hidden)]
15104pub fn bwt_copy_8u(u: &mut [u8], a: &[SaSint], n: SaSint) {
15105 if n <= 0 {
15106 return;
15107 }
15108
15109 let n_usize = usize::try_from(n).expect("n must be non-negative");
15110 for i in 0..n_usize {
15111 u[i] = a[i] as u8;
15112 }
15113}
15114
15115#[doc(hidden)]
15117pub fn bwt_copy_8u_omp(u: &mut [u8], a: &[SaSint], n: SaSint, threads: SaSint) {
15118 if threads == 1 || n < 65_536 {
15119 bwt_copy_8u(u, a, n);
15120 return;
15121 }
15122
15123 let n_usize = usize::try_from(n).expect("n must be non-negative");
15124 assert!(u.len() >= n_usize);
15125 assert!(a.len() >= n_usize);
15126 let threads_usize = usize::try_from(threads).expect("threads must be non-negative");
15127 let chunk_size = ((n_usize / threads_usize) & !15usize).max(16);
15128 let a_ptr = a.as_ptr() as usize;
15129 run_rayon_with_threads(threads_usize, || {
15130 u[..n_usize]
15131 .par_chunks_mut(chunk_size)
15132 .enumerate()
15133 .for_each(|(chunk_index, chunk)| {
15134 let start = chunk_index * chunk_size;
15135 let dst_ptr = chunk.as_mut_ptr();
15136 let src_ptr = unsafe { (a_ptr as *const SaSint).add(start) };
15137 for offset in 0..chunk.len() {
15138 unsafe {
15139 *dst_ptr.add(offset) = *src_ptr.add(offset) as u8;
15140 }
15141 }
15142 });
15143 });
15144}
15145
15146#[doc(hidden)]
15148pub fn accumulate_counts_s32_2(bucket00: &mut [SaSint], bucket01: &[SaSint]) {
15149 assert_eq!(bucket00.len(), bucket01.len());
15150 for (dst, src) in bucket00.iter_mut().zip(bucket01.iter()) {
15151 *dst += *src;
15152 }
15153}
15154
15155#[doc(hidden)]
15157pub fn accumulate_counts_s32_3(bucket00: &mut [SaSint], bucket01: &[SaSint], bucket02: &[SaSint]) {
15158 assert_eq!(bucket00.len(), bucket01.len());
15159 assert_eq!(bucket00.len(), bucket02.len());
15160 for ((dst, src1), src2) in bucket00
15161 .iter_mut()
15162 .zip(bucket01.iter())
15163 .zip(bucket02.iter())
15164 {
15165 *dst += *src1 + *src2;
15166 }
15167}
15168
15169#[doc(hidden)]
15171pub fn accumulate_counts_s32_4(
15172 bucket00: &mut [SaSint],
15173 bucket01: &[SaSint],
15174 bucket02: &[SaSint],
15175 bucket03: &[SaSint],
15176) {
15177 assert_eq!(bucket00.len(), bucket01.len());
15178 assert_eq!(bucket00.len(), bucket02.len());
15179 assert_eq!(bucket00.len(), bucket03.len());
15180 for (((dst, src1), src2), src3) in bucket00
15181 .iter_mut()
15182 .zip(bucket01.iter())
15183 .zip(bucket02.iter())
15184 .zip(bucket03.iter())
15185 {
15186 *dst += *src1 + *src2 + *src3;
15187 }
15188}
15189
15190#[doc(hidden)]
15192pub fn accumulate_counts_s32_5(
15193 bucket00: &mut [SaSint],
15194 bucket01: &[SaSint],
15195 bucket02: &[SaSint],
15196 bucket03: &[SaSint],
15197 bucket04: &[SaSint],
15198) {
15199 assert_eq!(bucket00.len(), bucket01.len());
15200 assert_eq!(bucket00.len(), bucket02.len());
15201 assert_eq!(bucket00.len(), bucket03.len());
15202 assert_eq!(bucket00.len(), bucket04.len());
15203 for ((((dst, src1), src2), src3), src4) in bucket00
15204 .iter_mut()
15205 .zip(bucket01.iter())
15206 .zip(bucket02.iter())
15207 .zip(bucket03.iter())
15208 .zip(bucket04.iter())
15209 {
15210 *dst += *src1 + *src2 + *src3 + *src4;
15211 }
15212}
15213
15214#[doc(hidden)]
15216pub fn accumulate_counts_s32_6(
15217 bucket00: &mut [SaSint],
15218 bucket01: &[SaSint],
15219 bucket02: &[SaSint],
15220 bucket03: &[SaSint],
15221 bucket04: &[SaSint],
15222 bucket05: &[SaSint],
15223) {
15224 assert_eq!(bucket00.len(), bucket01.len());
15225 assert_eq!(bucket00.len(), bucket02.len());
15226 assert_eq!(bucket00.len(), bucket03.len());
15227 assert_eq!(bucket00.len(), bucket04.len());
15228 assert_eq!(bucket00.len(), bucket05.len());
15229 for (((((dst, src1), src2), src3), src4), src5) in bucket00
15230 .iter_mut()
15231 .zip(bucket01.iter())
15232 .zip(bucket02.iter())
15233 .zip(bucket03.iter())
15234 .zip(bucket04.iter())
15235 .zip(bucket05.iter())
15236 {
15237 *dst += *src1 + *src2 + *src3 + *src4 + *src5;
15238 }
15239}
15240
15241#[doc(hidden)]
15243pub fn accumulate_counts_s32_7(
15244 bucket00: &mut [SaSint],
15245 bucket01: &[SaSint],
15246 bucket02: &[SaSint],
15247 bucket03: &[SaSint],
15248 bucket04: &[SaSint],
15249 bucket05: &[SaSint],
15250 bucket06: &[SaSint],
15251) {
15252 assert_eq!(bucket00.len(), bucket01.len());
15253 assert_eq!(bucket00.len(), bucket02.len());
15254 assert_eq!(bucket00.len(), bucket03.len());
15255 assert_eq!(bucket00.len(), bucket04.len());
15256 assert_eq!(bucket00.len(), bucket05.len());
15257 assert_eq!(bucket00.len(), bucket06.len());
15258 for ((((((dst, src1), src2), src3), src4), src5), src6) in bucket00
15259 .iter_mut()
15260 .zip(bucket01.iter())
15261 .zip(bucket02.iter())
15262 .zip(bucket03.iter())
15263 .zip(bucket04.iter())
15264 .zip(bucket05.iter())
15265 .zip(bucket06.iter())
15266 {
15267 *dst += *src1 + *src2 + *src3 + *src4 + *src5 + *src6;
15268 }
15269}
15270
15271#[doc(hidden)]
15273pub fn accumulate_counts_s32_8(
15274 bucket00: &mut [SaSint],
15275 bucket01: &[SaSint],
15276 bucket02: &[SaSint],
15277 bucket03: &[SaSint],
15278 bucket04: &[SaSint],
15279 bucket05: &[SaSint],
15280 bucket06: &[SaSint],
15281 bucket07: &[SaSint],
15282) {
15283 assert_eq!(bucket00.len(), bucket01.len());
15284 assert_eq!(bucket00.len(), bucket02.len());
15285 assert_eq!(bucket00.len(), bucket03.len());
15286 assert_eq!(bucket00.len(), bucket04.len());
15287 assert_eq!(bucket00.len(), bucket05.len());
15288 assert_eq!(bucket00.len(), bucket06.len());
15289 assert_eq!(bucket00.len(), bucket07.len());
15290 for (((((((dst, src1), src2), src3), src4), src5), src6), src7) in bucket00
15291 .iter_mut()
15292 .zip(bucket01.iter())
15293 .zip(bucket02.iter())
15294 .zip(bucket03.iter())
15295 .zip(bucket04.iter())
15296 .zip(bucket05.iter())
15297 .zip(bucket06.iter())
15298 .zip(bucket07.iter())
15299 {
15300 *dst += *src1 + *src2 + *src3 + *src4 + *src5 + *src6 + *src7;
15301 }
15302}
15303
15304#[doc(hidden)]
15306pub fn accumulate_counts_s32_9(
15307 bucket00: &mut [SaSint],
15308 bucket01: &[SaSint],
15309 bucket02: &[SaSint],
15310 bucket03: &[SaSint],
15311 bucket04: &[SaSint],
15312 bucket05: &[SaSint],
15313 bucket06: &[SaSint],
15314 bucket07: &[SaSint],
15315 bucket08: &[SaSint],
15316) {
15317 assert_eq!(bucket00.len(), bucket01.len());
15318 assert_eq!(bucket00.len(), bucket02.len());
15319 assert_eq!(bucket00.len(), bucket03.len());
15320 assert_eq!(bucket00.len(), bucket04.len());
15321 assert_eq!(bucket00.len(), bucket05.len());
15322 assert_eq!(bucket00.len(), bucket06.len());
15323 assert_eq!(bucket00.len(), bucket07.len());
15324 assert_eq!(bucket00.len(), bucket08.len());
15325 for ((((((((dst, src1), src2), src3), src4), src5), src6), src7), src8) in bucket00
15326 .iter_mut()
15327 .zip(bucket01.iter())
15328 .zip(bucket02.iter())
15329 .zip(bucket03.iter())
15330 .zip(bucket04.iter())
15331 .zip(bucket05.iter())
15332 .zip(bucket06.iter())
15333 .zip(bucket07.iter())
15334 .zip(bucket08.iter())
15335 {
15336 *dst += *src1 + *src2 + *src3 + *src4 + *src5 + *src6 + *src7 + *src8;
15337 }
15338}
15339
15340#[doc(hidden)]
15342pub fn accumulate_counts_s32(
15343 buckets: &mut [SaSint],
15344 bucket_size: FastSint,
15345 bucket_stride: FastSint,
15346 mut num_buckets: FastSint,
15347) {
15348 if num_buckets <= 1 {
15349 return;
15350 }
15351
15352 let bucket_size = usize::try_from(bucket_size).expect("bucket_size must be non-negative");
15353 let bucket_stride = usize::try_from(bucket_stride).expect("bucket_stride must be non-negative");
15354 let num_buckets_usize = usize::try_from(num_buckets).expect("num_buckets must be non-negative");
15355 assert!(buckets.len() >= bucket_size + (num_buckets_usize - 1) * bucket_stride);
15356 let bucket00_start = (num_buckets_usize - 1) * bucket_stride;
15357
15358 while num_buckets >= 9 {
15359 let start = bucket00_start
15360 - usize::try_from(num_buckets - 9).expect("non-negative") * bucket_stride;
15361 accumulate_counts_at(buckets, start, bucket_size, bucket_stride, 9);
15362 num_buckets -= 8;
15363 }
15364
15365 match num_buckets {
15366 1 => {}
15367 2..=8 => accumulate_counts_at(
15368 buckets,
15369 bucket00_start,
15370 bucket_size,
15371 bucket_stride,
15372 usize::try_from(num_buckets).expect("non-negative"),
15373 ),
15374 _ => {}
15375 }
15376}
15377
15378fn block_slice<T>(slice: &[T], block_start: FastSint, block_size: FastSint) -> &[T] {
15379 let start = usize::try_from(block_start).expect("block_start must be non-negative");
15380 let len = usize::try_from(block_size).expect("block_size must be non-negative");
15381 &slice[start..start + len]
15382}
15383
15384#[allow(dead_code)]
15385struct SharedMutArray<'a> {
15386 ptr: *mut SaSint,
15387 len: usize,
15388 _marker: PhantomData<&'a mut [SaSint]>,
15389}
15390
15391#[allow(dead_code)]
15392impl<'a> SharedMutArray<'a> {
15393 fn new(slice: &'a mut [SaSint]) -> Self {
15394 Self {
15395 ptr: slice.as_mut_ptr(),
15396 len: slice.len(),
15397 _marker: PhantomData,
15398 }
15399 }
15400
15401 fn len(&self) -> usize {
15402 self.len
15403 }
15404
15405 fn slice_mut(&mut self, start: usize, len: usize) -> &mut [SaSint] {
15406 assert!(start <= self.len);
15407 assert!(len <= self.len - start);
15408 unsafe {
15409 std::slice::from_raw_parts_mut(self.ptr.add(start), len)
15413 }
15414 }
15415}
15416
15417fn accumulate_counts_at(
15418 buckets: &mut [SaSint],
15419 bucket00_start: usize,
15420 bucket_size: usize,
15421 bucket_stride: usize,
15422 count: usize,
15423) {
15424 assert!((2..=9).contains(&count));
15425 assert!(bucket00_start >= (count - 1) * bucket_stride);
15426
15427 let dst_end = bucket00_start + bucket_size;
15428 let mut sums = vec![0; bucket_size];
15429
15430 for i in 0..count {
15431 let start = bucket00_start - i * bucket_stride;
15432 let end = start + bucket_size;
15433 for (sum, value) in sums.iter_mut().zip(buckets[start..end].iter()) {
15434 *sum += *value;
15435 }
15436 }
15437
15438 buckets[bucket00_start..dst_end].copy_from_slice(&sums);
15439}
15440
15441#[doc(hidden)]
15443pub fn thread_state_size() -> usize {
15444 mem::size_of::<ThreadState>()
15445}
15446
15447#[cfg(all(test, feature = "upstream-c"))]
15448mod tests {
15449 use super::*;
15450
15451 unsafe extern "C" {
15452 fn probe_public_libsais64(t: *const u8, sa: *mut SaSint, n: SaSint, fs: SaSint) -> SaSint;
15453 fn probe_public_libsais64_freq(
15454 t: *const u8,
15455 sa: *mut SaSint,
15456 n: SaSint,
15457 fs: SaSint,
15458 freq: *mut SaSint,
15459 ) -> SaSint;
15460 fn probe_public_libsais64_gsa(
15461 t: *const u8,
15462 sa: *mut SaSint,
15463 n: SaSint,
15464 fs: SaSint,
15465 ) -> SaSint;
15466 fn probe_public_libsais64_gsa_freq(
15467 t: *const u8,
15468 sa: *mut SaSint,
15469 n: SaSint,
15470 fs: SaSint,
15471 freq: *mut SaSint,
15472 ) -> SaSint;
15473 fn probe_public_libsais64_long(
15474 t: *mut SaSint,
15475 sa: *mut SaSint,
15476 n: SaSint,
15477 k: SaSint,
15478 fs: SaSint,
15479 ) -> SaSint;
15480 fn probe_public_libsais64_bwt(
15481 t: *const u8,
15482 u: *mut u8,
15483 a: *mut SaSint,
15484 n: SaSint,
15485 fs: SaSint,
15486 ) -> SaSint;
15487 fn probe_public_libsais64_bwt_freq(
15488 t: *const u8,
15489 u: *mut u8,
15490 a: *mut SaSint,
15491 n: SaSint,
15492 fs: SaSint,
15493 freq: *mut SaSint,
15494 ) -> SaSint;
15495 fn probe_public_libsais64_bwt_aux(
15496 t: *const u8,
15497 u: *mut u8,
15498 a: *mut SaSint,
15499 n: SaSint,
15500 fs: SaSint,
15501 r: SaSint,
15502 i: *mut SaSint,
15503 ) -> SaSint;
15504 fn probe_public_libsais64_bwt_aux_freq(
15505 t: *const u8,
15506 u: *mut u8,
15507 a: *mut SaSint,
15508 n: SaSint,
15509 fs: SaSint,
15510 freq: *mut SaSint,
15511 r: SaSint,
15512 i: *mut SaSint,
15513 ) -> SaSint;
15514 fn probe_public_libsais64_unbwt(
15515 t: *const u8,
15516 u: *mut u8,
15517 a: *mut SaSint,
15518 n: SaSint,
15519 i: SaSint,
15520 ) -> SaSint;
15521 fn probe_public_libsais64_unbwt_freq(
15522 t: *const u8,
15523 u: *mut u8,
15524 a: *mut SaSint,
15525 n: SaSint,
15526 freq: *const SaSint,
15527 i: SaSint,
15528 ) -> SaSint;
15529 fn probe_public_libsais64_unbwt_aux(
15530 t: *const u8,
15531 u: *mut u8,
15532 a: *mut SaSint,
15533 n: SaSint,
15534 r: SaSint,
15535 i: *const SaSint,
15536 ) -> SaSint;
15537 fn probe_public_libsais64_unbwt_aux_freq(
15538 t: *const u8,
15539 u: *mut u8,
15540 a: *mut SaSint,
15541 n: SaSint,
15542 freq: *const SaSint,
15543 r: SaSint,
15544 i: *const SaSint,
15545 ) -> SaSint;
15546 fn probe_public_libsais64_plcp(
15547 t: *const u8,
15548 sa: *const SaSint,
15549 plcp: *mut SaSint,
15550 n: SaSint,
15551 ) -> SaSint;
15552 fn probe_public_libsais64_plcp_gsa(
15553 t: *const u8,
15554 sa: *const SaSint,
15555 plcp: *mut SaSint,
15556 n: SaSint,
15557 ) -> SaSint;
15558 fn probe_public_libsais64_lcp(
15559 plcp: *const SaSint,
15560 sa: *const SaSint,
15561 lcp: *mut SaSint,
15562 n: SaSint,
15563 ) -> SaSint;
15564 fn probe_libsais64_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 fn probe_libsais64_gather_marked_lms_suffixes(
15572 sa: *mut SaSint,
15573 m: SaSint,
15574 l: FastSint,
15575 omp_block_start: FastSint,
15576 omp_block_size: FastSint,
15577 ) -> FastSint;
15578 fn probe_libsais64_renumber_and_gather_lms_suffixes_omp(
15579 sa: *mut SaSint,
15580 n: SaSint,
15581 m: SaSint,
15582 fs: SaSint,
15583 threads: SaSint,
15584 ) -> SaSint;
15585 fn probe_libsais64_renumber_distinct_lms_suffixes_32s_4k(
15586 sa: *mut SaSint,
15587 m: SaSint,
15588 name: SaSint,
15589 omp_block_start: FastSint,
15590 omp_block_size: FastSint,
15591 ) -> SaSint;
15592 fn probe_libsais64_renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
15593 sa: *mut SaSint,
15594 n: SaSint,
15595 m: SaSint,
15596 threads: SaSint,
15597 ) -> SaSint;
15598 fn probe_libsais64_renumber_unique_and_nonunique_lms_suffixes_32s(
15599 t: *mut SaSint,
15600 sa: *mut SaSint,
15601 m: SaSint,
15602 f: SaSint,
15603 omp_block_start: FastSint,
15604 omp_block_size: FastSint,
15605 ) -> SaSint;
15606 fn probe_libsais64_renumber_unique_and_nonunique_lms_suffixes_32s_omp(
15607 t: *mut SaSint,
15608 sa: *mut SaSint,
15609 m: SaSint,
15610 threads: SaSint,
15611 ) -> SaSint;
15612 }
15613
15614 #[test]
15615 fn libsais64_align_up_matches_power_of_two_alignment() {
15616 assert_eq!(align_up(0, 4096), 0);
15617 assert_eq!(align_up(1, 4096), 4096);
15618 assert_eq!(align_up(4095, 4096), 4096);
15619 assert_eq!(align_up(4096, 4096), 4096);
15620 assert_eq!(align_up(4097, 4096), 8192);
15621 assert_eq!(align_up(65, 64), 128);
15622 }
15623
15624 #[test]
15625 fn libsais64_shared_mut_array_projects_mutable_spans_from_one_backing_buffer() {
15626 let mut backing = vec![1, 2, 3, 4, 5, 6];
15627 let len;
15628 {
15629 let mut shared = SharedMutArray::new(&mut backing);
15630 shared.slice_mut(1, 3).copy_from_slice(&[20, 30, 40]);
15631 shared.slice_mut(4, 2).copy_from_slice(&[50, 60]);
15632 len = shared.len();
15633 }
15634 assert_eq!(backing, vec![1, 20, 30, 40, 50, 60]);
15635 assert_eq!(len, 6);
15636 }
15637
15638 #[test]
15639 fn libsais64_create_ctx_main_matches_single_thread_layout() {
15640 let ctx = create_ctx_main(1).expect("context");
15641 assert_eq!(ctx.buckets.len(), 8 * ALPHABET_SIZE);
15642 assert_eq!(ctx.threads, 1);
15643 assert!(ctx.thread_state.is_none());
15644 }
15645
15646 #[test]
15647 fn libsais64_create_ctx_main_allocates_thread_state_for_multi_threaded_mode() {
15648 let ctx = create_ctx_main(3).expect("context");
15649 let states = ctx.thread_state.expect("thread state");
15650 assert_eq!(states.len(), 3);
15651 assert!(states
15652 .iter()
15653 .all(|state| state.buckets.len() == 4 * ALPHABET_SIZE));
15654 assert!(states
15655 .iter()
15656 .all(|state| state.cache.len() == LIBSAIS_PER_THREAD_CACHE_SIZE));
15657 }
15658
15659 #[test]
15660 fn libsais64_create_ctx_wraps_single_thread_main_context() {
15661 let ctx = create_ctx().expect("context");
15662 assert_eq!(ctx.threads, 1);
15663 assert_eq!(ctx.buckets.len(), 8 * ALPHABET_SIZE);
15664 assert!(ctx.thread_state.is_none());
15665 }
15666
15667 #[test]
15668 fn libsais64_free_ctx_accepts_context_value() {
15669 let ctx = create_ctx().expect("context");
15670 free_ctx(ctx);
15671 }
15672
15673 #[test]
15674 fn libsais64_unbwt_create_ctx_main_allocates_expected_buffers() {
15675 let ctx = unbwt_create_ctx_main(3).expect("context");
15676 assert_eq!(ctx.bucket2.len(), ALPHABET_SIZE * ALPHABET_SIZE);
15677 assert_eq!(ctx.fastbits.len(), 1 + (1 << UNBWT_FASTBITS));
15678 assert_eq!(
15679 ctx.buckets.as_ref().expect("parallel buckets").len(),
15680 3 * (ALPHABET_SIZE + ALPHABET_SIZE * ALPHABET_SIZE)
15681 );
15682 assert_eq!(ctx.threads, 3);
15683 }
15684
15685 #[test]
15686 fn libsais64_unbwt_compute_histogram_counts_bytes() {
15687 let t = b"banana";
15688 let mut count = vec![0u64; ALPHABET_SIZE];
15689 unbwt_compute_histogram(t, t.len() as FastSint, &mut count);
15690 assert_eq!(count[b'a' as usize], 3);
15691 assert_eq!(count[b'b' as usize], 1);
15692 assert_eq!(count[b'n' as usize], 2);
15693 }
15694
15695 #[test]
15696 fn libsais64_unbwt_transpose_bucket2_swaps_matrix_entries() {
15697 let mut bucket2 = vec![0u64; ALPHABET_SIZE * ALPHABET_SIZE];
15698 bucket2[(2 << 8) + 1] = 7;
15699 bucket2[(1 << 8) + 2] = 9;
15700 unbwt_transpose_bucket2(&mut bucket2);
15701 assert_eq!(bucket2[(1 << 8) + 2], 7);
15702 assert_eq!(bucket2[(2 << 8) + 1], 9);
15703 }
15704
15705 #[test]
15706 fn libsais64_unbwt_init_single_builds_monotone_fastbits_and_writes_psi() {
15707 let t = b"annb\x00aa";
15708 let mut p = vec![0u64; t.len() + 1];
15709 let mut bucket2 = vec![0u64; ALPHABET_SIZE * ALPHABET_SIZE];
15710 let mut fastbits = vec![0u16; 1 + (1 << UNBWT_FASTBITS)];
15711 let i = vec![4u64];
15712
15713 unbwt_init_single(
15714 t,
15715 &mut p,
15716 t.len() as SaSint,
15717 None,
15718 &i,
15719 &mut bucket2,
15720 &mut fastbits,
15721 );
15722
15723 assert!(fastbits
15724 .iter()
15725 .all(|&value| usize::from(value) < ALPHABET_SIZE * ALPHABET_SIZE));
15726 assert!(fastbits.iter().any(|&value| value != 0));
15727 assert!(p.iter().any(|&value| value != 0));
15728 }
15729
15730 #[test]
15731 fn libsais64_unbwt_init_parallel_currently_matches_single_initializer() {
15732 let t = b"annb\x00aa";
15733 let mut p_single = vec![0u64; t.len() + 1];
15734 let mut p_parallel = vec![0u64; t.len() + 1];
15735 let mut bucket2_single = vec![0u64; ALPHABET_SIZE * ALPHABET_SIZE];
15736 let mut bucket2_parallel = vec![0u64; ALPHABET_SIZE * ALPHABET_SIZE];
15737 let mut fastbits_single = vec![0u16; 1 + (1 << UNBWT_FASTBITS)];
15738 let mut fastbits_parallel = vec![0u16; 1 + (1 << UNBWT_FASTBITS)];
15739 let i = vec![4u64];
15740 let mut scratch = vec![0u64; 2 * (ALPHABET_SIZE + ALPHABET_SIZE * ALPHABET_SIZE)];
15741
15742 unbwt_init_single(
15743 t,
15744 &mut p_single,
15745 t.len() as SaSint,
15746 None,
15747 &i,
15748 &mut bucket2_single,
15749 &mut fastbits_single,
15750 );
15751 unbwt_init_parallel(
15752 t,
15753 &mut p_parallel,
15754 t.len() as SaSint,
15755 None,
15756 &i,
15757 &mut bucket2_parallel,
15758 &mut fastbits_parallel,
15759 Some(&mut scratch),
15760 2,
15761 );
15762
15763 assert_eq!(p_parallel, p_single);
15764 assert_eq!(bucket2_parallel, bucket2_single);
15765 assert_eq!(fastbits_parallel, fastbits_single);
15766 }
15767
15768 #[test]
15769 fn libsais64_unbwt_decode_1_writes_big_endian_symbol_words() {
15770 let mut u = vec![0u8; 4];
15771 let p = vec![1u64, 0u64];
15772 let mut bucket2 = vec![0u64; ALPHABET_SIZE * ALPHABET_SIZE];
15773 bucket2[0x1234] = 0;
15774 bucket2[0x1235] = 2;
15775 let mut fastbits = vec![0u16; 1 + (1 << UNBWT_FASTBITS)];
15776 fastbits[0] = 0x1234;
15777 let mut i0 = 0usize;
15778
15779 unbwt_decode_1(&mut u, &p, &bucket2, &fastbits, 0, &mut i0, 2);
15780
15781 assert_eq!(u, vec![0x12, 0x35, 0x12, 0x35]);
15782 assert_eq!(i0, 0);
15783 }
15784
15785 #[test]
15786 fn libsais64_unbwt_decode_dispatches_two_block_tail_shape() {
15787 let mut u = vec![0u8; 8];
15788 let p = vec![1u64, 0u64];
15789 let mut bucket2 = vec![0u64; ALPHABET_SIZE * ALPHABET_SIZE];
15790 bucket2[0x1234] = 0;
15791 bucket2[0x1235] = 2;
15792 let mut fastbits = vec![0u16; 1 + (1 << UNBWT_FASTBITS)];
15793 fastbits[0] = 0x1234;
15794 let i = vec![0u64, 0u64];
15795
15796 unbwt_decode(&mut u, &p, 4, 2, &i, &bucket2, &fastbits, 2, 2);
15797
15798 assert_eq!(u, vec![0x12, 0x35, 0x12, 0x35, 0x00, 0x00, 0x00, 0x00]);
15799 }
15800
15801 fn brute_force_suffix_array_u8(t: &[u8]) -> Vec<SaSint> {
15802 let mut sa: Vec<SaSint> = (0..t.len())
15803 .map(|index| SaSint::try_from(index).expect("index must fit SaSint"))
15804 .collect();
15805 sa.sort_by(|&lhs, &rhs| {
15806 t[usize::try_from(lhs).expect("non-negative")..]
15807 .cmp(&t[usize::try_from(rhs).expect("non-negative")..])
15808 });
15809 sa
15810 }
15811
15812 fn brute_force_plcp_u8(t: &[u8], sa: &[SaSint]) -> Vec<SaSint> {
15813 let mut rank = vec![0usize; t.len()];
15814 for (i, &suffix) in sa.iter().enumerate() {
15815 rank[usize::try_from(suffix).expect("suffix index must be non-negative")] = i;
15816 }
15817
15818 let mut plcp = vec![0; t.len()];
15819 for i in 0..t.len() {
15820 let r = rank[i];
15821 let prev = if r == 0 {
15822 t.len()
15823 } else {
15824 usize::try_from(sa[r - 1]).expect("suffix index must be non-negative")
15825 };
15826 if prev == t.len() {
15827 plcp[i] = 0;
15828 continue;
15829 }
15830
15831 let mut l = 0usize;
15832 while i + l < t.len() && prev + l < t.len() && t[i + l] == t[prev + l] {
15833 l += 1;
15834 }
15835 plcp[i] = l as SaSint;
15836 }
15837 plcp
15838 }
15839
15840 fn brute_force_lcp_from_sa_u8(t: &[u8], sa: &[SaSint]) -> Vec<SaSint> {
15841 let mut lcp = vec![0; sa.len()];
15842 for i in 0..sa.len() {
15843 let lhs = usize::try_from(sa[i]).expect("suffix index must be non-negative");
15844 let rhs = if i == 0 {
15845 sa.len()
15846 } else {
15847 usize::try_from(sa[i - 1]).expect("suffix index must be non-negative")
15848 };
15849 if rhs == sa.len() {
15850 lcp[i] = 0;
15851 continue;
15852 }
15853
15854 let mut l = 0usize;
15855 while lhs + l < t.len() && rhs + l < t.len() && t[lhs + l] == t[rhs + l] {
15856 l += 1;
15857 }
15858 lcp[i] = l as SaSint;
15859 }
15860 lcp
15861 }
15862
15863 fn make_libsais64_recursive_main_32s_text(repeats: usize) -> Vec<SaSint> {
15864 let motif = [9, 4, 9, 2, 9, 4, 9, 1];
15865 let mut t = Vec::with_capacity(repeats * motif.len() + 1);
15866 for _ in 0..repeats {
15867 t.extend_from_slice(&motif);
15868 }
15869 t.push(0);
15870 t
15871 }
15872
15873 fn make_libsais64_large_main_32s_stress_text(len: usize, alphabet: SaSint) -> Vec<SaSint> {
15874 let mut state: u32 = 0x1357_9bdf;
15875 let mut t = Vec::with_capacity(len + 1);
15876
15877 for i in 0..len {
15878 state = state.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
15879 let mut value = ((state >> 16) % (alphabet as u32 - 1)) as SaSint + 1;
15880
15881 if i % 17 < 8 {
15882 value = ((i / 17) as SaSint % 11) + 1;
15883 }
15884 if i % 29 < 10 {
15885 value = (((i / 29) as SaSint * 3) % 19) + 1;
15886 }
15887 if i % 64 >= 48 {
15888 value = t[i - 48];
15889 }
15890
15891 t.push(value);
15892 }
15893
15894 t.push(0);
15895 t
15896 }
15897
15898 fn assert_libsais64_main_32s_entry_matches_public_c_long(
15899 t: Vec<SaSint>,
15900 k: SaSint,
15901 fs: SaSint,
15902 compare_full_sa: bool,
15903 ) {
15904 let n = t.len() as SaSint;
15905 let n_usize = t.len();
15906 let threads = 1;
15907 let extra = usize::try_from(fs).expect("fs must be non-negative");
15908
15909 let mut c_t = t.clone();
15910 let mut c_sa = vec![0; t.len() + extra];
15911 let c_result =
15912 unsafe { probe_public_libsais64_long(c_t.as_mut_ptr(), c_sa.as_mut_ptr(), n, k, fs) };
15913
15914 let mut rust_t = t;
15915 let mut rust_sa = vec![0; rust_t.len() + extra];
15916 let mut thread_state = alloc_thread_state(threads).expect("thread state");
15917 let rust_result = libsais64_main_32s_entry(
15918 &mut rust_t,
15919 &mut rust_sa,
15920 n,
15921 k,
15922 fs,
15923 threads,
15924 &mut thread_state,
15925 );
15926
15927 assert_eq!(rust_result, c_result);
15928 assert_eq!(rust_t, c_t);
15929 if compare_full_sa {
15930 assert_eq!(rust_sa, c_sa);
15931 } else {
15932 assert_eq!(&rust_sa[..n_usize], &c_sa[..n_usize]);
15933 }
15934 }
15935
15936 fn assert_libsais64_main_32s_entry_matches_public_c_long_for_branch(k: SaSint) {
15937 assert_libsais64_main_32s_entry_matches_public_c_long(
15938 vec![17, 3, 17, 9, 5, 9, 2, 11, 2, 7, 1, 7, 0],
15939 k,
15940 0,
15941 true,
15942 );
15943 }
15944
15945 #[test]
15946 fn libsais64_matches_bruteforce_suffix_array_for_small_text() {
15947 let t = b"banana";
15948 let mut sa = vec![0; t.len()];
15949 let mut freq = vec![0; ALPHABET_SIZE];
15950
15951 let result = libsais64(t, &mut sa, 0, Some(&mut freq));
15952
15953 assert_eq!(result, 0);
15954 assert_eq!(sa, brute_force_suffix_array_u8(t));
15955 assert_eq!(freq[b'a' as usize], 3);
15956 assert_eq!(freq[b'b' as usize], 1);
15957 assert_eq!(freq[b'n' as usize], 2);
15958 }
15959
15960 #[test]
15961 fn libsais64_int_matches_bruteforce_suffix_array_for_small_integer_text() {
15962 let mut t = vec![2, 1, 3, 1, 0];
15963 let expected = {
15964 let mut sa: Vec<SaSint> = (0..t.len())
15965 .map(|index| SaSint::try_from(index).expect("index must fit SaSint"))
15966 .collect();
15967 sa.sort_by(|&lhs, &rhs| {
15968 t[usize::try_from(lhs).expect("non-negative")..]
15969 .cmp(&t[usize::try_from(rhs).expect("non-negative")..])
15970 });
15971 sa
15972 };
15973 let mut sa = vec![0; t.len()];
15974
15975 let result = libsais64_int(&mut t, &mut sa, 4, 0);
15976
15977 assert_eq!(result, 0);
15978 assert_eq!(sa, expected);
15979 }
15980
15981 #[test]
15982 fn libsais64_plcp_matches_bruteforce_for_small_text() {
15983 let t = b"banana";
15984 let sa = brute_force_suffix_array_u8(t);
15985 let expected = brute_force_plcp_u8(t, &sa);
15986 let mut plcp = vec![0; t.len()];
15987
15988 let result = libsais64_plcp(t, &sa, &mut plcp);
15989
15990 assert_eq!(result, 0);
15991 assert_eq!(plcp, expected);
15992 }
15993
15994 #[test]
15995 fn libsais64_plcp_gsa_stops_at_separator() {
15996 let t = b"ab\0b\0";
15997 let sa = brute_force_suffix_array_u8(t);
15998 let mut plcp = vec![0; t.len()];
15999
16000 let result = libsais64_plcp_gsa(t, &sa, &mut plcp);
16001
16002 assert_eq!(result, 0);
16003 assert_eq!(plcp[2], 0);
16004 assert_eq!(plcp[4], 0);
16005 }
16006
16007 #[test]
16008 fn libsais64_lcp_matches_bruteforce_for_small_text() {
16009 let t = b"banana";
16010 let sa = brute_force_suffix_array_u8(t);
16011 let plcp = brute_force_plcp_u8(t, &sa);
16012 let expected = brute_force_lcp_from_sa_u8(t, &sa);
16013 let mut lcp = vec![0; t.len()];
16014
16015 let result = libsais64_lcp(&plcp, &sa, &mut lcp);
16016
16017 assert_eq!(result, 0);
16018 assert_eq!(lcp, expected);
16019 }
16020
16021 #[test]
16022 fn libsais64_unbwt_init_parallel_uses_block_partition_for_large_inputs() {
16023 let n = 70_003usize;
16024 let t: Vec<u8> = (0..n)
16025 .map(|i| i.wrapping_mul(37).wrapping_add(i >> 3) as u8)
16026 .collect();
16027 let i = [12_345u64];
16028
16029 let mut single_p = vec![0u64; n + 1];
16030 let mut threaded_p = vec![0u64; n + 1];
16031 let mut single_bucket2 = vec![0u64; ALPHABET_SIZE * ALPHABET_SIZE];
16032 let mut threaded_bucket2 = vec![0u64; ALPHABET_SIZE * ALPHABET_SIZE];
16033 let mut single_fastbits = vec![0u16; 1 + (1 << UNBWT_FASTBITS)];
16034 let mut threaded_fastbits = vec![0u16; 1 + (1 << UNBWT_FASTBITS)];
16035 let mut buckets = vec![0u64; 4 * (ALPHABET_SIZE + ALPHABET_SIZE * ALPHABET_SIZE)];
16036
16037 unbwt_init_single(
16038 &t,
16039 &mut single_p,
16040 n as SaSint,
16041 None,
16042 &i,
16043 &mut single_bucket2,
16044 &mut single_fastbits,
16045 );
16046 unbwt_init_parallel(
16047 &t,
16048 &mut threaded_p,
16049 n as SaSint,
16050 None,
16051 &i,
16052 &mut threaded_bucket2,
16053 &mut threaded_fastbits,
16054 Some(&mut buckets),
16055 4,
16056 );
16057
16058 assert_eq!(threaded_p, single_p);
16059 assert_eq!(threaded_bucket2, single_bucket2);
16060 assert_eq!(threaded_fastbits, single_fastbits);
16061 }
16062
16063 #[test]
16064 fn libsais64_radix_sort_lms_suffixes_8u_places_suffixes_by_bucket() {
16065 let t = vec![1_u8, 0, 1, 0];
16066 let mut sa = vec![9, 9, 9, 9, 0, 1, 2, 3];
16067 let mut induction_bucket = vec![0; 2 * ALPHABET_SIZE];
16068 induction_bucket[buckets_index2(0, 0)] = 2;
16069 induction_bucket[buckets_index2(1, 0)] = 4;
16070 radix_sort_lms_suffixes_8u(&t, &mut sa, &mut induction_bucket, 4, 4);
16071 assert_eq!(&sa[..4], &[1, 3, 0, 2]);
16072 }
16073
16074 #[test]
16075 fn libsais64_radix_sort_lms_suffixes_8u_omp_wraps_sequential_version() {
16076 let t = vec![9_u8, 1, 0, 1, 0];
16077 let mut sa = vec![9, 9, 9, 9, 9, 1, 2, 3, 4];
16078 let mut buckets = vec![0; 6 * ALPHABET_SIZE];
16079 buckets[4 * ALPHABET_SIZE + buckets_index2(0, 0)] = 2;
16080 buckets[4 * ALPHABET_SIZE + buckets_index2(1, 0)] = 4;
16081 let mut thread_state = alloc_thread_state(2).unwrap();
16082 radix_sort_lms_suffixes_8u_omp(&t, &mut sa, 9, 5, 0, &mut buckets, 2, &mut thread_state);
16083 assert_eq!(&sa[..4], &[2, 4, 1, 3]);
16084 }
16085
16086 #[test]
16087 fn libsais64_radix_sort_lms_suffixes_32s_6k_places_suffixes_by_bucket() {
16088 let t = vec![1, 0, 1, 0];
16089 let mut sa = vec![9, 9, 9, 9, 0, 1, 2, 3];
16090 let mut induction_bucket = vec![2, 4];
16091 radix_sort_lms_suffixes_32s_6k(&t, &mut sa, &mut induction_bucket, 4, 4);
16092 assert_eq!(&sa[..4], &[1, 3, 0, 2]);
16093 }
16094
16095 #[test]
16096 fn libsais64_radix_sort_lms_suffixes_32s_2k_places_suffixes_by_bucket() {
16097 let t = vec![1, 0, 1, 0];
16098 let mut sa = vec![9, 9, 9, 9, 0, 1, 2, 3];
16099 let mut induction_bucket = vec![2, 0, 4, 0];
16100 radix_sort_lms_suffixes_32s_2k(&t, &mut sa, &mut induction_bucket, 4, 4);
16101 assert_eq!(&sa[..4], &[1, 3, 0, 2]);
16102 }
16103
16104 #[test]
16105 fn libsais64_radix_sort_lms_suffixes_32s_6k_omp_wraps_sequential_version() {
16106 let t = vec![9, 1, 0, 1, 0];
16107 let mut sa = vec![9, 9, 9, 9, 9, 1, 2, 3, 4];
16108 let mut induction_bucket = vec![2, 4];
16109 let mut thread_state = alloc_thread_state(2).unwrap();
16110 radix_sort_lms_suffixes_32s_6k_omp(
16111 &t,
16112 &mut sa,
16113 9,
16114 5,
16115 &mut induction_bucket,
16116 2,
16117 &mut thread_state,
16118 );
16119 assert_eq!(&sa[..4], &[2, 4, 1, 3]);
16120 }
16121
16122 #[test]
16123 fn libsais64_radix_sort_lms_suffixes_32s_2k_omp_wraps_sequential_version() {
16124 let t = vec![9, 1, 0, 1, 0];
16125 let mut sa = vec![9, 9, 9, 9, 9, 1, 2, 3, 4];
16126 let mut induction_bucket = vec![2, 0, 4, 0];
16127 let mut thread_state = alloc_thread_state(2).unwrap();
16128 radix_sort_lms_suffixes_32s_2k_omp(
16129 &t,
16130 &mut sa,
16131 9,
16132 5,
16133 &mut induction_bucket,
16134 2,
16135 &mut thread_state,
16136 );
16137 assert_eq!(&sa[..4], &[2, 4, 1, 3]);
16138 }
16139
16140 #[test]
16141 fn libsais64_radix_sort_lms_suffixes_32s_block_omp_runs_cache_pipeline() {
16142 let t = vec![9, 1, 0, 1, 0];
16143 let mut sa_6k = vec![9, 9, 9, 9, 9, 1, 2, 3, 4];
16144 let mut bucket_6k = vec![2, 4];
16145 let mut cache = vec![ThreadCache::default(); 9];
16146 radix_sort_lms_suffixes_32s_6k_block_omp(
16147 &t,
16148 &mut sa_6k,
16149 &mut bucket_6k,
16150 &mut cache,
16151 5,
16152 4,
16153 2,
16154 );
16155 assert_eq!(&sa_6k[..4], &[2, 4, 1, 3]);
16156
16157 let mut sa_2k = vec![9, 9, 9, 9, 9, 1, 2, 3, 4];
16158 let mut bucket_2k = vec![2, 0, 4, 0];
16159 cache.fill(ThreadCache::default());
16160 radix_sort_lms_suffixes_32s_2k_block_omp(
16161 &t,
16162 &mut sa_2k,
16163 &mut bucket_2k,
16164 &mut cache,
16165 5,
16166 4,
16167 2,
16168 );
16169 assert_eq!(&sa_2k[..4], &[2, 4, 1, 3]);
16170 }
16171
16172 #[test]
16173 fn libsais64_radix_sort_lms_suffixes_8u_omp_uses_thread_state_for_large_inputs() {
16174 let m = 65_600usize;
16175 let n = 2 * m + 16;
16176 let start = n - m + 1;
16177 let t: Vec<u8> = (0..n).map(|i| (i % 4) as u8).collect();
16178 let suffixes: Vec<SaSint> = (0..m - 1).map(|i| i as SaSint).collect();
16179
16180 let mut buckets = vec![0; 6 * ALPHABET_SIZE];
16181 for &suffix in &suffixes {
16182 buckets[4 * ALPHABET_SIZE + buckets_index2(t[suffix as usize] as usize, 0)] += 1;
16183 }
16184 let mut sum = 0;
16185 for symbol in 0..ALPHABET_SIZE {
16186 let bucket = 4 * ALPHABET_SIZE + buckets_index2(symbol, 0);
16187 sum += buckets[bucket];
16188 buckets[bucket] = sum;
16189 }
16190
16191 let mut sa_single = vec![0; n];
16192 sa_single[start..start + suffixes.len()].copy_from_slice(&suffixes);
16193 let mut sa_threaded = sa_single.clone();
16194 let mut buckets_single = buckets.clone();
16195 let mut buckets_threaded = buckets;
16196 let mut thread_state = alloc_thread_state(4).unwrap();
16197 thread_state[3].m = m as FastSint;
16198
16199 radix_sort_lms_suffixes_8u_omp(
16200 &t,
16201 &mut sa_single,
16202 n as SaSint,
16203 m as SaSint,
16204 0,
16205 &mut buckets_single,
16206 1,
16207 &mut [],
16208 );
16209 radix_sort_lms_suffixes_8u_omp(
16210 &t,
16211 &mut sa_threaded,
16212 n as SaSint,
16213 m as SaSint,
16214 0,
16215 &mut buckets_threaded,
16216 4,
16217 &mut thread_state,
16218 );
16219
16220 assert_eq!(sa_threaded, sa_single);
16221 }
16222
16223 #[test]
16224 fn libsais64_radix_sort_lms_suffixes_32s_omp_uses_block_pipeline_for_large_inputs() {
16225 let m = 65_600usize;
16226 let n = 2 * m + 16;
16227 let start = n - m + 1;
16228 let t: Vec<SaSint> = (0..n).map(|i| (i % 4) as SaSint).collect();
16229 let suffixes: Vec<SaSint> = (0..m - 1).map(|i| i as SaSint).collect();
16230
16231 let mut bucket_ends = vec![0; 4];
16232 for &suffix in &suffixes {
16233 bucket_ends[t[suffix as usize] as usize] += 1;
16234 }
16235 let mut sum = 0;
16236 for bucket in &mut bucket_ends {
16237 sum += *bucket;
16238 *bucket = sum;
16239 }
16240
16241 let mut sa_single = vec![0; n];
16242 sa_single[start..start + suffixes.len()].copy_from_slice(&suffixes);
16243 let mut sa_threaded = sa_single.clone();
16244 let mut bucket_single = bucket_ends.clone();
16245 let mut bucket_threaded = bucket_ends.clone();
16246 let mut thread_state = alloc_thread_state(4).unwrap();
16247
16248 radix_sort_lms_suffixes_32s_6k_omp(
16249 &t,
16250 &mut sa_single,
16251 n as SaSint,
16252 m as SaSint,
16253 &mut bucket_single,
16254 1,
16255 &mut [],
16256 );
16257 radix_sort_lms_suffixes_32s_6k_omp(
16258 &t,
16259 &mut sa_threaded,
16260 n as SaSint,
16261 m as SaSint,
16262 &mut bucket_threaded,
16263 4,
16264 &mut thread_state,
16265 );
16266 assert_eq!(sa_threaded, sa_single);
16267 assert_eq!(bucket_threaded, bucket_single);
16268
16269 let mut bucket_2k = vec![0; 8];
16270 for (symbol, &end) in bucket_ends.iter().enumerate() {
16271 bucket_2k[buckets_index2(symbol, 0)] = end;
16272 }
16273 let mut sa_single = vec![0; n];
16274 sa_single[start..start + suffixes.len()].copy_from_slice(&suffixes);
16275 let mut sa_threaded = sa_single.clone();
16276 let mut bucket_single = bucket_2k.clone();
16277 let mut bucket_threaded = bucket_2k;
16278
16279 radix_sort_lms_suffixes_32s_2k_omp(
16280 &t,
16281 &mut sa_single,
16282 n as SaSint,
16283 m as SaSint,
16284 &mut bucket_single,
16285 1,
16286 &mut [],
16287 );
16288 radix_sort_lms_suffixes_32s_2k_omp(
16289 &t,
16290 &mut sa_threaded,
16291 n as SaSint,
16292 m as SaSint,
16293 &mut bucket_threaded,
16294 4,
16295 &mut thread_state,
16296 );
16297 assert_eq!(sa_threaded, sa_single);
16298 assert_eq!(bucket_threaded, bucket_single);
16299 }
16300
16301 #[test]
16302 fn libsais64_radix_sort_lms_suffixes_32s_1k_collects_lms_suffixes() {
16303 let t = vec![2, 1, 3, 1, 0];
16304 let mut sa = vec![0; t.len()];
16305 let mut buckets = vec![0, 2, 4, 5];
16306 let m = radix_sort_lms_suffixes_32s_1k(&t, &mut sa, t.len() as SaSint, &mut buckets);
16307 assert!(m >= 0);
16308 }
16309
16310 #[test]
16311 fn libsais64_radix_sort_set_markers_32s_6k_marks_target_suffixes() {
16312 let mut sa = vec![0; 6];
16313 let induction_bucket = vec![1, 3, 5];
16314 radix_sort_set_markers_32s_6k(&mut sa, &induction_bucket, 0, 3);
16315 assert_eq!(sa[1], SAINT_MIN);
16316 assert_eq!(sa[3], SAINT_MIN);
16317 assert_eq!(sa[5], SAINT_MIN);
16318 }
16319
16320 #[test]
16321 fn libsais64_radix_sort_set_markers_32s_4k_marks_target_suffixes() {
16322 let mut sa = vec![0; 6];
16323 let induction_bucket = vec![1, 0, 3, 0, 5, 0];
16324 radix_sort_set_markers_32s_4k(&mut sa, &induction_bucket, 0, 3);
16325 assert_eq!(sa[1], SUFFIX_GROUP_MARKER);
16326 assert_eq!(sa[3], SUFFIX_GROUP_MARKER);
16327 assert_eq!(sa[5], SUFFIX_GROUP_MARKER);
16328 }
16329
16330 #[test]
16331 fn libsais64_radix_sort_set_markers_32s_6k_omp_wraps_sequential_version() {
16332 let mut sa = vec![0; 6];
16333 let induction_bucket = vec![1, 3, 5];
16334 radix_sort_set_markers_32s_6k_omp(&mut sa, 4, &induction_bucket, 2);
16335 assert_eq!(sa[1], SAINT_MIN);
16336 assert_eq!(sa[3], SAINT_MIN);
16337 assert_eq!(sa[5], SAINT_MIN);
16338 }
16339
16340 #[test]
16341 fn libsais64_radix_sort_set_markers_32s_4k_omp_wraps_sequential_version() {
16342 let mut sa = vec![0; 6];
16343 let induction_bucket = vec![1, 0, 3, 0, 5, 0];
16344 radix_sort_set_markers_32s_4k_omp(&mut sa, 4, &induction_bucket, 2);
16345 assert_eq!(sa[1], SUFFIX_GROUP_MARKER);
16346 assert_eq!(sa[3], SUFFIX_GROUP_MARKER);
16347 assert_eq!(sa[5], SUFFIX_GROUP_MARKER);
16348 }
16349
16350 #[test]
16351 fn libsais64_radix_sort_set_markers_32s_omp_partitions_large_inputs() {
16352 let k = 65_600usize;
16353 let induction_bucket_6k: Vec<SaSint> = (0..k).map(|i| i as SaSint).collect();
16354 let mut sa_single = vec![0; k];
16355 let mut sa_threaded = vec![0; k];
16356 radix_sort_set_markers_32s_6k_omp(&mut sa_single, k as SaSint, &induction_bucket_6k, 1);
16357 radix_sort_set_markers_32s_6k_omp(&mut sa_threaded, k as SaSint, &induction_bucket_6k, 4);
16358 assert_eq!(sa_threaded, sa_single);
16359
16360 let mut induction_bucket_4k = vec![0; 2 * k];
16361 for i in 0..k {
16362 induction_bucket_4k[buckets_index2(i, 0)] = i as SaSint;
16363 }
16364 let mut sa_single = vec![0; k];
16365 let mut sa_threaded = vec![0; k];
16366 radix_sort_set_markers_32s_4k_omp(&mut sa_single, k as SaSint, &induction_bucket_4k, 1);
16367 radix_sort_set_markers_32s_4k_omp(&mut sa_threaded, k as SaSint, &induction_bucket_4k, 4);
16368 assert_eq!(sa_threaded, sa_single);
16369 }
16370
16371 #[test]
16372 fn libsais64_partial_sorting_scan_left_to_right_8u_emits_induced_suffixes() {
16373 let t = vec![2_u8, 1, 3, 1, 0];
16374 let mut sa = vec![2 | SAINT_MIN, 4, 0, 0, 0, 0];
16375 let mut buckets = vec![0; 6 * ALPHABET_SIZE];
16376 buckets[4 * ALPHABET_SIZE + buckets_index2(1, 0)] = 2;
16377 let d = partial_sorting_scan_left_to_right_8u(&t, &mut sa, &mut buckets, 0, 0, 2);
16378 assert!(d >= 0);
16379 assert!(sa.iter().any(|&v| v != 0));
16380 }
16381
16382 #[test]
16383 fn libsais64_partial_sorting_scan_left_to_right_8u_omp_wraps_sequential_version() {
16384 let t = vec![2_u8, 1, 3, 1, 0];
16385 let mut sa = vec![0; 8];
16386 let mut buckets = vec![0; 6 * ALPHABET_SIZE];
16387 buckets[4 * ALPHABET_SIZE + buckets_index2(0, 0)] = 1;
16388 let mut thread_state = alloc_thread_state(2).unwrap();
16389 let d = partial_sorting_scan_left_to_right_8u_omp(
16390 &t,
16391 &mut sa,
16392 5,
16393 4,
16394 &mut buckets,
16395 0,
16396 0,
16397 2,
16398 &mut thread_state,
16399 );
16400 assert!(d >= 1);
16401 }
16402
16403 #[test]
16404 fn libsais64_partial_sorting_scan_left_to_right_32s_6k_emits_induced_suffixes() {
16405 let t = vec![2, 1, 3, 1, 0];
16406 let mut sa = vec![2 | SAINT_MIN, 4, 0, 0, 0, 0];
16407 let mut buckets = vec![0; 4 * 4];
16408 buckets[buckets_index4(1, 0)] = 2;
16409 let d = partial_sorting_scan_left_to_right_32s_6k(&t, &mut sa, &mut buckets, 0, 0, 2);
16410 assert!(d >= 0);
16411 assert!(sa.iter().any(|&v| v != 0));
16412 }
16413
16414 #[test]
16415 fn libsais64_partial_sorting_scan_left_to_right_32s_4k_emits_induced_suffixes() {
16416 let t = vec![2, 1, 3, 1, 0];
16417 let k = 4usize;
16418 let mut sa = vec![2 | SUFFIX_GROUP_MARKER, 4, 0, 0, 0, 0];
16419 let mut buckets = vec![0; 4 * k];
16420 buckets[2 * k + 1] = 2;
16421 let d = partial_sorting_scan_left_to_right_32s_4k(
16422 &t,
16423 &mut sa,
16424 k as SaSint,
16425 &mut buckets,
16426 0,
16427 0,
16428 2,
16429 );
16430 assert!(d >= 0);
16431 assert!(sa.iter().any(|&v| v != 0));
16432 }
16433
16434 #[test]
16435 fn libsais64_partial_sorting_scan_left_to_right_32s_1k_emits_induced_suffixes() {
16436 let t = vec![2, 1, 3, 1, 0];
16437 let mut sa = vec![2, 4, 0, 0, 0, 0];
16438 let mut buckets = vec![0; 4];
16439 buckets[1] = 2;
16440 partial_sorting_scan_left_to_right_32s_1k(&t, &mut sa, &mut buckets, 0, 2);
16441 assert!(sa.iter().any(|&v| v != 0));
16442 }
16443
16444 #[test]
16445 fn libsais64_partial_sorting_scan_left_to_right_32s_6k_omp_wraps_sequential_version() {
16446 let t = vec![2, 1, 3, 1, 0];
16447 let mut sa = vec![0; 8];
16448 let mut buckets = vec![0; 4 * 4];
16449 let mut thread_state = alloc_thread_state(2).unwrap();
16450 let d = partial_sorting_scan_left_to_right_32s_6k_omp(
16451 &t,
16452 &mut sa,
16453 5,
16454 &mut buckets,
16455 0,
16456 0,
16457 2,
16458 &mut thread_state,
16459 );
16460 assert!(d >= 1);
16461 }
16462
16463 #[test]
16464 fn libsais64_partial_sorting_scan_left_to_right_32s_4k_omp_wraps_sequential_version() {
16465 let t = vec![2, 1, 3, 1, 0];
16466 let k = 4usize;
16467 let mut sa = vec![0; 8];
16468 let mut buckets = vec![0; 4 * k];
16469 let mut thread_state = alloc_thread_state(2).unwrap();
16470 let d = partial_sorting_scan_left_to_right_32s_4k_omp(
16471 &t,
16472 &mut sa,
16473 5,
16474 k as SaSint,
16475 &mut buckets,
16476 0,
16477 2,
16478 &mut thread_state,
16479 );
16480 assert!(d >= 1);
16481 }
16482
16483 #[test]
16484 fn libsais64_partial_sorting_scan_left_to_right_32s_1k_omp_wraps_sequential_version() {
16485 let t = vec![2, 1, 3, 1, 0];
16486 let mut sa = vec![0; 8];
16487 let mut buckets = vec![0; 4];
16488 let mut thread_state = alloc_thread_state(2).unwrap();
16489 partial_sorting_scan_left_to_right_32s_1k_omp(
16490 &t,
16491 &mut sa,
16492 5,
16493 &mut buckets,
16494 2,
16495 &mut thread_state,
16496 );
16497 assert!(sa.iter().any(|&v| v != 0));
16498 }
16499
16500 #[test]
16501 fn libsais64_partial_sorting_scan_left_to_right_32s_6k_block_gather_records_bucket_symbols() {
16502 let t = vec![3, 1, 2, 0];
16503 let mut sa = vec![2 | SAINT_MIN, 0, 0, 0];
16504 let mut cache = vec![ThreadCache::default(); 1];
16505
16506 partial_sorting_scan_left_to_right_32s_6k_block_gather(&t, &mut sa, &mut cache, 0, 1);
16507
16508 assert_eq!(cache[0].index, 2 | SAINT_MIN);
16509 assert_eq!(cache[0].symbol, buckets_index4(1, 1) as SaSint);
16510 }
16511
16512 #[test]
16513 fn libsais64_partial_sorting_scan_left_to_right_32s_1k_block_gather_zeroes_positive_entries() {
16514 let t = vec![3, 1, 2, 0];
16515 let mut sa = vec![2, 0, 0, 0];
16516 let mut cache = vec![ThreadCache::default(); 1];
16517
16518 partial_sorting_scan_left_to_right_32s_1k_block_gather(&t, &mut sa, &mut cache, 0, 1);
16519
16520 assert_eq!(cache[0].symbol, 1);
16521 assert_eq!(cache[0].index, 1);
16522 assert_eq!(sa[0], 0);
16523 }
16524
16525 #[test]
16526 fn libsais64_partial_sorting_scan_left_to_right_32s_1k_block_omp_uses_relative_cache() {
16527 let block_start = 20_000usize;
16528 let block_size = 16_384usize;
16529 let n = block_start + block_size + 8;
16530 let t = vec![1; n];
16531 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
16532
16533 let mut sa_single = vec![0; n];
16534 sa_single[block_start..block_start + block_size].copy_from_slice(&suffixes);
16535 let mut sa_threaded = sa_single.clone();
16536 let mut bucket_single = vec![0, 0];
16537 let mut bucket_threaded = bucket_single.clone();
16538 let mut cache = vec![ThreadCache::default(); 4 * LIBSAIS_PER_THREAD_CACHE_SIZE];
16539
16540 partial_sorting_scan_left_to_right_32s_1k(
16541 &t,
16542 &mut sa_single,
16543 &mut bucket_single,
16544 block_start as FastSint,
16545 block_size as FastSint,
16546 );
16547 partial_sorting_scan_left_to_right_32s_1k_block_omp(
16548 &t,
16549 &mut sa_threaded,
16550 &mut bucket_threaded,
16551 &mut cache,
16552 block_start as FastSint,
16553 block_size as FastSint,
16554 4,
16555 );
16556
16557 assert_eq!(sa_threaded, sa_single);
16558 assert_eq!(bucket_threaded, bucket_single);
16559 }
16560
16561 #[test]
16562 fn libsais64_partial_sorting_scan_left_to_right_8u_block_prepare_records_cache_and_counts() {
16563 let t = vec![2_u8, 1, 3, 1, 0];
16564 let sa = vec![2 | SAINT_MIN, 4, 0, 0, 0, 0];
16565 let k = 4;
16566 let mut buckets = vec![0; 4 * k];
16567 let mut cache = vec![ThreadCache::default(); 8];
16568 let mut state = ThreadState::new();
16569 let (position, count) = partial_sorting_scan_left_to_right_8u_block_prepare(
16570 &t,
16571 &sa,
16572 k as SaSint,
16573 &mut buckets,
16574 &mut cache,
16575 0,
16576 2,
16577 );
16578 state.position = position;
16579 state.count = count;
16580 assert!(state.count >= 1);
16581 assert!(cache
16582 .iter()
16583 .take(state.count as usize)
16584 .any(|entry| entry.symbol >= 0));
16585 }
16586
16587 #[test]
16588 fn libsais64_partial_sorting_scan_left_to_right_8u_block_place_writes_induced_values() {
16589 let mut sa = vec![0; 8];
16590 let mut buckets = vec![0; 8];
16591 buckets[0] = 0;
16592 buckets[1] = 1;
16593 let cache = vec![
16594 ThreadCache {
16595 index: 3 | SAINT_MIN,
16596 symbol: 0,
16597 },
16598 ThreadCache {
16599 index: 5,
16600 symbol: 1,
16601 },
16602 ];
16603 partial_sorting_scan_left_to_right_8u_block_place(&mut sa, &mut buckets, 2, &cache, 2, 0);
16604 assert!(sa[0] != 0 || sa[1] != 0);
16605 }
16606
16607 #[test]
16608 fn libsais64_partial_sorting_scan_left_to_right_8u_block_omp_wraps_sequential_version() {
16609 let t = vec![2_u8, 1, 3, 1, 0];
16610 let mut sa = vec![2 | SAINT_MIN, 4, 0, 0, 0, 0];
16611 let mut buckets = vec![0; 6 * ALPHABET_SIZE];
16612 let mut thread_state = alloc_thread_state(2).unwrap();
16613 let d = partial_sorting_scan_left_to_right_8u_block_omp(
16614 &t,
16615 &mut sa,
16616 4,
16617 &mut buckets,
16618 0,
16619 0,
16620 2,
16621 2,
16622 &mut thread_state,
16623 );
16624 assert!(d >= 0);
16625 }
16626
16627 #[test]
16628 fn libsais64_partial_sorting_shift_buckets_32s_6k_moves_temp_bucket_view_into_main_slots() {
16629 let k = 3usize;
16630 let mut buckets = vec![0; 6 * k];
16631 buckets[4 * k] = 10;
16632 buckets[4 * k + 1] = 11;
16633 buckets[4 * k + 2] = 12;
16634 buckets[4 * k + 3] = 13;
16635 partial_sorting_shift_buckets_32s_6k(k as SaSint, &mut buckets);
16636 assert_eq!(buckets[0], 10);
16637 assert_eq!(buckets[1], 11);
16638 assert_eq!(buckets[4], 12);
16639 assert_eq!(buckets[5], 13);
16640 }
16641
16642 #[test]
16643 fn libsais64_partial_sorting_scan_right_to_left_8u_emits_induced_suffixes() {
16644 let t = vec![0_u8, 1, 2, 1, 0];
16645 let mut sa = vec![0, 0, 4 | SAINT_MIN];
16646 let mut buckets = vec![0; 4 * ALPHABET_SIZE];
16647 buckets[buckets_index2(1, 1)] = 2;
16648
16649 let d = partial_sorting_scan_right_to_left_8u(&t, &mut sa, &mut buckets, 0, 2, 1);
16650
16651 assert_eq!(d, 1);
16652 assert_eq!(sa[1], 3 | SAINT_MIN);
16653 assert_eq!(buckets[buckets_index2(1, 1)], 1);
16654 assert_eq!(buckets[2 * ALPHABET_SIZE + buckets_index2(1, 1)], 1);
16655 }
16656
16657 #[test]
16658 fn libsais64_partial_gsa_scan_right_to_left_8u_skips_separator_bucket() {
16659 let t = vec![1_u8, 0, 0];
16660 let mut sa = vec![0, 2 | SAINT_MIN];
16661 let mut buckets = vec![0; 4 * ALPHABET_SIZE];
16662 buckets[buckets_index2(0, 1)] = 2;
16663
16664 let d = partial_gsa_scan_right_to_left_8u(&t, &mut sa, &mut buckets, 0, 1, 1);
16665
16666 assert_eq!(d, 1);
16667 assert_eq!(sa, vec![0, 2 | SAINT_MIN]);
16668 assert_eq!(buckets[buckets_index2(0, 1)], 2);
16669 }
16670
16671 #[test]
16672 fn libsais64_partial_sorting_scan_right_to_left_32s_6k_emits_induced_suffixes() {
16673 let t = vec![0, 1, 2, 1, 0];
16674 let mut sa = vec![0, 0, 4 | SAINT_MIN];
16675 let mut buckets = vec![0; 4 * 3];
16676 buckets[buckets_index4(1, 1)] = 2;
16677
16678 let d = partial_sorting_scan_right_to_left_32s_6k(&t, &mut sa, &mut buckets, 0, 2, 1);
16679
16680 assert_eq!(d, 1);
16681 assert_eq!(sa[1], 3 | SAINT_MIN);
16682 assert_eq!(buckets[buckets_index4(1, 1)], 1);
16683 assert_eq!(buckets[buckets_index4(1, 1) + 2], 1);
16684 }
16685
16686 #[test]
16687 fn libsais64_partial_sorting_scan_right_to_left_32s_1k_omp_wraps_sequential_version() {
16688 let t = vec![0, 1, 2, 1, 0];
16689 let mut sa = vec![0, 0, 4];
16690 let mut buckets = vec![0; 3];
16691 buckets[1] = 2;
16692 let mut thread_state = alloc_thread_state(2).unwrap();
16693
16694 partial_sorting_scan_right_to_left_32s_1k_omp(
16695 &t,
16696 &mut sa,
16697 3,
16698 &mut buckets,
16699 2,
16700 &mut thread_state,
16701 );
16702
16703 assert_eq!(sa[1], 3 | SAINT_MIN);
16704 assert_eq!(buckets[1], 1);
16705 }
16706
16707 #[test]
16708 fn libsais64_partial_sorting_scan_right_to_left_32s_6k_block_gather_records_symbols() {
16709 let t = vec![0, 1, 2, 1, 0];
16710 let sa = vec![0, 4 | SAINT_MIN, 0];
16711 let mut cache = vec![ThreadCache::default(); sa.len()];
16712
16713 partial_sorting_scan_right_to_left_32s_6k_block_gather(&t, &sa, &mut cache, 1, 1);
16714
16715 assert_eq!(cache[0].index, 4 | SAINT_MIN);
16716 assert_eq!(cache[0].symbol, buckets_index4(1, 1) as SaSint);
16717 }
16718
16719 #[test]
16720 fn libsais64_partial_sorting_scan_right_to_left_32s_4k_block_gather_zeroes_positive_entries() {
16721 let t = vec![0, 1, 2, 1, 0];
16722 let mut sa = vec![0, 4 | SUFFIX_GROUP_MARKER, 0];
16723 let mut cache = vec![ThreadCache::default(); sa.len()];
16724
16725 partial_sorting_scan_right_to_left_32s_4k_block_gather(&t, &mut sa, &mut cache, 1, 1);
16726
16727 assert_eq!(sa[1], 0);
16728 assert_eq!(cache[0].index, 4 | SUFFIX_GROUP_MARKER);
16729 assert_eq!(cache[0].symbol, buckets_index2(1, 1) as SaSint);
16730 }
16731
16732 #[test]
16733 fn libsais64_partial_sorting_scan_right_to_left_32s_1k_block_gather_stores_preinduced_entries()
16734 {
16735 let t = vec![0, 1, 2, 1, 0];
16736 let mut sa = vec![0, 4, 0];
16737 let mut cache = vec![ThreadCache::default(); sa.len()];
16738
16739 partial_sorting_scan_right_to_left_32s_1k_block_gather(&t, &mut sa, &mut cache, 1, 1);
16740
16741 assert_eq!(sa[1], 0);
16742 assert_eq!(cache[0].index, 3 | SAINT_MIN);
16743 assert_eq!(cache[0].symbol, 1);
16744 }
16745
16746 #[test]
16747 fn libsais64_partial_sorting_scan_right_to_left_32s_6k_block_sort_updates_bucket_and_marker_state(
16748 ) {
16749 let t = vec![0, 1, 2, 1, 0];
16750 let mut cache = vec![ThreadCache::default(); 3];
16751 cache[0].index = 4 | SAINT_MIN;
16752 cache[0].symbol = buckets_index4(1, 1) as SaSint;
16753 let mut buckets = vec![0; 4 * 3];
16754 buckets[buckets_index4(1, 1)] = 2;
16755
16756 let d = partial_sorting_scan_right_to_left_32s_6k_block_sort(
16757 &t,
16758 &mut buckets,
16759 0,
16760 &mut cache,
16761 1,
16762 1,
16763 );
16764
16765 assert_eq!(d, 1);
16766 assert_eq!(cache[0].index, 3 | SAINT_MIN);
16767 assert_eq!(buckets[buckets_index4(1, 1)], 1);
16768 assert_eq!(buckets[buckets_index4(1, 1) + 2], 1);
16769 }
16770
16771 #[test]
16772 fn libsais64_partial_sorting_scan_right_to_left_32s_1k_block_omp_places_cached_suffixes() {
16773 let t = vec![0, 1, 2, 1, 0];
16774 let mut sa = vec![0, 4, 0];
16775 let mut buckets = vec![0; 3];
16776 buckets[1] = 2;
16777 let mut cache = vec![ThreadCache::default(); sa.len()];
16778
16779 partial_sorting_scan_right_to_left_32s_1k_block_omp(
16780 &t,
16781 &mut sa,
16782 &mut buckets,
16783 &mut cache,
16784 1,
16785 1,
16786 2,
16787 );
16788
16789 assert_eq!(sa[1], 3 | SAINT_MIN);
16790 assert_eq!(buckets[1], 1);
16791 }
16792
16793 #[test]
16794 fn libsais64_partial_sorting_scan_right_to_left_32s_1k_block_omp_uses_relative_cache() {
16795 let block_start = 20_000usize;
16796 let block_size = 16_384usize;
16797 let n = block_start + block_size + 8;
16798 let t = vec![1; n];
16799 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
16800
16801 let mut sa_single = vec![0; n];
16802 sa_single[block_start..block_start + block_size].copy_from_slice(&suffixes);
16803 let mut sa_threaded = sa_single.clone();
16804 let mut bucket_single = vec![0, block_size as SaSint];
16805 let mut bucket_threaded = bucket_single.clone();
16806 let mut cache = vec![ThreadCache::default(); 4 * LIBSAIS_PER_THREAD_CACHE_SIZE];
16807
16808 partial_sorting_scan_right_to_left_32s_1k(
16809 &t,
16810 &mut sa_single,
16811 &mut bucket_single,
16812 block_start as FastSint,
16813 block_size as FastSint,
16814 );
16815 partial_sorting_scan_right_to_left_32s_1k_block_omp(
16816 &t,
16817 &mut sa_threaded,
16818 &mut bucket_threaded,
16819 &mut cache,
16820 block_start as FastSint,
16821 block_size as FastSint,
16822 4,
16823 );
16824
16825 assert_eq!(sa_threaded, sa_single);
16826 assert_eq!(bucket_threaded, bucket_single);
16827 }
16828
16829 #[test]
16830 fn libsais64_partial_sorting_gather_lms_suffixes_32s_4k_compacts_negative_marked_entries() {
16831 let mut sa = vec![1 | SUFFIX_GROUP_MARKER, -3, 5 | SUFFIX_GROUP_MARKER, -7];
16832 let n = sa.len() as FastSint;
16833
16834 let l = partial_sorting_gather_lms_suffixes_32s_4k(&mut sa, 0, n);
16835
16836 assert_eq!(l, 2);
16837 assert_eq!(sa[0], (SAINT_MIN | SUFFIX_GROUP_MARKER) - 3);
16838 assert_eq!(sa[1], (SAINT_MIN | SUFFIX_GROUP_MARKER) - 7);
16839 }
16840
16841 #[test]
16842 fn libsais64_partial_sorting_gather_lms_suffixes_32s_1k_compacts_negative_marked_entries() {
16843 let mut sa = vec![1, -3, 5, -7];
16844 let n = sa.len() as FastSint;
16845
16846 let l = partial_sorting_gather_lms_suffixes_32s_1k(&mut sa, 0, n);
16847
16848 assert_eq!(l, 2);
16849 assert_eq!(sa[0], SAINT_MAX - 2);
16850 assert_eq!(sa[1], SAINT_MAX - 6);
16851 }
16852
16853 #[test]
16854 fn libsais64_partial_sorting_gather_lms_suffixes_32s_4k_omp_wraps_sequential_version() {
16855 let mut sa = vec![1 | SUFFIX_GROUP_MARKER, -3, 5 | SUFFIX_GROUP_MARKER, -7];
16856 let mut thread_state = alloc_thread_state(2).unwrap();
16857
16858 partial_sorting_gather_lms_suffixes_32s_4k_omp(&mut sa, 4, 2, &mut thread_state);
16859
16860 assert_eq!(sa[0], (SAINT_MIN | SUFFIX_GROUP_MARKER) - 3);
16861 assert_eq!(sa[1], (SAINT_MIN | SUFFIX_GROUP_MARKER) - 7);
16862 }
16863
16864 #[test]
16865 fn libsais64_partial_sorting_gather_lms_suffixes_32s_1k_omp_wraps_sequential_version() {
16866 let mut sa = vec![1, -3, 5, -7];
16867 let mut thread_state = alloc_thread_state(2).unwrap();
16868
16869 partial_sorting_gather_lms_suffixes_32s_1k_omp(&mut sa, 4, 2, &mut thread_state);
16870
16871 assert_eq!(sa[0], SAINT_MAX - 2);
16872 assert_eq!(sa[1], SAINT_MAX - 6);
16873 }
16874
16875 #[test]
16876 fn libsais64_partial_sorting_gather_lms_suffixes_32s_omp_uses_block_partition() {
16877 let n = 65_600usize;
16878 let input_4k: Vec<SaSint> = (0..n)
16879 .map(|i| {
16880 let value = (i as SaSint) | SUFFIX_GROUP_MARKER;
16881 if i % 5 == 0 {
16882 value | SAINT_MIN
16883 } else {
16884 value
16885 }
16886 })
16887 .collect();
16888 let count_4k = input_4k.iter().filter(|&&value| value < 0).count();
16889
16890 let mut single = input_4k.clone();
16891 let mut threaded = input_4k;
16892 let mut thread_state = alloc_thread_state(4).unwrap();
16893 partial_sorting_gather_lms_suffixes_32s_4k_omp(&mut single, n as SaSint, 1, &mut []);
16894 partial_sorting_gather_lms_suffixes_32s_4k_omp(
16895 &mut threaded,
16896 n as SaSint,
16897 4,
16898 &mut thread_state,
16899 );
16900 assert_eq!(&threaded[..count_4k], &single[..count_4k]);
16901
16902 let input_1k: Vec<SaSint> = (0..n)
16903 .map(|i| {
16904 let value = i as SaSint;
16905 if i % 7 == 0 {
16906 value | SAINT_MIN
16907 } else {
16908 value
16909 }
16910 })
16911 .collect();
16912 let count_1k = input_1k.iter().filter(|&&value| value < 0).count();
16913
16914 let mut single = input_1k.clone();
16915 let mut threaded = input_1k;
16916 partial_sorting_gather_lms_suffixes_32s_1k_omp(&mut single, n as SaSint, 1, &mut []);
16917 partial_sorting_gather_lms_suffixes_32s_1k_omp(
16918 &mut threaded,
16919 n as SaSint,
16920 4,
16921 &mut thread_state,
16922 );
16923 assert_eq!(&threaded[..count_1k], &single[..count_1k]);
16924 }
16925
16926 #[test]
16927 fn libsais64_partial_sorting_shift_markers_8u_omp_toggles_segment_markers() {
16928 let mut sa = vec![1 | SAINT_MIN, 2 | SAINT_MIN, 3, 4 | SAINT_MIN, 5];
16929 let mut buckets = vec![0; 6 * ALPHABET_SIZE];
16930 buckets[4 * ALPHABET_SIZE + buckets_index2(1, 0)] = 5;
16931 buckets[buckets_index2(0, 0)] = 0;
16932 let len = sa.len() as SaSint;
16933 partial_sorting_shift_markers_8u_omp(&mut sa, len, &buckets, 1);
16934 assert!(sa.iter().any(|&v| (v & SAINT_MIN) == 0));
16935 }
16936
16937 #[test]
16938 fn libsais64_partial_sorting_shift_markers_32s_6k_omp_toggles_segment_markers() {
16939 let mut sa = vec![1 | SAINT_MIN, 2 | SAINT_MIN, 3, 4 | SAINT_MIN, 5];
16940 let k = 3usize;
16941 let mut buckets = vec![0; 6 * k];
16942 buckets[buckets_index4(1, 0)] = 5;
16943 buckets[4 * k + buckets_index2(0, 0)] = 0;
16944 partial_sorting_shift_markers_32s_6k_omp(&mut sa, k as SaSint, &buckets, 1);
16945 assert!(sa.iter().any(|&v| (v & SAINT_MIN) == 0));
16946 }
16947
16948 #[test]
16949 fn libsais64_partial_sorting_shift_markers_32s_4k_toggles_group_markers() {
16950 let mut sa = vec![
16951 1 | SUFFIX_GROUP_MARKER,
16952 2 | SUFFIX_GROUP_MARKER,
16953 3,
16954 4 | SUFFIX_GROUP_MARKER,
16955 ];
16956 let len = sa.len() as SaSint;
16957 partial_sorting_shift_markers_32s_4k(&mut sa, len);
16958 assert!(sa.iter().any(|&v| (v & SUFFIX_GROUP_MARKER) == 0));
16959 }
16960
16961 #[test]
16962 fn libsais64_clear_lms_suffixes_omp_zeroes_requested_bucket_ranges() {
16963 let mut sa = vec![5, 4, 3, 2, 1, 9];
16964 let n = sa.len() as SaSint;
16965 let bucket_start = vec![1, 4, 5];
16966 let bucket_end = vec![3, 5, 5];
16967
16968 clear_lms_suffixes_omp(&mut sa, n, 3, &bucket_start, &bucket_end, 2);
16969
16970 assert_eq!(sa, vec![5, 0, 0, 2, 0, 9]);
16971 }
16972
16973 #[test]
16974 fn libsais64_final_bwt_scan_left_to_right_8u_rewrites_sa_and_induces_suffixes() {
16975 let t = vec![0_u8, 1, 2, 1, 0];
16976 let mut sa = vec![1, 0, 0];
16977 let mut induction_bucket = vec![0, 1, 3];
16978
16979 final_bwt_scan_left_to_right_8u(&t, &mut sa, &mut induction_bucket, 0, 1);
16980
16981 assert_eq!(sa[0], 0);
16982 assert_eq!(induction_bucket[0], 1);
16983 }
16984
16985 #[test]
16986 fn libsais64_final_bwt_aux_scan_left_to_right_8u_updates_sampling_array() {
16987 let t = vec![0_u8, 1, 2, 1, 0];
16988 let mut sa = vec![1, 0, 0];
16989 let mut induction_bucket = vec![0, 1, 3];
16990 let mut i_out = vec![0; 2];
16991
16992 final_bwt_aux_scan_left_to_right_8u(
16993 &t,
16994 &mut sa,
16995 0,
16996 &mut i_out,
16997 &mut induction_bucket,
16998 0,
16999 1,
17000 );
17001
17002 assert_eq!(i_out[0], 1);
17003 }
17004
17005 #[test]
17006 fn libsais64_final_sorting_scan_left_to_right_8u_clears_marker_and_places_suffix() {
17007 let t = vec![0_u8, 1, 2, 1, 0];
17008 let mut sa = vec![1, 0, 0];
17009 let mut induction_bucket = vec![0, 1, 3];
17010
17011 final_sorting_scan_left_to_right_8u(&t, &mut sa, &mut induction_bucket, 0, 1);
17012
17013 assert_eq!(sa[0], 0);
17014 assert_eq!(induction_bucket[0], 1);
17015 }
17016
17017 #[test]
17018 fn libsais64_final_sorting_scan_left_to_right_32s_clears_marker_and_places_suffix() {
17019 let t = vec![0, 1, 2, 1, 0];
17020 let mut sa = vec![1, 0, 0];
17021 let mut induction_bucket = vec![0, 1, 3];
17022
17023 final_sorting_scan_left_to_right_32s(&t, &mut sa, &mut induction_bucket, 0, 1);
17024
17025 assert_eq!(sa[0], 0);
17026 assert_eq!(induction_bucket[0], 1);
17027 }
17028
17029 #[test]
17030 fn libsais64_final_bwt_scan_left_to_right_8u_block_prepare_records_cache_and_counts() {
17031 let t = vec![0_u8, 1, 2, 1, 0];
17032 let mut sa = vec![1, 2, 0];
17033 let mut buckets = vec![99; ALPHABET_SIZE];
17034 let mut cache = vec![ThreadCache::default(); 4];
17035
17036 let count = final_bwt_scan_left_to_right_8u_block_prepare(
17037 &t,
17038 &mut sa,
17039 ALPHABET_SIZE as SaSint,
17040 &mut buckets,
17041 &mut cache,
17042 0,
17043 2,
17044 );
17045
17046 assert_eq!(count, 2);
17047 assert_eq!(sa[0] & SAINT_MAX, 0);
17048 assert_eq!(sa[1], 1 | SAINT_MIN);
17049 assert_eq!(buckets[0], 1);
17050 assert_eq!(buckets[1], 1);
17051 assert_eq!(cache[0].symbol, 0);
17052 assert_eq!(cache[0].index & SAINT_MAX, 0);
17053 assert_eq!(cache[1].symbol, 1);
17054 assert_eq!(cache[1].index & SAINT_MAX, 1);
17055 }
17056
17057 #[test]
17058 fn libsais64_final_sorting_scan_left_to_right_8u_omp_wraps_sequential_behavior() {
17059 let t = vec![0_u8, 1, 2, 1, 0];
17060 let mut sa = vec![0; t.len()];
17061 let mut induction_bucket = vec![0, 1, 3];
17062 let mut expected_sa = sa.clone();
17063 let mut expected_bucket = induction_bucket.clone();
17064
17065 final_sorting_scan_left_to_right_8u_omp(
17066 &t,
17067 &mut expected_sa,
17068 t.len() as FastSint,
17069 ALPHABET_SIZE as SaSint,
17070 &mut expected_bucket,
17071 1,
17072 &mut [],
17073 );
17074
17075 let mut thread_state = alloc_thread_state(2).unwrap();
17076
17077 final_sorting_scan_left_to_right_8u_omp(
17078 &t,
17079 &mut sa,
17080 t.len() as FastSint,
17081 ALPHABET_SIZE as SaSint,
17082 &mut induction_bucket,
17083 2,
17084 &mut thread_state,
17085 );
17086
17087 assert_eq!(sa, expected_sa);
17088 assert_eq!(induction_bucket, expected_bucket);
17089 }
17090
17091 #[test]
17092 fn libsais64_final_bwt_scan_right_to_left_8u_returns_zero_index_and_induces_suffixes() {
17093 let t = vec![0_u8, 1, 2, 1, 0];
17094 let mut sa = vec![0, 2, 0];
17095 let mut induction_bucket = vec![1, 2, 3];
17096
17097 let index = final_bwt_scan_right_to_left_8u(&t, &mut sa, &mut induction_bucket, 0, 2);
17098
17099 assert_eq!(index, 0);
17100 assert_eq!(sa[1], 1);
17101 assert_eq!(induction_bucket[1], 1);
17102 }
17103
17104 #[test]
17105 fn libsais64_final_sorting_scan_right_to_left_8u_omp_matches_sequential_path() {
17106 let t = vec![0_u8, 1, 2, 1, 0];
17107 let mut sa = vec![0, 2, 0, 0];
17108 let mut induction_bucket = vec![1, 2, 3];
17109 let mut expected_sa = sa.clone();
17110 let mut expected_bucket = induction_bucket.clone();
17111
17112 final_sorting_scan_right_to_left_8u_omp(
17113 &t,
17114 &mut expected_sa,
17115 0,
17116 2,
17117 ALPHABET_SIZE as SaSint,
17118 &mut expected_bucket,
17119 1,
17120 &mut [],
17121 );
17122
17123 let mut thread_state = alloc_thread_state(2).unwrap();
17124 final_sorting_scan_right_to_left_8u_omp(
17125 &t,
17126 &mut sa,
17127 0,
17128 2,
17129 ALPHABET_SIZE as SaSint,
17130 &mut induction_bucket,
17131 2,
17132 &mut thread_state,
17133 );
17134
17135 assert_eq!(sa, expected_sa);
17136 assert_eq!(induction_bucket, expected_bucket);
17137 }
17138
17139 #[test]
17140 fn libsais64_induce_final_order_8u_omp_non_bwt_matches_direct_final_scans() {
17141 let t = vec![0_u8, 1, 2, 1, 0];
17142 let mut sa = vec![0, 2, 0, 0, 0];
17143 let mut buckets = vec![0; 8 * ALPHABET_SIZE];
17144 buckets[6 * ALPHABET_SIZE..6 * ALPHABET_SIZE + 3].copy_from_slice(&[0, 1, 3]);
17145 buckets[7 * ALPHABET_SIZE..7 * ALPHABET_SIZE + 3].copy_from_slice(&[2, 4, 5]);
17146
17147 let mut expected_sa = sa.clone();
17148 let mut expected_left = vec![0, 1, 3];
17149 let mut expected_right = vec![2, 4, 5];
17150 final_sorting_scan_left_to_right_8u_omp(
17151 &t,
17152 &mut expected_sa,
17153 t.len() as FastSint,
17154 ALPHABET_SIZE as SaSint,
17155 &mut expected_left,
17156 1,
17157 &mut [],
17158 );
17159 final_sorting_scan_right_to_left_8u_omp(
17160 &t,
17161 &mut expected_sa,
17162 0,
17163 t.len() as FastSint,
17164 ALPHABET_SIZE as SaSint,
17165 &mut expected_right,
17166 1,
17167 &mut [],
17168 );
17169
17170 let mut thread_state = alloc_thread_state(2).unwrap();
17171 let result = induce_final_order_8u_omp(
17172 &t,
17173 &mut sa,
17174 t.len() as SaSint,
17175 ALPHABET_SIZE as SaSint,
17176 LIBSAIS_FLAGS_NONE,
17177 0,
17178 None,
17179 &mut buckets,
17180 2,
17181 &mut thread_state,
17182 );
17183
17184 assert_eq!(result, 0);
17185 assert_eq!(sa, expected_sa);
17186 assert_eq!(
17187 &buckets[6 * ALPHABET_SIZE..6 * ALPHABET_SIZE + 3],
17188 expected_left.as_slice()
17189 );
17190 assert_eq!(
17191 &buckets[7 * ALPHABET_SIZE..7 * ALPHABET_SIZE + 3],
17192 expected_right.as_slice()
17193 );
17194 }
17195
17196 #[test]
17197 fn libsais64_count_helpers_match_c_predicates() {
17198 let sa = [1, -1, 0, -3, 4, 0, -9];
17199 assert_eq!(
17200 count_negative_marked_suffixes(&sa, 0, sa.len() as FastSint),
17201 3
17202 );
17203 assert_eq!(count_zero_marked_suffixes(&sa, 0, sa.len() as FastSint), 2);
17204 assert_eq!(count_negative_marked_suffixes(&sa, 2, 3), 1);
17205 assert_eq!(count_zero_marked_suffixes(&sa, 2, 3), 1);
17206 }
17207
17208 #[test]
17209 fn libsais64_flip_suffix_markers_omp_toggles_saint_min_bits() {
17210 let mut sa = vec![1, -2, 3, -4];
17211 flip_suffix_markers_omp(&mut sa, 4, 1);
17212 assert_eq!(
17213 sa,
17214 vec![1 ^ SAINT_MIN, -2 ^ SAINT_MIN, 3 ^ SAINT_MIN, -4 ^ SAINT_MIN]
17215 );
17216 }
17217
17218 #[test]
17219 fn libsais64_place_cached_suffixes_writes_indices_to_symbol_slots() {
17220 let mut sa = vec![0; 8];
17221 let cache = vec![
17222 ThreadCache {
17223 symbol: 2,
17224 index: 10,
17225 },
17226 ThreadCache {
17227 symbol: 5,
17228 index: 20,
17229 },
17230 ThreadCache {
17231 symbol: 1,
17232 index: 30,
17233 },
17234 ];
17235
17236 place_cached_suffixes(&mut sa, &cache, 0, cache.len() as FastSint);
17237
17238 assert_eq!(sa[2], 10);
17239 assert_eq!(sa[5], 20);
17240 assert_eq!(sa[1], 30);
17241 }
17242
17243 #[test]
17244 fn libsais64_compact_and_place_cached_suffixes_discards_negative_symbols() {
17245 let mut sa = vec![0; 8];
17246 let mut cache = vec![
17247 ThreadCache {
17248 symbol: 2,
17249 index: 10,
17250 },
17251 ThreadCache {
17252 symbol: -1,
17253 index: 99,
17254 },
17255 ThreadCache {
17256 symbol: 5,
17257 index: 20,
17258 },
17259 ThreadCache {
17260 symbol: -4,
17261 index: 77,
17262 },
17263 ThreadCache {
17264 symbol: 1,
17265 index: 30,
17266 },
17267 ];
17268 let cache_len = cache.len() as FastSint;
17269
17270 compact_and_place_cached_suffixes(&mut sa, &mut cache, 0, cache_len);
17271
17272 assert_eq!(sa[2], 10);
17273 assert_eq!(sa[5], 20);
17274 assert_eq!(sa[1], 30);
17275 assert_eq!(
17276 cache[0],
17277 ThreadCache {
17278 symbol: 2,
17279 index: 10
17280 }
17281 );
17282 assert_eq!(
17283 cache[1],
17284 ThreadCache {
17285 symbol: 5,
17286 index: 20
17287 }
17288 );
17289 assert_eq!(
17290 cache[2],
17291 ThreadCache {
17292 symbol: 1,
17293 index: 30
17294 }
17295 );
17296 }
17297
17298 #[test]
17299 fn libsais64_gather_lms_suffixes_32s_collects_expected_suffix_starts() {
17300 let t = vec![2, 1, 3, 1, 0];
17301 let mut sa = vec![0; t.len()];
17302 let m = gather_lms_suffixes_32s(&t, &mut sa, t.len() as SaSint);
17303 assert!(m >= 0);
17304 assert!(sa
17305 .iter()
17306 .all(|&value| value >= 0 && value <= t.len() as SaSint));
17307 assert!(sa[t.len() - 1] >= 1 && sa[t.len() - 1] <= t.len() as SaSint - 1);
17308 }
17309
17310 #[test]
17311 fn libsais64_gather_compacted_lms_suffixes_32s_skips_negative_marked_symbols() {
17312 let t = vec![2, -1, 3, 1, 0];
17313 let mut sa = vec![0; t.len()];
17314 let m = gather_compacted_lms_suffixes_32s(&t, &mut sa, t.len() as SaSint);
17315 assert!(m >= 0);
17316 assert!(sa
17317 .iter()
17318 .all(|&value| value >= 0 && value <= t.len() as SaSint));
17319 }
17320
17321 #[test]
17322 fn libsais64_count_lms_suffixes_32s_2k_counts_two_bucket_categories() {
17323 let t = vec![2, 1, 3, 1, 0];
17324 let mut buckets = vec![0; 2 * 4];
17325 count_lms_suffixes_32s_2k(&t, t.len() as SaSint, 4, &mut buckets);
17326 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
17327 }
17328
17329 #[test]
17330 fn libsais64_count_lms_suffixes_32s_4k_counts_four_bucket_categories() {
17331 let t = vec![2, 1, 3, 1, 0];
17332 let mut buckets = vec![0; 4 * 4];
17333 count_lms_suffixes_32s_4k(&t, t.len() as SaSint, 4, &mut buckets);
17334 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
17335 }
17336
17337 #[test]
17338 fn libsais64_count_compacted_lms_suffixes_32s_2k_masks_saint_bits() {
17339 let t = vec![2, SAINT_MIN | 1, 3, 1, 0];
17340 let mut buckets = vec![0; 2 * 4];
17341 count_compacted_lms_suffixes_32s_2k(&t, t.len() as SaSint, 4, &mut buckets);
17342 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
17343 }
17344
17345 #[test]
17346 fn libsais64_count_and_gather_lms_suffixes_8u_updates_sa_and_buckets() {
17347 let t = vec![2_u8, 1, 3, 1, 0];
17348 let mut sa = vec![0; t.len()];
17349 let mut buckets = vec![0; 4 * ALPHABET_SIZE];
17350 let m = count_and_gather_lms_suffixes_8u(
17351 &t,
17352 &mut sa,
17353 t.len() as SaSint,
17354 &mut buckets,
17355 0,
17356 t.len() as FastSint,
17357 );
17358 assert_eq!(m, 1);
17359 assert_eq!(sa[t.len() - 1], 1);
17360 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
17361 }
17362
17363 #[test]
17364 fn libsais64_count_and_gather_lms_suffixes_8u_omp_preserves_sequential_wrapper_behavior() {
17365 let t = vec![2_u8, 1, 3, 1, 0];
17366 let mut sa = vec![0; t.len()];
17367 let mut buckets = vec![0; 4 * ALPHABET_SIZE];
17368 let mut thread_state = alloc_thread_state(2).unwrap();
17369 let m = count_and_gather_lms_suffixes_8u_omp(
17370 &t,
17371 &mut sa,
17372 t.len() as SaSint,
17373 &mut buckets,
17374 2,
17375 &mut thread_state,
17376 );
17377 assert_eq!(m, 1);
17378 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
17379 }
17380
17381 #[test]
17382 fn libsais64_get_bucket_stride_prefers_aligned_sizes_when_space_allows() {
17383 assert_eq!(get_bucket_stride(8192, 1000, 2), 1024);
17384 assert_eq!(get_bucket_stride(256, 17, 2), 32);
17385 assert_eq!(get_bucket_stride(8, 17, 2), 17);
17386 }
17387
17388 #[test]
17389 fn libsais64_count_suffixes_32s_counts_symbol_histogram() {
17390 let t = vec![2, 1, 2, 3, 1, 0, 2];
17391 let mut buckets = vec![0; 4];
17392 count_suffixes_32s(&t, t.len() as SaSint, 4, &mut buckets);
17393 assert_eq!(buckets, vec![1, 2, 3, 1]);
17394 }
17395
17396 #[test]
17397 fn libsais64_initialize_buckets_start_and_end_8u_sets_ranges_and_freq() {
17398 let mut buckets = vec![0; 8 * ALPHABET_SIZE];
17399 buckets[buckets_index4(0, 0)] = 1;
17400 buckets[buckets_index4(1, 1)] = 2;
17401 buckets[buckets_index4(2, 3)] = 3;
17402 let mut freq = vec![0; ALPHABET_SIZE];
17403 let k = initialize_buckets_start_and_end_8u(&mut buckets, Some(&mut freq));
17404 assert_eq!(k, 3);
17405 assert_eq!(freq[0], 1);
17406 assert_eq!(freq[1], 2);
17407 assert_eq!(freq[2], 3);
17408 assert_eq!(buckets[6 * ALPHABET_SIZE], 0);
17409 assert_eq!(buckets[7 * ALPHABET_SIZE], 1);
17410 assert_eq!(buckets[6 * ALPHABET_SIZE + 1], 1);
17411 assert_eq!(buckets[7 * ALPHABET_SIZE + 1], 3);
17412 }
17413
17414 #[test]
17415 fn libsais64_initialize_buckets_start_and_end_32s_6k_sets_prefix_ranges() {
17416 let k = 3;
17417 let mut buckets = vec![0; 6 * k];
17418 buckets[buckets_index4(0, 0)] = 1;
17419 buckets[buckets_index4(0, 1)] = 2;
17420 buckets[buckets_index4(1, 2)] = 3;
17421 buckets[buckets_index4(2, 3)] = 4;
17422 initialize_buckets_start_and_end_32s_6k(k as SaSint, &mut buckets);
17423 assert_eq!(&buckets[4 * k..5 * k], &[0, 3, 6]);
17424 assert_eq!(&buckets[5 * k..6 * k], &[3, 6, 10]);
17425 }
17426
17427 #[test]
17428 fn libsais64_initialize_buckets_start_and_end_32s_4k_sets_prefix_ranges() {
17429 let k = 3;
17430 let mut buckets = vec![0; 4 * k];
17431 buckets[buckets_index2(0, 0)] = 1;
17432 buckets[buckets_index2(0, 1)] = 2;
17433 buckets[buckets_index2(1, 0)] = 3;
17434 buckets[buckets_index2(2, 1)] = 4;
17435 initialize_buckets_start_and_end_32s_4k(k as SaSint, &mut buckets);
17436 assert_eq!(&buckets[2 * k..3 * k], &[0, 3, 6]);
17437 assert_eq!(&buckets[3 * k..4 * k], &[3, 6, 10]);
17438 }
17439
17440 #[test]
17441 fn libsais64_initialize_buckets_end_32s_2k_rewrites_first_lanes_to_end_positions() {
17442 let k = 3;
17443 let mut buckets = vec![1, 2, 3, 4, 5, 6];
17444 initialize_buckets_end_32s_2k(k as SaSint, &mut buckets);
17445 assert_eq!(buckets[0], 3);
17446 assert_eq!(buckets[2], 10);
17447 assert_eq!(buckets[4], 21);
17448 }
17449
17450 #[test]
17451 fn libsais64_initialize_buckets_start_and_end_32s_2k_copies_start_positions() {
17452 let k = 3;
17453 let mut buckets = vec![3, 2, 10, 4, 21, 6];
17454 initialize_buckets_start_and_end_32s_2k(k as SaSint, &mut buckets);
17455 assert_eq!(&buckets[..k], &[3, 10, 21]);
17456 assert_eq!(&buckets[k..2 * k], &[0, 3, 10]);
17457 }
17458
17459 #[test]
17460 fn libsais64_initialize_buckets_start_32s_1k_builds_prefix_starts() {
17461 let mut buckets = vec![1, 2, 3];
17462 initialize_buckets_start_32s_1k(3, &mut buckets);
17463 assert_eq!(buckets, vec![0, 1, 3]);
17464 }
17465
17466 #[test]
17467 fn libsais64_initialize_buckets_end_32s_1k_builds_prefix_ends() {
17468 let mut buckets = vec![1, 2, 3];
17469 initialize_buckets_end_32s_1k(3, &mut buckets);
17470 assert_eq!(buckets, vec![1, 3, 6]);
17471 }
17472
17473 #[test]
17474 fn libsais64_initialize_buckets_for_lms_suffixes_radix_sort_8u_returns_total_lms_slots() {
17475 let t = vec![2_u8, 1, 3, 1, 0];
17476 let mut buckets = vec![0; 6 * ALPHABET_SIZE];
17477 buckets[buckets_index4(0, 1)] = 1;
17478 buckets[buckets_index4(1, 3)] = 2;
17479 let sum = initialize_buckets_for_lms_suffixes_radix_sort_8u(&t, &mut buckets, 4);
17480 assert!(sum >= 0);
17481 }
17482
17483 #[test]
17484 fn libsais64_initialize_buckets_for_lms_suffixes_radix_sort_32s_2k_rewrites_two_lane_prefixes()
17485 {
17486 let t = vec![2, 1, 3, 1, 0];
17487 let mut buckets = vec![0; 2 * 4];
17488 initialize_buckets_for_lms_suffixes_radix_sort_32s_2k(&t, 4, &mut buckets, 4);
17489 assert!(buckets.iter().any(|&v| v != 0));
17490 }
17491
17492 #[test]
17493 fn libsais64_initialize_buckets_for_lms_suffixes_radix_sort_32s_6k_returns_total_lms_slots() {
17494 let t = vec![2, 1, 3, 1, 0];
17495 let mut buckets = vec![0; 6 * 4];
17496 buckets[buckets_index4(0, 1)] = 1;
17497 buckets[buckets_index4(1, 3)] = 2;
17498 let sum = initialize_buckets_for_lms_suffixes_radix_sort_32s_6k(&t, 4, &mut buckets, 4);
17499 assert!(sum >= 0);
17500 }
17501
17502 #[test]
17503 fn libsais64_initialize_buckets_for_radix_and_partial_sorting_32s_4k_sets_start_end_views() {
17504 let t = vec![2, 1, 3, 1, 0];
17505 let k = 4usize;
17506 let mut buckets = vec![0; 4 * k];
17507 buckets[buckets_index2(0, 0)] = 1;
17508 buckets[buckets_index2(0, 1)] = 2;
17509 buckets[buckets_index2(1, 0)] = 3;
17510 initialize_buckets_for_radix_and_partial_sorting_32s_4k(&t, k as SaSint, &mut buckets, 4);
17511 assert_eq!(buckets[2 * k], 0);
17512 assert!(buckets[3 * k] >= buckets[2 * k]);
17513 }
17514
17515 #[test]
17516 fn libsais64_initialize_buckets_for_partial_sorting_8u_sets_start_and_distinct_views() {
17517 let t = vec![2_u8, 1, 3, 1, 0];
17518 let mut buckets = vec![0; 6 * ALPHABET_SIZE];
17519 buckets[buckets_index4(0, 0)] = 1;
17520 buckets[buckets_index4(0, 2)] = 2;
17521 initialize_buckets_for_partial_sorting_8u(&t, &mut buckets, 4, 3);
17522 assert!(buckets[0] >= 4);
17523 assert!(buckets[1] >= 0);
17524 assert!(buckets[4 * ALPHABET_SIZE] >= 4);
17525 }
17526
17527 #[test]
17528 fn libsais64_initialize_buckets_for_partial_sorting_32s_6k_rewrites_bucket_views() {
17529 let t = vec![2, 1, 3, 1, 0];
17530 let k = 4usize;
17531 let mut buckets = vec![0; 6 * k];
17532 buckets[buckets_index4(0, 0)] = 1;
17533 buckets[buckets_index4(0, 1)] = 2;
17534 buckets[buckets_index4(1, 2)] = 3;
17535 initialize_buckets_for_partial_sorting_32s_6k(&t, k as SaSint, &mut buckets, 4, 3);
17536 assert!(buckets[0] >= 4);
17537 assert!(buckets[4 * k] >= 4);
17538 }
17539
17540 #[test]
17541 fn libsais64_place_lms_suffixes_interval_32s_4k_moves_suffixes_into_bucket_intervals() {
17542 let mut sa = vec![10, 11, 12, 13, 14];
17543 let k = 3usize;
17544 let mut buckets = vec![0; 4 * k];
17545 buckets[buckets_index2(0, 1)] = 0;
17546 buckets[buckets_index2(1, 1)] = 2;
17547 buckets[buckets_index2(2, 1)] = 3;
17548 buckets[3 * k] = 2;
17549 buckets[3 * k + 1] = 5;
17550
17551 place_lms_suffixes_interval_32s_4k(&mut sa, 5, k as SaSint, 5, &buckets);
17552
17553 assert_eq!(sa, vec![0, 0, 0, 0, 14]);
17554 }
17555
17556 #[test]
17557 fn libsais64_place_lms_suffixes_interval_32s_2k_moves_suffixes_into_bucket_intervals() {
17558 let mut sa = vec![10, 11, 12, 13, 14];
17559 let mut buckets = vec![0; 2 * 3];
17560 buckets[buckets_index2(0, 0)] = 2;
17561 buckets[buckets_index2(0, 1)] = 0;
17562 buckets[buckets_index2(1, 0)] = 5;
17563 buckets[buckets_index2(1, 1)] = 2;
17564 buckets[buckets_index2(2, 0)] = 5;
17565 buckets[buckets_index2(2, 1)] = 3;
17566
17567 place_lms_suffixes_interval_32s_2k(&mut sa, 5, 3, 5, &buckets);
17568
17569 assert_eq!(sa, vec![0, 0, 0, 0, 14]);
17570 }
17571
17572 #[test]
17573 fn libsais64_place_lms_suffixes_interval_32s_1k_places_suffixes_by_symbol_bucket() {
17574 let t = vec![0, 1, 1, 2, 2];
17575 let mut sa = vec![1, 2, 3, 4, 99];
17576 let buckets = vec![0, 2, 5];
17577
17578 place_lms_suffixes_interval_32s_1k(&t, &mut sa, 3, 4, &buckets);
17579
17580 assert_eq!(sa, vec![1, 2, 0, 3, 4]);
17581 }
17582
17583 #[test]
17584 fn libsais64_accumulate_counts_helpers_match_prefix_bucket_addition() {
17585 let mut bucket00 = vec![4, 5, 6];
17586 let bucket01 = vec![1, 2, 3];
17587 let bucket02 = vec![7, 8, 9];
17588 let bucket03 = vec![10, 11, 12];
17589 let bucket04 = vec![13, 14, 15];
17590 let bucket05 = vec![16, 17, 18];
17591 let bucket06 = vec![19, 20, 21];
17592 let bucket07 = vec![22, 23, 24];
17593 let bucket08 = vec![25, 26, 27];
17594
17595 accumulate_counts_s32_2(&mut bucket00, &bucket01);
17596 assert_eq!(bucket00, vec![5, 7, 9]);
17597
17598 accumulate_counts_s32_3(&mut bucket00, &bucket01, &bucket02);
17599 assert_eq!(bucket00, vec![13, 17, 21]);
17600
17601 accumulate_counts_s32_4(&mut bucket00, &bucket01, &bucket02, &bucket03);
17602 assert_eq!(bucket00, vec![31, 38, 45]);
17603
17604 accumulate_counts_s32_5(&mut bucket00, &bucket01, &bucket02, &bucket03, &bucket04);
17605 assert_eq!(bucket00, vec![62, 73, 84]);
17606
17607 accumulate_counts_s32_6(
17608 &mut bucket00,
17609 &bucket01,
17610 &bucket02,
17611 &bucket03,
17612 &bucket04,
17613 &bucket05,
17614 );
17615 assert_eq!(bucket00, vec![109, 125, 141]);
17616
17617 accumulate_counts_s32_7(
17618 &mut bucket00,
17619 &bucket01,
17620 &bucket02,
17621 &bucket03,
17622 &bucket04,
17623 &bucket05,
17624 &bucket06,
17625 );
17626 assert_eq!(bucket00, vec![175, 197, 219]);
17627
17628 accumulate_counts_s32_8(
17629 &mut bucket00,
17630 &bucket01,
17631 &bucket02,
17632 &bucket03,
17633 &bucket04,
17634 &bucket05,
17635 &bucket06,
17636 &bucket07,
17637 );
17638 assert_eq!(bucket00, vec![263, 292, 321]);
17639
17640 accumulate_counts_s32_9(
17641 &mut bucket00,
17642 &bucket01,
17643 &bucket02,
17644 &bucket03,
17645 &bucket04,
17646 &bucket05,
17647 &bucket06,
17648 &bucket07,
17649 &bucket08,
17650 );
17651 assert_eq!(bucket00, vec![376, 413, 450]);
17652 }
17653
17654 #[test]
17655 fn libsais64_accumulate_counts_s32_matches_dispatch_for_small_bucket_counts() {
17656 let mut buckets = vec![1, 2, 3, 4, 5, 6, 7, 8];
17657 accumulate_counts_s32(&mut buckets, 2, 2, 4);
17658 assert_eq!(buckets, vec![1, 2, 3, 4, 5, 6, 16, 20]);
17659 }
17660
17661 #[test]
17662 fn libsais64_accumulate_counts_s32_matches_dispatch_for_nine_buckets() {
17663 let mut buckets = vec![
17664 1, 10, 2, 20, 3, 30, 4, 40, 5, 50, 6, 60, 7, 70, 8, 80, 9, 90,
17665 ];
17666 accumulate_counts_s32(&mut buckets, 2, 2, 9);
17667 assert_eq!(
17668 buckets,
17669 vec![1, 10, 2, 20, 3, 30, 4, 40, 5, 50, 6, 60, 7, 70, 8, 80, 45, 450]
17670 );
17671 }
17672
17673 #[test]
17674 fn libsais64_accumulate_counts_s32_matches_chunked_nine_then_tail_behavior() {
17675 let mut buckets = (1..=11).collect::<Vec<SaSint>>();
17676 accumulate_counts_s32(&mut buckets, 1, 1, 11);
17677 assert_eq!(buckets, vec![1, 2, 3, 4, 5, 6, 7, 8, 45, 10, 66]);
17678 }
17679
17680 #[test]
17681 fn libsais64_final_sorting_scan_left_to_right_32s_block_omp_places_cached_suffixes() {
17682 let t = vec![0, 1, 2, 1, 0];
17683 let mut sa = vec![1, 2, 0, 0];
17684 let mut induction_bucket = vec![0, 1, 3];
17685 let mut cache = vec![ThreadCache::default(); LIBSAIS_PER_THREAD_CACHE_SIZE];
17686
17687 final_sorting_scan_left_to_right_32s_block_omp(
17688 &t,
17689 &mut sa,
17690 &mut induction_bucket,
17691 &mut cache,
17692 0,
17693 2,
17694 2,
17695 );
17696
17697 assert_eq!(sa[0] & SAINT_MAX, 0);
17698 assert_eq!(sa[1] & SAINT_MAX, 1);
17699 assert_eq!(induction_bucket[0], 1);
17700 assert_eq!(induction_bucket[1], 2);
17701 }
17702
17703 #[test]
17704 fn libsais64_final_sorting_scan_left_to_right_8u_block_omp_uses_thread_buckets() {
17705 let block_start = 20_000usize;
17706 let block_size = 16_384usize;
17707 let n = block_start + block_size + 8;
17708 let t = vec![1_u8; n];
17709 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
17710
17711 let mut expected_sa = vec![0; n];
17712 expected_sa[block_start..block_start + block_size].copy_from_slice(&suffixes);
17713 let mut threaded_sa = expected_sa.clone();
17714 let mut expected_bucket = vec![0; ALPHABET_SIZE];
17715 let mut threaded_bucket = expected_bucket.clone();
17716 let mut thread_state = alloc_thread_state(4).unwrap();
17717
17718 final_sorting_scan_left_to_right_8u(
17719 &t,
17720 &mut expected_sa,
17721 &mut expected_bucket,
17722 block_start as FastSint,
17723 block_size as FastSint,
17724 );
17725 final_sorting_scan_left_to_right_8u_block_omp(
17726 &t,
17727 &mut threaded_sa,
17728 ALPHABET_SIZE as SaSint,
17729 &mut threaded_bucket,
17730 block_start as FastSint,
17731 block_size as FastSint,
17732 4,
17733 &mut thread_state,
17734 );
17735
17736 assert_eq!(threaded_sa, expected_sa);
17737 assert_eq!(threaded_bucket, expected_bucket);
17738 }
17739
17740 #[test]
17741 fn libsais64_final_sorting_scan_right_to_left_32s_block_omp_runs_block_pipeline() {
17742 let t = vec![0, 1, 2, 1, 0];
17743 let mut sa = vec![0, 2, 0, 0];
17744 let mut induction_bucket = vec![1, 2, 3];
17745 let mut expected_sa = sa.clone();
17746 let mut expected_bucket = induction_bucket.clone();
17747 let mut cache = vec![ThreadCache::default(); LIBSAIS_PER_THREAD_CACHE_SIZE];
17748
17749 final_sorting_scan_right_to_left_32s(&t, &mut expected_sa, &mut expected_bucket, 0, 2);
17750 final_sorting_scan_right_to_left_32s_block_omp(
17751 &t,
17752 &mut sa,
17753 &mut induction_bucket,
17754 &mut cache,
17755 0,
17756 2,
17757 2,
17758 );
17759
17760 assert_eq!(sa, expected_sa);
17761 assert_eq!(induction_bucket, expected_bucket);
17762 }
17763
17764 #[test]
17765 fn libsais64_bwt_copy_8u_copies_low_bytes_from_suffix_array_storage() {
17766 let a = vec![65, 255, 256, -1];
17767 let mut u = vec![0_u8; 4];
17768
17769 bwt_copy_8u(&mut u, &a, 4);
17770
17771 assert_eq!(u, vec![65, 255, 0, 255]);
17772 }
17773
17774 #[test]
17775 fn libsais64_bwt_copy_8u_omp_matches_sequential_copy() {
17776 let a = vec![1, 2, 3, 4, 5];
17777 let mut u = vec![0_u8; 5];
17778
17779 bwt_copy_8u_omp(&mut u, &a, 5, 4);
17780
17781 assert_eq!(u, vec![1, 2, 3, 4, 5]);
17782 }
17783
17784 #[test]
17785 fn libsais64_conversion_helpers_use_little_endian_word_layout() {
17786 let s = vec![11_u32, 22, 33, 44];
17787 let mut d = vec![0_u64; 4];
17788 convert_32u_to_64u(&s, &mut d, 1, 2);
17789 assert_eq!(d, vec![0, 22, 33, 0]);
17790
17791 let mut words = vec![5_u32, 6, 7, 8, 0, 0, 0, 0];
17792 convert_inplace_32u_to_64u(&mut words, 0, 4);
17793 assert_eq!(words, vec![5, 0, 6, 0, 7, 0, 8, 0]);
17794 convert_inplace_64u_to_32u(&mut words, 0, 4);
17795 assert_eq!(&words[..4], &[5, 6, 7, 8]);
17796
17797 let mut words = vec![9_u32, 10, 11, 12, 0, 0, 0, 0];
17798 convert_inplace_32u_to_64u_omp(&mut words, 4, 2);
17799 assert_eq!(words, vec![9, 0, 10, 0, 11, 0, 12, 0]);
17800 }
17801
17802 #[test]
17803 fn libsais64_32bit_workspace_sizing_matches_upstream_capacity_rules() {
17804 assert_eq!(libsais64_new_32bit_fs(10, 4), Some(18));
17805 assert_eq!(libsais64_new_32bit_fs(i32::MAX as usize - 4, 100), Some(4));
17806 assert_eq!(libsais64_new_32bit_fs(i32::MAX as usize + 1, 0), None);
17807 }
17808
17809 #[test]
17810 fn libsais64_32bit_suffix_adapter_widens_suffix_array_and_frequency() {
17811 let text = b"banana";
17812 let fs = 4;
17813 let new_fs = libsais64_new_32bit_fs(text.len(), fs).expect("small workspace");
17814 let mut sa64 = vec![-1; text.len() + fs as usize];
17815 let mut freq64 = vec![-1; ALPHABET_SIZE];
17816 let rc64 = libsais64_run_32bit_omp(text, &mut sa64, fs, Some(&mut freq64), 2, false)
17817 .expect("small input uses 32-bit adapter");
17818
17819 let mut sa32 = vec![-1; text.len() + new_fs as usize];
17820 let mut freq32 = vec![-1; ALPHABET_SIZE];
17821 let rc32 = crate::libsais_omp(text, &mut sa32, new_fs, Some(&mut freq32), 2);
17822
17823 assert_eq!(rc64, SaSint::from(rc32));
17824 assert_eq!(
17825 &sa64[..text.len()],
17826 &sa32[..text.len()]
17827 .iter()
17828 .map(|&value| SaSint::from(value as u32))
17829 .collect::<Vec<_>>()
17830 );
17831 assert_eq!(freq64[b'a' as usize], 3);
17832 assert_eq!(freq64[b'b' as usize], 1);
17833 assert_eq!(freq64[b'n' as usize], 2);
17834 assert_eq!(
17835 freq64[..ALPHABET_SIZE],
17836 freq32
17837 .iter()
17838 .map(|&value| SaSint::from(value))
17839 .collect::<Vec<_>>()
17840 );
17841 }
17842
17843 #[test]
17844 fn libsais64_32bit_gsa_adapter_widens_suffix_array_and_frequency() {
17845 let text = b"ban\0ana\0";
17846 let fs = 2;
17847 let mut sa64 = vec![-1; text.len() + fs as usize];
17848 let mut freq64 = vec![-1; ALPHABET_SIZE];
17849
17850 let rc = libsais64_run_32bit_omp(text, &mut sa64, fs, Some(&mut freq64), 2, true)
17851 .expect("small GSA input uses 32-bit adapter");
17852
17853 let mut direct_sa = vec![0; text.len()];
17854 let mut direct_freq = vec![0; ALPHABET_SIZE];
17855 assert_eq!(
17856 crate::libsais_gsa(text, &mut direct_sa, 0, Some(&mut direct_freq)),
17857 0
17858 );
17859 assert_eq!(rc, 0);
17860 assert_eq!(
17861 sa64[..text.len()],
17862 direct_sa
17863 .iter()
17864 .map(|&value| SaSint::from(value as u32))
17865 .collect::<Vec<_>>()
17866 );
17867 assert_eq!(
17868 freq64[..ALPHABET_SIZE],
17869 direct_freq
17870 .iter()
17871 .map(|&value| SaSint::from(value))
17872 .collect::<Vec<_>>()
17873 );
17874 }
17875
17876 #[test]
17877 fn libsais64_32bit_bwt_adapters_widen_frequency_and_aux_samples() {
17878 let text = b"mississippi";
17879 let fs = 6;
17880 let r = 4;
17881
17882 let mut bwt64 = vec![0; text.len()];
17883 let mut freq64 = vec![-1; ALPHABET_SIZE];
17884 let primary64 = libsais64_bwt_run_32bit_omp(text, &mut bwt64, fs, Some(&mut freq64), 2)
17885 .expect("small input uses 32-bit BWT adapter");
17886
17887 let mut bwt32 = vec![0; text.len()];
17888 let mut work32 = vec![0; text.len() + fs as usize * 2 + text.len()];
17889 let mut freq32 = vec![-1; ALPHABET_SIZE];
17890 let primary32 =
17891 crate::libsais_bwt_omp(text, &mut bwt32, &mut work32, 23, Some(&mut freq32), 2);
17892
17893 assert_eq!(primary64, SaSint::from(primary32));
17894 assert_eq!(bwt64, bwt32);
17895 assert_eq!(
17896 freq64[..ALPHABET_SIZE],
17897 freq32
17898 .iter()
17899 .map(|&value| SaSint::from(value))
17900 .collect::<Vec<_>>()
17901 );
17902
17903 let mut aux_bwt64 = vec![0; text.len()];
17904 let mut aux64 = vec![-1; (text.len() - 1) / r as usize + 1];
17905 let mut aux_freq64 = vec![-1; ALPHABET_SIZE];
17906 let rc64 = libsais64_bwt_aux_run_32bit_omp(
17907 text,
17908 &mut aux_bwt64,
17909 fs,
17910 Some(&mut aux_freq64),
17911 r,
17912 &mut aux64,
17913 2,
17914 )
17915 .expect("small input uses 32-bit aux BWT adapter");
17916
17917 let mut aux_bwt32 = vec![0; text.len()];
17918 let mut aux_work32 = vec![0; text.len() + fs as usize * 2 + text.len()];
17919 let mut aux32 = vec![-1; aux64.len()];
17920 let mut aux_freq32 = vec![-1; ALPHABET_SIZE];
17921 let rc32 = crate::libsais_bwt_aux_omp(
17922 text,
17923 &mut aux_bwt32,
17924 &mut aux_work32,
17925 23,
17926 Some(&mut aux_freq32),
17927 r as i32,
17928 &mut aux32,
17929 2,
17930 );
17931
17932 assert_eq!(rc64, SaSint::from(rc32));
17933 assert_eq!(aux_bwt64, aux_bwt32);
17934 assert_eq!(
17935 aux64,
17936 aux32
17937 .iter()
17938 .map(|&value| SaSint::from(value))
17939 .collect::<Vec<_>>()
17940 );
17941 assert_eq!(
17942 aux_freq64[..ALPHABET_SIZE],
17943 aux_freq32
17944 .iter()
17945 .map(|&value| SaSint::from(value))
17946 .collect::<Vec<_>>()
17947 );
17948 }
17949
17950 #[test]
17951 fn libsais64_bwt_copy_8u_omp_uses_block_partition_for_large_inputs() {
17952 let n = 65_600usize;
17953 let a: Vec<SaSint> = (0..n).map(|i| (i * 17) as SaSint).collect();
17954 let mut threaded = vec![0; n];
17955 let mut sequential = vec![0; n];
17956
17957 bwt_copy_8u_omp(&mut threaded, &a, n as SaSint, 4);
17958 bwt_copy_8u(&mut sequential, &a, n as SaSint);
17959
17960 assert_eq!(threaded, sequential);
17961 }
17962
17963 #[test]
17964 fn libsais64_flip_suffix_markers_omp_uses_block_partition_for_large_inputs() {
17965 let n = 65_600usize;
17966 let mut single: Vec<SaSint> = (0..n).map(|i| (i as SaSint) ^ SAINT_MIN).collect();
17967 let mut threaded = single.clone();
17968
17969 flip_suffix_markers_omp(&mut single, n as SaSint, 1);
17970 flip_suffix_markers_omp(&mut threaded, n as SaSint, 4);
17971
17972 assert_eq!(threaded, single);
17973 }
17974
17975 #[test]
17976 fn libsais64_renumber_lms_suffixes_8u_writes_names_into_second_half() {
17977 let mut sa = vec![1 | SAINT_MIN, 3, 0, 0];
17978
17979 let name = renumber_lms_suffixes_8u(&mut sa, 2, 0, 0, 2);
17980
17981 assert_eq!(name, 1);
17982 assert_eq!(sa[2], SAINT_MIN);
17983 assert_eq!(sa[3], SAINT_MIN | 1);
17984 }
17985
17986 #[test]
17987 fn libsais64_renumber_lms_suffixes_8u_matches_upstream_c_helper() {
17988 let mut sa_rust = vec![1 | SAINT_MIN, 3, 0, 0];
17989 let mut sa_c = sa_rust.clone();
17990
17991 let rust_name = renumber_lms_suffixes_8u(&mut sa_rust, 2, 0, 0, 2);
17992 let c_name =
17993 unsafe { probe_libsais64_renumber_lms_suffixes_8u(sa_c.as_mut_ptr(), 2, 0, 0, 2) };
17994
17995 assert_eq!(rust_name, c_name);
17996 assert_eq!(sa_rust, sa_c);
17997 }
17998
17999 #[test]
18000 fn libsais64_gather_marked_lms_suffixes_moves_negative_marked_entries_to_tail() {
18001 let mut sa = vec![0, 0, 1 | SAINT_MIN, 3];
18002
18003 let l = gather_marked_lms_suffixes(&mut sa, 2, 4, 0, 2);
18004
18005 assert_eq!(l, 3);
18006 assert_eq!(sa[3], 1);
18007 }
18008
18009 #[test]
18010 fn libsais64_gather_marked_lms_suffixes_matches_upstream_c_helper() {
18011 let mut sa_rust = vec![0, 0, 1 | SAINT_MIN, 3];
18012 let mut sa_c = sa_rust.clone();
18013
18014 let rust_l = gather_marked_lms_suffixes(&mut sa_rust, 2, 4, 0, 2);
18015 let c_l =
18016 unsafe { probe_libsais64_gather_marked_lms_suffixes(sa_c.as_mut_ptr(), 2, 4, 0, 2) };
18017
18018 assert_eq!(rust_l, c_l);
18019 assert_eq!(sa_rust, sa_c);
18020 }
18021
18022 #[test]
18023 fn libsais64_renumber_lms_suffixes_8u_omp_wraps_sequential_version() {
18024 let mut sa = vec![1 | SAINT_MIN, 3, 0, 0];
18025 let mut thread_state = alloc_thread_state(2).unwrap();
18026
18027 let name = renumber_lms_suffixes_8u_omp(&mut sa, 2, 2, &mut thread_state);
18028
18029 assert_eq!(name, 1);
18030 assert_eq!(sa[2], SAINT_MIN);
18031 }
18032
18033 #[test]
18034 fn libsais64_renumber_lms_suffixes_8u_omp_uses_block_partition_for_large_inputs() {
18035 let m = 65_600usize;
18036 let mut input = vec![0; 2 * m];
18037 for (i, slot) in input[..m].iter_mut().enumerate() {
18038 let suffix = (2 * i + 1) as SaSint;
18039 *slot = if i % 5 == 0 {
18040 suffix | SAINT_MIN
18041 } else {
18042 suffix
18043 };
18044 }
18045
18046 let mut single = input.clone();
18047 let mut threaded = input;
18048 let mut thread_state = alloc_thread_state(4).unwrap();
18049 let single_name = renumber_lms_suffixes_8u(&mut single, m as SaSint, 0, 0, m as FastSint);
18050 let threaded_name =
18051 renumber_lms_suffixes_8u_omp(&mut threaded, m as SaSint, 4, &mut thread_state);
18052
18053 assert_eq!(threaded_name, single_name);
18054 assert_eq!(threaded, single);
18055 }
18056
18057 #[test]
18058 fn libsais64_gather_marked_lms_suffixes_omp_uses_block_partition_for_large_inputs() {
18059 let n = 131_200usize;
18060 let half_n = n >> 1;
18061 let mut input = vec![-77; n];
18062 for (i, slot) in input[..half_n].iter_mut().enumerate() {
18063 let suffix = (3 * i + 1) as SaSint;
18064 *slot = if i % 7 == 0 {
18065 suffix | SAINT_MIN
18066 } else {
18067 suffix
18068 };
18069 }
18070 let marked_count = input[..half_n].iter().filter(|&&value| value < 0).count();
18071
18072 let mut single = input.clone();
18073 let mut threaded = input;
18074 let mut thread_state = alloc_thread_state(4).unwrap();
18075 let _ = gather_marked_lms_suffixes(&mut single, 0, n as FastSint, 0, half_n as FastSint);
18076 gather_marked_lms_suffixes_omp(&mut threaded, n as SaSint, 0, 0, 4, &mut thread_state);
18077
18078 assert_eq!(&threaded[n - marked_count..], &single[n - marked_count..]);
18079 }
18080
18081 #[test]
18082 fn libsais64_renumber_and_gather_lms_suffixes_omp_uses_large_input_paths() {
18083 let m = 65_600usize;
18084 let n = 2 * m;
18085 let mut input = vec![0; n];
18086 for (i, slot) in input[..m].iter_mut().enumerate() {
18087 let suffix = (2 * i + 1) as SaSint;
18088 *slot = if i % 5 == 0 {
18089 suffix | SAINT_MIN
18090 } else {
18091 suffix
18092 };
18093 }
18094
18095 let mut single = input.clone();
18096 let mut threaded = input;
18097 let mut single_state = alloc_thread_state(1).unwrap();
18098 let mut threaded_state = alloc_thread_state(4).unwrap();
18099 let single_name = renumber_and_gather_lms_suffixes_omp(
18100 &mut single,
18101 n as SaSint,
18102 m as SaSint,
18103 0,
18104 1,
18105 &mut single_state,
18106 );
18107 let threaded_name = renumber_and_gather_lms_suffixes_omp(
18108 &mut threaded,
18109 n as SaSint,
18110 m as SaSint,
18111 0,
18112 4,
18113 &mut threaded_state,
18114 );
18115
18116 assert_eq!(threaded_name, single_name);
18117 assert_eq!(threaded, single);
18118 }
18119
18120 #[test]
18121 fn libsais64_renumber_and_gather_lms_suffixes_omp_gathers_when_names_are_not_distinct() {
18122 let mut sa = vec![1 | SAINT_MIN, 3, 0, 0];
18123 let mut thread_state = alloc_thread_state(2).unwrap();
18124
18125 let name = renumber_and_gather_lms_suffixes_omp(&mut sa, 4, 2, 0, 2, &mut thread_state);
18126
18127 assert_eq!(name, 1);
18128 assert_eq!(sa[3], 1);
18129 }
18130
18131 #[test]
18132 fn libsais64_renumber_and_gather_lms_suffixes_omp_matches_upstream_c_helper() {
18133 let mut sa_rust = vec![1 | SAINT_MIN, 3, 0, 0];
18134 let mut sa_c = sa_rust.clone();
18135 let mut thread_state = alloc_thread_state(2).unwrap();
18136
18137 let rust_name =
18138 renumber_and_gather_lms_suffixes_omp(&mut sa_rust, 4, 2, 0, 2, &mut thread_state);
18139 let c_name = unsafe {
18140 probe_libsais64_renumber_and_gather_lms_suffixes_omp(sa_c.as_mut_ptr(), 4, 2, 0, 2)
18141 };
18142
18143 assert_eq!(rust_name, c_name);
18144 assert_eq!(sa_rust, sa_c);
18145 }
18146
18147 #[test]
18148 fn libsais64_renumber_distinct_lms_suffixes_32s_4k_masks_sources_and_writes_second_half() {
18149 let mut sa = vec![1 | SAINT_MIN, 3 | SAINT_MIN, 0, 0];
18150
18151 let name = renumber_distinct_lms_suffixes_32s_4k(&mut sa, 2, 1, 0, 2);
18152
18153 assert_eq!(name, 3);
18154 assert_eq!(sa[0], 1);
18155 assert_eq!(sa[1], 3);
18156 assert_eq!(sa[2], 1);
18157 assert_eq!(sa[3], 2 | SAINT_MIN);
18158 }
18159
18160 #[test]
18161 fn libsais64_renumber_distinct_lms_suffixes_32s_4k_matches_upstream_c_helper() {
18162 let mut sa_rust = vec![1 | SAINT_MIN, 3 | SAINT_MIN, 0, 0];
18163 let mut sa_c = sa_rust.clone();
18164
18165 let rust_name = renumber_distinct_lms_suffixes_32s_4k(&mut sa_rust, 2, 1, 0, 2);
18166 let c_name = unsafe {
18167 probe_libsais64_renumber_distinct_lms_suffixes_32s_4k(sa_c.as_mut_ptr(), 2, 1, 0, 2)
18168 };
18169
18170 assert_eq!(rust_name, c_name);
18171 assert_eq!(sa_rust, sa_c);
18172 }
18173
18174 #[test]
18175 fn libsais64_mark_distinct_lms_suffixes_32s_propagates_previous_nonzero_marker() {
18176 let mut sa = vec![0, 0, SAINT_MIN | 5, 0, SAINT_MIN | 7];
18177
18178 mark_distinct_lms_suffixes_32s(&mut sa, 2, 0, 3);
18179
18180 assert_eq!(sa[2], 5);
18181 assert_eq!(sa[3], 0);
18182 assert_eq!(sa[4], SAINT_MIN | 7);
18183 }
18184
18185 #[test]
18186 fn libsais64_clamp_lms_suffixes_length_32s_keeps_only_negative_lengths() {
18187 let mut sa = vec![0, 0, SAINT_MIN | 5, 7, SAINT_MIN | 3];
18188
18189 clamp_lms_suffixes_length_32s(&mut sa, 2, 0, 3);
18190
18191 assert_eq!(sa[2], 5);
18192 assert_eq!(sa[3], 0);
18193 assert_eq!(sa[4], 3);
18194 }
18195
18196 #[test]
18197 fn libsais64_renumber_and_mark_distinct_lms_suffixes_32s_4k_omp_marks_second_half_when_names_repeat(
18198 ) {
18199 let mut sa = vec![1 | SAINT_MIN, 3 | SAINT_MIN, 0, 0];
18200 let mut thread_state = alloc_thread_state(2).unwrap();
18201
18202 let name =
18203 renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(&mut sa, 4, 2, 2, &mut thread_state);
18204
18205 assert_eq!(name, 2);
18206 assert_eq!(sa[2], 1);
18207 assert_eq!(sa[3], SAINT_MIN | 2);
18208 }
18209
18210 #[test]
18211 fn libsais64_renumber_and_mark_distinct_lms_suffixes_32s_4k_omp_matches_upstream_c_helper() {
18212 let mut sa_rust = vec![1 | SAINT_MIN, 3 | SAINT_MIN, 0, 0];
18213 let mut sa_c = sa_rust.clone();
18214 let mut thread_state = alloc_thread_state(2).unwrap();
18215
18216 let rust_name = renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
18217 &mut sa_rust,
18218 4,
18219 2,
18220 2,
18221 &mut thread_state,
18222 );
18223 let c_name = unsafe {
18224 probe_libsais64_renumber_and_mark_distinct_lms_suffixes_32s_4k_omp(
18225 sa_c.as_mut_ptr(),
18226 4,
18227 2,
18228 2,
18229 )
18230 };
18231
18232 assert_eq!(rust_name, c_name);
18233 assert_eq!(sa_rust, sa_c);
18234 }
18235
18236 #[test]
18237 fn libsais64_renumber_and_mark_distinct_lms_suffixes_32s_1k_omp_handles_single_lms_suffix() {
18238 let t = vec![2, 1, 0];
18239 let mut sa = vec![0; t.len()];
18240
18241 let name = renumber_and_mark_distinct_lms_suffixes_32s_1k_omp(&t, &mut sa, 3, 1, 1);
18242
18243 assert_eq!(name, 1);
18244 assert_eq!(sa[1], SAINT_MIN | 1);
18245 }
18246
18247 #[test]
18248 fn libsais64_main_32s_entry_matches_public_c_long_on_6k_branch() {
18249 assert_libsais64_main_32s_entry_matches_public_c_long_for_branch(300);
18250 }
18251
18252 #[test]
18253 fn libsais64_main_32s_entry_matches_public_c_long_on_4k_branch() {
18254 assert_libsais64_main_32s_entry_matches_public_c_long_for_branch(400);
18255 }
18256
18257 #[test]
18258 fn libsais64_main_32s_entry_matches_public_c_long_on_2k_branch() {
18259 assert_libsais64_main_32s_entry_matches_public_c_long_for_branch(700);
18260 }
18261
18262 #[test]
18263 fn libsais64_main_32s_entry_matches_public_c_long_on_1k_branch() {
18264 assert_libsais64_main_32s_entry_matches_public_c_long_for_branch(1501);
18265 }
18266
18267 #[test]
18268 fn libsais64_main_32s_entry_matches_public_c_long_on_recursive_repetitive_6k_case() {
18269 assert_libsais64_main_32s_entry_matches_public_c_long(
18270 make_libsais64_recursive_main_32s_text(24),
18271 300,
18272 0,
18273 true,
18274 );
18275 }
18276
18277 #[test]
18278 fn libsais64_main_32s_entry_matches_public_c_long_on_recursive_repetitive_1k_case() {
18279 assert_libsais64_main_32s_entry_matches_public_c_long(
18280 make_libsais64_recursive_main_32s_text(24),
18281 1501,
18282 0,
18283 true,
18284 );
18285 }
18286
18287 #[test]
18288 fn libsais64_main_32s_entry_matches_public_c_long_on_large_generated_6k_case() {
18289 assert_libsais64_main_32s_entry_matches_public_c_long(
18290 make_libsais64_large_main_32s_stress_text(1024, 300),
18291 300,
18292 0,
18293 true,
18294 );
18295 }
18296
18297 #[test]
18298 fn libsais64_main_32s_entry_matches_public_c_long_on_large_generated_6k_case_with_fs() {
18299 assert_libsais64_main_32s_entry_matches_public_c_long(
18300 make_libsais64_large_main_32s_stress_text(1024, 300),
18301 300,
18302 2048,
18303 false,
18304 );
18305 }
18306
18307 #[test]
18308 fn libsais64_main_32s_entry_matches_public_c_long_on_large_generated_4k_case() {
18309 assert_libsais64_main_32s_entry_matches_public_c_long(
18310 make_libsais64_large_main_32s_stress_text(1024, 400),
18311 400,
18312 0,
18313 true,
18314 );
18315 }
18316
18317 #[test]
18318 fn libsais64_main_32s_entry_matches_public_c_long_on_large_generated_4k_case_with_fs() {
18319 assert_libsais64_main_32s_entry_matches_public_c_long(
18320 make_libsais64_large_main_32s_stress_text(1024, 400),
18321 400,
18322 2048,
18323 false,
18324 );
18325 }
18326
18327 #[test]
18328 fn libsais64_main_32s_entry_matches_public_c_long_on_large_generated_2k_case() {
18329 assert_libsais64_main_32s_entry_matches_public_c_long(
18330 make_libsais64_large_main_32s_stress_text(1024, 700),
18331 700,
18332 0,
18333 true,
18334 );
18335 }
18336
18337 #[test]
18338 fn libsais64_main_32s_entry_matches_public_c_long_on_large_generated_2k_case_with_fs() {
18339 assert_libsais64_main_32s_entry_matches_public_c_long(
18340 make_libsais64_large_main_32s_stress_text(1024, 700),
18341 700,
18342 2048,
18343 false,
18344 );
18345 }
18346
18347 #[test]
18348 fn libsais64_main_32s_entry_matches_public_c_long_on_large_generated_1k_case_with_fs() {
18349 assert_libsais64_main_32s_entry_matches_public_c_long(
18350 make_libsais64_large_main_32s_stress_text(1024, 1501),
18351 1501,
18352 2048,
18353 false,
18354 );
18355 }
18356
18357 #[test]
18358 fn libsais64_reconstruct_lms_suffixes_maps_indices_from_tail_interval() {
18359 let mut sa = vec![0, 1, 2, 7, 11, 13];
18360
18361 reconstruct_lms_suffixes(&mut sa, 6, 3, 0, 3);
18362
18363 assert_eq!(&sa[..3], &[7, 11, 13]);
18364 }
18365
18366 #[test]
18367 fn libsais64_reconstruct_lms_suffixes_omp_wraps_sequential_version() {
18368 let mut sa = vec![0, 1, 2, 7, 11, 13];
18369
18370 reconstruct_lms_suffixes_omp(&mut sa, 6, 3, 2);
18371
18372 assert_eq!(&sa[..3], &[7, 11, 13]);
18373 }
18374
18375 #[test]
18376 fn libsais64_lms_late_omp_wrappers_use_block_partitions_for_large_inputs() {
18377 let m = 65_600usize;
18378 let n = 2 * m;
18379 let mut input = vec![0; n];
18380 for (i, slot) in input[..m].iter_mut().enumerate() {
18381 let suffix = (2 * i + 1) as SaSint;
18382 *slot = if i % 5 == 0 {
18383 suffix | SAINT_MIN
18384 } else {
18385 suffix
18386 };
18387 }
18388
18389 let mut single = input.clone();
18390 let mut threaded = input.clone();
18391 let mut thread_state = alloc_thread_state(4).unwrap();
18392 let single_name = renumber_lms_suffixes_8u(&mut single, m as SaSint, 0, 0, m as FastSint);
18393 let threaded_name =
18394 renumber_lms_suffixes_8u_omp(&mut threaded, m as SaSint, 4, &mut thread_state);
18395 assert_eq!(threaded_name, single_name);
18396 assert_eq!(threaded, single);
18397
18398 let mut single = input.clone();
18399 let mut threaded = input.clone();
18400 let mut single_state = alloc_thread_state(1).unwrap();
18401 let mut threaded_state = alloc_thread_state(4).unwrap();
18402 let single_name = renumber_and_gather_lms_suffixes_omp(
18403 &mut single,
18404 n as SaSint,
18405 m as SaSint,
18406 0,
18407 1,
18408 &mut single_state,
18409 );
18410 let threaded_name = renumber_and_gather_lms_suffixes_omp(
18411 &mut threaded,
18412 n as SaSint,
18413 m as SaSint,
18414 0,
18415 4,
18416 &mut threaded_state,
18417 );
18418 assert_eq!(threaded_name, single_name);
18419 assert_eq!(threaded, single);
18420
18421 let mut single = input.clone();
18422 let mut threaded = input;
18423 let marked_count = single[..m].iter().filter(|&&value| value < 0).count();
18424 let _ = gather_marked_lms_suffixes(&mut single, 0, n as FastSint, 0, m as FastSint);
18425 gather_marked_lms_suffixes_omp(&mut threaded, n as SaSint, 0, 0, 4, &mut thread_state);
18426 assert_eq!(&threaded[n - marked_count..], &single[n - marked_count..]);
18427 }
18428
18429 #[test]
18430 fn libsais64_reconstruct_lms_suffixes_omp_uses_block_partition_for_large_inputs() {
18431 let m = 65_600usize;
18432 let n = 2 * m;
18433 let mut input = vec![0; n];
18434 for (i, slot) in input[..m].iter_mut().enumerate() {
18435 *slot = (m - 1 - i) as SaSint;
18436 }
18437 for (i, slot) in input[m..].iter_mut().enumerate() {
18438 *slot = (i * 17 + 3) as SaSint;
18439 }
18440
18441 let mut single = input.clone();
18442 let mut threaded = input;
18443 reconstruct_lms_suffixes(&mut single, n as SaSint, m as SaSint, 0, m as FastSint);
18444 reconstruct_lms_suffixes_omp(&mut threaded, n as SaSint, m as SaSint, 4);
18445
18446 assert_eq!(threaded, single);
18447 }
18448
18449 #[test]
18450 fn libsais64_renumber_unique_and_nonunique_lms_suffixes_32s_marks_new_unique_names() {
18451 let mut t = vec![0, 0, 0, 0];
18452 let mut sa = vec![0, 2, -1, 5];
18453
18454 let f = renumber_unique_and_nonunique_lms_suffixes_32s(&mut t, &mut sa, 2, 0, 0, 2);
18455
18456 assert_eq!(f, 1);
18457 assert_eq!(t[0], SAINT_MIN);
18458 assert_eq!(sa[2], SAINT_MIN);
18459 assert_eq!(sa[3], 4);
18460 }
18461
18462 #[test]
18463 fn libsais64_renumber_unique_and_nonunique_lms_suffixes_32s_matches_upstream_c_helper() {
18464 let mut t_rust = vec![0, 0, 0, 0];
18465 let mut sa_rust = vec![0, 2, -1, 5];
18466 let mut t_c = t_rust.clone();
18467 let mut sa_c = sa_rust.clone();
18468
18469 let rust_f =
18470 renumber_unique_and_nonunique_lms_suffixes_32s(&mut t_rust, &mut sa_rust, 2, 0, 0, 2);
18471 let c_f = unsafe {
18472 probe_libsais64_renumber_unique_and_nonunique_lms_suffixes_32s(
18473 t_c.as_mut_ptr(),
18474 sa_c.as_mut_ptr(),
18475 2,
18476 0,
18477 0,
18478 2,
18479 )
18480 };
18481
18482 assert_eq!(rust_f, c_f);
18483 assert_eq!(t_rust, t_c);
18484 assert_eq!(sa_rust, sa_c);
18485 }
18486
18487 #[test]
18488 fn libsais64_renumber_unique_and_nonunique_lms_suffixes_32s_omp_matches_upstream_c_helper() {
18489 let mut t_rust = vec![0, 0, 0, 0];
18490 let mut sa_rust = vec![0, 2, -1, 5];
18491 let mut t_c = t_rust.clone();
18492 let mut sa_c = sa_rust.clone();
18493 let mut thread_state = alloc_thread_state(1).unwrap();
18494
18495 let rust_f = renumber_unique_and_nonunique_lms_suffixes_32s_omp(
18496 &mut t_rust,
18497 &mut sa_rust,
18498 2,
18499 1,
18500 &mut thread_state,
18501 );
18502 let c_f = unsafe {
18503 probe_libsais64_renumber_unique_and_nonunique_lms_suffixes_32s_omp(
18504 t_c.as_mut_ptr(),
18505 sa_c.as_mut_ptr(),
18506 2,
18507 1,
18508 )
18509 };
18510
18511 assert_eq!(rust_f, c_f);
18512 assert_eq!(t_rust, t_c);
18513 assert_eq!(sa_rust, sa_c);
18514 }
18515
18516 #[test]
18517 fn libsais64_renumber_unique_and_nonunique_lms_suffixes_32s_omp_uses_block_partition() {
18518 let m = 65_600usize;
18519 let n = 2 * m;
18520 let t = vec![0; n];
18521 let mut sa = vec![0; n];
18522 for i in 0..m {
18523 sa[i] = (2 * i) as SaSint;
18524 sa[m + i] = if i % 5 == 0 {
18525 -((i as SaSint) + 1)
18526 } else {
18527 i as SaSint + 7
18528 };
18529 }
18530
18531 let mut single_t = t.clone();
18532 let mut single_sa = sa.clone();
18533 let mut threaded_t = t;
18534 let mut threaded_sa = sa;
18535 let mut thread_state = alloc_thread_state(4).unwrap();
18536 let single_f = renumber_unique_and_nonunique_lms_suffixes_32s(
18537 &mut single_t,
18538 &mut single_sa,
18539 m as SaSint,
18540 0,
18541 0,
18542 m as FastSint,
18543 );
18544 let threaded_f = renumber_unique_and_nonunique_lms_suffixes_32s_omp(
18545 &mut threaded_t,
18546 &mut threaded_sa,
18547 m as SaSint,
18548 4,
18549 &mut thread_state,
18550 );
18551
18552 assert_eq!(threaded_f, single_f);
18553 assert_eq!(threaded_t, single_t);
18554 assert_eq!(threaded_sa, single_sa);
18555 }
18556
18557 #[test]
18558 fn libsais64_compact_unique_and_nonunique_lms_suffixes_32s_splits_unique_and_nonunique_ranges()
18559 {
18560 let mut sa = vec![0, 0, 0, 0, SAINT_MIN, 4];
18561 let mut l = 2;
18562 let mut r = 6;
18563
18564 compact_unique_and_nonunique_lms_suffixes_32s(&mut sa, 2, &mut l, &mut r, 0, 2);
18565
18566 assert_eq!(l, 2);
18567 assert_eq!(r, 6);
18568 assert_eq!(sa[2], 0);
18569 assert_eq!(sa[3] & SAINT_MAX, 0);
18570 }
18571
18572 #[test]
18573 fn libsais64_compact_lms_suffixes_32s_omp_runs_renumber_then_compaction() {
18574 let mut t = vec![0, 0, 0, 0];
18575 let mut sa = vec![0, 2, -1, 5, 77, 88];
18576 let mut thread_state = alloc_thread_state(2).unwrap();
18577
18578 let f = compact_lms_suffixes_32s_omp(&mut t, &mut sa, 4, 2, 2, 2, &mut thread_state);
18579
18580 assert_eq!(f, 1);
18581 assert_eq!(sa[2] & SAINT_MAX, 0);
18582 assert_eq!(sa[5], 3);
18583 }
18584
18585 #[test]
18586 fn libsais64_compact_unique_and_nonunique_lms_suffixes_32s_omp_uses_block_partition() {
18587 let n = 131_200usize;
18588 let m = 65_600usize;
18589 let fs = m + 32;
18590 let half_n = n >> 1;
18591 let f = m / 5;
18592 let mut sa = vec![0; n + fs];
18593 for i in 0..half_n {
18594 sa[m + i] = if i % 5 == 0 {
18595 SAINT_MIN | i as SaSint
18596 } else {
18597 i as SaSint + 1
18598 };
18599 }
18600 for i in 0..f {
18601 sa[m - f + i] = (10_000 + i) as SaSint;
18602 }
18603
18604 let mut single = sa.clone();
18605 let mut threaded = sa;
18606 let mut single_state = alloc_thread_state(1).unwrap();
18607 let mut threaded_state = alloc_thread_state(4).unwrap();
18608 compact_unique_and_nonunique_lms_suffixes_32s_omp(
18609 &mut single,
18610 n as SaSint,
18611 m as SaSint,
18612 fs as SaSint,
18613 f as SaSint,
18614 1,
18615 &mut single_state,
18616 );
18617 compact_unique_and_nonunique_lms_suffixes_32s_omp(
18618 &mut threaded,
18619 n as SaSint,
18620 m as SaSint,
18621 fs as SaSint,
18622 f as SaSint,
18623 4,
18624 &mut threaded_state,
18625 );
18626
18627 let unique_dst = n + fs - m;
18628 assert_eq!(
18629 &threaded[unique_dst..unique_dst + f],
18630 &single[unique_dst..unique_dst + f]
18631 );
18632 }
18633
18634 #[test]
18635 fn libsais64_compact_lms_suffixes_32s_omp_uses_large_input_paths() {
18636 let n = 131_200usize;
18637 let m = 65_600usize;
18638 let fs = m + 32;
18639 let t = vec![0; n];
18640 let mut sa = vec![0; n + fs];
18641 for i in 0..m {
18642 sa[i] = (2 * i) as SaSint;
18643 sa[m + i] = if i % 5 == 0 {
18644 -((i as SaSint) + 1)
18645 } else {
18646 i as SaSint + 7
18647 };
18648 }
18649
18650 let mut single_t = t.clone();
18651 let mut single_sa = sa.clone();
18652 let mut threaded_t = t;
18653 let mut threaded_sa = sa;
18654 let mut single_state = alloc_thread_state(1).unwrap();
18655 let mut threaded_state = alloc_thread_state(4).unwrap();
18656 let single_f = compact_lms_suffixes_32s_omp(
18657 &mut single_t,
18658 &mut single_sa,
18659 n as SaSint,
18660 m as SaSint,
18661 fs as SaSint,
18662 1,
18663 &mut single_state,
18664 );
18665 let threaded_f = compact_lms_suffixes_32s_omp(
18666 &mut threaded_t,
18667 &mut threaded_sa,
18668 n as SaSint,
18669 m as SaSint,
18670 fs as SaSint,
18671 4,
18672 &mut threaded_state,
18673 );
18674
18675 assert_eq!(threaded_f, single_f);
18676 assert_eq!(threaded_t, single_t);
18677 let unique_dst = n + fs - m;
18678 let unique_len = usize::try_from(threaded_f).expect("f must be non-negative");
18679 assert_eq!(
18680 &threaded_sa[unique_dst..unique_dst + unique_len],
18681 &single_sa[unique_dst..unique_dst + unique_len]
18682 );
18683 }
18684
18685 #[test]
18686 fn libsais64_merge_unique_lms_suffixes_32s_noops_for_empty_block() {
18687 let mut t = vec![1, SAINT_MIN, 2, SAINT_MIN];
18688 let mut sa = vec![0, 0, 1, 3];
18689 let before_t = t.clone();
18690 let before_sa = sa.clone();
18691
18692 merge_unique_lms_suffixes_32s(&mut t, &mut sa, 4, 1, 0, 0, 0);
18693
18694 assert_eq!(t, before_t);
18695 assert_eq!(sa, before_sa);
18696 }
18697
18698 #[test]
18699 fn libsais64_merge_unique_lms_suffixes_32s_omp_uses_block_partition_for_large_inputs() {
18700 let n = 65_600usize;
18701 let m = 1_024usize;
18702 let mut t = vec![1; n];
18703 for i in (0..n).step_by(257) {
18704 t[i] = SAINT_MIN | ((i % 251) as SaSint);
18705 }
18706 let f = t.iter().filter(|&&value| value < 0).count();
18707 let mut sa = vec![-1; n];
18708 let src = n - m - 1;
18709 for i in 0..f {
18710 sa[src + i] = i as SaSint;
18711 }
18712
18713 let mut single_t = t.clone();
18714 let mut single_sa = sa.clone();
18715 let mut threaded_t = t;
18716 let mut threaded_sa = sa;
18717 let mut thread_state = alloc_thread_state(4).unwrap();
18718 merge_unique_lms_suffixes_32s_omp(
18719 &mut single_t,
18720 &mut single_sa,
18721 n as SaSint,
18722 m as SaSint,
18723 1,
18724 &mut [],
18725 );
18726 merge_unique_lms_suffixes_32s_omp(
18727 &mut threaded_t,
18728 &mut threaded_sa,
18729 n as SaSint,
18730 m as SaSint,
18731 4,
18732 &mut thread_state,
18733 );
18734
18735 assert_eq!(threaded_t, single_t);
18736 assert_eq!(threaded_sa, single_sa);
18737 }
18738
18739 #[test]
18740 fn libsais64_merge_nonunique_lms_suffixes_32s_noops_for_empty_block() {
18741 let mut sa = vec![0, 7, 0, 13, 11];
18742 let before = sa.clone();
18743
18744 merge_nonunique_lms_suffixes_32s(&mut sa, 4, 1, 0, 0, 0);
18745
18746 assert_eq!(sa, before);
18747 }
18748
18749 #[test]
18750 fn libsais64_merge_compacted_lms_suffixes_32s_omp_preserves_input_text_and_fills_zero_slots() {
18751 let mut t = vec![1, 2, 3, 4];
18752 let mut sa = vec![0, 1, 2, 3, 4, 5];
18753 let before_t = t.clone();
18754 let mut thread_state = alloc_thread_state(2).unwrap();
18755
18756 merge_compacted_lms_suffixes_32s_omp(&mut t, &mut sa, 4, 1, 1, 2, &mut thread_state);
18757
18758 assert_eq!(t, before_t);
18759 assert_eq!(sa[0], 3);
18760 assert_eq!(sa[1], 1);
18761 }
18762
18763 #[test]
18764 fn libsais64_merge_nonunique_lms_suffixes_32s_omp_uses_block_partition_for_large_inputs() {
18765 let n = 131_200usize;
18766 let m = 65_600usize;
18767 let f = 7usize;
18768 let mut sa = vec![1; n];
18769 let zero_count = (0..m).filter(|i| i % 17 == 0).count();
18770 for i in (0..m).step_by(17) {
18771 sa[i] = 0;
18772 }
18773 let src = n - m - 1 + f;
18774 for i in 0..zero_count {
18775 sa[src + i] = 10_000 + i as SaSint;
18776 }
18777
18778 let mut single = sa.clone();
18779 let mut threaded = sa;
18780 let mut thread_state = alloc_thread_state(4).unwrap();
18781 merge_nonunique_lms_suffixes_32s_omp(
18782 &mut single,
18783 n as SaSint,
18784 m as SaSint,
18785 f as SaSint,
18786 1,
18787 &mut [],
18788 );
18789 merge_nonunique_lms_suffixes_32s_omp(
18790 &mut threaded,
18791 n as SaSint,
18792 m as SaSint,
18793 f as SaSint,
18794 4,
18795 &mut thread_state,
18796 );
18797
18798 assert_eq!(threaded, single);
18799 }
18800
18801 #[test]
18802 fn libsais64_merge_compacted_lms_suffixes_32s_omp_uses_block_partition_for_large_inputs() {
18803 let n = 131_200usize;
18804 let m = 65_600usize;
18805 let mut t = vec![1; n];
18806 for i in (0..n).step_by(257) {
18807 t[i] = SAINT_MIN | ((i % 251) as SaSint);
18808 }
18809 let f = t.iter().filter(|&&value| value < 0).count();
18810
18811 let mut sa = vec![1; n];
18812 let zero_count = (0..m).filter(|i| i % 17 == 0).count();
18813 for i in (0..m).step_by(17) {
18814 sa[i] = 0;
18815 }
18816 let unique_src = n - m - 1;
18817 for i in 0..f {
18818 sa[unique_src + i] = i as SaSint;
18819 }
18820 for i in 0..zero_count {
18821 sa[unique_src + f + i] = 10_000 + i as SaSint;
18822 }
18823
18824 let mut single_t = t.clone();
18825 let mut single_sa = sa.clone();
18826 let mut threaded_t = t;
18827 let mut threaded_sa = sa;
18828 let mut single_state = alloc_thread_state(1).unwrap();
18829 let mut threaded_state = alloc_thread_state(4).unwrap();
18830 merge_compacted_lms_suffixes_32s_omp(
18831 &mut single_t,
18832 &mut single_sa,
18833 n as SaSint,
18834 m as SaSint,
18835 f as SaSint,
18836 1,
18837 &mut single_state,
18838 );
18839 merge_compacted_lms_suffixes_32s_omp(
18840 &mut threaded_t,
18841 &mut threaded_sa,
18842 n as SaSint,
18843 m as SaSint,
18844 f as SaSint,
18845 4,
18846 &mut threaded_state,
18847 );
18848
18849 assert_eq!(threaded_t, single_t);
18850 assert_eq!(threaded_sa, single_sa);
18851 }
18852
18853 #[test]
18854 fn libsais64_final_bwt_left_to_right_8u_block_omp_uses_thread_buckets() {
18855 let block_start = 20_000usize;
18856 let block_size = 16_384usize;
18857 let n = block_start + block_size + 8;
18858 let t = vec![1_u8; n];
18859 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
18860
18861 let mut expected_sa = vec![0; n];
18862 expected_sa[block_start..block_start + block_size].copy_from_slice(&suffixes);
18863 let mut threaded_sa = expected_sa.clone();
18864 let mut expected_bucket = vec![0; ALPHABET_SIZE];
18865 let mut threaded_bucket = expected_bucket.clone();
18866 let mut thread_state = alloc_thread_state(4).unwrap();
18867
18868 final_bwt_scan_left_to_right_8u(
18869 &t,
18870 &mut expected_sa,
18871 &mut expected_bucket,
18872 block_start as FastSint,
18873 block_size as FastSint,
18874 );
18875 final_bwt_scan_left_to_right_8u_block_omp(
18876 &t,
18877 &mut threaded_sa,
18878 ALPHABET_SIZE as SaSint,
18879 &mut threaded_bucket,
18880 block_start as FastSint,
18881 block_size as FastSint,
18882 4,
18883 &mut thread_state,
18884 );
18885
18886 assert_eq!(threaded_sa, expected_sa);
18887 assert_eq!(threaded_bucket, expected_bucket);
18888 }
18889
18890 #[test]
18891 fn libsais64_final_bwt_aux_left_to_right_8u_block_omp_uses_thread_buckets() {
18892 let block_start = 20_000usize;
18893 let block_size = 16_384usize;
18894 let n = block_start + block_size + 8;
18895 let t = vec![1_u8; n];
18896 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
18897
18898 let mut expected_sa = vec![0; n];
18899 expected_sa[block_start..block_start + block_size].copy_from_slice(&suffixes);
18900 let mut threaded_sa = expected_sa.clone();
18901 let mut expected_i = vec![0; n];
18902 let mut threaded_i = vec![0; n];
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_bwt_aux_scan_left_to_right_8u(
18908 &t,
18909 &mut expected_sa,
18910 0,
18911 &mut expected_i,
18912 &mut expected_bucket,
18913 block_start as FastSint,
18914 block_size as FastSint,
18915 );
18916 final_bwt_aux_scan_left_to_right_8u_block_omp(
18917 &t,
18918 &mut threaded_sa,
18919 ALPHABET_SIZE as SaSint,
18920 0,
18921 &mut threaded_i,
18922 &mut threaded_bucket,
18923 block_start as FastSint,
18924 block_size as FastSint,
18925 4,
18926 &mut thread_state,
18927 );
18928
18929 assert_eq!(threaded_sa, expected_sa);
18930 assert_eq!(threaded_i, expected_i);
18931 assert_eq!(threaded_bucket, expected_bucket);
18932 }
18933
18934 #[test]
18935 fn libsais64_final_sorting_right_to_left_8u_block_omp_uses_thread_buckets() {
18936 let block_start = 20_000usize;
18937 let block_size = 16_384usize;
18938 let n = block_start + block_size + 8;
18939 let t = vec![1_u8; n];
18940 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
18941
18942 let mut expected_sa = vec![0; n];
18943 expected_sa[block_start..block_start + block_size].copy_from_slice(&suffixes);
18944 let mut threaded_sa = expected_sa.clone();
18945 let mut expected_bucket = vec![0; ALPHABET_SIZE];
18946 expected_bucket[1] = n as SaSint;
18947 let mut threaded_bucket = expected_bucket.clone();
18948 let mut thread_state = alloc_thread_state(4).unwrap();
18949
18950 final_sorting_scan_right_to_left_8u(
18951 &t,
18952 &mut expected_sa,
18953 &mut expected_bucket,
18954 block_start as FastSint,
18955 block_size as FastSint,
18956 );
18957 final_sorting_scan_right_to_left_8u_block_omp(
18958 &t,
18959 &mut threaded_sa,
18960 ALPHABET_SIZE as SaSint,
18961 &mut threaded_bucket,
18962 block_start as FastSint,
18963 block_size as FastSint,
18964 4,
18965 &mut thread_state,
18966 );
18967
18968 assert_eq!(threaded_sa, expected_sa);
18969 assert_eq!(threaded_bucket, expected_bucket);
18970 }
18971
18972 #[test]
18973 fn libsais64_final_bwt_right_to_left_8u_block_omp_uses_thread_buckets() {
18974 let block_start = 20_000usize;
18975 let block_size = 16_384usize;
18976 let n = block_start + block_size + 8;
18977 let t = vec![1_u8; n];
18978 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
18979
18980 let mut expected_sa = vec![0; n];
18981 expected_sa[block_start..block_start + block_size].copy_from_slice(&suffixes);
18982 let mut threaded_sa = expected_sa.clone();
18983 let mut expected_bucket = vec![0; ALPHABET_SIZE];
18984 expected_bucket[1] = n as SaSint;
18985 let mut threaded_bucket = expected_bucket.clone();
18986 let mut thread_state = alloc_thread_state(4).unwrap();
18987
18988 final_bwt_scan_right_to_left_8u(
18989 &t,
18990 &mut expected_sa,
18991 &mut expected_bucket,
18992 block_start as FastSint,
18993 block_size as FastSint,
18994 );
18995 final_bwt_scan_right_to_left_8u_block_omp(
18996 &t,
18997 &mut threaded_sa,
18998 ALPHABET_SIZE as SaSint,
18999 &mut threaded_bucket,
19000 block_start as FastSint,
19001 block_size as FastSint,
19002 4,
19003 &mut thread_state,
19004 );
19005
19006 assert_eq!(threaded_sa, expected_sa);
19007 assert_eq!(threaded_bucket, expected_bucket);
19008 }
19009
19010 #[test]
19011 fn libsais64_final_bwt_aux_right_to_left_8u_block_omp_uses_thread_buckets() {
19012 let block_start = 20_000usize;
19013 let block_size = 16_384usize;
19014 let n = block_start + block_size + 8;
19015 let t = vec![1_u8; n];
19016 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
19017
19018 let mut expected_sa = vec![0; n];
19019 expected_sa[block_start..block_start + block_size].copy_from_slice(&suffixes);
19020 let mut threaded_sa = expected_sa.clone();
19021 let mut expected_i = vec![0; n];
19022 let mut threaded_i = vec![0; n];
19023 let mut expected_bucket = vec![0; ALPHABET_SIZE];
19024 expected_bucket[1] = n as SaSint;
19025 let mut threaded_bucket = expected_bucket.clone();
19026 let mut thread_state = alloc_thread_state(4).unwrap();
19027
19028 final_bwt_aux_scan_right_to_left_8u(
19029 &t,
19030 &mut expected_sa,
19031 0,
19032 &mut expected_i,
19033 &mut expected_bucket,
19034 block_start as FastSint,
19035 block_size as FastSint,
19036 );
19037 final_bwt_aux_scan_right_to_left_8u_block_omp(
19038 &t,
19039 &mut threaded_sa,
19040 ALPHABET_SIZE as SaSint,
19041 0,
19042 &mut threaded_i,
19043 &mut threaded_bucket,
19044 block_start as FastSint,
19045 block_size as FastSint,
19046 4,
19047 &mut thread_state,
19048 );
19049
19050 assert_eq!(threaded_sa, expected_sa);
19051 assert_eq!(threaded_i, expected_i);
19052 assert_eq!(threaded_bucket, expected_bucket);
19053 }
19054
19055 #[test]
19056 fn libsais64_final_gsa_right_to_left_8u_block_omp_uses_thread_buckets() {
19057 let block_start = 20_000usize;
19058 let block_size = 16_384usize;
19059 let n = block_start + block_size + 8;
19060 let t = vec![1_u8; n];
19061 let suffixes: Vec<SaSint> = (2..2 + block_size).map(|i| i as SaSint).collect();
19062
19063 let mut expected_sa = vec![0; n];
19064 expected_sa[block_start..block_start + block_size].copy_from_slice(&suffixes);
19065 let mut threaded_sa = expected_sa.clone();
19066 let mut expected_bucket = vec![0; ALPHABET_SIZE];
19067 expected_bucket[1] = n as SaSint;
19068 let mut threaded_bucket = expected_bucket.clone();
19069 let mut thread_state = alloc_thread_state(4).unwrap();
19070
19071 final_gsa_scan_right_to_left_8u(
19072 &t,
19073 &mut expected_sa,
19074 &mut expected_bucket,
19075 block_start as FastSint,
19076 block_size as FastSint,
19077 );
19078 final_gsa_scan_right_to_left_8u_block_omp(
19079 &t,
19080 &mut threaded_sa,
19081 ALPHABET_SIZE as SaSint,
19082 &mut threaded_bucket,
19083 block_start as FastSint,
19084 block_size as FastSint,
19085 4,
19086 &mut thread_state,
19087 );
19088
19089 assert_eq!(threaded_sa, expected_sa);
19090 assert_eq!(threaded_bucket, expected_bucket);
19091 }
19092
19093 #[test]
19094 fn libsais64_count_and_gather_lms_suffixes_8u_omp_uses_block_partition_for_large_inputs() {
19095 let n = 65_600usize;
19096 let text: Vec<u8> = (0..n)
19097 .map(|i| 1 + ((i * 37 + i / 17) % 251) as u8)
19098 .collect();
19099
19100 let mut sa_threaded = vec![-99; n];
19101 let mut sa_scalar = vec![-99; n];
19102 let mut buckets_threaded = vec![0; 4 * ALPHABET_SIZE];
19103 let mut buckets_scalar = vec![0; 4 * ALPHABET_SIZE];
19104 let mut thread_state = alloc_thread_state(4).unwrap();
19105
19106 let m_threaded = count_and_gather_lms_suffixes_8u_omp(
19107 &text,
19108 &mut sa_threaded,
19109 n as SaSint,
19110 &mut buckets_threaded,
19111 4,
19112 &mut thread_state,
19113 );
19114 let m_scalar = count_and_gather_lms_suffixes_8u(
19115 &text,
19116 &mut sa_scalar,
19117 n as SaSint,
19118 &mut buckets_scalar,
19119 0,
19120 n as FastSint,
19121 );
19122
19123 assert_eq!(m_threaded, m_scalar);
19124 assert_eq!(
19125 &sa_threaded[n - m_threaded as usize..],
19126 &sa_scalar[n - m_scalar as usize..]
19127 );
19128 assert_eq!(buckets_threaded, buckets_scalar);
19129 }
19130
19131 #[test]
19132 fn libsais64_gather_lms_suffixes_8u_omp_uses_thread_state_for_large_inputs() {
19133 let n = 65_600usize;
19134 let text: Vec<u8> = (0..n)
19135 .map(|i| 1 + ((i * 37 + i / 17) % 251) as u8)
19136 .collect();
19137 let mut thread_state = alloc_thread_state(4).unwrap();
19138 let mut count_sa = vec![-99; n];
19139 let mut buckets = vec![0; 4 * ALPHABET_SIZE];
19140 let m = count_and_gather_lms_suffixes_8u_omp(
19141 &text,
19142 &mut count_sa,
19143 n as SaSint,
19144 &mut buckets,
19145 4,
19146 &mut thread_state,
19147 );
19148
19149 let mut threaded = vec![-99; n];
19150 let mut scalar = vec![-99; n];
19151 gather_lms_suffixes_8u_omp(&text, &mut threaded, n as SaSint, 4, &mut thread_state);
19152 gather_lms_suffixes_8u(
19153 &text,
19154 &mut scalar,
19155 n as SaSint,
19156 n as FastSint - 1,
19157 0,
19158 n as FastSint,
19159 );
19160
19161 assert_eq!(&threaded[n - m as usize..], &scalar[n - m as usize..]);
19162 }
19163
19164 #[test]
19165 fn libsais64_count_and_gather_lms_suffixes_32s_4k_updates_counts_and_suffixes() {
19166 let t = vec![2, 1, 3, 1, 0];
19167 let mut sa = vec![0; t.len()];
19168 let mut buckets = vec![0; 4 * 4];
19169 let m = count_and_gather_lms_suffixes_32s_4k(
19170 &t,
19171 &mut sa,
19172 t.len() as SaSint,
19173 4,
19174 &mut buckets,
19175 0,
19176 t.len() as FastSint,
19177 );
19178 assert!(m >= 0);
19179 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
19180 }
19181
19182 #[test]
19183 fn libsais64_count_and_gather_lms_suffixes_32s_2k_updates_counts_and_suffixes() {
19184 let t = vec![2, 1, 3, 1, 0];
19185 let mut sa = vec![0; t.len()];
19186 let mut buckets = vec![0; 2 * 4];
19187 let m = count_and_gather_lms_suffixes_32s_2k(
19188 &t,
19189 &mut sa,
19190 t.len() as SaSint,
19191 4,
19192 &mut buckets,
19193 0,
19194 t.len() as FastSint,
19195 );
19196 assert!(m >= 0);
19197 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
19198 }
19199
19200 #[test]
19201 fn libsais64_count_and_gather_compacted_lms_suffixes_32s_2k_updates_counts_and_suffixes() {
19202 let t = vec![2, SAINT_MIN | 1, 3, 1, 0];
19203 let mut sa = vec![0; t.len()];
19204 let mut buckets = vec![0; 2 * 4];
19205 let m = count_and_gather_compacted_lms_suffixes_32s_2k(
19206 &t,
19207 &mut sa,
19208 t.len() as SaSint,
19209 4,
19210 &mut buckets,
19211 0,
19212 t.len() as FastSint,
19213 );
19214 assert!(m >= 0);
19215 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
19216 }
19217
19218 #[test]
19219 fn libsais64_count_and_gather_lms_suffixes_32s_4k_nofs_omp_wraps_sequential_version() {
19220 let t = vec![2, 1, 3, 1, 0];
19221 let mut sa = vec![0; t.len()];
19222 let mut buckets = vec![0; 4 * 4];
19223 let m = count_and_gather_lms_suffixes_32s_4k_nofs_omp(
19224 &t,
19225 &mut sa,
19226 t.len() as SaSint,
19227 4,
19228 &mut buckets,
19229 2,
19230 );
19231 assert!(m >= 0);
19232 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
19233 }
19234
19235 #[test]
19236 fn libsais64_count_and_gather_lms_suffixes_32s_2k_nofs_omp_wraps_sequential_version() {
19237 let t = vec![2, 1, 3, 1, 0];
19238 let mut sa = vec![0; t.len()];
19239 let mut buckets = vec![0; 2 * 4];
19240 let m = count_and_gather_lms_suffixes_32s_2k_nofs_omp(
19241 &t,
19242 &mut sa,
19243 t.len() as SaSint,
19244 4,
19245 &mut buckets,
19246 2,
19247 );
19248 assert!(m >= 0);
19249 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
19250 }
19251
19252 #[test]
19253 fn libsais64_count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp_wraps_sequential_version()
19254 {
19255 let t = vec![2, SAINT_MIN | 1, 3, 1, 0];
19256 let mut sa = vec![0; t.len()];
19257 let mut buckets = vec![0; 2 * 4];
19258 let m = count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(
19259 &t,
19260 &mut sa,
19261 t.len() as SaSint,
19262 4,
19263 &mut buckets,
19264 2,
19265 );
19266 assert!(m >= 0);
19267 assert_eq!(buckets.iter().sum::<SaSint>(), t.len() as SaSint);
19268 }
19269
19270 #[test]
19271 fn libsais64_count_and_gather_lms_suffixes_32s_nofs_omp_uses_large_input_paths() {
19272 let n = 65_600usize;
19273 let k = 257usize;
19274 let text: Vec<SaSint> = (0..n)
19275 .map(|i| 1 + ((i * 37 + i / 17) % (k - 1)) as SaSint)
19276 .collect();
19277
19278 let mut sa_threaded = vec![-99; n];
19279 let mut sa_scalar = vec![-99; n];
19280 let mut buckets_threaded = vec![0; 4 * k];
19281 let mut buckets_scalar = vec![0; 4 * k];
19282 let m_threaded = count_and_gather_lms_suffixes_32s_4k_nofs_omp(
19283 &text,
19284 &mut sa_threaded,
19285 n as SaSint,
19286 k as SaSint,
19287 &mut buckets_threaded,
19288 4,
19289 );
19290 let m_scalar = count_and_gather_lms_suffixes_32s_4k(
19291 &text,
19292 &mut sa_scalar,
19293 n as SaSint,
19294 k as SaSint,
19295 &mut buckets_scalar,
19296 0,
19297 n as FastSint,
19298 );
19299 assert_eq!(m_threaded, m_scalar);
19300 assert_eq!(
19301 &sa_threaded[n - m_threaded as usize..],
19302 &sa_scalar[n - m_scalar as usize..]
19303 );
19304 assert_eq!(buckets_threaded, buckets_scalar);
19305
19306 let mut sa_threaded = vec![-99; n];
19307 let mut sa_scalar = vec![-99; n];
19308 let mut buckets_threaded = vec![0; 2 * k];
19309 let mut buckets_scalar = vec![0; 2 * k];
19310 let m_threaded = count_and_gather_lms_suffixes_32s_2k_nofs_omp(
19311 &text,
19312 &mut sa_threaded,
19313 n as SaSint,
19314 k as SaSint,
19315 &mut buckets_threaded,
19316 4,
19317 );
19318 let m_scalar = count_and_gather_lms_suffixes_32s_2k(
19319 &text,
19320 &mut sa_scalar,
19321 n as SaSint,
19322 k as SaSint,
19323 &mut buckets_scalar,
19324 0,
19325 n as FastSint,
19326 );
19327 assert_eq!(m_threaded, m_scalar);
19328 assert_eq!(
19329 &sa_threaded[n - m_threaded as usize..],
19330 &sa_scalar[n - m_scalar as usize..]
19331 );
19332 assert_eq!(buckets_threaded, buckets_scalar);
19333 }
19334
19335 #[test]
19336 fn libsais64_count_and_gather_lms_suffixes_32s_fs_omp_uses_large_input_paths() {
19337 let n = 65_600usize;
19338 let k = 257usize;
19339 let text: Vec<SaSint> = (0..n)
19340 .map(|i| 1 + ((i * 37 + i / 17) % (k - 1)) as SaSint)
19341 .collect();
19342 let mut thread_state = alloc_thread_state(4).unwrap();
19343
19344 let mut sa_threaded = vec![-99; n];
19345 let mut sa_scalar = vec![-99; n];
19346 let mut buckets_threaded = vec![0; 4 * k];
19347 let mut buckets_scalar = vec![0; 4 * k];
19348 let m_threaded = count_and_gather_lms_suffixes_32s_4k_fs_omp(
19349 &text,
19350 &mut sa_threaded,
19351 n as SaSint,
19352 k as SaSint,
19353 &mut buckets_threaded,
19354 0,
19355 4,
19356 &mut thread_state,
19357 );
19358 let m_scalar = count_and_gather_lms_suffixes_32s_4k(
19359 &text,
19360 &mut sa_scalar,
19361 n as SaSint,
19362 k as SaSint,
19363 &mut buckets_scalar,
19364 0,
19365 n as FastSint,
19366 );
19367 assert_eq!(m_threaded, m_scalar);
19368 assert_eq!(
19369 &sa_threaded[n - m_threaded as usize..],
19370 &sa_scalar[n - m_scalar as usize..]
19371 );
19372 assert_eq!(buckets_threaded, buckets_scalar);
19373
19374 let mut sa_threaded = vec![-99; n];
19375 let mut sa_scalar = vec![-99; n];
19376 let mut buckets_threaded = vec![0; 2 * k];
19377 let mut buckets_scalar = vec![0; 2 * k];
19378 let m_threaded = count_and_gather_lms_suffixes_32s_2k_fs_omp(
19379 &text,
19380 &mut sa_threaded,
19381 n as SaSint,
19382 k as SaSint,
19383 &mut buckets_threaded,
19384 0,
19385 4,
19386 &mut thread_state,
19387 );
19388 let m_scalar = count_and_gather_lms_suffixes_32s_2k(
19389 &text,
19390 &mut sa_scalar,
19391 n as SaSint,
19392 k as SaSint,
19393 &mut buckets_scalar,
19394 0,
19395 n as FastSint,
19396 );
19397 assert_eq!(m_threaded, m_scalar);
19398 assert_eq!(
19399 &sa_threaded[n - m_threaded as usize..],
19400 &sa_scalar[n - m_scalar as usize..]
19401 );
19402 assert_eq!(buckets_threaded, buckets_scalar);
19403 }
19404
19405 #[test]
19406 fn libsais64_count_and_gather_compacted_lms_suffixes_32s_nofs_omp_uses_large_input_path() {
19407 let n = 65_600usize;
19408 let k = 257usize;
19409 let text: Vec<SaSint> = (0..n)
19410 .map(|i| {
19411 let value = 1 + ((i * 37 + i / 17) % (k - 1)) as SaSint;
19412 if i % 19 == 0 {
19413 value | SAINT_MIN
19414 } else {
19415 value
19416 }
19417 })
19418 .collect();
19419
19420 let mut sa_threaded = vec![-99; n];
19421 let mut sa_split = vec![-99; n];
19422 let mut buckets_threaded = vec![0; 2 * k];
19423 let mut buckets_split = vec![0; 2 * k];
19424 let m_threaded = count_and_gather_compacted_lms_suffixes_32s_2k_nofs_omp(
19425 &text,
19426 &mut sa_threaded,
19427 n as SaSint,
19428 k as SaSint,
19429 &mut buckets_threaded,
19430 4,
19431 );
19432 count_compacted_lms_suffixes_32s_2k(&text, n as SaSint, k as SaSint, &mut buckets_split);
19433 let m_split = gather_compacted_lms_suffixes_32s(&text, &mut sa_split, n as SaSint);
19434
19435 assert_eq!(m_threaded, m_split);
19436 assert_eq!(
19437 &sa_threaded[n - m_threaded as usize..],
19438 &sa_split[n - m_split as usize..]
19439 );
19440 assert_eq!(buckets_threaded, buckets_split);
19441 }
19442
19443 #[test]
19444 fn libsais64_count_and_gather_compacted_lms_suffixes_32s_fs_omp_uses_large_input_path() {
19445 let n = 65_600usize;
19446 let k = 257usize;
19447 let text: Vec<SaSint> = (0..n)
19448 .map(|i| {
19449 let value = 1 + ((i * 37 + i / 17) % (k - 1)) as SaSint;
19450 if i % 19 == 0 {
19451 value | SAINT_MIN
19452 } else {
19453 value
19454 }
19455 })
19456 .collect();
19457
19458 let mut sa_threaded = vec![-99; 2 * n];
19459 let mut sa_scalar = vec![-99; n];
19460 let mut buckets_threaded = vec![0; 2 * k];
19461 let mut buckets_scalar = vec![0; 2 * k];
19462 let mut thread_state = alloc_thread_state(4).unwrap();
19463 count_and_gather_compacted_lms_suffixes_32s_2k_fs_omp(
19464 &text,
19465 &mut sa_threaded,
19466 n as SaSint,
19467 k as SaSint,
19468 &mut buckets_threaded,
19469 0,
19470 4,
19471 &mut thread_state,
19472 );
19473 let m_scalar = count_and_gather_compacted_lms_suffixes_32s_2k(
19474 &text,
19475 &mut sa_scalar,
19476 n as SaSint,
19477 k as SaSint,
19478 &mut buckets_scalar,
19479 0,
19480 n as FastSint,
19481 );
19482
19483 assert_eq!(
19484 &sa_threaded[n - m_scalar as usize..n],
19485 &sa_scalar[n - m_scalar as usize..]
19486 );
19487 assert_eq!(buckets_threaded, buckets_scalar);
19488 }
19489
19490 #[test]
19491 fn libsais64_plcp_lcp_omp_wrappers_match_single_thread_on_large_inputs() {
19492 let n = 65_600usize;
19493 let text: Vec<u8> = (0..n).map(|i| (1 + (i % 251)) as u8).collect();
19494 let sa: Vec<SaSint> = (0..n as SaSint).collect();
19495
19496 let mut plcp_single = vec![0; n];
19497 let mut plcp_threaded = vec![0; n];
19498 compute_phi_omp(&sa, &mut plcp_single, n as SaSint, 1);
19499 compute_phi_omp(&sa, &mut plcp_threaded, n as SaSint, 4);
19500 assert_eq!(plcp_threaded, plcp_single);
19501
19502 compute_plcp_omp(&text, &mut plcp_single, n as SaSint, 1);
19503 compute_plcp_omp(&text, &mut plcp_threaded, n as SaSint, 4);
19504 assert_eq!(plcp_threaded, plcp_single);
19505
19506 let mut lcp_single = vec![0; n];
19507 let mut lcp_threaded = vec![0; n];
19508 compute_lcp_omp(&plcp_single, &sa, &mut lcp_single, n as SaSint, 1);
19509 compute_lcp_omp(&plcp_threaded, &sa, &mut lcp_threaded, n as SaSint, 4);
19510 assert_eq!(lcp_threaded, lcp_single);
19511 }
19512
19513 fn assert_libsais64_matches_c(text: &[u8]) {
19514 let mut rust_sa = vec![0; text.len()];
19515 let mut c_sa = vec![0; text.len()];
19516
19517 let rust_rc = libsais64(text, &mut rust_sa, 0, None);
19518 let c_rc = unsafe {
19519 probe_public_libsais64(text.as_ptr(), c_sa.as_mut_ptr(), text.len() as SaSint, 0)
19520 };
19521
19522 assert_eq!(rust_rc, c_rc);
19523 assert_eq!(rust_sa, c_sa);
19524 }
19525
19526 fn assert_libsais64_gsa_matches_c(text: &[u8]) {
19527 let mut rust_sa = vec![0; text.len()];
19528 let mut c_sa = vec![0; text.len()];
19529
19530 let rust_rc = libsais64_gsa(text, &mut rust_sa, 0, None);
19531 let c_rc = unsafe {
19532 probe_public_libsais64_gsa(text.as_ptr(), c_sa.as_mut_ptr(), text.len() as SaSint, 0)
19533 };
19534
19535 assert_eq!(rust_rc, c_rc);
19536 assert_eq!(rust_sa, c_sa);
19537 }
19538
19539 fn assert_libsais64_long_matches_c(text: &[SaSint], k: SaSint) {
19540 let mut rust_t = text.to_vec();
19541 let mut c_t = text.to_vec();
19542 let mut rust_sa = vec![0; text.len()];
19543 let mut c_sa = vec![0; text.len()];
19544
19545 let rust_rc = libsais64_long(&mut rust_t, &mut rust_sa, k, 0);
19546 let c_rc = unsafe {
19547 probe_public_libsais64_long(
19548 c_t.as_mut_ptr(),
19549 c_sa.as_mut_ptr(),
19550 c_t.len() as SaSint,
19551 k,
19552 0,
19553 )
19554 };
19555
19556 assert_eq!(rust_rc, c_rc);
19557 assert_eq!(rust_t, c_t);
19558 assert_eq!(rust_sa, c_sa);
19559 }
19560
19561 fn assert_libsais64_bwt_matches_c(text: &[u8]) {
19562 let mut rust_u = vec![0; text.len()];
19563 let mut rust_a = vec![0; text.len()];
19564 let mut c_u = vec![0; text.len()];
19565 let mut c_a = vec![0; text.len()];
19566
19567 let rust_rc = libsais64_bwt(text, &mut rust_u, &mut rust_a, 0, None);
19568 let c_rc = unsafe {
19569 probe_public_libsais64_bwt(
19570 text.as_ptr(),
19571 c_u.as_mut_ptr(),
19572 c_a.as_mut_ptr(),
19573 text.len() as SaSint,
19574 0,
19575 )
19576 };
19577
19578 assert_eq!(rust_rc, c_rc);
19579 assert_eq!(rust_u, c_u);
19580 }
19581
19582 fn assert_libsais64_bwt_aux_matches_c(text: &[u8], r: SaSint) {
19583 let aux_len = if text.is_empty() {
19584 0
19585 } else {
19586 (text.len() - 1) / r as usize + 1
19587 };
19588 let mut rust_u = vec![0; text.len()];
19589 let mut rust_a = vec![0; text.len()];
19590 let mut rust_i = vec![0; aux_len];
19591 let mut c_u = vec![0; text.len()];
19592 let mut c_a = vec![0; text.len()];
19593 let mut c_i = vec![0; aux_len];
19594
19595 let rust_rc = libsais64_bwt_aux(text, &mut rust_u, &mut rust_a, 0, None, r, &mut rust_i);
19596 let c_rc = unsafe {
19597 probe_public_libsais64_bwt_aux(
19598 text.as_ptr(),
19599 c_u.as_mut_ptr(),
19600 c_a.as_mut_ptr(),
19601 text.len() as SaSint,
19602 0,
19603 r,
19604 c_i.as_mut_ptr(),
19605 )
19606 };
19607
19608 assert_eq!(rust_rc, c_rc);
19609 assert_eq!(rust_u, c_u);
19610 assert_eq!(rust_i, c_i);
19611 }
19612
19613 fn assert_libsais64_freq_outputs_match_c(text: &[u8], gsa_text: &[u8]) {
19614 let mut rust_sa = vec![0; text.len()];
19615 let mut c_sa = vec![0; text.len()];
19616 let mut rust_freq = vec![-1; ALPHABET_SIZE];
19617 let mut c_freq = vec![-1; ALPHABET_SIZE];
19618
19619 let rust_rc = libsais64(text, &mut rust_sa, 0, Some(&mut rust_freq));
19620 let c_rc = unsafe {
19621 probe_public_libsais64_freq(
19622 text.as_ptr(),
19623 c_sa.as_mut_ptr(),
19624 text.len() as SaSint,
19625 0,
19626 c_freq.as_mut_ptr(),
19627 )
19628 };
19629 assert_eq!(rust_rc, c_rc);
19630 assert_eq!(rust_sa, c_sa);
19631 assert_eq!(rust_freq, c_freq);
19632
19633 let mut rust_gsa = vec![0; gsa_text.len()];
19634 let mut c_gsa = vec![0; gsa_text.len()];
19635 rust_freq.fill(-1);
19636 c_freq.fill(-1);
19637 let rust_rc = libsais64_gsa(gsa_text, &mut rust_gsa, 0, Some(&mut rust_freq));
19638 let c_rc = unsafe {
19639 probe_public_libsais64_gsa_freq(
19640 gsa_text.as_ptr(),
19641 c_gsa.as_mut_ptr(),
19642 gsa_text.len() as SaSint,
19643 0,
19644 c_freq.as_mut_ptr(),
19645 )
19646 };
19647 assert_eq!(rust_rc, c_rc);
19648 assert_eq!(rust_gsa, c_gsa);
19649 assert_eq!(rust_freq, c_freq);
19650
19651 let mut rust_u = vec![0; text.len()];
19652 let mut rust_a = vec![0; text.len()];
19653 let mut c_u = vec![0; text.len()];
19654 let mut c_a = vec![0; text.len()];
19655 rust_freq.fill(-1);
19656 c_freq.fill(-1);
19657 let rust_rc = libsais64_bwt(text, &mut rust_u, &mut rust_a, 0, Some(&mut rust_freq));
19658 let c_rc = unsafe {
19659 probe_public_libsais64_bwt_freq(
19660 text.as_ptr(),
19661 c_u.as_mut_ptr(),
19662 c_a.as_mut_ptr(),
19663 text.len() as SaSint,
19664 0,
19665 c_freq.as_mut_ptr(),
19666 )
19667 };
19668 assert_eq!(rust_rc, c_rc);
19669 assert_eq!(rust_u, c_u);
19670 assert_eq!(rust_freq, c_freq);
19671
19672 let r = 4;
19673 let aux_len = (text.len() - 1) / r as usize + 1;
19674 let mut rust_i = vec![0; aux_len];
19675 let mut c_i = vec![0; aux_len];
19676 rust_freq.fill(-1);
19677 c_freq.fill(-1);
19678 let rust_rc = libsais64_bwt_aux(
19679 text,
19680 &mut rust_u,
19681 &mut rust_a,
19682 0,
19683 Some(&mut rust_freq),
19684 r,
19685 &mut rust_i,
19686 );
19687 let c_rc = unsafe {
19688 probe_public_libsais64_bwt_aux_freq(
19689 text.as_ptr(),
19690 c_u.as_mut_ptr(),
19691 c_a.as_mut_ptr(),
19692 text.len() as SaSint,
19693 0,
19694 c_freq.as_mut_ptr(),
19695 r,
19696 c_i.as_mut_ptr(),
19697 )
19698 };
19699 assert_eq!(rust_rc, c_rc);
19700 assert_eq!(rust_u, c_u);
19701 assert_eq!(rust_i, c_i);
19702 assert_eq!(rust_freq, c_freq);
19703 }
19704
19705 fn assert_libsais64_unbwt_matches_c(text: &[u8]) {
19706 let mut bwt = vec![0; text.len()];
19707 let mut work = vec![0; text.len()];
19708 let primary = libsais64_bwt(text, &mut bwt, &mut work, 0, None);
19709 assert!(primary >= 0);
19710
19711 let mut rust_u = vec![0; text.len()];
19712 let mut rust_a = vec![0; text.len() + 1];
19713 let mut c_u = vec![0; text.len()];
19714 let mut c_a = vec![0; text.len() + 1];
19715
19716 let rust_rc = libsais64_unbwt(&bwt, &mut rust_u, &mut rust_a, None, primary);
19717 let c_rc = unsafe {
19718 probe_public_libsais64_unbwt(
19719 bwt.as_ptr(),
19720 c_u.as_mut_ptr(),
19721 c_a.as_mut_ptr(),
19722 bwt.len() as SaSint,
19723 primary,
19724 )
19725 };
19726
19727 assert_eq!(rust_rc, c_rc);
19728 assert_eq!(rust_u, c_u);
19729 assert_eq!(rust_u, text);
19730 }
19731
19732 fn assert_libsais64_unbwt_aux_matches_c(text: &[u8], r: SaSint) {
19733 let mut bwt = vec![0; text.len()];
19734 let mut work = vec![0; text.len()];
19735 let mut aux = vec![0; (text.len() - 1) / r as usize + 1];
19736 let bwt_rc = libsais64_bwt_aux(text, &mut bwt, &mut work, 0, None, r, &mut aux);
19737 assert_eq!(bwt_rc, 0);
19738
19739 let mut rust_u = vec![0; text.len()];
19740 let mut rust_a = vec![0; text.len() + 1];
19741 let mut c_u = vec![0; text.len()];
19742 let mut c_a = vec![0; text.len() + 1];
19743
19744 let rust_rc = libsais64_unbwt_aux(&bwt, &mut rust_u, &mut rust_a, None, r, &aux);
19745 let c_rc = unsafe {
19746 probe_public_libsais64_unbwt_aux(
19747 bwt.as_ptr(),
19748 c_u.as_mut_ptr(),
19749 c_a.as_mut_ptr(),
19750 bwt.len() as SaSint,
19751 r,
19752 aux.as_ptr(),
19753 )
19754 };
19755
19756 assert_eq!(rust_rc, c_rc);
19757 assert_eq!(rust_u, c_u);
19758 assert_eq!(rust_u, text);
19759 }
19760
19761 fn assert_libsais64_unbwt_freq_matches_c(text: &[u8]) {
19762 let mut freq = vec![0; ALPHABET_SIZE];
19763 let mut bwt = vec![0; text.len()];
19764 let mut work = vec![0; text.len()];
19765 let primary = libsais64_bwt(text, &mut bwt, &mut work, 0, Some(&mut freq));
19766 assert!(primary >= 0);
19767
19768 let mut rust_u = vec![0; text.len()];
19769 let mut rust_a = vec![0; text.len() + 1];
19770 let mut c_u = vec![0; text.len()];
19771 let mut c_a = vec![0; text.len() + 1];
19772
19773 let rust_rc = libsais64_unbwt(&bwt, &mut rust_u, &mut rust_a, Some(&freq), primary);
19774 let c_rc = unsafe {
19775 probe_public_libsais64_unbwt_freq(
19776 bwt.as_ptr(),
19777 c_u.as_mut_ptr(),
19778 c_a.as_mut_ptr(),
19779 bwt.len() as SaSint,
19780 freq.as_ptr(),
19781 primary,
19782 )
19783 };
19784 assert_eq!(rust_rc, c_rc);
19785 assert_eq!(rust_u, c_u);
19786 assert_eq!(rust_u, text);
19787
19788 let r = 4;
19789 let mut aux = vec![0; (text.len() - 1) / r as usize + 1];
19790 let bwt_rc = libsais64_bwt_aux(text, &mut bwt, &mut work, 0, Some(&mut freq), r, &mut aux);
19791 assert_eq!(bwt_rc, 0);
19792
19793 rust_u.fill(0);
19794 rust_a.fill(0);
19795 c_u.fill(0);
19796 c_a.fill(0);
19797 let rust_rc = libsais64_unbwt_aux(&bwt, &mut rust_u, &mut rust_a, Some(&freq), r, &aux);
19798 let c_rc = unsafe {
19799 probe_public_libsais64_unbwt_aux_freq(
19800 bwt.as_ptr(),
19801 c_u.as_mut_ptr(),
19802 c_a.as_mut_ptr(),
19803 bwt.len() as SaSint,
19804 freq.as_ptr(),
19805 r,
19806 aux.as_ptr(),
19807 )
19808 };
19809 assert_eq!(rust_rc, c_rc);
19810 assert_eq!(rust_u, c_u);
19811 assert_eq!(rust_u, text);
19812 }
19813
19814 fn assert_libsais64_plcp_lcp_matches_c(text: &[u8]) {
19815 let mut sa = vec![0; text.len()];
19816 let sa_rc = libsais64(text, &mut sa, 0, None);
19817 assert_eq!(sa_rc, 0);
19818
19819 let mut rust_plcp = vec![0; text.len()];
19820 let mut c_plcp = vec![0; text.len()];
19821 let rust_plcp_rc = libsais64_plcp(text, &sa, &mut rust_plcp);
19822 let c_plcp_rc = unsafe {
19823 probe_public_libsais64_plcp(
19824 text.as_ptr(),
19825 sa.as_ptr(),
19826 c_plcp.as_mut_ptr(),
19827 text.len() as SaSint,
19828 )
19829 };
19830 assert_eq!(rust_plcp_rc, c_plcp_rc);
19831 assert_eq!(rust_plcp, c_plcp);
19832
19833 let mut rust_lcp = vec![0; text.len()];
19834 let mut c_lcp = vec![0; text.len()];
19835 let rust_lcp_rc = libsais64_lcp(&rust_plcp, &sa, &mut rust_lcp);
19836 let c_lcp_rc = unsafe {
19837 probe_public_libsais64_lcp(
19838 c_plcp.as_ptr(),
19839 sa.as_ptr(),
19840 c_lcp.as_mut_ptr(),
19841 text.len() as SaSint,
19842 )
19843 };
19844 assert_eq!(rust_lcp_rc, c_lcp_rc);
19845 assert_eq!(rust_lcp, c_lcp);
19846 }
19847
19848 fn assert_libsais64_plcp_gsa_matches_c(text: &[u8]) {
19849 let mut sa = vec![0; text.len()];
19850 assert_eq!(libsais64_gsa(text, &mut sa, 0, None), 0);
19851
19852 let mut rust_plcp = vec![0; text.len()];
19853 let mut c_plcp = vec![0; text.len()];
19854 let rust_rc = libsais64_plcp_gsa(text, &sa, &mut rust_plcp);
19855 let c_rc = unsafe {
19856 probe_public_libsais64_plcp_gsa(
19857 text.as_ptr(),
19858 sa.as_ptr(),
19859 c_plcp.as_mut_ptr(),
19860 text.len() as SaSint,
19861 )
19862 };
19863
19864 assert_eq!(rust_rc, c_rc);
19865 assert_eq!(rust_plcp, c_plcp);
19866 }
19867
19868 fn assert_libsais64_bwt_aux_round_trips(text: &[u8], r: SaSint) {
19869 let mut bwt = vec![0; text.len()];
19870 let mut work = vec![0; text.len()];
19871 let mut restored = vec![0; text.len()];
19872 let mut aux = vec![0; (text.len() - 1) / r as usize + 1];
19873
19874 let bwt_rc = libsais64_bwt_aux(text, &mut bwt, &mut work, 0, None, r, &mut aux);
19875 assert_eq!(bwt_rc, 0);
19876
19877 let unbwt_rc = libsais64_unbwt_aux(&bwt, &mut restored, &mut work, None, r, &aux);
19878 assert_eq!(unbwt_rc, 0);
19879 assert_eq!(restored, text);
19880 }
19881
19882 #[test]
19883 fn public_libsais64_matches_upstream_c() {
19884 for text in [
19885 b"".as_slice(),
19886 b"a",
19887 b"banana",
19888 b"mississippi",
19889 b"abracadabra",
19890 b"AAAAAAAAAAAAAAAA",
19891 b"zyxwvutsrqponmlk",
19892 ] {
19893 assert_libsais64_matches_c(text);
19894 }
19895 }
19896
19897 #[test]
19898 fn public_libsais64_bwt_matches_upstream_c() {
19899 for text in [
19900 b"".as_slice(),
19901 b"a",
19902 b"banana",
19903 b"mississippi",
19904 b"abracadabra",
19905 b"AAAAAAAAAAAAAAAA",
19906 b"zyxwvutsrqponmlk",
19907 ] {
19908 assert_libsais64_bwt_matches_c(text);
19909 }
19910 }
19911
19912 #[test]
19913 fn public_libsais64_gsa_matches_upstream_c() {
19914 for text in [
19915 b"\0".as_slice(),
19916 b"banana\0",
19917 b"ban\0ana\0",
19918 b"miss\0issippi\0",
19919 b"a\0a\0a\0",
19920 ] {
19921 assert_libsais64_gsa_matches_c(text);
19922 }
19923 }
19924
19925 #[test]
19926 fn public_libsais64_long_matches_upstream_c() {
19927 for (text, k) in [
19928 (&[][..], 0),
19929 (&[0][..], 1),
19930 (&[1, 2, 1, 0][..], 3),
19931 (&[2, 1, 2, 1, 0][..], 3),
19932 (&[3, 3, 3, 2, 1, 0][..], 4),
19933 ] {
19934 assert_libsais64_long_matches_c(text, k);
19935 }
19936 }
19937
19938 #[test]
19939 fn public_libsais64_plcp_lcp_matches_upstream_c() {
19940 for text in [
19941 b"".as_slice(),
19942 b"a",
19943 b"banana",
19944 b"mississippi",
19945 b"abracadabra",
19946 b"AAAAAAAAAAAAAAAA",
19947 b"zyxwvutsrqponmlk",
19948 ] {
19949 assert_libsais64_plcp_lcp_matches_c(text);
19950 }
19951 }
19952
19953 #[test]
19954 fn public_libsais64_plcp_gsa_matches_upstream_c() {
19955 for text in [
19956 b"\0".as_slice(),
19957 b"banana\0",
19958 b"ban\0ana\0",
19959 b"miss\0issippi\0",
19960 b"a\0a\0a\0",
19961 ] {
19962 assert_libsais64_plcp_gsa_matches_c(text);
19963 }
19964 }
19965
19966 #[test]
19967 fn libsais64_bwt_and_unbwt_round_trip_small_text() {
19968 let t = b"banana";
19969 let mut bwt = vec![0u8; t.len()];
19970 let mut a = vec![0; t.len()];
19971
19972 let primary = libsais64_bwt(t, &mut bwt, &mut a, 0, None);
19973 assert!(primary > 0);
19974
19975 let mut restored = vec![0u8; t.len()];
19976 let result = libsais64_unbwt(&bwt, &mut restored, &mut a, None, primary);
19977
19978 assert_eq!(result, 0);
19979 assert_eq!(restored, t);
19980 }
19981
19982 #[test]
19983 fn libsais64_bwt_aux_and_unbwt_aux_round_trip_small_text() {
19984 let t = b"mississippi";
19985 let mut bwt = vec![0u8; t.len()];
19986 let mut a = vec![0; t.len()];
19987 let mut samples = vec![0; 4];
19988
19989 let result = libsais64_bwt_aux(t, &mut bwt, &mut a, 0, None, 4, &mut samples);
19990 assert_eq!(result, 0);
19991
19992 let mut restored = vec![0u8; t.len()];
19993 let result = libsais64_unbwt_aux(&bwt, &mut restored, &mut a, None, 4, &samples);
19994
19995 assert_eq!(result, 0);
19996 assert_eq!(restored, t);
19997 }
19998
19999 #[test]
20000 fn libsais64_bwt_aux_and_unbwt_aux_omp_round_trip_small_text() {
20001 let t = b"mississippi";
20002 let mut bwt = vec![0u8; t.len()];
20003 let mut a = vec![0; t.len()];
20004 let mut samples = vec![0; 4];
20005
20006 let result = libsais64_bwt_aux(t, &mut bwt, &mut a, 0, None, 4, &mut samples);
20007 assert_eq!(result, 0);
20008
20009 let mut restored = vec![0u8; t.len()];
20010 let result = libsais64_unbwt_aux_omp(&bwt, &mut restored, &mut a, None, 4, &samples, 2);
20011
20012 assert_eq!(result, 0);
20013 assert_eq!(restored, t);
20014 }
20015
20016 #[test]
20017 fn libsais64_real_world_round_trip_on_upstream_readme() {
20018 let t = include_bytes!("../libsais/README.md");
20019 let mut bwt = vec![0u8; t.len()];
20020 let mut a = vec![0; t.len()];
20021
20022 let primary = libsais64_bwt(t, &mut bwt, &mut a, 0, None);
20023 assert!(primary > 0);
20024
20025 let mut restored = vec![0u8; t.len()];
20026 let result = libsais64_unbwt(&bwt, &mut restored, &mut a, None, primary);
20027
20028 assert_eq!(result, 0);
20029 assert_eq!(restored, t);
20030 }
20031
20032 #[test]
20033 fn libsais64_real_world_aux_omp_round_trip_on_upstream_c_source() {
20034 let t = include_bytes!("../libsais/src/libsais.c");
20035 let mut bwt = vec![0u8; t.len()];
20036 let mut a = vec![0; t.len()];
20037 let r = 128;
20038 let mut samples = vec![0; (t.len() - 1) / usize::try_from(r).expect("fits") + 1];
20039
20040 let result = libsais64_bwt_aux(t, &mut bwt, &mut a, 0, None, r, &mut samples);
20041 assert_eq!(result, 0);
20042
20043 let mut restored = vec![0u8; t.len()];
20044 let result = libsais64_unbwt_aux_omp(&bwt, &mut restored, &mut a, None, r, &samples, 2);
20045
20046 assert_eq!(result, 0);
20047 assert_eq!(restored, t);
20048 }
20049
20050 #[test]
20051 fn libsais64_bwt_aux_rejects_undersized_sampling_array() {
20052 let t = b"upstream source text";
20053 let mut bwt = vec![0u8; t.len()];
20054 let mut a = vec![0; t.len()];
20055 let mut samples = vec![0; 1];
20056
20057 let result = libsais64_bwt_aux(t, &mut bwt, &mut a, 0, None, 2, &mut samples);
20058
20059 assert_eq!(result, -1);
20060
20061 let result = libsais64_bwt_aux(t, &mut bwt, &mut a, 0, None, 0, &mut samples);
20062
20063 assert_eq!(result, -1);
20064 }
20065
20066 #[test]
20067 fn libsais64_bwt_aux_omp_rejects_invalid_sampling_rate_without_panicking() {
20068 let t = b"upstream source text";
20069 let mut bwt = vec![0u8; t.len()];
20070 let mut a = vec![0; t.len()];
20071 let mut samples = vec![0; 4];
20072
20073 let result = libsais64_bwt_aux_omp(t, &mut bwt, &mut a, 0, None, 0, &mut samples, 2);
20074
20075 assert_eq!(result, -1);
20076 }
20077
20078 #[test]
20079 fn public_libsais64_empty_and_singleton_inputs_follow_public_contract() {
20080 let mut empty_sa = Vec::new();
20081 let mut empty_freq = vec![-1; ALPHABET_SIZE];
20082 assert_eq!(libsais64(b"", &mut empty_sa, 0, Some(&mut empty_freq)), 0);
20083 assert!(empty_freq.iter().all(|&value| value == 0));
20084
20085 empty_freq.fill(-1);
20086 assert_eq!(
20087 libsais64_omp(b"", &mut empty_sa, 0, Some(&mut empty_freq), 2),
20088 0
20089 );
20090 assert!(empty_freq.iter().all(|&value| value == 0));
20091
20092 empty_freq.fill(-1);
20093 assert_eq!(
20094 libsais64_gsa(b"", &mut empty_sa, 0, Some(&mut empty_freq)),
20095 0
20096 );
20097 assert!(empty_freq.iter().all(|&value| value == 0));
20098
20099 let mut empty_bwt = Vec::new();
20100 let mut empty_work = Vec::new();
20101 empty_freq.fill(-1);
20102 assert_eq!(
20103 libsais64_bwt(
20104 b"",
20105 &mut empty_bwt,
20106 &mut empty_work,
20107 0,
20108 Some(&mut empty_freq)
20109 ),
20110 0
20111 );
20112 assert!(empty_freq.iter().all(|&value| value == 0));
20113
20114 let mut empty_aux = vec![-1];
20115 empty_freq.fill(-1);
20116 assert_eq!(
20117 libsais64_bwt_aux(
20118 b"",
20119 &mut empty_bwt,
20120 &mut empty_work,
20121 0,
20122 Some(&mut empty_freq),
20123 2,
20124 &mut empty_aux
20125 ),
20126 0
20127 );
20128 assert_eq!(empty_aux[0], 0);
20129 assert!(empty_freq.iter().all(|&value| value == 0));
20130
20131 let text = b"z";
20132 let mut sa = vec![-1; 1];
20133 let mut freq = vec![-1; ALPHABET_SIZE];
20134 assert_eq!(libsais64(text, &mut sa, 0, Some(&mut freq)), 0);
20135 assert_eq!(sa, vec![0]);
20136 assert_eq!(freq[b'z' as usize], 1);
20137 assert_eq!(freq.iter().sum::<SaSint>(), 1);
20138
20139 sa.fill(-1);
20140 freq.fill(-1);
20141 let mut ctx = create_ctx().expect("context");
20142 assert_eq!(
20143 libsais64_ctx(&mut ctx, text, &mut sa, 0, Some(&mut freq)),
20144 0
20145 );
20146 assert_eq!(sa, vec![0]);
20147 assert_eq!(freq[b'z' as usize], 1);
20148 assert_eq!(freq.iter().sum::<SaSint>(), 1);
20149
20150 sa.fill(-1);
20151 freq.fill(-1);
20152 assert_eq!(libsais64_omp(text, &mut sa, 0, Some(&mut freq), 2), 0);
20153 assert_eq!(sa, vec![0]);
20154 assert_eq!(freq[b'z' as usize], 1);
20155 assert_eq!(freq.iter().sum::<SaSint>(), 1);
20156
20157 let mut gsa_sa = vec![-1; 1];
20158 let mut gsa_freq = vec![-1; ALPHABET_SIZE];
20159 assert_eq!(libsais64_gsa(b"\0", &mut gsa_sa, 0, Some(&mut gsa_freq)), 0);
20160 assert_eq!(gsa_sa, vec![0]);
20161 assert_eq!(gsa_freq[0], 1);
20162 assert_eq!(gsa_freq.iter().sum::<SaSint>(), 1);
20163
20164 let mut bwt = vec![0; 1];
20165 let mut work = vec![0; 1];
20166 freq.fill(-1);
20167 assert_eq!(
20168 libsais64_bwt(text, &mut bwt, &mut work, 0, Some(&mut freq)),
20169 1
20170 );
20171 assert_eq!(bwt, text);
20172 assert_eq!(freq[b'z' as usize], 1);
20173 assert_eq!(freq.iter().sum::<SaSint>(), 1);
20174
20175 let mut aux = vec![-1];
20176 bwt.fill(0);
20177 work.fill(0);
20178 freq.fill(-1);
20179 assert_eq!(
20180 libsais64_bwt_aux(text, &mut bwt, &mut work, 0, Some(&mut freq), 2, &mut aux),
20181 0
20182 );
20183 assert_eq!(bwt, text);
20184 assert_eq!(aux[0], 1);
20185 assert_eq!(freq[b'z' as usize], 1);
20186 assert_eq!(freq.iter().sum::<SaSint>(), 1);
20187 }
20188
20189 #[test]
20190 fn public_libsais64_rejects_invalid_aux_sampling_without_panicking() {
20191 let text = b"banana";
20192 let mut u = vec![0; text.len()];
20193 let mut a = vec![0; text.len() + 1];
20194 let mut aux = vec![0; 2];
20195
20196 assert_eq!(
20197 libsais64_bwt_aux(text, &mut u, &mut a, 0, None, 0, &mut aux),
20198 -1
20199 );
20200 assert_eq!(
20201 libsais64_bwt_aux(text, &mut u, &mut a, 0, None, 3, &mut aux),
20202 -1
20203 );
20204 assert_eq!(libsais64_unbwt_aux(text, &mut u, &mut a, None, 0, &aux), -1);
20205 assert_eq!(
20206 libsais64_unbwt_aux_omp(text, &mut u, &mut a, None, 0, &aux, 1),
20207 -1
20208 );
20209 }
20210
20211 #[test]
20212 fn libsais64_unbwt_aux_rejects_invalid_sampling_range() {
20213 let t = b"abc";
20214 let mut u = vec![0u8; t.len()];
20215 let mut a = vec![0; t.len()];
20216
20217 let result = libsais64_unbwt_aux(t, &mut u, &mut a, None, 2, &[0, 4]);
20218
20219 assert_eq!(result, -1);
20220
20221 assert_eq!(libsais64_unbwt_aux(t, &mut u, &mut a, None, 0, &[1]), -1);
20222
20223 let mut ctx = unbwt_create_ctx().expect("context");
20224 assert_eq!(
20225 libsais64_unbwt_aux_ctx(&mut ctx, t, &mut u, &mut a, None, 0, &[1]),
20226 -1
20227 );
20228 assert_eq!(
20229 libsais64_unbwt_aux_omp(t, &mut u, &mut a, None, 0, &[1], 2),
20230 -1
20231 );
20232 }
20233
20234 #[test]
20235 fn public_libsais64_omp_rejects_undersized_suffix_arrays() {
20236 let text = b"banana";
20237 let mut short_sa = vec![0; text.len() - 1];
20238 let mut int_text = vec![1, 2, 1, 0];
20239 let mut short_int_sa = vec![0; int_text.len() - 1];
20240
20241 assert_eq!(libsais64_omp(text, &mut short_sa, 0, None, 1), -1);
20242 assert_eq!(
20243 libsais64_gsa_omp(b"banana\0", &mut short_sa, 0, None, 1),
20244 -1
20245 );
20246 assert_eq!(
20247 libsais64_int_omp(&mut int_text, &mut short_int_sa, 3, 0, 1),
20248 -1
20249 );
20250 }
20251
20252 #[test]
20253 #[ignore = "large real-data regression; requires local yeast FASTA fixture"]
20254 fn public_libsais64_omp_handles_minibwa_yeast_two_strand_index_input() {
20255 let path = "/data/henriksson/github/claude/star/.tmp/yeast_conformance/Saccharomyces_cerevisiae.R64-1-1.dna.toplevel.fa";
20256 let Ok(fasta) = std::fs::read_to_string(path) else {
20257 eprintln!("skipping missing fixture: {path}");
20258 return;
20259 };
20260 let mut forward = Vec::new();
20261 for line in fasta.lines() {
20262 if line.starts_with('>') {
20263 continue;
20264 }
20265 forward.extend(line.as_bytes().iter().filter_map(|&c| match c {
20266 b'A' | b'a' => Some(0),
20267 b'C' | b'c' => Some(1),
20268 b'G' | b'g' => Some(2),
20269 b'T' | b't' => Some(3),
20270 _ => None,
20271 }));
20272 }
20273 assert!(
20274 forward.len() > 12_000_000,
20275 "fixture should exercise the large-input 64-bit path"
20276 );
20277
20278 let mut text = Vec::with_capacity(forward.len() * 2);
20279 text.extend_from_slice(&forward);
20280 text.extend(forward.iter().rev().map(|&c| 3 - c));
20281
20282 const FS: SaSint = 10_000;
20283 let mut sa = vec![0; text.len() + FS as usize];
20284 assert_eq!(libsais64_omp(&text, &mut sa, FS, None, 4), 0);
20285 }
20286
20287 #[test]
20288 #[ignore = "large real-data regression; requires local minibwa yeast fixture"]
20289 fn public_libsais64_omp_matches_plain_on_minibwa_yeast_two_strand_index_input() {
20290 let l2b_path =
20291 "/data/henriksson/github/claude/minibwa/.tmp/compare-yeast-now/ref.split.rust.l2b";
20292 let fasta_path =
20293 "/data/henriksson/github/claude/minibwa/.tmp/large-real/yeast/ref.sanitized.fa";
20294 let forward = if let Ok(bytes) = std::fs::read(l2b_path) {
20295 assert!(bytes.len() >= 64, "short l2b fixture: {l2b_path}");
20296 assert_eq!(&bytes[..4], b"L2B\x01", "bad l2b magic in {l2b_path}");
20297 let n_ctg = u64::from_le_bytes(bytes[8..16].try_into().unwrap()) as usize;
20298 let tot_len = u64::from_le_bytes(bytes[16..24].try_into().unwrap()) as usize;
20299 let n_ambi = u64::from_le_bytes(bytes[24..32].try_into().unwrap()) as usize;
20300 let n_mask = u64::from_le_bytes(bytes[32..40].try_into().unwrap()) as usize;
20301 let n_pac = u64::from_le_bytes(bytes[56..64].try_into().unwrap()) as usize;
20302 let pac_start = 64 + 8 * n_ctg + 16 * n_ambi + 16 * n_mask;
20303 assert!(
20304 bytes.len() >= pac_start + 8 * n_pac,
20305 "truncated l2b pac in {l2b_path}"
20306 );
20307 let mut pac = Vec::with_capacity(n_pac);
20308 for chunk in bytes[pac_start..pac_start + 8 * n_pac].chunks_exact(8) {
20309 pac.push(u64::from_le_bytes(chunk.try_into().unwrap()));
20310 }
20311 (0..tot_len)
20312 .map(|i| ((pac[i >> 5] >> ((i & 31) << 1)) & 3) as u8)
20313 .collect::<Vec<_>>()
20314 } else if let Ok(fasta) = std::fs::read_to_string(fasta_path) {
20315 let mut rng = 11u64;
20316 let mut forward = Vec::new();
20317 for line in fasta.lines() {
20318 if line.starts_with('>') {
20319 continue;
20320 }
20321 forward.extend(line.bytes().map(|b| {
20322 let mut c = match b {
20323 b'A' | b'a' => 0,
20324 b'C' | b'c' => 1,
20325 b'G' | b'g' => 2,
20326 b'T' | b't' | b'U' | b'u' => 3,
20327 _ => {
20328 rng = rng.wrapping_add(0x9e3779b97f4a7c15);
20329 let mut z = rng;
20330 z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
20331 z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
20332 4 | ((z ^ (z >> 31)) & 3) as u8
20333 }
20334 };
20335 if b < b'A' || b > b'Z' {
20336 c |= 1 << 3;
20337 }
20338 c & 3
20339 }));
20340 }
20341 forward
20342 } else {
20343 eprintln!("skipping missing fixtures: {l2b_path} and {fasta_path}");
20344 return;
20345 };
20346 assert!(
20347 forward.len() > 12_000_000,
20348 "fixture should exercise the minibwa yeast index workload"
20349 );
20350
20351 let mut text = Vec::with_capacity(forward.len() * 2);
20352 text.extend_from_slice(&forward);
20353 text.extend(forward.iter().rev().map(|&c| 3 - c));
20354
20355 const FS: SaSint = 10_000;
20356 let mut plain_sa = vec![0; text.len() + FS as usize + 1];
20357 let mut omp_sa = vec![0; text.len() + FS as usize + 1];
20358 assert_eq!(libsais64(&text, &mut plain_sa[1..], FS, None), 0);
20359 assert_eq!(libsais64_omp(&text, &mut omp_sa[1..], FS, None, 4), 0);
20360 plain_sa[0] = text.len() as SaSint;
20361 omp_sa[0] = text.len() as SaSint;
20362 if let Some(i) = plain_sa[..=text.len()]
20363 .iter()
20364 .zip(&omp_sa[..=text.len()])
20365 .position(|(plain, omp)| plain != omp)
20366 {
20367 panic!(
20368 "first suffix-array diff at {i}: plain={} omp={}",
20369 plain_sa[i], omp_sa[i]
20370 );
20371 }
20372 }
20373
20374 #[test]
20375 #[ignore = "large real-data regression; requires local minibwa or STAR yeast FASTA fixture"]
20376 fn direct_libsais64_main_handles_minibwa_yeast_two_strand_index_input() {
20377 let minibwa_path =
20378 "/data/henriksson/github/claude/minibwa/.tmp/large-real/yeast/ref.sanitized.fa";
20379 let star_path = "/data/henriksson/github/claude/star/.tmp/yeast_conformance/Saccharomyces_cerevisiae.R64-1-1.dna.toplevel.fa";
20380 let (path, fasta) = if let Ok(fasta) = std::fs::read_to_string(minibwa_path) {
20381 (minibwa_path, fasta)
20382 } else if let Ok(fasta) = std::fs::read_to_string(star_path) {
20383 (star_path, fasta)
20384 } else {
20385 eprintln!("skipping missing fixtures: {minibwa_path} and {star_path}");
20386 return;
20387 };
20388 let mut forward = Vec::new();
20389 for line in fasta.lines() {
20390 if line.starts_with('>') {
20391 continue;
20392 }
20393 forward.extend(line.as_bytes().iter().filter_map(|&c| match c {
20394 b'A' | b'a' => Some(0),
20395 b'C' | b'c' => Some(1),
20396 b'G' | b'g' => Some(2),
20397 b'T' | b't' => Some(3),
20398 _ => None,
20399 }));
20400 }
20401 assert!(
20402 forward.len() > 12_000_000,
20403 "fixture {path} should exercise the minibwa yeast index workload"
20404 );
20405
20406 let mut text = Vec::with_capacity(forward.len() * 2);
20407 text.extend_from_slice(&forward);
20408 text.extend(forward.iter().rev().map(|&c| 3 - c));
20409
20410 const FS: SaSint = 10_000;
20411 let mut sa = vec![0; text.len() + FS as usize];
20412 assert_eq!(
20413 libsais64_main(&text, &mut sa, LIBSAIS_FLAGS_NONE, 0, None, FS, None, 1),
20414 0
20415 );
20416 }
20417
20418 #[test]
20419 #[ignore = "large real-data regression; requires local yeast FASTA fixture"]
20420 fn public_libsais64_matches_c_on_minibwa_yeast_two_strand_index_input() {
20421 let path = "/data/henriksson/github/claude/star/.tmp/yeast_conformance/Saccharomyces_cerevisiae.R64-1-1.dna.toplevel.fa";
20422 let Ok(fasta) = std::fs::read_to_string(path) else {
20423 eprintln!("skipping missing fixture: {path}");
20424 return;
20425 };
20426 let mut forward = Vec::new();
20427 for line in fasta.lines() {
20428 if line.starts_with('>') {
20429 continue;
20430 }
20431 forward.extend(line.as_bytes().iter().filter_map(|&c| match c {
20432 b'A' | b'a' => Some(0),
20433 b'C' | b'c' => Some(1),
20434 b'G' | b'g' => Some(2),
20435 b'T' | b't' => Some(3),
20436 _ => None,
20437 }));
20438 }
20439 let mut text = Vec::with_capacity(forward.len() * 2);
20440 text.extend_from_slice(&forward);
20441 text.extend(forward.iter().rev().map(|&c| 3 - c));
20442
20443 const FS: SaSint = 10_000;
20444 let mut rust_sa = vec![0; text.len() + FS as usize];
20445 let mut c_sa = vec![0; text.len() + FS as usize];
20446 let rust_rc = libsais64(&text, &mut rust_sa, FS, None);
20447 let c_rc = unsafe {
20448 probe_public_libsais64(text.as_ptr(), c_sa.as_mut_ptr(), text.len() as SaSint, FS)
20449 };
20450 assert_eq!(rust_rc, c_rc);
20451 if let Some(i) = rust_sa[..text.len()]
20452 .iter()
20453 .zip(&c_sa[..text.len()])
20454 .position(|(r, c)| r != c)
20455 {
20456 panic!(
20457 "first suffix-array diff at {i}: rust={} c={}",
20458 rust_sa[i], c_sa[i]
20459 );
20460 }
20461 }
20462
20463 #[test]
20464 #[ignore = "large real-data regression; requires local yeast FASTA fixture"]
20465 fn public_libsais64_omp_matches_c_on_minibwa_yeast_two_strand_index_input() {
20466 let path = "/data/henriksson/github/claude/star/.tmp/yeast_conformance/Saccharomyces_cerevisiae.R64-1-1.dna.toplevel.fa";
20467 let Ok(fasta) = std::fs::read_to_string(path) else {
20468 eprintln!("skipping missing fixture: {path}");
20469 return;
20470 };
20471 let mut forward = Vec::new();
20472 for line in fasta.lines() {
20473 if line.starts_with('>') {
20474 continue;
20475 }
20476 forward.extend(line.as_bytes().iter().filter_map(|&c| match c {
20477 b'A' | b'a' => Some(0),
20478 b'C' | b'c' => Some(1),
20479 b'G' | b'g' => Some(2),
20480 b'T' | b't' => Some(3),
20481 _ => None,
20482 }));
20483 }
20484 let mut text = Vec::with_capacity(forward.len() * 2);
20485 text.extend_from_slice(&forward);
20486 text.extend(forward.iter().rev().map(|&c| 3 - c));
20487
20488 const FS: SaSint = 10_000;
20489 let mut rust_sa = vec![0; text.len() + FS as usize];
20490 let mut c_sa = vec![0; text.len() + FS as usize];
20491 let rust_rc = libsais64_omp(&text, &mut rust_sa, FS, None, 4);
20492 let c_rc = unsafe {
20493 probe_public_libsais64(text.as_ptr(), c_sa.as_mut_ptr(), text.len() as SaSint, FS)
20494 };
20495 assert_eq!(rust_rc, c_rc);
20496 if let Some(i) = rust_sa[..text.len()]
20497 .iter()
20498 .zip(&c_sa[..text.len()])
20499 .position(|(r, c)| r != c)
20500 {
20501 panic!(
20502 "first omp suffix-array diff at {i}: rust={} c={}",
20503 rust_sa[i], c_sa[i]
20504 );
20505 }
20506 }
20507
20508 #[test]
20509 fn public_libsais64_ctx_rejects_invalid_public_arguments() {
20510 let text = b"banana";
20511 let mut ctx = create_ctx().unwrap();
20512 let mut short_sa = vec![0; text.len() - 1];
20513 let mut full_sa = vec![0; text.len()];
20514 let mut short_freq = vec![0; ALPHABET_SIZE - 1];
20515 let mut short_u = vec![0; text.len() - 1];
20516 let mut full_u = vec![0; text.len()];
20517 let mut short_a = vec![0; text.len() - 1];
20518 let mut full_a = vec![0; text.len()];
20519 let mut aux = vec![0; 2];
20520
20521 assert_eq!(libsais64_ctx(&mut ctx, text, &mut short_sa, 0, None), -1);
20522 assert_eq!(
20523 libsais64_ctx(&mut ctx, text, &mut full_sa, 0, Some(&mut short_freq)),
20524 -1
20525 );
20526 assert_eq!(
20527 libsais64_gsa_ctx(&mut ctx, b"banana", &mut full_sa, 0, None),
20528 -1
20529 );
20530 assert_eq!(
20531 libsais64_gsa_ctx(&mut ctx, b"banana\0", &mut short_sa, 0, None),
20532 -1
20533 );
20534 assert_eq!(
20535 libsais64_bwt_ctx(&mut ctx, text, &mut short_u, &mut full_a, 0, None),
20536 -1
20537 );
20538 assert_eq!(
20539 libsais64_bwt_ctx(&mut ctx, text, &mut full_u, &mut short_a, 0, None),
20540 -1
20541 );
20542 assert_eq!(
20543 libsais64_bwt_ctx(
20544 &mut ctx,
20545 text,
20546 &mut full_u,
20547 &mut full_a,
20548 0,
20549 Some(&mut short_freq)
20550 ),
20551 -1
20552 );
20553 assert_eq!(
20554 libsais64_bwt_aux_ctx(
20555 &mut ctx,
20556 text,
20557 &mut full_u,
20558 &mut full_a,
20559 0,
20560 None,
20561 0,
20562 &mut aux
20563 ),
20564 -1
20565 );
20566 assert_eq!(
20567 libsais64_bwt_aux_ctx(
20568 &mut ctx,
20569 text,
20570 &mut full_u,
20571 &mut full_a,
20572 0,
20573 None,
20574 3,
20575 &mut aux
20576 ),
20577 -1
20578 );
20579 assert_eq!(
20580 libsais64_bwt_aux_ctx(
20581 &mut ctx,
20582 text,
20583 &mut full_u,
20584 &mut full_a,
20585 0,
20586 None,
20587 4,
20588 &mut []
20589 ),
20590 -1
20591 );
20592
20593 let mut missing_thread_state_ctx = Context {
20594 buckets: vec![0; 8 * ALPHABET_SIZE],
20595 thread_state: None,
20596 threads: 2,
20597 };
20598 assert_eq!(
20599 libsais64_ctx(&mut missing_thread_state_ctx, text, &mut full_sa, 0, None),
20600 -2
20601 );
20602
20603 let mut zero_thread_ctx = Context {
20604 buckets: vec![0; 8 * ALPHABET_SIZE],
20605 thread_state: None,
20606 threads: 0,
20607 };
20608 assert_eq!(
20609 libsais64_ctx(&mut zero_thread_ctx, text, &mut full_sa, 0, None),
20610 -2
20611 );
20612
20613 let mut short_thread_state_ctx = create_ctx_main(2).expect("context");
20614 short_thread_state_ctx
20615 .thread_state
20616 .as_mut()
20617 .expect("thread state")
20618 .truncate(1);
20619 assert_eq!(
20620 libsais64_ctx(&mut short_thread_state_ctx, text, &mut full_sa, 0, None),
20621 -2
20622 );
20623 }
20624
20625 #[test]
20626 fn public_libsais64_unbwt_ctx_rejects_invalid_public_arguments() {
20627 let text = b"banana";
20628 let mut bwt = vec![0; text.len()];
20629 let mut work = vec![0; text.len()];
20630 let primary = libsais64_bwt(text, &mut bwt, &mut work, 0, None);
20631 let mut ctx = unbwt_create_ctx().unwrap();
20632
20633 let mut short_u = vec![0; text.len() - 1];
20634 let mut full_u = vec![0; text.len()];
20635 let mut short_a = vec![0; text.len() - 1];
20636 let mut full_a = vec![0; text.len()];
20637 let short_freq = vec![0; ALPHABET_SIZE - 1];
20638 let good_aux = vec![primary, 4];
20639
20640 assert_eq!(
20641 libsais64_unbwt_ctx(&mut ctx, &bwt, &mut short_u, &mut full_a, None, primary),
20642 -1
20643 );
20644 assert_eq!(
20645 libsais64_unbwt_ctx(&mut ctx, &bwt, &mut full_u, &mut short_a, None, primary),
20646 -1
20647 );
20648 assert_eq!(
20649 libsais64_unbwt_ctx(
20650 &mut ctx,
20651 &bwt,
20652 &mut full_u,
20653 &mut full_a,
20654 Some(&short_freq),
20655 primary
20656 ),
20657 -1
20658 );
20659 assert_eq!(
20660 libsais64_unbwt_ctx(&mut ctx, &bwt, &mut full_u, &mut full_a, None, 0),
20661 -1
20662 );
20663 assert_eq!(
20664 libsais64_unbwt_aux_ctx(&mut ctx, &bwt, &mut full_u, &mut full_a, None, 3, &good_aux),
20665 -1
20666 );
20667 assert_eq!(
20668 libsais64_unbwt_aux_ctx(
20669 &mut ctx,
20670 &bwt,
20671 &mut full_u,
20672 &mut full_a,
20673 None,
20674 4,
20675 &[primary]
20676 ),
20677 -1
20678 );
20679
20680 let mut malformed_ctx = UnbwtContext {
20681 bucket2: Vec::new(),
20682 fastbits: Vec::new(),
20683 buckets: None,
20684 threads: 1,
20685 };
20686 assert_eq!(
20687 libsais64_unbwt_ctx(
20688 &mut malformed_ctx,
20689 &bwt,
20690 &mut full_u,
20691 &mut full_a,
20692 None,
20693 primary
20694 ),
20695 -2
20696 );
20697
20698 let mut missing_parallel_buckets_ctx = UnbwtContext {
20699 bucket2: vec![0; ALPHABET_SIZE * ALPHABET_SIZE],
20700 fastbits: vec![0; 1 + (1 << UNBWT_FASTBITS)],
20701 buckets: None,
20702 threads: 2,
20703 };
20704 assert_eq!(
20705 libsais64_unbwt_ctx(
20706 &mut missing_parallel_buckets_ctx,
20707 &bwt,
20708 &mut full_u,
20709 &mut full_a,
20710 None,
20711 primary
20712 ),
20713 -2
20714 );
20715 }
20716
20717 #[test]
20718 fn public_libsais64_lcp_helpers_reject_invalid_suffix_entries() {
20719 let text = b"banana";
20720 let mut plcp = vec![0; text.len()];
20721 let mut lcp = vec![0; text.len()];
20722 let int_text = vec![1, 2, 1, 0];
20723 let mut int_plcp = vec![0; int_text.len()];
20724
20725 assert_eq!(libsais64_plcp(text, &[0, 1, -1, 3, 4, 5], &mut plcp), -1);
20726 assert_eq!(libsais64_plcp(text, &[0, 1, 2, 3, 4, 6], &mut plcp), -1);
20727 assert_eq!(libsais64_lcp(&plcp, &[0, 1, -1, 3, 4, 5], &mut lcp), -1);
20728 assert_eq!(libsais64_lcp(&plcp, &[0, 1, 2, 3, 4, 6], &mut lcp), -1);
20729 assert_eq!(
20730 libsais64_plcp_int(&int_text, &[0, 1, -1, 3], &mut int_plcp),
20731 -1
20732 );
20733 assert_eq!(
20734 libsais64_plcp_int_omp(&int_text, &[0, 1, 2, 4], &mut int_plcp, 1),
20735 -1
20736 );
20737 }
20738
20739 #[test]
20740 fn public_libsais64_context_wrappers_match_direct_calls() {
20741 let text = b"banana";
20742 let gsa_text = b"ban\0ana\0";
20743 let mut ctx = create_ctx().unwrap();
20744
20745 let mut direct_sa = vec![0; text.len()];
20746 let mut ctx_sa = vec![0; text.len()];
20747 assert_eq!(libsais64(text, &mut direct_sa, 0, None), 0);
20748 assert_eq!(libsais64_ctx(&mut ctx, text, &mut ctx_sa, 0, None), 0);
20749 assert_eq!(ctx_sa, direct_sa);
20750
20751 let mut direct_gsa = vec![0; gsa_text.len()];
20752 let mut ctx_gsa = vec![0; gsa_text.len()];
20753 assert_eq!(libsais64_gsa(gsa_text, &mut direct_gsa, 0, None), 0);
20754 assert_eq!(
20755 libsais64_gsa_ctx(&mut ctx, gsa_text, &mut ctx_gsa, 0, None),
20756 0
20757 );
20758 assert_eq!(ctx_gsa, direct_gsa);
20759
20760 let mut direct_bwt = vec![0; text.len()];
20761 let mut direct_work = vec![0; text.len()];
20762 let mut ctx_bwt = vec![0; text.len()];
20763 let mut ctx_work = vec![0; text.len()];
20764 assert_eq!(
20765 libsais64_bwt(text, &mut direct_bwt, &mut direct_work, 0, None),
20766 libsais64_bwt_ctx(&mut ctx, text, &mut ctx_bwt, &mut ctx_work, 0, None)
20767 );
20768 assert_eq!(ctx_bwt, direct_bwt);
20769
20770 let mut direct_aux = vec![0; 2];
20771 let mut ctx_aux = vec![0; 2];
20772 assert_eq!(
20773 libsais64_bwt_aux(
20774 text,
20775 &mut direct_bwt,
20776 &mut direct_work,
20777 0,
20778 None,
20779 4,
20780 &mut direct_aux
20781 ),
20782 libsais64_bwt_aux_ctx(
20783 &mut ctx,
20784 text,
20785 &mut ctx_bwt,
20786 &mut ctx_work,
20787 0,
20788 None,
20789 4,
20790 &mut ctx_aux
20791 )
20792 );
20793 assert_eq!(ctx_bwt, direct_bwt);
20794 assert_eq!(ctx_aux, direct_aux);
20795 }
20796
20797 #[test]
20798 fn libsais64_ctx_matches_plain_entry_point_for_small_text() {
20799 let t = b"mississippi";
20800 let mut sa_plain = vec![0; t.len()];
20801 let mut sa_ctx = vec![0; t.len()];
20802 let plain = libsais64(t, &mut sa_plain, 0, None);
20803
20804 let mut ctx = create_ctx().expect("context");
20805 let with_ctx = libsais64_ctx(&mut ctx, t, &mut sa_ctx, 0, None);
20806
20807 assert_eq!(plain, 0);
20808 assert_eq!(with_ctx, 0);
20809 assert_eq!(sa_ctx, sa_plain);
20810 }
20811
20812 #[test]
20813 fn public_libsais64_unbwt_context_wrappers_match_direct_calls() {
20814 let text = b"banana";
20815 let mut bwt = vec![0; text.len()];
20816 let mut work = vec![0; text.len()];
20817 let primary = libsais64_bwt(text, &mut bwt, &mut work, 0, None);
20818 let mut ctx = unbwt_create_ctx().unwrap();
20819
20820 let mut direct = vec![0; text.len()];
20821 let mut direct_work = vec![0; text.len() + 1];
20822 let mut via_ctx = vec![0; text.len()];
20823 let mut ctx_work = vec![0; text.len() + 1];
20824 assert_eq!(
20825 libsais64_unbwt(&bwt, &mut direct, &mut direct_work, None, primary),
20826 0
20827 );
20828 assert_eq!(
20829 libsais64_unbwt_ctx(&mut ctx, &bwt, &mut via_ctx, &mut ctx_work, None, primary),
20830 0
20831 );
20832 assert_eq!(via_ctx, direct);
20833
20834 let mut aux = vec![0; 2];
20835 assert_eq!(
20836 libsais64_bwt_aux(text, &mut bwt, &mut work, 0, None, 4, &mut aux),
20837 0
20838 );
20839 assert_eq!(
20840 libsais64_unbwt_aux(&bwt, &mut direct, &mut direct_work, None, 4, &aux),
20841 0
20842 );
20843 assert_eq!(
20844 libsais64_unbwt_aux_ctx(&mut ctx, &bwt, &mut via_ctx, &mut ctx_work, None, 4, &aux),
20845 0
20846 );
20847 assert_eq!(via_ctx, direct);
20848 }
20849
20850 #[test]
20851 fn public_libsais64_ctx_frequency_wrappers_match_direct_calls() {
20852 let text = b"banana";
20853 let gsa_text = b"ban\0ana\0";
20854 let mut ctx = create_ctx().unwrap();
20855
20856 let mut direct_sa = vec![0; text.len()];
20857 let mut ctx_sa = vec![0; text.len()];
20858 let mut direct_freq = vec![-1; ALPHABET_SIZE];
20859 let mut ctx_freq = vec![-1; ALPHABET_SIZE];
20860 assert_eq!(
20861 libsais64(text, &mut direct_sa, 0, Some(&mut direct_freq)),
20862 0
20863 );
20864 assert_eq!(
20865 libsais64_ctx(&mut ctx, text, &mut ctx_sa, 0, Some(&mut ctx_freq)),
20866 0
20867 );
20868 assert_eq!(ctx_sa, direct_sa);
20869 assert_eq!(ctx_freq, direct_freq);
20870
20871 let mut direct_gsa = vec![0; gsa_text.len()];
20872 let mut ctx_gsa = vec![0; gsa_text.len()];
20873 direct_freq.fill(-1);
20874 ctx_freq.fill(-1);
20875 assert_eq!(
20876 libsais64_gsa(gsa_text, &mut direct_gsa, 0, Some(&mut direct_freq)),
20877 0
20878 );
20879 assert_eq!(
20880 libsais64_gsa_ctx(&mut ctx, gsa_text, &mut ctx_gsa, 0, Some(&mut ctx_freq)),
20881 0
20882 );
20883 assert_eq!(ctx_gsa, direct_gsa);
20884 assert_eq!(ctx_freq, direct_freq);
20885
20886 let mut direct_bwt = vec![0; text.len()];
20887 let mut direct_work = vec![0; text.len()];
20888 let mut ctx_bwt = vec![0; text.len()];
20889 let mut ctx_work = vec![0; text.len()];
20890 direct_freq.fill(-1);
20891 ctx_freq.fill(-1);
20892 assert_eq!(
20893 libsais64_bwt(
20894 text,
20895 &mut direct_bwt,
20896 &mut direct_work,
20897 0,
20898 Some(&mut direct_freq)
20899 ),
20900 libsais64_bwt_ctx(
20901 &mut ctx,
20902 text,
20903 &mut ctx_bwt,
20904 &mut ctx_work,
20905 0,
20906 Some(&mut ctx_freq)
20907 )
20908 );
20909 assert_eq!(ctx_bwt, direct_bwt);
20910 assert_eq!(ctx_freq, direct_freq);
20911
20912 let mut direct_aux = vec![0; 2];
20913 let mut ctx_aux = vec![0; 2];
20914 direct_freq.fill(-1);
20915 ctx_freq.fill(-1);
20916 assert_eq!(
20917 libsais64_bwt_aux(
20918 text,
20919 &mut direct_bwt,
20920 &mut direct_work,
20921 0,
20922 Some(&mut direct_freq),
20923 4,
20924 &mut direct_aux
20925 ),
20926 libsais64_bwt_aux_ctx(
20927 &mut ctx,
20928 text,
20929 &mut ctx_bwt,
20930 &mut ctx_work,
20931 0,
20932 Some(&mut ctx_freq),
20933 4,
20934 &mut ctx_aux
20935 )
20936 );
20937 assert_eq!(ctx_bwt, direct_bwt);
20938 assert_eq!(ctx_aux, direct_aux);
20939 assert_eq!(ctx_freq, direct_freq);
20940 }
20941
20942 #[test]
20943 fn public_libsais64_unbwt_ctx_frequency_wrappers_match_direct_calls() {
20944 let text = b"abracadabra";
20945 let mut freq = vec![0; ALPHABET_SIZE];
20946 let mut bwt = vec![0; text.len()];
20947 let mut work = vec![0; text.len()];
20948 let primary = libsais64_bwt(text, &mut bwt, &mut work, 0, Some(&mut freq));
20949 assert!(primary >= 0);
20950
20951 let mut ctx = unbwt_create_ctx().unwrap();
20952 let mut direct = vec![0; text.len()];
20953 let mut direct_work = vec![0; text.len() + 1];
20954 let mut via_ctx = vec![0; text.len()];
20955 let mut ctx_work = vec![0; text.len() + 1];
20956 assert_eq!(
20957 libsais64_unbwt(&bwt, &mut direct, &mut direct_work, Some(&freq), primary),
20958 libsais64_unbwt_ctx(
20959 &mut ctx,
20960 &bwt,
20961 &mut via_ctx,
20962 &mut ctx_work,
20963 Some(&freq),
20964 primary
20965 )
20966 );
20967 assert_eq!(via_ctx, direct);
20968 assert_eq!(via_ctx, text);
20969
20970 let mut aux = vec![0; (text.len() - 1) / 4 + 1];
20971 assert_eq!(
20972 libsais64_bwt_aux(text, &mut bwt, &mut work, 0, Some(&mut freq), 4, &mut aux),
20973 0
20974 );
20975 direct.fill(0);
20976 direct_work.fill(0);
20977 via_ctx.fill(0);
20978 ctx_work.fill(0);
20979 assert_eq!(
20980 libsais64_unbwt_aux(&bwt, &mut direct, &mut direct_work, Some(&freq), 4, &aux),
20981 libsais64_unbwt_aux_ctx(
20982 &mut ctx,
20983 &bwt,
20984 &mut via_ctx,
20985 &mut ctx_work,
20986 Some(&freq),
20987 4,
20988 &aux
20989 )
20990 );
20991 assert_eq!(via_ctx, direct);
20992 assert_eq!(via_ctx, text);
20993 }
20994
20995 #[test]
20996 fn public_libsais64_omp_wrappers_match_direct_calls() {
20997 let text = b"banana";
20998 let gsa_text = b"ban\0ana\0";
20999
21000 let mut direct_sa = vec![0; text.len()];
21001 let mut omp_sa = vec![0; text.len()];
21002 assert_eq!(libsais64(text, &mut direct_sa, 0, None), 0);
21003 assert_eq!(libsais64_omp(text, &mut omp_sa, 0, None, 2), 0);
21004 assert_eq!(omp_sa, direct_sa);
21005 assert_eq!(libsais64_omp(text, &mut omp_sa, 0, None, -1), -1);
21006
21007 let mut direct_gsa = vec![0; gsa_text.len()];
21008 let mut omp_gsa = vec![0; gsa_text.len()];
21009 assert_eq!(libsais64_gsa(gsa_text, &mut direct_gsa, 0, None), 0);
21010 assert_eq!(libsais64_gsa_omp(gsa_text, &mut omp_gsa, 0, None, 2), 0);
21011 assert_eq!(omp_gsa, direct_gsa);
21012 assert_eq!(libsais64_gsa_omp(gsa_text, &mut omp_gsa, 0, None, -1), -1);
21013
21014 let int_text = vec![2, 1, 3, 1, 0];
21015 let mut direct_int_text = int_text.clone();
21016 let mut omp_int_text = int_text.clone();
21017 let mut direct_int_sa = vec![0; int_text.len()];
21018 let mut omp_int_sa = vec![0; int_text.len()];
21019 assert_eq!(
21020 libsais64_int(&mut direct_int_text, &mut direct_int_sa, 4, 0),
21021 0
21022 );
21023 assert_eq!(
21024 libsais64_int_omp(&mut omp_int_text, &mut omp_int_sa, 4, 0, 2),
21025 0
21026 );
21027 assert_eq!(omp_int_sa, direct_int_sa);
21028 assert_eq!(
21029 libsais64_int_omp(&mut omp_int_text, &mut omp_int_sa, 4, 0, -1),
21030 -1
21031 );
21032
21033 let long_text = vec![3, 1, 4, 1, 5, 0];
21034 let mut direct_long_text = long_text.clone();
21035 let mut omp_long_text = long_text.clone();
21036 let mut direct_long_sa = vec![0; long_text.len()];
21037 let mut omp_long_sa = vec![0; long_text.len()];
21038 assert_eq!(
21039 libsais64_long(&mut direct_long_text, &mut direct_long_sa, 6, 0),
21040 0
21041 );
21042 assert_eq!(
21043 libsais64_long_omp(&mut omp_long_text, &mut omp_long_sa, 6, 0, 2),
21044 0
21045 );
21046 assert_eq!(omp_long_sa, direct_long_sa);
21047 assert_eq!(
21048 libsais64_long_omp(&mut omp_long_text, &mut omp_long_sa, 6, 0, -1),
21049 -1
21050 );
21051
21052 let mut direct_bwt = vec![0; text.len()];
21053 let mut direct_work = vec![0; text.len()];
21054 let mut omp_bwt = vec![0; text.len()];
21055 let mut omp_work = vec![0; text.len()];
21056 assert_eq!(
21057 libsais64_bwt(text, &mut direct_bwt, &mut direct_work, 0, None),
21058 libsais64_bwt_omp(text, &mut omp_bwt, &mut omp_work, 0, None, 2)
21059 );
21060 assert_eq!(omp_bwt, direct_bwt);
21061 assert_eq!(
21062 libsais64_bwt_omp(text, &mut omp_bwt, &mut omp_work, 0, None, -1),
21063 -1
21064 );
21065
21066 let mut direct_aux = vec![0; 2];
21067 let mut omp_aux = vec![0; 2];
21068 assert_eq!(
21069 libsais64_bwt_aux(
21070 text,
21071 &mut direct_bwt,
21072 &mut direct_work,
21073 0,
21074 None,
21075 4,
21076 &mut direct_aux
21077 ),
21078 libsais64_bwt_aux_omp(
21079 text,
21080 &mut omp_bwt,
21081 &mut omp_work,
21082 0,
21083 None,
21084 4,
21085 &mut omp_aux,
21086 2
21087 )
21088 );
21089 assert_eq!(omp_bwt, direct_bwt);
21090 assert_eq!(omp_aux, direct_aux);
21091 assert_eq!(
21092 libsais64_bwt_aux_omp(
21093 text,
21094 &mut omp_bwt,
21095 &mut omp_work,
21096 0,
21097 None,
21098 4,
21099 &mut omp_aux,
21100 -1
21101 ),
21102 -1
21103 );
21104 }
21105
21106 #[test]
21107 fn public_libsais64_plcp_omp_wrappers_match_direct_calls() {
21108 let text = b"banana";
21109 let mut sa = vec![0; text.len()];
21110 assert_eq!(libsais64(text, &mut sa, 0, None), 0);
21111
21112 let mut direct_plcp = vec![0; text.len()];
21113 let mut omp_plcp = vec![0; text.len()];
21114 assert_eq!(libsais64_plcp(text, &sa, &mut direct_plcp), 0);
21115 assert_eq!(libsais64_plcp_omp(text, &sa, &mut omp_plcp, 2), 0);
21116 assert_eq!(omp_plcp, direct_plcp);
21117 assert_eq!(libsais64_plcp_omp(text, &sa, &mut omp_plcp, -1), -1);
21118
21119 let mut direct_lcp = vec![0; text.len()];
21120 let mut omp_lcp = vec![0; text.len()];
21121 assert_eq!(libsais64_lcp(&direct_plcp, &sa, &mut direct_lcp), 0);
21122 assert_eq!(libsais64_lcp_omp(&direct_plcp, &sa, &mut omp_lcp, 2), 0);
21123 assert_eq!(omp_lcp, direct_lcp);
21124 assert_eq!(libsais64_lcp_omp(&direct_plcp, &sa, &mut omp_lcp, -1), -1);
21125
21126 let gsa_text = b"ban\0ana\0";
21127 let mut gsa = vec![0; gsa_text.len()];
21128 assert_eq!(libsais64_gsa(gsa_text, &mut gsa, 0, None), 0);
21129 let mut direct_gsa_plcp = vec![0; gsa_text.len()];
21130 let mut omp_gsa_plcp = vec![0; gsa_text.len()];
21131 assert_eq!(libsais64_plcp_gsa(gsa_text, &gsa, &mut direct_gsa_plcp), 0);
21132 assert_eq!(
21133 libsais64_plcp_gsa_omp(gsa_text, &gsa, &mut omp_gsa_plcp, 2),
21134 0
21135 );
21136 assert_eq!(omp_gsa_plcp, direct_gsa_plcp);
21137 assert_eq!(
21138 libsais64_plcp_gsa_omp(gsa_text, &gsa, &mut omp_gsa_plcp, -1),
21139 -1
21140 );
21141
21142 let int_text = vec![2, 1, 3, 1, 0];
21143 let mut int_text_for_sa = int_text.clone();
21144 let mut int_sa = vec![0; int_text.len()];
21145 assert_eq!(libsais64_int(&mut int_text_for_sa, &mut int_sa, 4, 0), 0);
21146 let mut direct_int_plcp = vec![0; int_text.len()];
21147 let mut omp_int_plcp = vec![0; int_text.len()];
21148 assert_eq!(
21149 libsais64_plcp_int(&int_text, &int_sa, &mut direct_int_plcp),
21150 0
21151 );
21152 assert_eq!(
21153 libsais64_plcp_int_omp(&int_text, &int_sa, &mut omp_int_plcp, 2),
21154 0
21155 );
21156 assert_eq!(omp_int_plcp, direct_int_plcp);
21157 assert_eq!(
21158 libsais64_plcp_int_omp(&int_text, &int_sa, &mut omp_int_plcp, -1),
21159 -1
21160 );
21161 }
21162
21163 #[test]
21164 fn public_libsais64_omp_frequency_wrappers_match_direct_calls() {
21165 let text = b"banana";
21166 let gsa_text = b"ban\0ana\0";
21167
21168 let mut direct_sa = vec![0; text.len()];
21169 let mut omp_sa = vec![0; text.len()];
21170 let mut direct_freq = vec![-1; ALPHABET_SIZE];
21171 let mut omp_freq = vec![-1; ALPHABET_SIZE];
21172 assert_eq!(
21173 libsais64(text, &mut direct_sa, 0, Some(&mut direct_freq)),
21174 0
21175 );
21176 assert_eq!(
21177 libsais64_omp(text, &mut omp_sa, 0, Some(&mut omp_freq), 2),
21178 0
21179 );
21180 assert_eq!(omp_sa, direct_sa);
21181 assert_eq!(omp_freq, direct_freq);
21182
21183 let mut direct_gsa = vec![0; gsa_text.len()];
21184 let mut omp_gsa = vec![0; gsa_text.len()];
21185 direct_freq.fill(-1);
21186 omp_freq.fill(-1);
21187 assert_eq!(
21188 libsais64_gsa(gsa_text, &mut direct_gsa, 0, Some(&mut direct_freq)),
21189 0
21190 );
21191 assert_eq!(
21192 libsais64_gsa_omp(gsa_text, &mut omp_gsa, 0, Some(&mut omp_freq), 2),
21193 0
21194 );
21195 assert_eq!(omp_gsa, direct_gsa);
21196 assert_eq!(omp_freq, direct_freq);
21197
21198 let mut direct_bwt = vec![0; text.len()];
21199 let mut direct_work = vec![0; text.len()];
21200 let mut omp_bwt = vec![0; text.len()];
21201 let mut omp_work = vec![0; text.len()];
21202 direct_freq.fill(-1);
21203 omp_freq.fill(-1);
21204 assert_eq!(
21205 libsais64_bwt(
21206 text,
21207 &mut direct_bwt,
21208 &mut direct_work,
21209 0,
21210 Some(&mut direct_freq)
21211 ),
21212 libsais64_bwt_omp(text, &mut omp_bwt, &mut omp_work, 0, Some(&mut omp_freq), 2)
21213 );
21214 assert_eq!(omp_bwt, direct_bwt);
21215 assert_eq!(omp_freq, direct_freq);
21216
21217 let mut direct_aux = vec![0; 2];
21218 let mut omp_aux = vec![0; 2];
21219 direct_freq.fill(-1);
21220 omp_freq.fill(-1);
21221 assert_eq!(
21222 libsais64_bwt_aux(
21223 text,
21224 &mut direct_bwt,
21225 &mut direct_work,
21226 0,
21227 Some(&mut direct_freq),
21228 4,
21229 &mut direct_aux
21230 ),
21231 libsais64_bwt_aux_omp(
21232 text,
21233 &mut omp_bwt,
21234 &mut omp_work,
21235 0,
21236 Some(&mut omp_freq),
21237 4,
21238 &mut omp_aux,
21239 2
21240 )
21241 );
21242 assert_eq!(omp_bwt, direct_bwt);
21243 assert_eq!(omp_aux, direct_aux);
21244 assert_eq!(omp_freq, direct_freq);
21245 }
21246
21247 #[test]
21248 fn public_libsais64_unbwt_omp_frequency_wrappers_match_direct_calls() {
21249 let text = b"abracadabra";
21250 let mut freq = vec![0; ALPHABET_SIZE];
21251 let mut bwt = vec![0; text.len()];
21252 let mut work = vec![0; text.len()];
21253 let primary = libsais64_bwt(text, &mut bwt, &mut work, 0, Some(&mut freq));
21254 assert!(primary >= 0);
21255
21256 let mut direct = vec![0; text.len()];
21257 let mut direct_work = vec![0; text.len() + 1];
21258 let mut omp = vec![0; text.len()];
21259 let mut omp_work = vec![0; text.len() + 1];
21260 assert_eq!(
21261 libsais64_unbwt(&bwt, &mut direct, &mut direct_work, Some(&freq), primary),
21262 libsais64_unbwt_omp(&bwt, &mut omp, &mut omp_work, Some(&freq), primary, 2)
21263 );
21264 assert_eq!(omp, direct);
21265 assert_eq!(omp, text);
21266
21267 let mut aux = vec![0; (text.len() - 1) / 4 + 1];
21268 assert_eq!(
21269 libsais64_bwt_aux(text, &mut bwt, &mut work, 0, Some(&mut freq), 4, &mut aux),
21270 0
21271 );
21272 direct.fill(0);
21273 direct_work.fill(0);
21274 omp.fill(0);
21275 omp_work.fill(0);
21276 assert_eq!(
21277 libsais64_unbwt_aux(&bwt, &mut direct, &mut direct_work, Some(&freq), 4, &aux),
21278 libsais64_unbwt_aux_omp(&bwt, &mut omp, &mut omp_work, Some(&freq), 4, &aux, 2)
21279 );
21280 assert_eq!(omp, direct);
21281 assert_eq!(omp, text);
21282 }
21283
21284 #[test]
21285 fn public_libsais64_bwt_aux_matches_upstream_c() {
21286 for text in [
21287 b"banana".as_slice(),
21288 b"mississippi",
21289 b"abracadabra",
21290 b"AAAAAAAAAAAAAAAA",
21291 b"zyxwvutsrqponmlk",
21292 ] {
21293 assert_libsais64_bwt_aux_matches_c(text, 4);
21294 }
21295 }
21296
21297 #[test]
21298 fn public_libsais64_frequency_outputs_match_upstream_c() {
21299 assert_libsais64_freq_outputs_match_c(b"banana", b"ban\0ana\0");
21300 }
21301
21302 #[test]
21303 fn public_libsais64_unbwt_with_frequency_matches_upstream_c() {
21304 assert_libsais64_unbwt_freq_matches_c(b"abracadabra");
21305 }
21306
21307 #[test]
21308 fn public_libsais64_unbwt_matches_upstream_c() {
21309 for text in [
21310 b"a".as_slice(),
21311 b"banana",
21312 b"mississippi",
21313 b"abracadabra",
21314 b"AAAAAAAAAAAAAAAA",
21315 b"zyxwvutsrqponmlk",
21316 ] {
21317 assert_libsais64_unbwt_matches_c(text);
21318 }
21319 }
21320
21321 #[test]
21322 fn public_libsais64_unbwt_aux_matches_upstream_c() {
21323 for text in [
21324 b"banana".as_slice(),
21325 b"mississippi",
21326 b"abracadabra",
21327 b"AAAAAAAAAAAAAAAA",
21328 b"zyxwvutsrqponmlk",
21329 ] {
21330 assert_libsais64_unbwt_aux_matches_c(text, 4);
21331 }
21332 }
21333
21334 #[test]
21335 fn public_libsais64_bwt_aux_round_trips() {
21336 for text in [
21337 b"banana".as_slice(),
21338 b"mississippi",
21339 b"abracadabra",
21340 b"AAAAAAAAAAAAAAAA",
21341 b"zyxwvutsrqponmlk",
21342 ] {
21343 assert_libsais64_bwt_aux_round_trips(text, 4);
21344 }
21345 }
21346
21347 fn deterministic_large_input(seed: u64, len: usize, alphabet: u32) -> Vec<u8> {
21348 let mut rng = seed | 1;
21349 (0..len)
21350 .map(|_| {
21351 rng = rng.wrapping_add(0x9e3779b97f4a7c15);
21352 let mut z = rng;
21353 z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
21354 z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
21355 z ^= z >> 31;
21356 (z % u64::from(alphabet)) as u8
21357 })
21358 .collect()
21359 }
21360
21361 #[test]
21364 fn libsais64_omp_threads_4_matches_single_thread_on_large_input() {
21365 let text = deterministic_large_input(0xBADF00D, 250_000, 8);
21366 let mut sa_single = vec![0 as SaSint; text.len() + 1];
21367 let mut sa_omp = vec![0 as SaSint; text.len() + 1];
21368 assert_eq!(libsais64(&text, &mut sa_single, 0, None), 0);
21369 assert_eq!(libsais64_omp(&text, &mut sa_omp, 0, None, 4), 0);
21370 assert_eq!(
21371 sa_single, sa_omp,
21372 "libsais64 threads=4 SA must match threads=1 SA"
21373 );
21374 }
21375
21376 #[test]
21378 fn libsais64_omp_does_not_deadlock_when_invoked_from_rayon_worker() {
21379 use rayon::prelude::*;
21380 let text = deterministic_large_input(0x5EED64, 100_000, 4);
21381 let inputs: Vec<&[u8]> = vec![&text, &text, &text, &text];
21382 let results: Vec<SaSint> = inputs
21383 .par_iter()
21384 .map(|t| {
21385 let mut sa = vec![0 as SaSint; t.len() + 1];
21386 libsais64_omp(t, &mut sa, 0, None, 4)
21387 })
21388 .collect();
21389 assert!(results.iter().all(|&rc| rc == 0));
21390 }
21391}