1use crate::codec::{Codec, dna::Dna};
59use crate::seq::{Seq, SeqArray, SeqSlice};
60use crate::{Complement, ComplementMut};
61
62const IUPAC_COMPLEMENT_TABLE: [u8; 16] = {
63 let mut table = [0; 16];
64
65 table[Iupac::A as usize] = Iupac::T as u8;
66 table[Iupac::C as usize] = Iupac::G as u8;
67 table[Iupac::G as usize] = Iupac::C as u8;
68 table[Iupac::T as usize] = Iupac::A as u8;
69 table[Iupac::Y as usize] = Iupac::R as u8;
70 table[Iupac::R as usize] = Iupac::Y as u8;
71 table[Iupac::W as usize] = Iupac::W as u8;
72 table[Iupac::S as usize] = Iupac::S as u8;
73 table[Iupac::K as usize] = Iupac::M as u8;
74 table[Iupac::M as usize] = Iupac::K as u8;
75 table[Iupac::D as usize] = Iupac::H as u8;
76 table[Iupac::V as usize] = Iupac::B as u8;
77 table[Iupac::H as usize] = Iupac::D as u8;
78 table[Iupac::B as usize] = Iupac::V as u8;
79 table[Iupac::N as usize] = Iupac::N as u8;
80
81 table
82};
83
84#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Codec)]
85#[bits(4)]
86#[repr(u8)]
87pub enum Iupac {
88 A = 0b1000,
89 C = 0b0100,
90 G = 0b0010,
91 T = 0b0001,
92 R = 0b1010,
93 Y = 0b0101,
94 S = 0b0110,
95 W = 0b1001,
96 K = 0b0011,
97 M = 0b1100,
98 B = 0b0111,
99 D = 0b1011,
100 H = 0b1101,
101 V = 0b1110,
102 N = 0b1111,
103 #[display('-')]
104 X = 0b0000,
105}
106
107impl From<Dna> for Iupac {
108 fn from(dna: Dna) -> Self {
109 match dna {
110 Dna::A => Iupac::A,
111 Dna::C => Iupac::C,
112 Dna::G => Iupac::G,
113 Dna::T => Iupac::T,
114 }
115 }
116}
117
118impl Seq<Iupac> {
119 #[must_use]
120 pub fn contains(&self, rhs: &SeqSlice<Iupac>) -> bool {
121 if rhs.len() != self.len() {
122 return false;
123 }
124
125 self.as_ref() & rhs == rhs
126 }
127}
128
129impl<const N: usize, const W: usize> SeqArray<Iupac, N, W> {
130 #[must_use]
131 pub fn contains(&self, rhs: &SeqSlice<Iupac>) -> bool {
132 if N != rhs.len() {
133 return false;
134 }
135 self.as_ref() & rhs == rhs
136 }
137}
138
139impl SeqSlice<Iupac> {
140 #[must_use]
141 pub fn contains(&self, rhs: &SeqSlice<Iupac>) -> bool {
142 if self.len() != rhs.len() {
143 return false;
144 }
145 self & rhs == rhs
146 }
147}
148
149impl ComplementMut for Iupac {
151 fn comp(&mut self) {
152 *self = Iupac::unsafe_from_bits(IUPAC_COMPLEMENT_TABLE[*self as usize]);
159
160 }
168}
169
170impl Complement for Iupac {}
171
172#[cfg(test)]
202mod tests {
203 use crate::prelude::*;
204
205 #[test]
206 fn iupac_ops() {
207 let seq = iupac!("AGCTNNCAGTCGACGTATGTASWAGG");
208
209 let pattern = iupac!("NAYGN");
210 let missing = iupac!("NATNG");
211
212 let matches: Vec<Seq<Iupac>> = seq
213 .windows(pattern.len())
214 .filter(|w| pattern.contains(w) && !missing.contains(w))
215 .collect();
216
217 assert_eq!(matches, vec![iupac!("GACGT"), iupac!("TATGT")]);
218 }
219
220 #[test]
221 fn iupac_gap_containment() {
222 let gap_array = SeqArray::<Iupac, 1, 1> {
223 _p: core::marker::PhantomData,
224 ba: crate::Ba::default(),
225 };
226 let gap: Seq<Iupac> = gap_array.as_ref().into();
227 assert_eq!(gap, iupac!("-"));
228
229 for symbol in Iupac::items() {
230 let seq: Seq<Iupac> = std::iter::once(symbol).collect();
231 assert!(seq.contains(&gap));
232 assert!(seq.as_ref().contains(&gap));
233 assert_eq!(gap.contains(&seq), symbol == Iupac::X);
234 assert_eq!(gap.as_ref().contains(&seq), symbol == Iupac::X);
235 assert_eq!(gap_array.contains(&seq), symbol == Iupac::X);
236 }
237 }
238
239 #[test]
240 fn iupac_complement() {
241 assert_eq!(
242 iupac!("AGCTYRWSKMDVHBN").to_comp(),
243 iupac!("TCGARYWSMKHBDVN")
244 );
245 }
246
247 #[test]
248 fn iupac_bits_sanity() {
249 assert_eq!(Iupac::A.to_bits(), 0b1000);
250 assert_eq!(Iupac::C.to_bits(), 0b0100);
251
252 assert_eq!(Iupac::unsafe_from_bits(0b1000), Iupac::A);
253 assert_eq!(Iupac::unsafe_from_bits(0b0100), Iupac::C);
254
255 assert_eq!(Iupac::unsafe_from_bits(Iupac::A.to_bits()), Iupac::A);
256 assert_eq!(Iupac::unsafe_from_bits(Iupac::C.to_bits()), Iupac::C);
257 }
258}