#[inline]
pub fn qual_to_error_prob(qual: u8, offset: u8) -> f64 {
let q = (qual.saturating_sub(offset)) as f64;
10.0_f64.powf(-q / 10.0)
}
pub fn build_qual_table(offset: u8) -> Vec<f64> {
(0..=255).map(|q| qual_to_error_prob(q, offset)).collect()
}
pub fn detect_quality_offset(qual: &[u8]) -> u8 {
if qual.is_empty() {
return 33; }
let min_qual = qual.iter().min().copied().unwrap_or(33);
if min_qual < 59 {
33 } else {
64 }
}
pub fn convert_quality_offset(qual: &mut [u8], from: u8, to: u8) {
let diff = to as i16 - from as i16;
for q in qual {
*q = (*q as i16 + diff).max(to as i16).min(126) as u8;
}
}
pub fn average_quality(qual: &[u8], offset: u8) -> f64 {
if qual.is_empty() {
return 0.0;
}
let sum: f64 = qual
.iter()
.map(|&q| (q.saturating_sub(offset)) as f64)
.sum();
sum / qual.len() as f64
}
pub fn quality_percentile(qual: &[u8], offset: u8, percentile: f64) -> u8 {
if qual.is_empty() {
return offset;
}
let mut sorted: Vec<u8> = qual.iter().map(|&q| q.saturating_sub(offset)).collect();
sorted.sort_unstable();
let idx = ((qual.len() - 1) as f64 * percentile / 100.0) as usize;
sorted[idx]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_qual_to_error_prob() {
let offset = 33;
let q40 = qual_to_error_prob(73, offset);
assert!((q40 - 0.0001).abs() < 1e-6);
let q30 = qual_to_error_prob(63, offset);
assert!((q30 - 0.001).abs() < 1e-6);
let q20 = qual_to_error_prob(53, offset);
assert!((q20 - 0.01).abs() < 1e-6);
}
#[test]
fn test_build_qual_table() {
let table = build_qual_table(33);
assert_eq!(table.len(), 256);
assert!((table[73] - 0.0001).abs() < 1e-6); assert!((table[63] - 0.001).abs() < 1e-6); assert!((table[53] - 0.01).abs() < 1e-6); }
#[test]
fn test_detect_quality_offset() {
let phred33_qual = vec![35, 40, 45, 50, 55]; assert_eq!(detect_quality_offset(&phred33_qual), 33);
let phred64_qual = vec![65, 70, 75, 80, 85]; assert_eq!(detect_quality_offset(&phred64_qual), 64);
assert_eq!(detect_quality_offset(&[]), 33);
}
#[test]
fn test_convert_quality_offset() {
let mut qual = vec![33, 40, 50, 60, 70]; convert_quality_offset(&mut qual, 33, 64);
assert_eq!(qual, vec![64, 71, 81, 91, 101]);
convert_quality_offset(&mut qual, 64, 33);
assert_eq!(qual, vec![33, 40, 50, 60, 70]);
}
#[test]
fn test_convert_quality_offset_clamp() {
let mut qual = vec![33, 126, 200];
convert_quality_offset(&mut qual, 33, 64);
for &q in &qual {
assert!(q >= 64 && q <= 126);
}
}
#[test]
fn test_average_quality() {
let qual = vec![43, 43, 43, 43]; let avg = average_quality(&qual, 33);
assert!((avg - 10.0).abs() < 1e-6);
let qual2 = vec![33, 43, 53, 63]; let avg2 = average_quality(&qual2, 33);
assert!((avg2 - 15.0).abs() < 1e-6); }
#[test]
fn test_quality_percentile() {
let qual = vec![33, 38, 43, 48, 53];
assert_eq!(quality_percentile(&qual, 33, 0.0), 0);
assert_eq!(quality_percentile(&qual, 33, 50.0), 10);
assert_eq!(quality_percentile(&qual, 33, 100.0), 20);
}
#[test]
fn test_empty_quality() {
let qual: Vec<u8> = vec![];
assert_eq!(average_quality(&qual, 33), 0.0);
assert_eq!(quality_percentile(&qual, 33, 50.0), 33);
}
}