use alloc::vec;
use alloc::vec::Vec;
const EMPTY: u32 = u32::MAX;
const NAIVE_THRESHOLD: usize = 10;
trait Symbol: Copy + Ord {
const WIDE: bool;
fn index(self) -> usize;
}
impl Symbol for u8 {
const WIDE: bool = false;
#[inline]
fn index(self) -> usize {
usize::from(self)
}
}
impl Symbol for u32 {
const WIDE: bool = true;
#[inline]
fn index(self) -> usize {
self as usize
}
}
const PREFETCH_DISTANCE: usize = 32;
#[inline(always)]
fn prefetch_ahead<T: Symbol>(s: &[T], sa: &[u32], buf: &[u32], near: usize, far: usize) {
let n = sa.len();
if far < n {
let pos = unsafe { *sa.get_unchecked(far) } as usize;
crate::decoding::prefetch::prefetch_l1_at(
s.as_ptr().wrapping_add(pos.wrapping_sub(1)).cast(),
);
}
if T::WIDE && near < n {
let near = unsafe { *sa.get_unchecked(near) } as usize;
let at = near.wrapping_sub(1).min(n - 1);
let c = unsafe { s.get_unchecked(at) }.index();
crate::decoding::prefetch::prefetch_l1_at(buf.as_ptr().wrapping_add(c).cast());
}
}
pub(crate) fn suffix_array(text: &[u8]) -> Vec<u32> {
assert!(
text.len() < 1 << 31,
"a suffix array of {} bytes does not fit 31-bit positions",
text.len()
);
let mut sa = vec![0u32; text.len()];
sa_is(text, &mut sa, usize::from(u8::MAX));
sa
}
struct Types {
words: Vec<u64>,
}
impl Types {
fn new<T: Symbol>(s: &[T]) -> Self {
let n = s.len();
let mut words = vec![0u64; n.div_ceil(64)];
let mut next = false;
for i in (0..n - 1).rev() {
let small = if s[i] == s[i + 1] {
next
} else {
s[i] < s[i + 1]
};
words[i / 64] |= u64::from(small) << (i % 64);
next = small;
}
Self { words }
}
#[inline]
fn is_s(&self, i: usize) -> bool {
(self.words[i / 64] >> (i % 64)) & 1 != 0
}
#[inline]
fn is_lms(&self, p: usize, n: usize) -> bool {
p > 0 && p < n && self.is_s(p) && !self.is_s(p - 1)
}
}
fn bucket_starts(counts: &[u32], buf: &mut [u32]) {
let mut sum = 0;
for (slot, &count) in buf.iter_mut().zip(counts) {
*slot = sum;
sum += count;
}
}
fn bucket_ends(counts: &[u32], buf: &mut [u32]) {
let mut sum = 0;
for (slot, &count) in buf.iter_mut().zip(counts) {
sum += count;
*slot = sum;
}
}
fn sa_is<T: Symbol>(s: &[T], space: &mut [u32], upper: usize) {
let n = s.len();
let (sa, free) = space.split_at_mut(n);
match n {
0 => return,
1 => {
sa[0] = 0;
return;
}
2 => {
sa.copy_from_slice(if s[0] < s[1] { &[0, 1] } else { &[1, 0] });
return;
}
_ => {}
}
if n < NAIVE_THRESHOLD {
for (at, slot) in sa.iter_mut().enumerate() {
*slot = at as u32;
}
sa.sort_unstable_by(|&a, &b| s[a as usize..].cmp(&s[b as usize..]));
return;
}
let types = Types::new(s);
let k = upper + 1;
let mut owned: Vec<u32>;
let (counts, buf) = if free.len() >= 2 * k {
let (counts, rest) = free.split_at_mut(k);
counts.fill(0);
(counts, &mut rest[..k])
} else {
owned = vec![0u32; 2 * k];
owned.split_at_mut(k)
};
for &c in s {
counts[c.index()] += 1;
}
let counts = &*counts;
sa.fill(0);
bucket_ends(counts, buf);
let mut m = 0;
for p in (1..n).rev() {
if types.is_lms(p, n) {
let c = s[p].index();
buf[c] -= 1;
sa[buf[c] as usize] = p as u32;
m += 1;
}
}
induce(s, sa, counts, buf);
if m == 0 {
return;
}
let mut k = 0;
for i in 0..n {
let v = sa[i] as usize;
if types.is_lms(v, n) {
sa[k] = v as u32;
k += 1;
}
}
debug_assert_eq!(k, m);
sa[m..].fill(EMPTY);
let substring_end = |p: usize| {
let mut end = p + 1;
while end < n && !types.is_lms(end, n) {
end += 1;
}
end
};
let mut name = 0u32;
let mut prev = sa[0] as usize;
let mut prev_end = substring_end(prev);
debug_assert!(m + prev / 2 < n);
sa[m + prev / 2] = 0;
for i in 1..m {
let cur = sa[i] as usize;
let cur_end = substring_end(cur);
let same = cur_end - cur == prev_end - prev
&& cur_end < n
&& prev_end < n
&& s[cur..=cur_end] == s[prev..=prev_end];
if !same {
name += 1;
}
debug_assert!(m + cur / 2 < n);
sa[m + cur / 2] = name;
prev = cur;
prev_end = cur_end;
}
let mut j = n;
for i in (m..n).rev() {
if sa[i] != EMPTY {
j -= 1;
sa[j] = sa[i];
}
}
debug_assert_eq!(j, n - m);
{
let (front, reduced) = sa.split_at_mut(n - m);
if name as usize + 1 == m {
for (at, &rank) in reduced.iter().enumerate() {
front[rank as usize] = at as u32;
}
} else {
sa_is(&*reduced, front, name as usize);
}
let order = &mut front[..m];
let mut at = 0;
for p in 1..n {
if types.is_lms(p, n) {
reduced[at] = p as u32;
at += 1;
}
}
debug_assert_eq!(at, m);
for slot in order.iter_mut() {
*slot = reduced[*slot as usize];
}
}
bucket_ends(counts, buf);
let mut i = m;
let mut j = n;
while i > 0 {
let mut p = sa[i - 1];
let c = s[p as usize].index();
let end = buf[c] as usize;
while end < j {
j -= 1;
sa[j] = 0;
}
loop {
debug_assert!(j >= i);
j -= 1;
sa[j] = p;
i -= 1;
if i == 0 {
break;
}
p = sa[i - 1];
if s[p as usize].index() != c {
break;
}
}
}
sa[..j].fill(0);
induce(s, sa, counts, buf);
}
fn induce<T: Symbol>(s: &[T], sa: &mut [u32], counts: &[u32], buf: &mut [u32]) {
let n = s.len();
let live = |v: u32| (v as i32) > 0;
bucket_starts(counts, buf);
let last = n - 1;
let mut c1 = s[last].index();
let mut b = buf[c1] as usize;
sa[b] = if s[last - 1] < s[last] {
!(last as u32)
} else {
last as u32
};
b += 1;
for i in 0..n {
prefetch_ahead(s, sa, buf, i + PREFETCH_DISTANCE, i + 2 * PREFETCH_DISTANCE);
let v = unsafe { *sa.get_unchecked(i) };
unsafe { *sa.get_unchecked_mut(i) = !v };
if live(v) {
let j = v as usize - 1;
let c0 = unsafe { s.get_unchecked(j) }.index();
if c0 != c1 {
debug_assert!(c0 < buf.len() && c1 < buf.len());
unsafe {
*buf.get_unchecked_mut(c1) = b as u32;
b = *buf.get_unchecked(c0) as usize;
}
c1 = c0;
}
let dead = j > 0 && unsafe { s.get_unchecked(j - 1) }.index() < c1;
debug_assert!(b < n);
unsafe { *sa.get_unchecked_mut(b) = if dead { !(j as u32) } else { j as u32 } };
b += 1;
}
}
bucket_ends(counts, buf);
let mut c1 = 0usize;
let mut b = buf[0] as usize;
for i in (0..n).rev() {
prefetch_ahead(
s,
sa,
buf,
i.wrapping_sub(PREFETCH_DISTANCE),
i.wrapping_sub(2 * PREFETCH_DISTANCE),
);
let v = unsafe { *sa.get_unchecked(i) };
if live(v) {
let j = v as usize - 1;
let c0 = unsafe { s.get_unchecked(j) }.index();
if c0 != c1 {
debug_assert!(c0 < buf.len() && c1 < buf.len());
unsafe {
*buf.get_unchecked_mut(c1) = b as u32;
b = *buf.get_unchecked(c0) as usize;
}
c1 = c0;
}
let dead = j == 0 || unsafe { s.get_unchecked(j - 1) }.index() > c1;
debug_assert!(b > 0);
b -= 1;
unsafe { *sa.get_unchecked_mut(b) = if dead { !(j as u32) } else { j as u32 } };
} else {
unsafe { *sa.get_unchecked_mut(i) = !v };
}
}
}
#[cfg(test)]
mod tests;