const DNA_COMPLEMENT: [u8; 256] = [
b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'$', b'#', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'-', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'T', b'V', b'G', b'H', b'N', b'N', b'C', b'D', b'N', b'N', b'M', b'N', b'K', b'N', b'N', b'N', b'Q', b'Y', b'S', b'A', b'A', b'B', b'W', b'N', b'R', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b't', b'v', b'g', b'h', b'N', b'N', b'c', b'd', b'N', b'N', b'm', b'N', b'k', b'n', b'N', b'N', b'q', b'y', b's', b'a', b'a', b'b', b'w', b'N', b'r', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', b'N', ];
#[inline]
pub fn complement(c: u8) -> u8 {
DNA_COMPLEMENT[c as usize]
}
pub fn reverse_complement(seq: &[u8]) -> Vec<u8> {
seq.iter()
.rev()
.map(|&c| DNA_COMPLEMENT[c as usize])
.collect()
}
pub fn reverse_complement_in_place(seq: &mut [u8]) {
let len = seq.len();
let swap_size = len / 2;
for i in 0..swap_size {
let j = len - 1 - i;
let tmp = seq[i];
seq[i] = DNA_COMPLEMENT[seq[j] as usize];
seq[j] = DNA_COMPLEMENT[tmp as usize];
}
if len % 2 == 1 {
seq[swap_size] = DNA_COMPLEMENT[seq[swap_size] as usize];
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_complement_basic() {
assert_eq!(complement(b'A'), b'T');
assert_eq!(complement(b'T'), b'A');
assert_eq!(complement(b'C'), b'G');
assert_eq!(complement(b'G'), b'C');
assert_eq!(complement(b'N'), b'N');
}
#[test]
fn test_complement_lowercase() {
assert_eq!(complement(b'a'), b't');
assert_eq!(complement(b't'), b'a');
assert_eq!(complement(b'c'), b'g');
assert_eq!(complement(b'g'), b'c');
assert_eq!(complement(b'n'), b'n');
}
#[test]
fn test_complement_iupac() {
assert_eq!(complement(b'R'), b'Y'); assert_eq!(complement(b'Y'), b'R'); assert_eq!(complement(b'S'), b'S'); assert_eq!(complement(b'W'), b'W'); assert_eq!(complement(b'K'), b'M'); assert_eq!(complement(b'M'), b'K'); assert_eq!(complement(b'B'), b'V'); assert_eq!(complement(b'D'), b'H'); assert_eq!(complement(b'H'), b'D'); assert_eq!(complement(b'V'), b'B'); }
#[test]
fn test_complement_special() {
assert_eq!(complement(b'$'), b'#');
assert_eq!(complement(b'#'), b'$');
assert_eq!(complement(b'-'), b'-');
}
#[test]
fn test_reverse_complement_basic() {
assert_eq!(reverse_complement(b"ACGT"), b"ACGT");
assert_eq!(reverse_complement(b"AAAA"), b"TTTT");
assert_eq!(reverse_complement(b"CGCG"), b"CGCG");
assert_eq!(reverse_complement(b"ATCG"), b"CGAT");
}
#[test]
fn test_reverse_complement_lowercase() {
assert_eq!(reverse_complement(b"acgt"), b"acgt");
assert_eq!(reverse_complement(b"aaaa"), b"tttt");
}
#[test]
fn test_reverse_complement_mixed() {
assert_eq!(reverse_complement(b"AcGt"), b"aCgT");
assert_eq!(reverse_complement(b"ATCGN"), b"NCGAT");
}
#[test]
fn test_reverse_complement_empty() {
assert_eq!(reverse_complement(b""), b"");
}
#[test]
fn test_reverse_complement_single() {
assert_eq!(reverse_complement(b"A"), b"T");
assert_eq!(reverse_complement(b"G"), b"C");
}
#[test]
fn test_reverse_complement_in_place_basic() {
let mut seq = b"ACGT".to_vec();
reverse_complement_in_place(&mut seq);
assert_eq!(seq, b"ACGT");
let mut seq = b"AAAA".to_vec();
reverse_complement_in_place(&mut seq);
assert_eq!(seq, b"TTTT");
let mut seq = b"ATCG".to_vec();
reverse_complement_in_place(&mut seq);
assert_eq!(seq, b"CGAT");
}
#[test]
fn test_reverse_complement_in_place_odd_length() {
let mut seq = b"ATCGN".to_vec();
reverse_complement_in_place(&mut seq);
assert_eq!(seq, b"NCGAT");
let mut seq = b"A".to_vec();
reverse_complement_in_place(&mut seq);
assert_eq!(seq, b"T");
}
#[test]
fn test_reverse_complement_in_place_empty() {
let mut seq = b"".to_vec();
reverse_complement_in_place(&mut seq);
assert_eq!(seq, b"");
}
#[test]
fn test_reverse_complement_equivalence() {
let test_cases = vec![
&b"ACGT"[..],
&b"AAAA"[..],
&b"ATCGN"[..],
&b"CGCGCG"[..],
&b"A"[..],
&b""[..],
&b"ATCGATCGATCG"[..],
];
for &test in &test_cases {
let rc_alloc = reverse_complement(test);
let mut seq = test.to_vec();
reverse_complement_in_place(&mut seq);
assert_eq!(rc_alloc, seq, "Mismatch for input: {:?}", std::str::from_utf8(test));
}
}
#[test]
fn test_reverse_complement_involution() {
let test = b"ATCGATCG";
let rc = reverse_complement(test);
let rcrc = reverse_complement(&rc);
assert_eq!(test, &rcrc[..]);
}
}