pub fn reverse_complement(seq: &str) -> String {
seq.chars().rev().map(complement_char).collect()
}
pub(crate) fn complement_base(base: u8) -> Option<u8> {
Some(match base.to_ascii_uppercase() {
b'A' => b'T',
b'T' | b'U' => b'A',
b'G' => b'C',
b'C' => b'G',
b'R' => b'Y',
b'Y' => b'R',
b'S' => b'S',
b'W' => b'W',
b'K' => b'M',
b'M' => b'K',
b'B' => b'V',
b'V' => b'B',
b'D' => b'H',
b'H' => b'D',
b'N' => b'N',
_ => return None,
})
}
fn complement_char(c: char) -> char {
match c {
'A' => 'T',
'T' | 'U' => 'A',
'G' => 'C',
'C' => 'G',
'R' => 'Y',
'Y' => 'R',
'S' => 'S',
'W' => 'W',
'K' => 'M',
'M' => 'K',
'B' => 'V',
'V' => 'B',
'D' => 'H',
'H' => 'D',
'N' => 'N',
'a' => 't',
't' | 'u' => 'a',
'g' => 'c',
'c' => 'g',
'r' => 'y',
'y' => 'r',
's' => 's',
'w' => 'w',
'k' => 'm',
'm' => 'k',
'b' => 'v',
'v' => 'b',
'd' => 'h',
'h' => 'd',
'n' => 'n',
other => other,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_reverse_complement_basic() {
assert_eq!(reverse_complement("ATGC"), "GCAT");
}
#[test]
fn test_reverse_complement_empty() {
assert_eq!(reverse_complement(""), "");
}
#[test]
fn test_reverse_complement_single_base() {
assert_eq!(reverse_complement("A"), "T");
assert_eq!(reverse_complement("T"), "A");
assert_eq!(reverse_complement("G"), "C");
assert_eq!(reverse_complement("C"), "G");
}
#[test]
fn test_reverse_complement_lowercase() {
assert_eq!(reverse_complement("atgc"), "gcat");
assert_eq!(reverse_complement("aattggcc"), "ggccaatt");
}
#[test]
fn test_reverse_complement_mixed_case() {
assert_eq!(reverse_complement("AtGc"), "gCaT");
assert_eq!(reverse_complement("AaTtGgCc"), "gGcCaAtT");
}
#[test]
fn test_reverse_complement_with_n() {
assert_eq!(reverse_complement("ATGN"), "NCAT");
assert_eq!(reverse_complement("NNNN"), "NNNN");
assert_eq!(reverse_complement("ANT"), "ANT");
}
#[test]
fn test_reverse_complement_preserves_unknown_chars() {
assert_eq!(reverse_complement("ATGXZQ"), "QZXCAT");
assert_eq!(reverse_complement("A-T-G"), "C-A-T");
}
#[test]
fn test_reverse_complement_iupac_ambiguity_codes() {
assert_eq!(reverse_complement("R"), "Y");
assert_eq!(reverse_complement("Y"), "R");
assert_eq!(reverse_complement("S"), "S");
assert_eq!(reverse_complement("W"), "W");
assert_eq!(reverse_complement("K"), "M");
assert_eq!(reverse_complement("M"), "K");
assert_eq!(reverse_complement("B"), "V");
assert_eq!(reverse_complement("V"), "B");
assert_eq!(reverse_complement("D"), "H");
assert_eq!(reverse_complement("H"), "D");
assert_eq!(reverse_complement("RYNK"), "MNRY");
assert_eq!(reverse_complement("ryk"), "mry");
assert_eq!(reverse_complement("AUGC"), "GCAT");
}
#[test]
fn test_reverse_complement_palindrome() {
assert_eq!(reverse_complement("ATAT"), "ATAT");
assert_eq!(reverse_complement("GCGC"), "GCGC");
}
#[test]
fn test_reverse_complement_long_sequence() {
let seq = "ATGCATGCATGCATGCATGCATGCATGCATGCATGCATGC";
let rc = reverse_complement(seq);
assert_eq!(reverse_complement(&rc), seq);
}
#[test]
fn test_reverse_complement_real_codon() {
assert_eq!(reverse_complement("ATG"), "CAT");
assert_eq!(reverse_complement("TAA"), "TTA");
assert_eq!(reverse_complement("TAG"), "CTA");
assert_eq!(reverse_complement("TGA"), "TCA");
}
}