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}