use crate::codec::{Codec, dna::Dna};
use crate::seq::{Seq, SeqArray, SeqSlice};
use crate::{Complement, ComplementMut};
const IUPAC_COMPLEMENT_TABLE: [u8; 16] = {
let mut table = [0; 16];
table[Iupac::A as usize] = Iupac::T as u8;
table[Iupac::C as usize] = Iupac::G as u8;
table[Iupac::G as usize] = Iupac::C as u8;
table[Iupac::T as usize] = Iupac::A as u8;
table[Iupac::Y as usize] = Iupac::R as u8;
table[Iupac::R as usize] = Iupac::Y as u8;
table[Iupac::W as usize] = Iupac::W as u8;
table[Iupac::S as usize] = Iupac::S as u8;
table[Iupac::K as usize] = Iupac::M as u8;
table[Iupac::M as usize] = Iupac::K as u8;
table[Iupac::D as usize] = Iupac::H as u8;
table[Iupac::V as usize] = Iupac::B as u8;
table[Iupac::H as usize] = Iupac::D as u8;
table[Iupac::B as usize] = Iupac::V as u8;
table[Iupac::N as usize] = Iupac::N as u8;
table
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Codec)]
#[bits(4)]
#[repr(u8)]
pub enum Iupac {
A = 0b1000,
C = 0b0100,
G = 0b0010,
T = 0b0001,
R = 0b1010,
Y = 0b0101,
S = 0b0110,
W = 0b1001,
K = 0b0011,
M = 0b1100,
B = 0b0111,
D = 0b1011,
H = 0b1101,
V = 0b1110,
N = 0b1111,
#[display('-')]
X = 0b0000,
}
impl From<Dna> for Iupac {
fn from(dna: Dna) -> Self {
match dna {
Dna::A => Iupac::A,
Dna::C => Iupac::C,
Dna::G => Iupac::G,
Dna::T => Iupac::T,
}
}
}
impl Seq<Iupac> {
#[must_use]
pub fn contains(&self, rhs: &SeqSlice<Iupac>) -> bool {
if rhs.len() != self.len() {
return false;
}
self.as_ref() & rhs == rhs
}
}
impl<const N: usize, const W: usize> SeqArray<Iupac, N, W> {
#[must_use]
pub fn contains(&self, rhs: &SeqSlice<Iupac>) -> bool {
if N != rhs.len() {
return false;
}
self.as_ref() & rhs == rhs
}
}
impl SeqSlice<Iupac> {
#[must_use]
pub fn contains(&self, rhs: &SeqSlice<Iupac>) -> bool {
if self.len() != rhs.len() {
return false;
}
self & rhs == rhs
}
}
impl ComplementMut for Iupac {
fn comp(&mut self) {
*self = Iupac::unsafe_from_bits(IUPAC_COMPLEMENT_TABLE[*self as usize]);
}
}
impl Complement for Iupac {}
#[cfg(test)]
mod tests {
use crate::prelude::*;
#[test]
fn iupac_ops() {
let seq = iupac!("AGCTNNCAGTCGACGTATGTASWAGG");
let pattern = iupac!("NAYGN");
let missing = iupac!("NATNG");
let matches: Vec<Seq<Iupac>> = seq
.windows(pattern.len())
.filter(|w| pattern.contains(w) && !missing.contains(w))
.collect();
assert_eq!(matches, vec![iupac!("GACGT"), iupac!("TATGT")]);
}
#[test]
fn iupac_gap_containment() {
let gap_array = SeqArray::<Iupac, 1, 1> {
_p: core::marker::PhantomData,
ba: crate::Ba::default(),
};
let gap: Seq<Iupac> = gap_array.as_ref().into();
assert_eq!(gap, iupac!("-"));
for symbol in Iupac::items() {
let seq: Seq<Iupac> = std::iter::once(symbol).collect();
assert!(seq.contains(&gap));
assert!(seq.as_ref().contains(&gap));
assert_eq!(gap.contains(&seq), symbol == Iupac::X);
assert_eq!(gap.as_ref().contains(&seq), symbol == Iupac::X);
assert_eq!(gap_array.contains(&seq), symbol == Iupac::X);
}
}
#[test]
fn iupac_complement() {
assert_eq!(
iupac!("AGCTYRWSKMDVHBN").to_comp(),
iupac!("TCGARYWSMKHBDVN")
);
}
#[test]
fn iupac_bits_sanity() {
assert_eq!(Iupac::A.to_bits(), 0b1000);
assert_eq!(Iupac::C.to_bits(), 0b0100);
assert_eq!(Iupac::unsafe_from_bits(0b1000), Iupac::A);
assert_eq!(Iupac::unsafe_from_bits(0b0100), Iupac::C);
assert_eq!(Iupac::unsafe_from_bits(Iupac::A.to_bits()), Iupac::A);
assert_eq!(Iupac::unsafe_from_bits(Iupac::C.to_bits()), Iupac::C);
}
}