#[inline]
pub fn phred_to_prob(phred_char: u8) -> f64 {
let q = (phred_char.saturating_sub(33)) as f64;
10f64.powf(-q / 10.0)
}
#[inline]
pub fn prob_to_phred(prob: f64) -> f64 {
if prob <= 0.0 {
return 60.0;
}
-10.0 * prob.log10()
}
pub fn mean_quality(qual: &[u8]) -> f64 {
if qual.is_empty() {
return 0.0;
}
let sum_prob: f64 = qual.iter().map(|&c| phred_to_prob(c)).sum();
let mean_prob = sum_prob / qual.len() as f64;
prob_to_phred(mean_prob)
}
pub fn arithmetic_mean_quality(qual: &[u8]) -> f64 {
if qual.is_empty() {
return 0.0;
}
let sum: u64 = qual.iter().map(|&c| (c.saturating_sub(33)) as u64).sum();
sum as f64 / qual.len() as f64
}
pub fn count_above_threshold(qual: &[u8], threshold: u8) -> usize {
let threshold_char = threshold + 33;
qual.iter().filter(|&&c| c >= threshold_char).count()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_phred_to_prob() {
assert!((phred_to_prob(b'!') - 1.0).abs() < 1e-9);
assert!((phred_to_prob(b'+') - 0.1).abs() < 1e-9);
assert!((phred_to_prob(b'5') - 0.01).abs() < 1e-9);
assert!((phred_to_prob(b'?') - 0.001).abs() < 1e-9);
}
#[test]
fn test_mean_quality_uniform() {
let qual = vec![b'5'; 100];
let m = mean_quality(&qual);
assert!((m - 20.0).abs() < 0.01);
}
#[test]
fn test_mean_quality_differs_from_arithmetic() {
let mut qual = vec![b'I'; 50]; qual.extend(vec![b'+'; 50]); let prob_mean = mean_quality(&qual);
let arith_mean = arithmetic_mean_quality(&qual);
assert!((arith_mean - 25.0).abs() < 0.01);
assert!(prob_mean < 14.0);
assert!(prob_mean > 12.0);
}
}