1use std::fmt;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
7#[repr(u8)]
8pub enum Nuc {
9 A = 0,
11 C = 1,
13 G = 2,
15 T = 3,
17 #[default]
19 N = 4,
20}
21
22impl Nuc {
23 pub const MAX_PACKED: u8 = 0x3F; pub fn from_ascii(b: u8) -> Option<Nuc> {
31 match b {
32 b'A' | b'a' => Some(Nuc::A),
33 b'C' | b'c' => Some(Nuc::C),
34 b'G' | b'g' => Some(Nuc::G),
35 b'T' | b't' => Some(Nuc::T),
36 b'N' | b'n' => Some(Nuc::N),
37 b'R' | b'r' | b'Y' | b'y' | b'M' | b'm' | b'K' | b'k' | b'S' | b's' | b'W' | b'w' | b'H' | b'h' | b'B' | b'b' | b'V' | b'v' | b'D' | b'd' | b'X' | b'x' | b'U' | b'u' => Some(Nuc::N),
51 _ => None,
52 }
53 }
54
55 pub fn to_ascii(self) -> u8 {
57 match self {
58 Nuc::A => b'A',
59 Nuc::C => b'C',
60 Nuc::G => b'G',
61 Nuc::T => b'T',
62 Nuc::N => b'N',
63 }
64 }
65
66 pub fn complement(self) -> Nuc {
68 match self {
69 Nuc::A => Nuc::T,
70 Nuc::C => Nuc::G,
71 Nuc::G => Nuc::C,
72 Nuc::T => Nuc::A,
73 Nuc::N => Nuc::N,
74 }
75 }
76
77 pub fn is_valid(self) -> bool {
79 matches!(self, Nuc::A | Nuc::C | Nuc::G | Nuc::T)
80 }
81}
82
83impl fmt::Display for Nuc {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 write!(f, "{}", self.to_ascii() as char)
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
96#[repr(transparent)]
97pub struct NucPair(u8);
98
99impl NucPair {
100 pub fn new(lo: Nuc, hi: Nuc) -> Self {
102 NucPair((lo as u8) | ((hi as u8) << 2))
103 }
104
105 pub fn lo(self) -> Nuc {
107 match self.0 & 0x03 {
108 0 => Nuc::A,
109 1 => Nuc::C,
110 2 => Nuc::G,
111 3 => Nuc::T,
112 _ => unreachable!(),
113 }
114 }
115
116 pub fn hi(self) -> Nuc {
118 match (self.0 >> 2) & 0x03 {
119 0 => Nuc::A,
120 1 => Nuc::C,
121 2 => Nuc::G,
122 3 => Nuc::T,
123 _ => unreachable!(),
124 }
125 }
126
127 pub fn as_u8(self) -> u8 {
129 self.0
130 }
131
132 pub fn from_u8(b: u8) -> Self {
134 NucPair(b & 0x0F)
135 }
136}
137
138pub fn revcomp(seq: &[Nuc]) -> Vec<Nuc> {
140 seq.iter().rev().map(|&n| n.complement()).collect()
141}
142
143#[allow(dead_code)]
145pub fn revcomp_in_place(seq: &mut [Nuc]) {
146 seq.reverse();
147 for n in seq.iter_mut() {
148 *n = n.complement();
149 }
150}
151
152pub trait RevComp {
154 type Output;
156
157 fn revcomp(&self) -> Self::Output;
159}
160
161impl RevComp for [Nuc] {
162 type Output = Vec<Nuc>;
163
164 fn revcomp(&self) -> Self::Output {
165 revcomp(self)
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 #[test]
174 fn test_nuc_roundtrip() {
175 assert_eq!(Nuc::from_ascii(b'A'), Some(Nuc::A));
176 assert_eq!(Nuc::from_ascii(b'c'), Some(Nuc::C));
177 assert_eq!(Nuc::from_ascii(b'G'), Some(Nuc::G));
178 assert_eq!(Nuc::from_ascii(b't'), Some(Nuc::T));
179 assert_eq!(Nuc::from_ascii(b'N'), Some(Nuc::N));
180 assert_eq!(Nuc::from_ascii(b'X'), Some(Nuc::N));
182 assert_eq!(Nuc::from_ascii(b'R'), Some(Nuc::N));
183 assert_eq!(Nuc::from_ascii(b'Y'), Some(Nuc::N));
184 assert_eq!(Nuc::from_ascii(b'!'), None);
186 assert_eq!(Nuc::from_ascii(b' '), None);
187 }
188
189 #[test]
190 fn test_nuc_complement() {
191 assert_eq!(Nuc::A.complement(), Nuc::T);
192 assert_eq!(Nuc::C.complement(), Nuc::G);
193 assert_eq!(Nuc::G.complement(), Nuc::C);
194 assert_eq!(Nuc::T.complement(), Nuc::A);
195 assert_eq!(Nuc::N.complement(), Nuc::N);
196 }
197
198 #[test]
199 fn test_nucpair() {
200 let pair = NucPair::new(Nuc::A, Nuc::C);
201 assert_eq!(pair.lo(), Nuc::A);
202 assert_eq!(pair.hi(), Nuc::C);
203 assert_eq!(pair.as_u8(), 0x04); }
205
206 #[test]
207 fn test_revcomp() {
208 let seq = [Nuc::A, Nuc::C, Nuc::G, Nuc::T];
209 let rc = seq.revcomp();
210 assert_eq!(rc, vec![Nuc::A, Nuc::C, Nuc::G, Nuc::T]); }
212}