use std::ffi::CString;
pub fn is_symmetric(seq: &str) -> bool {
if seq.is_empty() {
return false;
}
let Ok(c_seq) = CString::new(seq.to_ascii_uppercase()) else {
return false;
};
unsafe { primer3_sys::symmetry(c_seq.as_ptr()) == 1 }
}
pub fn divalent_to_monovalent(divalent: f64, dntp: f64) -> f64 {
unsafe { primer3_sys::divalent_to_monovalent(divalent, dntp) }
}
fn complement_byte(b: u8) -> u8 {
match b {
b'A' => b'T',
b'T' => b'A',
b'G' => b'C',
b'C' => b'G',
b'a' => b't',
b't' => 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',
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',
other => other,
}
}
pub fn reverse_complement(seq: &str) -> String {
debug_assert!(seq.is_ascii(), "reverse_complement requires ASCII input");
let bytes: Vec<u8> = seq.bytes().rev().map(complement_byte).collect();
String::from_utf8(bytes).expect("complement_byte preserves ASCII validity")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_complement_byte_standard_bases() {
assert_eq!(complement_byte(b'A'), b'T');
assert_eq!(complement_byte(b'T'), b'A');
assert_eq!(complement_byte(b'G'), b'C');
assert_eq!(complement_byte(b'C'), b'G');
}
#[test]
fn test_complement_byte_lowercase() {
assert_eq!(complement_byte(b'a'), b't');
assert_eq!(complement_byte(b't'), b'a');
assert_eq!(complement_byte(b'g'), b'c');
assert_eq!(complement_byte(b'c'), b'g');
}
#[test]
fn test_complement_byte_iupac() {
assert_eq!(complement_byte(b'R'), b'Y');
assert_eq!(complement_byte(b'Y'), b'R');
assert_eq!(complement_byte(b'S'), b'S');
assert_eq!(complement_byte(b'W'), b'W');
assert_eq!(complement_byte(b'K'), b'M');
assert_eq!(complement_byte(b'M'), b'K');
assert_eq!(complement_byte(b'N'), b'N');
}
#[test]
fn test_complement_byte_passthrough() {
assert_eq!(complement_byte(b'X'), b'X');
assert_eq!(complement_byte(b'0'), b'0');
}
#[test]
fn test_reverse_complement_empty() {
assert_eq!(reverse_complement(""), "");
}
#[test]
fn test_reverse_complement_single() {
assert_eq!(reverse_complement("A"), "T");
assert_eq!(reverse_complement("C"), "G");
}
#[test]
fn test_reverse_complement_standard() {
assert_eq!(reverse_complement("ATCG"), "CGAT");
assert_eq!(reverse_complement("AAAA"), "TTTT");
assert_eq!(reverse_complement("GCGC"), "GCGC");
}
#[test]
fn test_reverse_complement_mixed_case() {
assert_eq!(reverse_complement("aAtTgGcC"), "GgCcAaTt");
}
#[test]
fn test_reverse_complement_palindrome() {
assert_eq!(reverse_complement("ATAT"), "ATAT");
}
}