#![allow(clippy::needless_range_loop)]
use ::hdrhistogram::*;
use ::ndarray::*;
use ::rayon::prelude::*;
use ::std::fs::OpenOptions;
use ::std::io::prelude::*;
use ::std::result;
use std::io;
use crate::base::sequence::*;
pub struct ReadBaseDistribution {
pub readsizehisto: hdrhistogram::Histogram<u64>,
upper_histo: usize,
pub histo_out: usize,
pub non_acgt: usize,
pub acgt_distribution: Array2<f64>,
}
impl ReadBaseDistribution {
pub fn new(readmaxsize: usize, prec: u8, d: ndarray::Ix2) -> ReadBaseDistribution {
let precision = if prec > 5 {
log::error!(
"precision for histogram construction should be in range 1..5, restting to 5"
);
5
} else {
prec
};
let histo = Histogram::new_with_bounds(1, readmaxsize as u64, precision).unwrap();
let array: Array2<f64> = Array2::zeros(d);
ReadBaseDistribution {
readsizehisto: histo,
upper_histo: readmaxsize,
histo_out: 0,
non_acgt: 0_usize,
acgt_distribution: array,
}
}
pub fn ascii_dump_acgt_distribution(&self, name: &String) -> result::Result<(), io::Error> {
let fileres = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(name);
match fileres {
Ok(mut file) => {
let (nbrow, _) = self.acgt_distribution.dim();
for i in 0..nbrow {
writeln!(
file,
"{} {} {} {} ",
self.acgt_distribution[[i, 0]],
self.acgt_distribution[[i, 1]],
self.acgt_distribution[[i, 2]],
self.acgt_distribution[[i, 3]]
)?; }
file.flush()?;
Ok(())
}
Err(e) => {
println!("could not open file {}", name);
Err(e)
}
} }
pub fn ascii_dump_readlen_distribution(&self, name: &str) -> result::Result<(), io::Error> {
let nb_entries = self.readsizehisto.len() as usize;
if nb_entries == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"histogram error!, empty histogram",
));
}
let maxlen = self.readsizehisto.len();
println!(
"ascii_dump_readlen_distribution nb_entries {} maxlen {}",
nb_entries, maxlen
);
if nb_entries < 100 {
println!(
"Error : ascii_dump_readlen_distribution nb_entries too small : {}",
nb_entries
);
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"histogram error!",
));
}
let nbslot = nb_entries / 100;
let mut readsize: Vec<u64> = (0..(nbslot + 1)).map(|_| 0u64).collect();
for i in 0..(nbslot + 1) {
readsize[i] = self
.readsizehisto
.value_at_quantile(i as f64 / nbslot as f64);
}
let nb_points = 1000usize;
let mut nb_read_vec = Vec::<usize>::with_capacity(nb_points);
let mut abscisse = Vec::<usize>::with_capacity(nb_points);
let mut first_i = 0;
let mut current_i = 0;
for j in 0..nb_points {
let threshold = (maxlen * j as u64) / (nb_points as u64);
while readsize[current_i] < threshold && current_i < nbslot {
current_i += 1;
}
if current_i < nbslot && current_i > first_i {
let nb_in_slot = ((current_i - first_i) * nb_entries) / nbslot;
nb_read_vec.push(nb_in_slot);
abscisse.push(readsize[current_i] as usize);
}
first_i = current_i;
}
let fileres = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(name);
match fileres {
Ok(mut file) => {
for i in 0..nb_read_vec.len() {
writeln!(file, "{} {} ", abscisse[i], nb_read_vec[i])?; }
file.flush()?;
Ok(())
}
Err(e) => {
println!("could not open file {}", name);
Err(e)
}
} }
fn record_read_len(&mut self, sz: usize) {
log::trace!("record_read_len sz : {}", sz);
let res = self.readsizehisto.record(sz as u64);
if res.is_err() {
self.histo_out += 1;
}
} }
#[allow(dead_code)]
fn get_base_count(seq_array: &Vec<Sequence>, maxreadlen: usize) {
println!(" in get_base_count");
let start_t = std::time::Instant::now();
let prec = 3;
let v_ref = &seq_array;
let low = 0;
let up = v_ref.len();
let mut base_distribution =
get_base_count_by_slice(v_ref.get(low..up).unwrap(), maxreadlen, prec).unwrap();
base_distribution.acgt_distribution *= 1. / (seq_array.len() as f64);
let elapsed_t = start_t.elapsed().as_secs();
println!(" elapsed time (s) in get_base_count {} ", elapsed_t);
base_distribution
.ascii_dump_acgt_distribution(&String::from("bases.histo-1thread"))
.unwrap();
}
fn get_base_count_by_slice(
seq_array: &[Sequence],
maxreadlen: usize,
prec: u8,
) -> result::Result<Box<ReadBaseDistribution>, ()> {
log::trace!(" in get_base_count_by_slice len : {} ", seq_array.len());
let start_t = std::time::Instant::now();
let sz: usize = 101;
let mut base_distribution: Box<ReadBaseDistribution> = Box::new(ReadBaseDistribution::new(
maxreadlen,
prec,
ndarray::Dim([sz, 4]),
));
let mut histo: Vec<u64> = vec![0, 0, 0, 0];
let mut nb_bad = 0;
for i in 0..seq_array.len() {
let decompressed = seq_array[i].decompress();
base_distribution.record_read_len(decompressed.len());
nb_bad += seq_array[i].base_count(&mut histo);
for j in 0..4 {
let percent: usize =
((100 * histo[j]) as f64 / decompressed.len() as f64).round() as usize;
base_distribution.acgt_distribution[[percent, j]] += 1.;
}
}
base_distribution.non_acgt = nb_bad;
let elapsed_t = start_t.elapsed().as_secs();
println!(" elapsed time (s) in get_base_count {} ", elapsed_t);
if nb_bad > 0 {
log::trace!(" out get_base_count_par_slice nb_bad : {} ", nb_bad);
}
Ok(base_distribution)
}
pub fn get_base_count_par(
seq_array: &Vec<Sequence>,
maxreadlen: usize,
prec: u8,
) -> Option<Box<ReadBaseDistribution>> {
log::info!(" in get_base_count_par");
let nbthreads: usize = 2;
let nb_cpus = num_cpus::get_physical();
log::info!(
" in get_base_count_par, number of cpus found : {} ",
nb_cpus
);
let start_t = std::time::Instant::now();
let v_ref = &seq_array;
let distrib_collector: Vec<result::Result<Box<ReadBaseDistribution>, ()>> = (0..nbthreads)
.into_par_iter()
.map(|i| {
let low = (v_ref.len() / nbthreads) * i;
let up = if i < nbthreads - 1 {
(v_ref.len() / nbthreads) * (i + 1)
} else {
v_ref.len()
};
log::trace!(
" in get_base_count_par thread {} , low up {} {} ",
i,
low,
up
);
let res: result::Result<Box<ReadBaseDistribution>, ()> =
get_base_count_by_slice(v_ref.get(low..up).unwrap(), maxreadlen, prec);
res
})
.collect();
let sz = 101;
let mut base_distribution: Box<ReadBaseDistribution> = Box::new(ReadBaseDistribution::new(
maxreadlen,
prec,
ndarray::Dim([sz as usize, 4]),
));
for distrib in &distrib_collector {
let ref_distrib = distrib.as_ref().unwrap();
base_distribution.readsizehisto += &ref_distrib.readsizehisto;
base_distribution.upper_histo = maxreadlen;
base_distribution.histo_out += ref_distrib.histo_out;
base_distribution.non_acgt += ref_distrib.non_acgt;
base_distribution.acgt_distribution += &(ref_distrib.acgt_distribution);
} base_distribution.acgt_distribution *= 1. / (seq_array.len() as f64);
let elapsed_t = start_t.elapsed().as_secs();
println!(" elapsed time (s) in get_base_count {} ", elapsed_t);
log::info!(
"nb read outside max size for histogram {}",
base_distribution.histo_out
);
base_distribution
.ascii_dump_acgt_distribution(&String::from("bases.histo"))
.unwrap();
Some(base_distribution)
}