Skip to main content

bio_seq/codec/masked/
iupac.rs

1use crate::codec::Codec;
2//use crate::{Complement, Maskable, Reverse, ReverseComplement};
3use crate::{ComplementMut, Maskable, MaskableMut}; //, ReverseComplementMut, ReverseMut};
4
5/// 5-bit encoding for maskable IUPAC symbols
6/// The middle bit is the mask flag and symbols are complemented by reversing the bit pattern
7#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Codec)]
8#[bits(5)]
9#[repr(u8)]
10pub enum Iupac {
11    A = 0b10000,
12    C = 0b01000,
13    G = 0b00010,
14    T = 0b00001,
15
16    Y = 0b01001,
17    R = 0b10010,
18    W = 0b10001,
19    S = 0b01010,
20
21    K = 0b00011,
22    M = 0b11000,
23    D = 0b10011,
24    V = 0b11010,
25
26    H = 0b11001,
27    B = 0b01011,
28
29    N = 0b11011,
30    #[display('-')]
31    X = 0b00000,
32
33    #[display('a')]
34    AMasked = 0b10100,
35    #[display('c')]
36    CMasked = 0b01100,
37    #[display('g')]
38    GMasked = 0b00110,
39    #[display('t')]
40    TMasked = 0b00101,
41
42    #[display('y')]
43    YMasked = 0b01101,
44    #[display('r')]
45    RMasked = 0b10110,
46    #[display('w')]
47    WMasked = 0b10101,
48    #[display('s')]
49    SMasked = 0b01110,
50
51    #[display('k')]
52    KMasked = 0b00111,
53    #[display('m')]
54    MMasked = 0b11100,
55    #[display('d')]
56    DMasked = 0b10111,
57    #[display('v')]
58    VMasked = 0b11110,
59
60    #[display('h')]
61    HMasked = 0b11101,
62    #[display('b')]
63    BMasked = 0b01111,
64
65    #[display('n')]
66    NMasked = 0b11111,
67    #[display('.')]
68    XMasked = 0b00100,
69}
70
71impl ComplementMut for Iupac {
72    /// This representation can be complemented by reversing the bit pattern
73    fn comp(&mut self) {
74        let bits = self.to_bits();
75        *self = Self::unsafe_from_bits(
76            ((bits & 0b10000) >> 4)
77                | ((bits & 0b01000) >> 2)
78                | (bits & 0b00100)
79                | ((bits & 0b00010) << 2)
80                | ((bits & 0b00001) << 4),
81        );
82    }
83}
84
85impl MaskableMut for Iupac {
86    /// Setting the middle bit sets the mask flag
87    fn mask(&mut self) {
88        let b = *self as u8 | 0b00100;
89        *self = Self::unsafe_from_bits(b);
90    }
91
92    /// Unsetting the middle bit clears the mask flag
93    fn unmask(&mut self) {
94        let b = *self as u8 & 0b11011;
95        *self = Self::unsafe_from_bits(b);
96    }
97}
98
99impl Maskable for Iupac {}
100
101#[cfg(test)]
102mod tests {
103    use crate::codec::masked;
104    use crate::prelude::*;
105
106    #[test]
107    fn mask_iupac_seq() {
108        let mut seq = Seq::<masked::Iupac>::try_from("A.TCGCgtcataN--A").unwrap();
109
110        assert_eq!(seq.to_mask().to_string(), "a.tcgcgtcatan..a".to_string());
111
112        seq.mask();
113        assert_eq!(seq.to_unmask().to_string(), "A-TCGCGTCATAN--A".to_string());
114        seq.unmask();
115        assert_eq!(seq.to_mask().to_string(), "a.tcgcgtcatan..a".to_string());
116    }
117
118    #[test]
119    fn comp_iupac_seq() {
120        let mut seq = Seq::<masked::Iupac>::try_from("A.TCGCgtcataN--A").unwrap();
121
122        assert_ne!(seq.to_comp().to_string(), "A.TCGCgtcataN--A".to_string());
123        assert_eq!(seq.to_comp().to_string(), "T.AGCGcagtatN--T".to_string());
124
125        seq.comp();
126        assert_eq!(seq.to_comp().to_string(), "A.TCGCgtcataN--A".to_string());
127    }
128}