use std::collections::HashMap;
use gtars_core::models::region::Region;
use gtars_core::models::RegionSet;
pub fn partition_genome_into_bins(chrom_sizes: &HashMap<String, u32>, n_bins: u32) -> RegionSet {
let mut regions = Vec::new();
let chrom_max_length = match chrom_sizes.values().max() {
Some(&v) => v,
None => return RegionSet::from(regions),
};
let bin_size: u32 = if n_bins == 0 { chrom_max_length.max(1) } else { (chrom_max_length / n_bins).max(1) };
for (chr, size) in chrom_sizes {
let mut start = 1;
while start <= *size {
let end = (start + bin_size - 1).min(*size);
regions.push(Region {
chr: chr.clone(),
start,
end,
rest: None,
});
start = end + 1;
}
}
RegionSet::from(regions)
}
pub fn median_abs_distance(dists: &[i64]) -> Option<f64> {
let mut sorted: Vec<f64> = dists
.iter()
.filter(|&&d| d != i64::MAX)
.map(|&d| (d as f64).abs())
.collect();
if sorted.is_empty() {
return None;
}
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap());
let n = sorted.len();
if n % 2 == 0 {
Some((sorted[n / 2 - 1] + sorted[n / 2]) / 2.0)
} else {
Some(sorted[n / 2])
}
}
pub use gtars_core::utils::chrom_karyotype_key;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_partition_genome_into_bins() {
let mut chrom_sizes = HashMap::new();
chrom_sizes.insert("chr1".to_string(), 100);
chrom_sizes.insert("chr2".to_string(), 50);
let rs = partition_genome_into_bins(&chrom_sizes, 5);
assert_eq!(rs.regions.len(), 8);
let chr1_regions: Vec<_> = rs.regions.iter().filter(|r| r.chr == "chr1").collect();
assert_eq!(chr1_regions.len(), 5);
let chr2_regions: Vec<_> = rs.regions.iter().filter(|r| r.chr == "chr2").collect();
assert_eq!(chr2_regions.len(), 3);
let last = chr2_regions.iter().max_by_key(|r| r.start).unwrap();
assert_eq!(last.end, 50);
}
#[test]
fn test_chrom_karyotype_sort_order() {
let mut chroms = vec!["chrM", "chrX", "chr2", "chr10", "chr1", "chrY", "chrUn_gl"];
chroms.sort_by_key(|c| chrom_karyotype_key(c));
assert_eq!(chroms, vec!["chr1", "chr2", "chr10", "chrX", "chrY", "chrM", "chrUn_gl"]);
}
#[test]
fn test_partition_genome_bins_larger_than_chrom() {
let mut chrom_sizes = HashMap::new();
chrom_sizes.insert("chr1".to_string(), 10);
let rs = partition_genome_into_bins(&chrom_sizes, 250);
assert_eq!(rs.regions.len(), 10);
}
#[test]
fn test_median_abs_distance_filters_sentinels() {
let dists = vec![10, -20, i64::MAX, 30];
let median = median_abs_distance(&dists).unwrap();
assert!((median - 20.0).abs() < 1e-10);
}
#[test]
fn test_median_abs_distance_all_sentinels() {
let dists = vec![i64::MAX, i64::MAX];
assert!(median_abs_distance(&dists).is_none());
}
#[test]
fn test_median_abs_distance_empty() {
assert!(median_abs_distance(&[]).is_none());
}
#[test]
fn test_median_abs_distance_even_count() {
let dists = vec![-10, 20, -30, 40];
let median = median_abs_distance(&dists).unwrap();
assert!((median - 25.0).abs() < 1e-10);
}
#[test]
fn test_chrom_karyotype_without_prefix() {
let mut chroms = vec!["MT", "X", "2", "1", "Y"];
chroms.sort_by_key(|c| chrom_karyotype_key(c));
assert_eq!(chroms, vec!["1", "2", "X", "Y", "MT"]);
}
#[test]
fn test_partition_genome_empty_chrom_sizes() {
let chrom_sizes = HashMap::new();
let rs = partition_genome_into_bins(&chrom_sizes, 10);
assert!(rs.regions.is_empty());
}
#[test]
fn test_partition_genome_zero_bins() {
let mut chrom_sizes = HashMap::new();
chrom_sizes.insert("chr1".to_string(), 100);
let rs = partition_genome_into_bins(&chrom_sizes, 0);
assert_eq!(rs.regions.len(), 1);
assert_eq!(rs.regions[0].start, 1);
assert_eq!(rs.regions[0].end, 100);
}
}