use crate::errors::Error;
use crate::utils::Rc;
#[inline]
pub fn is_ambiguous(c: char) -> Result<bool, Error> {
match c {
'A' | 'T' | 'C' | 'G' | 'U' => Ok(false),
'R' | 'Y' | 'S' | 'W' | 'M' | 'K' | 'B' | 'D' | 'H' | 'V' | 'N' => Ok(true),
_ => Err(Error::InvalidDna),
}
}
pub fn count_ambiguous(s: &str) -> Result<usize, Error> {
let mut n = 0;
for c in s.chars() {
if is_ambiguous(c)? {
n += 1;
}
}
Ok(n)
}
pub fn reverse_complement(s: &str) -> Result<String, Error> {
let mut rev = String::with_capacity(s.len());
for x in s.chars().rev() {
rev.push(match x {
'A' => 'T',
'C' => 'G',
'G' => 'C',
'T' => 'A',
'Y' => 'R',
'R' => 'Y',
'S' => 'S',
'W' => 'W',
'M' => 'K',
'K' => 'M',
'B' => 'V',
'D' => 'H',
'H' => 'D',
'V' => 'B',
'N' => 'N',
_ => return Err(Error::InvalidDna),
})
}
Ok(rev)
}
#[inline]
pub fn dna_match(c1: char, c2: char) -> bool {
match c1 {
'A' => c2 == 'A',
'T' => c2 == 'T',
'C' => c2 == 'C',
'G' => c2 == 'G',
'R' => c2 == 'A' || c2 == 'G',
'Y' => c2 == 'C' || c2 == 'T',
'S' => c2 == 'G' || c2 == 'G',
'W' => c2 == 'A' || c2 == 'T',
'K' => c2 == 'G' || c2 == 'T',
'M' => c2 == 'A' || c2 == 'C',
'B' => c2 == 'C' || c2 == 'G' || c2 == 'T',
'D' => c2 == 'A' || c2 == 'G' || c2 == 'T',
'H' => c2 == 'A' || c2 == 'C' || c2 == 'T',
'V' => c2 == 'A' || c2 == 'C' || c2 == 'G',
'N' => c2 == 'A' || c2 == 'G' || c2 == 'T' || c2 == 'C',
_ => unreachable!(),
}
}
pub fn mismatches(s1: &str, s2: &str) -> usize {
s1.chars()
.zip(s2.chars())
.filter(|(x, y)| !dna_match(*x, *y))
.count()
}
pub fn validate(s: &str) -> Result<(), Error> {
count_ambiguous(s).map(|_| ())
}
pub struct DisambiguationIterator<'a> {
sequence: &'a str,
buffer: Rc<String>,
pos: Vec<usize>,
state: Vec<usize>,
variants: Vec<&'static str>,
remaining: usize,
}
impl<'a> DisambiguationIterator<'a> {
pub fn new(sequence: &'a str) -> Result<Self, Error> {
let buffer = Rc::new(String::new());
let mut pos = Vec::new();
let mut state = Vec::new();
let mut variants = Vec::new();
let mut remaining = 1;
for (i, c) in sequence
.chars()
.enumerate()
.filter(|(_, c)| is_ambiguous(*c).unwrap_or(true))
{
let var = match c {
'R' => "AG",
'Y' => "CT",
'S' => "GC",
'W' => "AT",
'K' => "GT",
'M' => "AC",
'B' => "CGT",
'D' => "AGT",
'H' => "ACT",
'V' => "ACG",
'N' => "ACGT",
_ => return Err(Error::InvalidDna),
};
remaining *= var.len();
variants.push(var);
pos.push(i);
state.push(var.len() - 1);
}
Ok(DisambiguationIterator {
buffer,
pos,
state,
variants,
remaining,
sequence,
})
}
}
impl<'a> Iterator for DisambiguationIterator<'a> {
type Item = Rc<String>;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let buffer = match Rc::get_mut(&mut self.buffer) {
Some(b) => b,
None => {
self.buffer = Rc::new(self.sequence.to_string());
Rc::get_mut(&mut self.buffer).unwrap()
}
};
if buffer.is_empty() {
buffer.push_str(self.sequence);
}
for i in 0..self.pos.len() {
self.state[i] += 1;
if self.state[i] == self.variants[i].len() {
self.state[i] = 0;
} else {
break;
}
}
for i in 0..self.pos.len() {
buffer.replace_range(
self.pos[i]..self.pos[i] + 1,
&self.variants[i][self.state[i]..self.state[i] + 1],
);
}
self.remaining -= 1;
Some(self.buffer.clone())
}
}
pub fn disambiguate<'a>(s: &'a str) -> Result<Vec<String>, Error> {
Ok(DisambiguationIterator::new(s)?
.map(|s| s.clone().as_ref().to_owned())
.collect())
}