packed_seq/
packed_n_seq.rs1use wide::i8x32;
2
3use crate::packed_seq::read_slice_32;
4
5use super::*;
6
7#[derive(Clone, Copy, Debug, MemSize, MemDbg)]
8#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
9pub struct PackedNSeq<'s> {
10 pub seq: PackedSeq<'s>,
11 pub ambiguous: BitSeq<'s>,
12}
13
14#[derive(Clone, Debug, MemSize, MemDbg, Default)]
15#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
16#[cfg_attr(feature = "epserde", derive(epserde::Epserde))]
17pub struct PackedNSeqVec {
18 pub seq: PackedSeqVec,
19 pub ambiguous: BitSeqVec,
20}
21
22impl<'s> PackedNSeq<'s> {
24 pub fn to_revcomp(&self) -> PackedNSeqVec {
25 PackedNSeqVec {
26 seq: self.seq.to_revcomp(),
27 ambiguous: self.ambiguous.to_revcomp(),
28 }
29 }
30
31 pub fn slice(&self, range: Range<usize>) -> PackedNSeq<'s> {
32 PackedNSeq {
33 seq: self.seq.slice(range.clone()),
34 ambiguous: self.ambiguous.slice(range),
35 }
36 }
37}
38
39impl PackedNSeqVec {
41 pub fn clear(&mut self) {
42 self.seq.clear();
43 self.ambiguous.clear();
44 }
45
46 #[cfg(feature = "rand")]
47 pub fn random(len: usize, n_frac: f32) -> Self {
48 let seq = PackedSeqVec::random(len);
49 let ambiguous = BitSeqVec::random(len, n_frac);
50 Self { seq, ambiguous }
51 }
52
53 pub fn as_slice(&self) -> PackedNSeq<'_> {
54 PackedNSeq {
55 seq: self.seq.as_slice(),
56 ambiguous: self.ambiguous.as_slice(),
57 }
58 }
59
60 pub fn slice(&self, range: Range<usize>) -> PackedNSeq<'_> {
61 self.as_slice().slice(range).to_owned()
62 }
63
64 pub fn push_ascii(&mut self, seq: &[u8]) -> Range<usize> {
65 let r1 = self.seq.push_ascii(seq);
66 let r2 = self.ambiguous.push_ascii(seq);
67 assert_eq!(r1, r2);
68 r1
69 }
70
71 pub fn from_ascii(seq: &[u8]) -> Self {
72 Self {
73 seq: PackedSeqVec::from_ascii(seq),
74 ambiguous: BitSeqVec::from_ascii(seq),
75 }
76 }
77
78 pub fn from_ascii_and_quality(seq: &[u8], quality: &[u8], threshold: usize) -> Self {
80 assert_eq!(seq.len(), quality.len());
81
82 let mut ambiguous = BitSeqVec::from_ascii(seq);
83
84 {
86 let t = (b'!' + threshold as u8) as i8;
87 let t_simd = i8x32::splat(t);
88 let ambiguous = ambiguous.seq.as_chunks_mut::<4>().0;
89 for i in (0..quality.len()).step_by(32) {
90 let chunk = i8x32::from(unsafe {
91 std::mem::transmute::<_, i8x32>(read_slice_32(quality, i))
92 });
93
94 let mask = t_simd.simd_lt(chunk).to_bitmask() as u32;
95 let ambi = &mut ambiguous[i / 32];
96 *ambi = (u32::from_ne_bytes(*ambi) | mask).to_ne_bytes();
97 }
98 }
99
100 Self {
101 seq: PackedSeqVec::from_ascii(seq),
102 ambiguous,
103 }
104 }
105
106 pub fn push_from_ascii_and_quality(&mut self, seq: &[u8], quality: &[u8], min_qual: u8) {
108 let r = self.seq.push_ascii(seq);
109 let r2 = self.ambiguous.push_ascii(seq);
110 assert_eq!(r, r2);
111
112 assert_eq!(seq.len(), quality.len());
113
114 let t = b'!' + min_qual;
116 let t_simd = i8x32::splat(t as i8);
117
118 let mut idx = r2.start;
119 let mut i = 0;
120 while idx % 8 != 0 {
121 self.ambiguous.seq[idx / 8] |= ((quality[i] < t) as u8) << (idx % 8);
122 idx += 1;
123 i += 1;
124 }
125 let quality = &quality[i..];
126
127 let ambiguous = self.ambiguous.seq[idx / 8..].as_chunks_mut::<4>().0;
128 for i in (0..quality.len()).step_by(32) {
129 let chunk =
130 i8x32::from(unsafe { std::mem::transmute::<_, i8x32>(read_slice_32(quality, i)) });
131
132 let mask = t_simd.simd_lt(chunk).to_bitmask() as u32;
133 let ambi = &mut ambiguous[i / 32];
134 *ambi = (u32::from_ne_bytes(*ambi) | mask).to_ne_bytes();
135 }
136 }
137
138 #[cfg(feature = "file_io")]
140 pub fn from_fastx(path: &std::path::Path) -> Self {
141 let mut ascii = Vec::with_capacity(16000);
142
143 let mut seq = Self::default();
144
145 let mut reader = needletail::parse_fastx_file(&path).unwrap();
146 while let Some(record) = reader.next() {
147 let seqrec = record.expect("Invalid FASTA/Q record");
148 ascii.extend_from_slice(&seqrec.seq());
149 ascii.push(b'N');
150
151 if ascii.len() > 16000 {
153 seq.push_ascii(&ascii);
154 ascii.clear();
155 }
156 }
157
158 if ascii.len() > 0 {
159 seq.push_ascii(&ascii);
160 }
161
162 seq
163 }
164
165 #[cfg(feature = "file_io")]
168 pub fn from_fastq_with_quality(path: &std::path::Path, min_qual: u8) -> Self {
169 let mut ascii = vec![];
170 let mut qual = vec![];
171
172 let mut seq = Self::default();
173
174 let mut reader = needletail::parse_fastx_file(&path).unwrap();
175 while let Some(record) = reader.next() {
176 let seqrec = record.expect("Invalid FASTA/Q record");
177 ascii.extend_from_slice(&seqrec.seq());
178 qual.extend_from_slice(&seqrec.qual().unwrap());
179 ascii.push(b'N');
180 qual.push(0);
181
182 if ascii.len() > 16000 {
184 seq.push_from_ascii_and_quality(&ascii, &qual, min_qual);
185 ascii.clear();
186 qual.clear();
187 }
188 }
189
190 if ascii.len() > 0 {
191 seq.push_from_ascii_and_quality(&ascii, &qual, min_qual);
192 }
193
194 seq
195 }
196}