Skip to main content

bio_seq/codec/
dna.rs

1//! 2-bit DNA representation: `A: 00, C: 01, G: 10, T: 11`
2
3use crate::codec::Codec;
4//use crate::kmer::Kmer;
5//use crate::seq::{Seq, SeqArray, SeqSlice};
6use crate::{Complement, ComplementMut};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
9#[repr(u8)]
10pub enum Dna {
11    A = 0b00,
12    C = 0b01,
13    G = 0b10,
14    T = 0b11,
15}
16
17impl Codec for Dna {
18    const BITS: u8 = 2;
19
20    /// Transmute a `u8` into a nucleotide
21    ///
22    /// SAFETY: This only looks at the lower 2 bits of the `u8`
23    fn unsafe_from_bits(b: u8) -> Self {
24        debug_assert!(b < 4);
25        unsafe { std::mem::transmute(b & 0b11) }
26    }
27
28    /// We can verify that a byte is a valid `Dna` value if it's
29    /// between 0 and 3.
30    fn try_from_bits(b: u8) -> Option<Self> {
31        if b < 4 {
32            Some(unsafe { std::mem::transmute::<u8, Dna>(b) })
33        } else {
34            None
35        }
36    }
37
38    /// The ASCII values of 'A', 'C', 'G', and 'T' can be translated into
39    /// the numbers 0, 1, 2, and 3 using bitwise operations: `(b * 3) >> 3`.
40    /// In other words, multiply the ASCII value by 3 and shift right.
41    ///
42    /// SAFETY: bytes that aren't `A`, `C`, `G`, or `T` yield an arbitrary
43    /// nucleotide.
44    fn unsafe_from_ascii(b: u8) -> Self {
45        Dna::unsafe_from_bits((b.wrapping_mul(3) >> 3) & 0b11)
46    }
47
48    fn try_from_ascii(c: u8) -> Option<Self> {
49        match c {
50            b'A' => Some(Dna::A),
51            b'C' => Some(Dna::C),
52            b'G' => Some(Dna::G),
53            b'T' => Some(Dna::T),
54            _ => None,
55        }
56    }
57
58    fn to_char(self) -> char {
59        match self {
60            Dna::A => 'A',
61            Dna::C => 'C',
62            Dna::G => 'G',
63            Dna::T => 'T',
64        }
65    }
66
67    fn to_bits(self) -> u8 {
68        self as u8
69    }
70
71    fn items() -> impl Iterator<Item = Self> {
72        vec![Dna::A, Dna::C, Dna::G, Dna::T].into_iter()
73    }
74}
75
76/// This 2-bit representation of nucleotides lends itself to a very fast
77/// complement implementation with bitwise xor
78impl ComplementMut for Dna {
79    fn comp(&mut self) {
80        *self = Dna::unsafe_from_bits(*self as u8 ^ 0b11);
81    }
82}
83
84impl Complement for Dna {}
85
86#[cfg(test)]
87mod tests {
88    use crate::prelude::*;
89
90    #[test]
91    fn dna_kmer_equality() {
92        assert_eq!(
93            Kmer::<Dna, 8>::try_from(dna!("TGCACATG")).unwrap(),
94            Kmer::<Dna, 8>::try_from(dna!("TGCACATG")).unwrap()
95        );
96        assert_ne!(
97            Kmer::<Dna, 7>::try_from(dna!("GTGACGA")).unwrap(),
98            Kmer::<Dna, 7>::try_from(dna!("GTGAAGA")).unwrap()
99        );
100    }
101
102    #[test]
103    fn dna_kmer_macro() {
104        assert_eq!(
105            kmer!("TGCACATG"),
106            Kmer::<Dna, 8>::try_from(dna!("TGCACATG")).unwrap()
107        );
108        assert_ne!(
109            kmer!("GTGACGA"),
110            Kmer::<Dna, 7>::try_from(dna!("GTGAAGA")).unwrap()
111        );
112    }
113
114    /*
115    #[test]
116    fn dna_kmer_complement() {
117        assert_eq!(
118            format!(
119                "{:b}",
120                Kmer::<Dna, 8>::try_from(dna!("AAAAAAAA"))
121                    .unwrap()
122                    .comp()
123                    .bs
124            ),
125            format!(
126                "{:b}",
127                Kmer::<Dna, 8>::try_from(dna!("TTTTTTTT")).unwrap().bs
128            )
129        );
130
131        assert_eq!(
132            Kmer::<Dna, 1>::try_from(dna!("C")).unwrap().comp(),
133            Kmer::<Dna, 1>::try_from(dna!("G")).unwrap()
134        );
135
136        assert_eq!(
137            Kmer::<Dna, 16>::from(dna!("AAAATGCACATGTTTT")).comp(),
138            Kmer::<Dna, 16>::from(dna!("TTTTACGTGTACAAAA"))
139        );
140    }
141    */
142}