1use crate::io_utils::{FqReader, Output};
2use crate::stats::{convert_p_err_to_q_score, Q2PConverter};
3use rayon::prelude::*;
4use std::fmt::Write;
5
6pub fn get_result_wo_qthreshold(path: &str, asciibase: usize) -> Result<(), std::io::Error> {
34 let (maxlen, qualset) = get_maxlen_and_qualset(path)?;
35 cal_seq_all(path, maxlen, &qualset, asciibase)?;
36 Ok(())
37}
38
39pub fn get_result_with_qthreshold(
68 path: &str,
69 q_plus_ascii: u8,
70 asciibase: usize,
71) -> Result<(), std::io::Error> {
72 let (maxlen, qualset) = get_maxlen_and_qualset(path)?;
73 cal_seq_with_q(path, maxlen, &qualset, asciibase, q_plus_ascii)?;
74 Ok(())
75}
76
77fn cal_seq_all(
78 path: &str,
79 maxlen: usize,
80 qual_set: &[usize],
81 asciibases: usize,
82) -> Result<(), std::io::Error> {
83 let qplookup = Q2PConverter::new(asciibases as u8);
84 let mut seq_count_mat: Vec<[usize; 256]> = vec![[0; 256]; maxlen];
85 let mut seq_pos_sum: Vec<usize> = vec![0; maxlen];
86 let mut seq_all: [usize; 256] = [0; 256];
87 let mut qual_count_mat: Vec<[usize; 256]> = vec![[0; 256]; maxlen];
88 let mut qual_all: [usize; 256] = [0; 256];
89
90 let fq = FqReader::new(path)?;
91 for record in fq.records() {
92 match record {
93 Ok(read) => {
94 for (i, (&b, &q)) in read.seq().iter().zip(read.qual()).enumerate() {
95 let ub = b as usize;
96 seq_count_mat[i][ub] += 1;
97 seq_all[ub] += 1;
98 seq_pos_sum[i] += 1;
99 let uq = q as usize;
100 qual_count_mat[i][uq] += 1;
101 qual_all[uq] += 1;
102 }
103 }
104 Err(e) => eprintln!("Error read FASTQ: {}", e),
105 }
106 }
107
108 let mut output = Output::new();
109 let mut buf = String::new();
110
111 buf.push_str("POS\t#bases\t%A\t%C\t%G\t%T\t%N\tavgQ\terrQ\t");
112 get_qual_cols(&mut buf, qual_set, asciibases);
113 output.write(&buf)?;
114 buf.clear();
115
116 let mut total: usize = seq_all.iter().sum();
117 let mut total_f64: f64 = total as f64;
118 write!(&mut buf, "All\t{}\t", total).unwrap();
119 get_seq_result(&mut buf, total_f64, &seq_all);
120 get_avg_err(
121 &mut buf, total_f64, &qual_all, qual_set, &qplookup, asciibases,
122 );
123 get_qual_result(&mut buf, total_f64, &qual_all, qual_set);
124 output.write(&buf)?;
125
126 for i in 0..maxlen {
127 buf.clear();
128
129 total = seq_pos_sum[i];
130 total_f64 = total as f64;
131 write!(&mut buf, "{}\t{}\t", i + 1, total).unwrap();
132 get_seq_result(&mut buf, total_f64, &seq_count_mat[i]);
133 get_avg_err(
134 &mut buf,
135 total_f64,
136 &qual_count_mat[i],
137 qual_set,
138 &qplookup,
139 asciibases,
140 );
141 get_qual_result(&mut buf, total_f64, &qual_count_mat[i], qual_set);
142 output.write(&buf)?;
143 }
144 Ok(())
145}
146fn cal_seq_with_q(
147 path: &str,
148 maxlen: usize,
149 qual_set: &[usize],
150 asciibases: usize,
151 q_plus_ascii: u8,
152) -> Result<(), std::io::Error> {
153 let qplookup = Q2PConverter::new(asciibases as u8);
154 let mut seq_count_mat: Vec<[usize; 256]> = vec![[0; 256]; maxlen];
155 let mut seq_all: [usize; 256] = [0; 256];
156 let mut qual_count_mat: Vec<[usize; 256]> = vec![[0; 256]; maxlen];
157 let mut qual_all: [usize; 256] = [0; 256];
158 let mut qual_q_count: Vec<[usize; 2]> = vec![[0; 2]; maxlen]; let mut qual_q_count_all: [usize; 2] = [0; 2];
160
161 let fq = FqReader::new(path)?;
162 for record in fq.records() {
163 match record {
164 Ok(read) => {
165 for (i, (&b, &q)) in read.seq().iter().zip(read.qual()).enumerate() {
166 let ub = b as usize;
167 seq_count_mat[i][ub] += 1;
168 seq_all[ub] += 1;
169 let uq = q as usize;
170 qual_count_mat[i][uq] += 1;
171 qual_all[uq] += 1;
172 if q >= q_plus_ascii {
173 qual_q_count[i][1] += 1;
174 qual_q_count_all[1] += 1;
175 } else {
176 qual_q_count[i][0] += 1;
177 qual_q_count_all[0] += 1;
178 }
179 }
180 }
181 Err(e) => eprintln!("Error read fASTQ: {}", e),
182 }
183 }
184
185 let mut output = Output::new();
186 let mut buf = String::with_capacity(1024);
187
188 let column = "POS\t#bases\t%A\t%C\t%G\t%T\t%N\tavgQ\terrQ\t%low\t%high\n";
189 output.write(column)?;
190
191 let mut total = qual_q_count_all[0] + qual_q_count_all[1];
192 let mut total_f64 = total as f64;
193 write!(buf, "All\t{}\t", total).unwrap();
194 get_seq_result(&mut buf, total_f64, &seq_all);
195 get_avg_err(
196 &mut buf, total_f64, &qual_all, qual_set, &qplookup, asciibases,
197 );
198 get_qual_result_with_q(&mut buf, total_f64, &qual_q_count_all);
199 output.write(&buf)?;
200
201 for i in 0..maxlen {
202 buf.clear();
203 total = qual_q_count[i][0] + qual_q_count[i][1];
204 total_f64 = total as f64;
205 write!(buf, "{}\t{}\t", i + 1, total).unwrap();
206 get_seq_result(&mut buf, total_f64, &seq_count_mat[i]);
207 get_avg_err(
208 &mut buf,
209 total_f64,
210 &qual_count_mat[i],
211 qual_set,
212 &qplookup,
213 asciibases,
214 );
215 get_qual_result_with_q(&mut buf, total_f64, &qual_q_count[i]);
216 output.write(&buf)?;
217 }
218 Ok(())
219}
220fn get_maxlen_and_qualset(path: &str) -> Result<(usize, Vec<usize>), std::io::Error> {
221 let fq = FqReader::new(path)?;
222 let mut maxlen: usize = 0;
223 let mut qual_set: [bool; 256] = [false; 256];
224 for record in fq.records() {
225 match record {
226 Ok(read) => {
227 let len = read.seq().len();
228 if len > maxlen {
229 maxlen = len;
230 }
231
232 for &qual in read.qual() {
233 qual_set[qual as usize] = true;
234 }
235 }
236 Err(e) => eprintln!("Error read FASTQ: {}", e),
237 }
238 }
239 let uniq_qset: Vec<usize> = qual_set
240 .iter()
241 .enumerate()
242 .filter_map(|(i, &b)| if b { Some(i) } else { None })
243 .collect();
244 Ok((maxlen, uniq_qset))
245}
246
247fn get_avg_err(
252 buf: &mut String,
253 total: f64,
254 qual_count: &[usize; 256],
255 qual_set: &[usize],
256 qplookup: &Q2PConverter,
257 asciibases: usize,
258) {
259 let sum: f64 = qual_set
260 .par_iter()
261 .map(|&q| ((q - asciibases) as f64) * (qual_count[q] as f64))
262 .sum();
263 let avg_q = sum / total;
264 let sum: f64 = qual_set
265 .par_iter()
266 .map(|&q| qplookup.get_prob(q as u8) * (qual_count[q] as f64))
267 .sum();
268 let err_q = convert_p_err_to_q_score(sum / total);
269
270 write!(buf, "{:.1}\t{:.1}\t", avg_q, f64::abs(err_q)).unwrap();
271}
272fn get_qual_result(buf: &mut String, total: f64, qual_count: &[usize; 256], qual_set: &[usize]) {
274 for (i, &q) in qual_set.iter().enumerate() {
275 if i > 0 {
276 buf.push('\t');
277 }
278 write!(buf, "{:.1}", qual_count[q] as f64 * 100.0 / total).unwrap();
279 }
280 buf.push('\n');
281}
282fn get_qual_result_with_q(buf: &mut String, total_f64: f64, qual_count: &[usize; 2]) {
284 writeln!(
285 buf,
286 "{:.1}\t{:.1}",
287 qual_count[0] as f64 * 100.0 / total_f64,
288 qual_count[1] as f64 * 100.0 / total_f64,
289 )
290 .unwrap();
291}
292fn get_seq_result(buf: &mut String, total_f64: f64, seq_count: &[usize; 256]) {
293 write!(
294 buf,
295 "{:.1}\t{:.1}\t{:.1}\t{:.1}\t{:.1}\t",
296 100.0 * (seq_count[b'A' as usize] + seq_count[b'a' as usize]) as f64 / total_f64,
297 100.0 * (seq_count[b'C' as usize] + seq_count[b'c' as usize]) as f64 / total_f64,
298 100.0 * (seq_count[b'G' as usize] + seq_count[b'g' as usize]) as f64 / total_f64,
299 100.0 * (seq_count[b'T' as usize] + seq_count[b't' as usize]) as f64 / total_f64,
300 100.0 * (seq_count[b'N' as usize] + seq_count[b'n' as usize]) as f64 / total_f64,
301 )
302 .unwrap();
303}
304fn get_qual_cols(buf: &mut String, qual_set: &[usize], asciibases: usize) {
305 for (i, &q) in qual_set.iter().enumerate() {
306 if i > 0 {
307 buf.push('\t');
308 }
309 write!(buf, "%Q{}", q - asciibases).unwrap();
310 }
311 buf.push('\n');
312}