#[cfg(target_arch = "x86")]
use std::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
use std::alloc;
static BYTE_LUT: [u8; 128] = {
let mut lut = [0u8; 128];
lut[b'a' as usize] = 0b000;
lut[b'c' as usize] = 0b001;
lut[b't' as usize] = 0b010;
lut[b'g' as usize] = 0b011;
lut[b'n' as usize] = 0b100;
lut[b'A' as usize] = 0b000;
lut[b'C' as usize] = 0b001;
lut[b'T' as usize] = 0b010;
lut[b'G' as usize] = 0b011;
lut[b'N' as usize] = 0b100;
lut
};
static BITS_LUT: [u8; 5] = {
let mut lut = [0u8; 5];
lut[0b000] = b'A';
lut[0b001] = b'C';
lut[0b010] = b'T';
lut[0b011] = b'G';
lut[0b100] = b'N';
lut
};
pub fn n_to_bits2_lut(n: &[u8]) -> Vec<u64> {
let mut res = vec![0u64; (n.len() / 27) + if n.len() % 27 == 0 {0} else {1}];
let len = n.len() / 3;
unsafe {
for i in 0..len {
let idx = i * 3;
let res_offset = i / 9;
let res_shift = (i % 9) * 7;
let a = *BYTE_LUT.get_unchecked(*n.get_unchecked(idx) as usize);
let b = (*BYTE_LUT.get_unchecked(*n.get_unchecked(idx + 1) as usize)) * 5;
let c = (*BYTE_LUT.get_unchecked(*n.get_unchecked(idx + 2) as usize)) * 25;
let encoding = (a + b + c) as u64;
*res.get_unchecked_mut(res_offset) = *res.get_unchecked(res_offset) | (encoding << res_shift);
}
let leftover = n.len() % 3;
if leftover > 0 {
let idx = len * 3;
let res_offset = len / 9;
let res_shift = (len % 9) * 7;
let a = *BYTE_LUT.get_unchecked(*n.get_unchecked(idx) as usize);
let b = if leftover >= 2 {(*BYTE_LUT.get_unchecked(*n.get_unchecked(idx + 1) as usize)) * 5} else {0};
let encoding = (a + b) as u64;
*res.get_unchecked_mut(res_offset) = *res.get_unchecked(res_offset) | (encoding << res_shift);
}
}
res
}
pub fn bits_to_n2_lut(bits: &[u64], len: usize) -> Vec<u8> {
if len > (bits.len() * 27) {
panic!("The length is greater than the number of nucleotides!");
}
let triplets = len / 3 + if len % 3 == 0 {0} else {1};
unsafe {
let layout = alloc::Layout::from_size_align_unchecked(bits.len() * 27, 1);
let res_ptr = alloc::alloc(layout);
for i in 0..triplets {
let idx = (i * 3) as isize;
let offset = i / 9;
let shift = (i % 9) * 7;
let curr = (*bits.get_unchecked(offset) >> shift) & 0b01111111;
let a = (curr % 5) as usize;
let b = ((curr / 5) % 5) as usize;
let c = (curr / 25) as usize;
*res_ptr.offset(idx) = *BITS_LUT.get_unchecked(a);
*res_ptr.offset(idx + 1) = *BITS_LUT.get_unchecked(b);
*res_ptr.offset(idx + 2) = *BITS_LUT.get_unchecked(c);
}
Vec::from_raw_parts(res_ptr, len, bits.len() * 27)
}
}
union AlignedArray {
v: __m256i,
a: [u64; 4]
}
pub fn n_to_bits2_pext(n: &[u8]) -> Vec<u64> {
let mut ptr = n.as_ptr();
let end_idx = if n.len() < 5 {0} else {(n.len() - 5) / 27};
let len = (n.len() / 27) + if n.len() % 27 == 0 {0} else {1};
unsafe {
let layout = alloc::Layout::from_size_align_unchecked(len << 3, 8);
let res_ptr = alloc::alloc(layout) as *mut u64;
let lut = {
let mut lut = 0;
lut |= 0b000 << (((b'A' as i64) & 0b111) << 3);
lut |= 0b001 << (((b'C' as i64) & 0b111) << 3);
lut |= 0b010 << (((b'T' as i64) & 0b111) << 3);
lut |= 0b010 << (((b'U' as i64) & 0b111) << 3);
lut |= 0b011 << (((b'G' as i64) & 0b111) << 3);
lut |= 0b100 << (((b'N' as i64) & 0b111) << 3);
_mm256_set1_epi64x(lut)
};
let permute_mask = _mm256_set_epi32(6, 5, 4, 3, 3, 2, 1, 0);
let lo_shuffle_mask = _mm256_set_epi16(-1, -1, -1, -1, 0xFF1Cu16 as i16, 0xFF19u16 as i16, 0xFF16u16 as i16, 0xFF13u16 as i16,
-1, -1, -1, 0xFF0Cu16 as i16, 0xFF09u16 as i16, 0xFF06u16 as i16, 0xFF03u16 as i16, 0xFF00u16 as i16);
let hi_shuffle_mask = _mm256_set_epi16(-1, -1, -1, -1, 0x1E1Du16 as i16, 0x1B1Au16 as i16, 0x1817u16 as i16, 0x1514u16 as i16,
-1, -1, -1, 0x0E0Du16 as i16, 0x0B0Au16 as i16, 0x0807u16 as i16, 0x0504u16 as i16, 0x0201u16 as i16);
let mul_25_5 = _mm256_set1_epi16(0x1905); let pack_right_mask = 0x007F007F007F007Fu64;
let mut arr = [AlignedArray{v: _mm256_undefined_si256()}, AlignedArray{v: _mm256_undefined_si256()}];
for i in 0..end_idx as isize {
let v = _mm256_loadu_si256(ptr as *const __m256i);
let v = _mm256_shuffle_epi8(lut, v);
let v = _mm256_permutevar8x32_epi32(v, permute_mask);
let a = _mm256_shuffle_epi8(v, lo_shuffle_mask);
let b = _mm256_shuffle_epi8(v, hi_shuffle_mask);
let b = _mm256_maddubs_epi16(b, mul_25_5);
let arr_idx = (i as usize) & 1;
(*arr.get_unchecked_mut(arr_idx)).v = _mm256_add_epi16(a, b);
let a = _pext_u64((*arr.get_unchecked(arr_idx)).a[0], pack_right_mask);
let b = (*arr.get_unchecked(arr_idx)).a[1];
let c = _pext_u64((*arr.get_unchecked(arr_idx)).a[2], pack_right_mask);
*res_ptr.offset(i) = a | (b << 28) | (c << 35);
ptr = ptr.offset(27);
}
if end_idx < len {
let end = n_to_bits2_lut(&n[(end_idx * 27)..]);
for i in 0..end.len() {
*res_ptr.offset((end_idx + i) as isize) = *end.get_unchecked(i);
}
}
Vec::from_raw_parts(res_ptr, len, len)
}
}
pub fn bits_to_n2_pdep(bits: &[u64], len: usize) -> Vec<u8> {
if len > (bits.len() * 27) {
panic!("The length is greater than the number of nucleotides!");
}
unsafe {
let layout = alloc::Layout::from_size_align_unchecked(bits.len() * 27 + 5, 32);
let res_ptr = alloc::alloc(layout);
let mut ptr = res_ptr;
let deposit_mask = 0x7F7F7F7F7F7F7F7Fu64; let shuffle_mask = _mm256_set_epi16(-1, -1, -1, 0xFF04u16 as i16, 0xFF03u16 as i16, 0xFF02u16 as i16, 0xFF01u16 as i16, 0xFF00u16 as i16,
-1, -1, -1, -1, 0xFF03u16 as i16, 0xFF02u16 as i16, 0xFF01u16 as i16, 0xFF00u16 as i16);
let mul5 = _mm256_set1_epi16(5);
let div5 = _mm256_set1_epi16(((1u32 << 16) / 5 + 1) as i16);
let div25 = _mm256_set1_epi16(((1u32 << 16) / 25 + 1) as i16);
let a_shuffle_mask = _mm256_set_epi64x(0xFFFFFF08FFFF06FFu64 as i64, 0xFF04FFFF02FFFF00u64 as i64, 0xFFFFFF08FFFF06FFu64 as i64, 0xFF04FFFF02FFFF00u64 as i64);
let b_shuffle_mask = _mm256_set_epi64x(0xFFFF08FFFF06FFFFu64 as i64, 0x04FFFF02FFFF00FFu64 as i64, 0xFFFF08FFFF06FFFFu64 as i64, 0x04FFFF02FFFF00FFu64 as i64);
let c_shuffle_mask = _mm256_set_epi64x(0xFF08FFFF06FFFF04u64 as i64, 0xFFFF02FFFF00FFFFu64 as i64, 0xFF08FFFF06FFFF04u64 as i64, 0xFFFF02FFFF00FFFFu64 as i64);
let permute_mask = _mm256_set_epi32(7, 7, 6, 5, 4, 2, 1, 0);
let lut = {
let mut lut = 0;
lut |= (b'A' as i64) << 0;
lut |= (b'C' as i64) << 8;
lut |= (b'T' as i64) << 16;
lut |= (b'G' as i64) << 24;
lut |= (b'N' as i64) << 32;
_mm256_set1_epi64x(lut)
};
for i in 0..bits.len() {
let curr = *bits.get_unchecked(i) as i64;
let a = _pdep_u64(curr as u64, deposit_mask) as i64;
let b = ((curr >> 56) << 32) | (a >> 32);
let v = _mm256_set_epi64x(0, b, 0, a);
let v = _mm256_shuffle_epi8(v, shuffle_mask);
let v_rem5 = _mm256_mullo_epi16(v, div5);
let v_rem25 = _mm256_mullo_epi16(v, div25);
let a = _mm256_mulhi_epu16(v_rem5, mul5);
let b = _mm256_mulhi_epu16(v_rem25, mul5);
let c = _mm256_mulhi_epu16(v, div25);
let a = _mm256_shuffle_epi8(a, a_shuffle_mask);
let b = _mm256_shuffle_epi8(b, b_shuffle_mask);
let c = _mm256_shuffle_epi8(c, c_shuffle_mask);
let ab = _mm256_or_si256(a, b);
let abc = _mm256_or_si256(ab, c);
let v = _mm256_permutevar8x32_epi32(abc, permute_mask);
let v = _mm256_shuffle_epi8(lut, v);
_mm256_storeu_si256(ptr as *mut __m256i, v);
ptr = ptr.offset(27);
}
Vec::from_raw_parts(res_ptr, len, bits.len() * 27 + 5)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_n_to_bits2_lut() {
assert_eq!(n_to_bits2_lut(b"ATCGNATCGNATCGNATCGNATCGNATCGNATCGN"),
vec![0b11011010100100010111010001111101000110110101001000101110100011, 0b1011101000111110100]);
assert_eq!(n_to_bits2_lut(b"ATCGN"), vec![0b101110100011]);
}
#[test]
fn test_bits_to_n2_lut() {
assert_eq!(bits_to_n2_lut(&vec![0b11011010100100010111010001111101000110110101001000101110100011, 0b1011101000111110100], 35),
"ATCGNATCGNATCGNATCGNATCGNATCGNATCGN".as_bytes());
}
#[test]
fn test_n_to_bits2_pext() {
assert_eq!(n_to_bits2_pext(b"ATCGNATCGNATCGNATCGNATCGNATCGNATCGN"),
vec![0b11011010100100010111010001111101000110110101001000101110100011, 0b1011101000111110100]);
assert_eq!(n_to_bits2_pext(b"ATCGN"), vec![0b101110100011]);
}
#[test]
fn test_bits_to_n2_pdep() {
assert_eq!(bits_to_n2_pdep(&vec![0b11011010100100010111010001111101000110110101001000101110100011, 0b1011101000111110100], 35),
"ATCGNATCGNATCGNATCGNATCGNATCGNATCGN".as_bytes());
}
}