use std::sync::LazyLock;
pub struct LookupTables {
pub nt16: [u8; 256],
pub nt6: [u8; 256],
pub comp: [u8; 256],
pub bitcnt: [u8; 16],
pub nt16_str: [u8; 16],
}
impl LookupTables {
pub fn new() -> Self {
Self {
nt16: init_nt16_table(),
nt6: init_nt6_table(),
comp: init_comp_table(),
bitcnt: init_bitcnt_table(),
nt16_str: init_nt16_str_table(),
}
}
}
impl Default for LookupTables {
fn default() -> Self {
Self::new()
}
}
fn init_nt16_table() -> [u8; 256] {
let mut table = [15u8; 256];
table[b'A' as usize] = 1;
table[b'C' as usize] = 2;
table[b'G' as usize] = 4;
table[b'T' as usize] = 8;
table[b'U' as usize] = 8;
table[b'R' as usize] = 5; table[b'Y' as usize] = 10; table[b'M' as usize] = 3; table[b'K' as usize] = 12; table[b'S' as usize] = 6; table[b'W' as usize] = 9; table[b'H' as usize] = 11; table[b'B' as usize] = 14; table[b'V' as usize] = 7; table[b'D' as usize] = 13; table[b'N' as usize] = 15;
table[b'a' as usize] = 1;
table[b'c' as usize] = 2;
table[b'g' as usize] = 4;
table[b't' as usize] = 8;
table[b'u' as usize] = 8;
table[b'r' as usize] = 5;
table[b'y' as usize] = 10;
table[b'm' as usize] = 3;
table[b'k' as usize] = 12;
table[b's' as usize] = 6;
table[b'w' as usize] = 9;
table[b'h' as usize] = 11;
table[b'b' as usize] = 14;
table[b'v' as usize] = 7;
table[b'd' as usize] = 13;
table[b'n' as usize] = 15;
table
}
fn init_nt6_table() -> [u8; 256] {
let mut table = [0u8; 256];
table[b'A' as usize] = 1;
table[b'C' as usize] = 2;
table[b'G' as usize] = 3;
table[b'T' as usize] = 4;
table[b'N' as usize] = 5;
table[b'a' as usize] = 1;
table[b'c' as usize] = 2;
table[b'g' as usize] = 3;
table[b't' as usize] = 4;
table[b'n' as usize] = 5;
table
}
fn init_comp_table() -> [u8; 256] {
let mut table = [0u8; 256];
for (i, item) in table.iter_mut().enumerate() {
*item = i as u8;
}
table[b'A' as usize] = b'T';
table[b'C' as usize] = b'G';
table[b'G' as usize] = b'C';
table[b'T' as usize] = b'A';
table[b'a' as usize] = b't';
table[b'c' as usize] = b'g';
table[b'g' as usize] = b'c';
table[b't' as usize] = b'a';
table[b'R' as usize] = b'Y'; table[b'Y' as usize] = b'R'; table[b'M' as usize] = b'K'; table[b'K' as usize] = b'M'; table[b'S' as usize] = b'S'; table[b'W' as usize] = b'W'; table[b'H' as usize] = b'D'; table[b'B' as usize] = b'V'; table[b'V' as usize] = b'B'; table[b'D' as usize] = b'H'; table[b'N' as usize] = b'N';
table[b'r' as usize] = b'y';
table[b'y' as usize] = b'r';
table[b'm' as usize] = b'k';
table[b'k' as usize] = b'm';
table[b's' as usize] = b's';
table[b'w' as usize] = b'w';
table[b'h' as usize] = b'd';
table[b'b' as usize] = b'v';
table[b'v' as usize] = b'b';
table[b'd' as usize] = b'h';
table[b'n' as usize] = b'n';
table
}
fn init_bitcnt_table() -> [u8; 16] {
let mut table = [0u8; 16];
for (i, item) in table.iter_mut().enumerate() {
*item = i.count_ones() as u8;
}
table
}
fn init_nt16_str_table() -> [u8; 16] {
[
b'=', b'A', b'C', b'M', b'G', b'R', b'S', b'V', b'T', b'W', b'Y', b'H', b'K', b'D', b'B',
b'N',
]
}
pub static LOOKUP_TABLES: LazyLock<LookupTables> = LazyLock::new(LookupTables::new);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nt16_table() {
let tables = &LOOKUP_TABLES;
assert_eq!(tables.nt16[b'A' as usize], 1);
assert_eq!(tables.nt16[b'C' as usize], 2);
assert_eq!(tables.nt16[b'G' as usize], 4);
assert_eq!(tables.nt16[b'T' as usize], 8);
assert_eq!(tables.nt16[b'a' as usize], 1);
assert_eq!(tables.nt16[b'c' as usize], 2);
assert_eq!(tables.nt16[b'R' as usize], 5); assert_eq!(tables.nt16[b'Y' as usize], 10); assert_eq!(tables.nt16[b'N' as usize], 15);
}
#[test]
fn test_nt6_table() {
let tables = &LOOKUP_TABLES;
assert_eq!(tables.nt6[b'A' as usize], 1);
assert_eq!(tables.nt6[b'C' as usize], 2);
assert_eq!(tables.nt6[b'G' as usize], 3);
assert_eq!(tables.nt6[b'T' as usize], 4);
assert_eq!(tables.nt6[b'N' as usize], 5);
assert_eq!(tables.nt6[b'X' as usize], 0); }
#[test]
fn test_comp_table() {
let tables = &LOOKUP_TABLES;
assert_eq!(tables.comp[b'A' as usize], b'T');
assert_eq!(tables.comp[b'T' as usize], b'A');
assert_eq!(tables.comp[b'G' as usize], b'C');
assert_eq!(tables.comp[b'C' as usize], b'G');
assert_eq!(tables.comp[b'a' as usize], b't');
assert_eq!(tables.comp[b'g' as usize], b'c');
assert_eq!(tables.comp[b'R' as usize], b'Y');
assert_eq!(tables.comp[b'S' as usize], b'S'); }
#[test]
fn test_bitcnt_table() {
let tables = &LOOKUP_TABLES;
assert_eq!(tables.bitcnt[0b0000], 0);
assert_eq!(tables.bitcnt[0b0001], 1);
assert_eq!(tables.bitcnt[0b0011], 2);
assert_eq!(tables.bitcnt[0b1111], 4);
}
#[test]
fn test_nt16_str_table() {
let tables = &LOOKUP_TABLES;
assert_eq!(tables.nt16_str[1], b'A');
assert_eq!(tables.nt16_str[2], b'C');
assert_eq!(tables.nt16_str[4], b'G');
assert_eq!(tables.nt16_str[8], b'T');
assert_eq!(tables.nt16_str[15], b'N');
}
#[test]
fn test_reverse_complement_logic() {
let tables = &LOOKUP_TABLES;
let seq = b"ACGT";
let mut rev_comp = Vec::new();
for &base in seq.iter().rev() {
rev_comp.push(tables.comp[base as usize]);
}
assert_eq!(rev_comp, b"ACGT"); }
}