#![allow(non_snake_case)]
use crate::Error;
use crate::sigma::Sigma;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg(test)]
enum LSType {
L,
S,
}
struct TypeBits {
bits: Vec<u64>,
lms_bits: Vec<u64>,
}
struct LmsPositions<'a> {
bits: &'a [u64],
word_idx: usize,
word: u64,
}
struct LmsPositionsRev<'a> {
bits: &'a [u64],
word_idx: usize,
word: u64,
}
impl Iterator for LmsPositions<'_> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.word != 0 {
let bit = self.word.trailing_zeros() as usize;
self.word &= self.word - 1;
return Some((self.word_idx - 1) * 64 + bit);
}
self.word = *self.bits.get(self.word_idx)?;
self.word_idx += 1;
}
}
}
impl Iterator for LmsPositionsRev<'_> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.word != 0 {
let bit = 63 - self.word.leading_zeros() as usize;
self.word &= !(1u64 << bit);
return Some((self.word_idx << 6) + bit);
}
if self.word_idx == 0 {
return None;
}
self.word_idx -= 1;
self.word = unsafe { *self.bits.get_unchecked(self.word_idx) };
}
}
}
impl TypeBits {
fn new(len: usize) -> Self {
Self {
bits: vec![0u64; len.div_ceil(64)],
lms_bits: vec![0u64; len.div_ceil(64)],
}
}
#[inline(always)]
fn is_s(&self, idx: usize) -> bool {
unsafe { ((*self.bits.get_unchecked(idx >> 6) >> (idx & 63)) & 1) != 0 }
}
#[inline(always)]
fn is_l(&self, idx: usize) -> bool {
!self.is_s(idx)
}
#[inline(always)]
fn is_lms(&self, idx: usize) -> bool {
unsafe { ((*self.lms_bits.get_unchecked(idx >> 6) >> (idx & 63)) & 1) != 0 }
}
fn lms_positions(&self) -> LmsPositions<'_> {
LmsPositions {
bits: &self.lms_bits,
word_idx: 0,
word: 0,
}
}
fn lms_positions_rev(&self) -> LmsPositionsRev<'_> {
LmsPositionsRev {
bits: &self.lms_bits,
word_idx: self.lms_bits.len(),
word: 0,
}
}
}
trait Symbol: Copy + Eq {
fn to_usize(self) -> usize;
}
impl Symbol for u8 {
#[inline(always)]
fn to_usize(self) -> usize {
self as usize
}
}
impl Symbol for u16 {
#[inline(always)]
fn to_usize(self) -> usize {
self as usize
}
}
impl Symbol for u32 {
#[inline(always)]
fn to_usize(self) -> usize {
self as usize
}
}
impl Symbol for usize {
#[inline(always)]
fn to_usize(self) -> usize {
self
}
}
trait Index: Symbol + Default {
const MAX: Self;
fn from_usize(x: usize) -> Self;
}
impl Index for usize {
const MAX: Self = usize::MAX;
#[inline(always)]
fn from_usize(x: usize) -> Self {
x
}
}
impl Index for u32 {
const MAX: Self = u32::MAX;
#[inline(always)]
fn from_usize(x: usize) -> Self {
debug_assert!(x < u32::MAX as usize);
x as u32
}
}
#[cfg(test)]
fn get_types<Sym: Symbol>(S: &[Sym]) -> Vec<LSType> {
let mut prev = (LSType::S, 0usize);
let mut types = vec![LSType::S; S.len()];
for i in (0..S.len()).rev() {
let s = S[i].to_usize();
prev = if s < prev.1 || (s == prev.1 && prev.0 == LSType::S) {
(LSType::S, s)
} else {
(LSType::L, s)
};
types[i] = prev.0;
}
types
}
fn get_type_bits<Sym: Symbol>(S: &[Sym]) -> TypeBits {
let mut prev_is_s = true;
let mut prev_symbol = 0usize;
let mut types = TypeBits::new(S.len());
if S.is_empty() {
return types;
}
let mut word_idx = (S.len() - 1) >> 6;
let mut word = 0u64;
let mut lms_word_idx = usize::MAX;
let mut lms_word = 0u64;
for i in (0..S.len()).rev() {
let next_word_idx = i >> 6;
if next_word_idx != word_idx {
types.bits[word_idx] = word;
word_idx = next_word_idx;
word = 0;
}
let symbol = S[i].to_usize();
let is_s = symbol < prev_symbol || (symbol == prev_symbol && prev_is_s);
if is_s {
word |= 1u64 << (i & 63);
}
if !is_s && prev_is_s {
let lms_idx = i + 1;
let next_lms_word_idx = lms_idx >> 6;
if next_lms_word_idx != lms_word_idx {
if lms_word_idx != usize::MAX {
types.lms_bits[lms_word_idx] = lms_word;
}
lms_word_idx = next_lms_word_idx;
lms_word = 0;
}
lms_word |= 1u64 << (lms_idx & 63);
}
prev_is_s = is_s;
prev_symbol = symbol;
}
types.bits[word_idx] = word;
if lms_word_idx != usize::MAX {
types.lms_bits[lms_word_idx] = lms_word;
}
types
}
#[cfg(test)]
#[inline(always)]
fn is_lms(T: &[LSType], i: usize) -> bool {
i > 0 && unsafe { *T.get_unchecked(i - 1) == LSType::L && *T.get_unchecked(i) == LSType::S }
}
fn induce_L<Sym: Symbol, Idx: Index, Bucket: Index>(
bucket_starts: &[Bucket],
S: &[Sym],
SA: &mut [Idx],
T: &TypeBits,
buckets: &mut [Bucket],
) -> Result<(), Error> {
buckets.copy_from_slice(bucket_starts);
for i in 0..S.len() {
let j = unsafe { SA.get_unchecked(i).to_usize().wrapping_sub(1) };
if j < S.len() && T.is_l(j) {
let symbol = unsafe { S.get_unchecked(j).to_usize() };
let bucket = unsafe { buckets.get_unchecked_mut(symbol) };
let dest = bucket.to_usize();
unsafe {
*SA.get_unchecked_mut(dest) = Idx::from_usize(j);
}
*bucket = Bucket::from_usize(dest + 1);
}
}
Ok(())
}
fn induce_S<Sym: Symbol, Idx: Index, Bucket: Index>(
bucket_limits: &[Bucket],
S: &[Sym],
SA: &mut [Idx],
T: &TypeBits,
buckets: &mut [Bucket],
) -> Result<(), Error> {
buckets.copy_from_slice(bucket_limits);
for i in (0..S.len()).rev() {
let j = unsafe { SA.get_unchecked(i).to_usize().wrapping_sub(1) };
if j < S.len() && T.is_s(j) {
let symbol = unsafe { S.get_unchecked(j).to_usize() };
let dest = unsafe {
let bucket = buckets.get_unchecked_mut(symbol);
let dest = bucket.to_usize() - 1;
*bucket = Bucket::from_usize(dest);
dest
};
unsafe {
*SA.get_unchecked_mut(dest) = Idx::from_usize(j);
}
}
}
Ok(())
}
fn bucket_starts_and_limits<Sym: Symbol, Idx: Index>(
k: usize,
s: &[Sym],
) -> Result<(Vec<Idx>, Vec<Idx>), Error> {
let mut bucket_limits = vec![Idx::from_usize(0); k];
for &symbol in s {
let symbol = symbol.to_usize();
if symbol >= k {
return Err(Error::InvalidSigma);
}
bucket_limits[symbol] = Idx::from_usize(bucket_limits[symbol].to_usize() + 1);
}
let mut sum = 0usize;
for limit in bucket_limits.iter_mut() {
sum += limit.to_usize();
*limit = Idx::from_usize(sum);
}
let mut bucket_starts = vec![Idx::from_usize(0); k];
let mut prev = 0usize;
for (idx, limit) in bucket_limits.iter().copied().enumerate() {
bucket_starts[idx] = Idx::from_usize(prev);
prev = limit.to_usize();
}
Ok((bucket_starts, bucket_limits))
}
fn sais_impl<Sym: Symbol, Idx: Index, Bucket: Index>(
bucket_starts: &[Bucket],
bucket_limits: &[Bucket],
S: &[Sym],
SA: &mut [Idx],
) -> Result<(), Error> {
assert_eq!(bucket_starts.len(), bucket_limits.len());
assert!(
bucket_limits
.last()
.copied()
.map(|x| x.to_usize())
.unwrap_or(0)
== S.len()
);
assert!(bucket_starts.len() <= S.len());
assert!(S.len() == SA.len());
assert!(!S.is_empty());
assert_eq!(S[S.len() - 1].to_usize(), 0);
if S.iter()
.any(|symbol| symbol.to_usize() >= bucket_starts.len())
{
return Err(Error::InvalidSigma);
}
let T = get_type_bits(S);
let mut buckets = bucket_limits.to_vec();
buckets.copy_from_slice(bucket_limits);
SA.fill(Index::MAX);
for i in T.lms_positions() {
let symbol = unsafe { S.get_unchecked(i).to_usize() };
let dest = unsafe {
let bucket = buckets.get_unchecked_mut(symbol);
let dest = bucket.to_usize() - 1;
*bucket = Bucket::from_usize(dest);
dest
};
unsafe {
*SA.get_unchecked_mut(dest) = Idx::from_usize(i);
}
}
induce_L(bucket_starts, S, SA, &T, &mut buckets)?;
induce_S(bucket_limits, S, SA, &T, &mut buckets)?;
let mut substrings = 0usize;
for i in 0..S.len() {
let pos = unsafe { SA.get_unchecked(i).to_usize() };
if T.is_lms(pos) {
let value = unsafe { *SA.get_unchecked(i) };
unsafe {
*SA.get_unchecked_mut(substrings) = value;
}
substrings += 1;
}
}
SA[substrings..].fill(Index::MAX);
let mut previous_lms = usize::MAX;
for i in T.lms_positions() {
if previous_lms != usize::MAX {
unsafe {
*SA.get_unchecked_mut(substrings + previous_lms / 2) =
Idx::from_usize(i - previous_lms + 1);
}
}
previous_lms = i;
}
if previous_lms != usize::MAX {
unsafe {
*SA.get_unchecked_mut(substrings + previous_lms / 2) =
Idx::from_usize(S.len() - previous_lms);
}
}
let mut name = 0usize;
let mut prev = usize::MAX;
let mut prev_len = 0usize;
for i in 0..substrings {
let pos: usize = unsafe { SA.get_unchecked(i).to_usize() };
let pos_len = unsafe { SA.get_unchecked(substrings + pos / 2).to_usize() };
let mut diff = prev == usize::MAX;
if !diff {
for d in 0..pos_len.min(prev_len) {
let pos_d = pos + d;
let prev_d = prev + d;
if unsafe {
S.get_unchecked(pos_d) != S.get_unchecked(prev_d)
|| T.is_s(pos_d) != T.is_s(prev_d)
} {
diff = true;
break;
}
}
}
if diff {
name += 1;
prev = pos;
prev_len = pos_len;
}
let pos = pos / 2;
SA[substrings + pos] = Idx::from_usize(name - 1);
}
let mut coalesce = S.len();
for i in T.lms_positions_rev() {
coalesce -= 1;
unsafe {
*SA.get_unchecked_mut(coalesce) = *SA.get_unchecked(substrings + i / 2);
}
}
debug_assert_eq!(coalesce, S.len() - substrings);
let (SA1, S1) = SA.split_at_mut(substrings);
let (_, S1) = S1.split_at_mut(S1.len() - substrings);
if name < substrings {
let (bucket_starts1, bucket_limits1) = bucket_starts_and_limits::<_, Bucket>(name, S1)?;
sais_impl(&bucket_starts1, &bucket_limits1, S1, SA1)?;
} else {
for i in 0..S1.len() {
SA1[S1[i].to_usize()] = Idx::from_usize(i);
}
}
let mut j = 0;
for i in T.lms_positions() {
unsafe {
*S1.get_unchecked_mut(j) = Idx::from_usize(i);
}
j += 1;
}
for i in 0..substrings {
let value = unsafe { *S1.get_unchecked(SA1.get_unchecked(i).to_usize()) };
unsafe {
*SA1.get_unchecked_mut(i) = value;
}
}
SA[substrings..].fill(Index::MAX);
buckets.copy_from_slice(bucket_limits);
for i in (0..substrings).rev() {
j = unsafe { SA.get_unchecked(i).to_usize() };
unsafe {
*SA.get_unchecked_mut(i) = Index::MAX;
}
let symbol = unsafe { S.get_unchecked(j).to_usize() };
let dest = unsafe {
let bucket = buckets.get_unchecked_mut(symbol);
let dest = bucket.to_usize() - 1;
*bucket = Bucket::from_usize(dest);
dest
};
unsafe {
*SA.get_unchecked_mut(dest) = Idx::from_usize(j);
}
}
induce_L(bucket_starts, S, SA, &T, &mut buckets)?;
induce_S(bucket_limits, S, SA, &T, &mut buckets)?;
Ok(())
}
pub fn sais(sigma: &Sigma, S: &[u32], SA: &mut [usize]) -> Result<(), Error> {
assert!(sigma.K() <= S.len());
assert!(S.len() == SA.len());
assert!(!S.is_empty());
assert_eq!(S[S.len() - 1], 0);
let mut bucket_starts = vec![0usize; sigma.K()];
sigma.bucket_starts(&mut bucket_starts)?;
let mut bucket_limits = vec![0usize; sigma.K()];
sigma.bucket_limits(&mut bucket_limits)?;
sais_impl(&bucket_starts, &bucket_limits, S, SA)
}
fn sais_u32_index<Sym: Symbol>(sigma: &Sigma, S: &[Sym], SA: &mut [u32]) -> Result<(), Error> {
assert!(S.len() < u32::MAX as usize);
assert!(sigma.K() <= S.len());
assert!(S.len() == SA.len());
assert!(!S.is_empty());
assert_eq!(S[S.len() - 1].to_usize(), 0);
let mut bucket_starts_usize = vec![0usize; sigma.K()];
sigma.bucket_starts(&mut bucket_starts_usize)?;
let bucket_starts: Vec<u32> = bucket_starts_usize
.into_iter()
.map(u32::try_from)
.collect::<Result<_, _>>()?;
let mut bucket_limits_usize = vec![0usize; sigma.K()];
sigma.bucket_limits(&mut bucket_limits_usize)?;
let bucket_limits: Vec<u32> = bucket_limits_usize
.into_iter()
.map(u32::try_from)
.collect::<Result<_, _>>()?;
sais_impl(&bucket_starts, &bucket_limits, S, SA)
}
pub fn sais_u8_u32(sigma: &Sigma, S: &[u8], SA: &mut [u32]) -> Result<(), Error> {
sais_u32_index(sigma, S, SA)
}
pub fn sais_u16_u32(sigma: &Sigma, S: &[u16], SA: &mut [u32]) -> Result<(), Error> {
sais_u32_index(sigma, S, SA)
}
pub fn sais_u32(sigma: &Sigma, S: &[u32], SA: &mut [u32]) -> Result<(), Error> {
sais_u32_index(sigma, S, SA)
}
#[cfg(test)]
mod tests {
use buffertk::Unpackable;
use super::super::test_cases_for;
use super::super::test_util::TestCase;
use super::*;
fn sigma_for_text(text: &[u32]) -> Vec<u8> {
let mut buf = Vec::new();
let mut builder = crate::builder::Builder::new(&mut buf);
Sigma::construct(text.iter().copied(), &mut builder).expect("sigma should construct");
drop(builder);
buf
}
fn naive_suffix_array<Sym: Ord>(S: &[Sym]) -> Vec<usize> {
let mut SA: Vec<usize> = (0..S.len()).collect();
SA.sort_by(|&lhs, &rhs| S[lhs..].cmp(&S[rhs..]));
SA
}
fn check_sais_u8_u32(t: &TestCase) {
let sigma = t.sigma();
let sigma = Sigma::unpack(&sigma).expect("test should unpack").0;
let S: Vec<u8> = t.S.iter().map(|x| u8::try_from(*x).unwrap()).collect();
let mut SA = vec![0u32; t.S.len()];
sais_u8_u32(&sigma, &S, &mut SA).unwrap();
let expected: Vec<u32> = t.SA.iter().map(|x| *x as u32).collect();
assert_eq!(expected, SA);
}
test_cases_for!(sais_u8_u32, super::check_sais_u8_u32);
fn check_sais_u16_u32(t: &TestCase) {
let sigma = t.sigma();
let sigma = Sigma::unpack(&sigma).expect("test should unpack").0;
let S: Vec<u16> = t.S.iter().map(|x| u16::try_from(*x).unwrap()).collect();
let mut SA = vec![0u32; t.S.len()];
sais_u16_u32(&sigma, &S, &mut SA).unwrap();
let expected: Vec<u32> = t.SA.iter().map(|x| *x as u32).collect();
assert_eq!(expected, SA);
}
test_cases_for!(sais_u16_u32, super::check_sais_u16_u32);
fn check_sais_u32(t: &TestCase) {
let sigma = t.sigma();
let sigma = Sigma::unpack(&sigma).expect("test should unpack").0;
let mut SA = vec![0u32; t.S.len()];
sais_u32(&sigma, t.S, &mut SA).unwrap();
let expected: Vec<u32> = t.SA.iter().map(|x| *x as u32).collect();
assert_eq!(expected, SA);
}
test_cases_for!(sais_u32, super::check_sais_u32);
#[test]
fn invalid_symbol_rejected() {
let sigma = sigma_for_text(&[1, 2]);
let sigma = Sigma::unpack(&sigma).expect("test should unpack").0;
let mut SA = vec![0usize; 3];
assert_eq!(Err(Error::InvalidSigma), sais(&sigma, &[1, 3, 0], &mut SA));
let mut SA = vec![0u32; 3];
assert_eq!(
Err(Error::InvalidSigma),
sais_u8_u32(&sigma, &[1, 3, 0], &mut SA)
);
}
#[test]
fn u16_symbols_above_u8() {
let text: Vec<u32> = (0..600).map(|idx| idx * 37 % 300 + 1).collect();
let sigma = sigma_for_text(&text);
let sigma = Sigma::unpack(&sigma).expect("test should unpack").0;
let mut S: Vec<u16> = text
.iter()
.map(|t| sigma.char_to_sigma(*t).unwrap() as u16)
.collect();
S.push(0);
let mut SA = vec![0u32; S.len()];
sais_u16_u32(&sigma, &S, &mut SA).unwrap();
let expected: Vec<u32> = naive_suffix_array(&S)
.into_iter()
.map(|x| x as u32)
.collect();
assert_eq!(expected, SA);
}
fn check_get_types(t: &TestCase) {
let types: Vec<LSType> = t
.lstype
.chars()
.map(|c| if c == 'L' { LSType::L } else { LSType::S })
.collect();
let returned = get_types(t.S);
assert_eq!(&types, &returned);
}
test_cases_for!(get_types, super::check_get_types);
fn check_is_lms(t: &TestCase) {
let types = get_types(t.S);
let returned: String = types
.iter()
.enumerate()
.map(|(i, _)| if is_lms(&types, i) { '*' } else { ' ' })
.collect();
assert_eq!(t.lmspos, returned);
}
test_cases_for!(is_lms, super::check_is_lms);
fn check_sais(t: &TestCase) {
let sigma = t.sigma();
let sigma = Sigma::unpack(&sigma).expect("test should unpack").0;
let mut SA = vec![0usize; t.S.len()];
sais(&sigma, t.S, &mut SA).unwrap();
assert_eq!(t.SA, SA);
}
test_cases_for!(sais, super::check_sais);
}