use super::trim_pairwise;
use super::CombinatorialBarcode;
use super::Chemistry;
use seq_io::fastq::Reader as FastqReader;
use std::io::Cursor;
use std::cmp::min;
use crate::fileformat::shard::CellID;
use crate::fileformat::shard::ReadPair;
#[derive(Clone)]
pub struct AtrandiWGSChemistry {
barcode: CombinatorialBarcode,
num_reads_pass: usize,
num_reads_fail: usize,
num_adapt_trim1: usize,
}
impl AtrandiWGSChemistry {
pub fn new() -> AtrandiWGSChemistry {
let atrandi_bcs = include_bytes!("atrandi_barcodes.tsv");
let barcode = CombinatorialBarcode::read_barcodes(Cursor::new(atrandi_bcs));
AtrandiWGSChemistry {
barcode: barcode,
num_reads_pass: 0,
num_reads_fail: 0,
num_adapt_trim1: 0,
}
}
}
impl Chemistry for AtrandiWGSChemistry {
fn prepare(
&mut self,
_fastq_file_r1: &mut FastqReader<Box<dyn std::io::Read>>,
fastq_file_r2: &mut FastqReader<Box<dyn std::io::Read>>
) -> anyhow::Result<()> {
println!("Preparing to debarcode Atrandi WGS data");
self.barcode.find_probable_barcode_boundaries(fastq_file_r2, 1000).expect("Failed to detect barcode setup from reads");
Ok(())
}
fn detect_barcode_and_trim(
&mut self,
r1_seq: &[u8],
r1_qual: &[u8],
r2_seq: &[u8],
r2_qual: &[u8]
) -> (bool, CellID, ReadPair) {
let total_distance_cutoff = 1; let (isok, bc) = self.barcode.detect_barcode(
r2_seq,
false,
total_distance_cutoff
);
if isok {
let r1_from=0;
let mut r1_to=r1_seq.len();
let barcode_size = 8+4+8+4+8+4+8 + 2;
let r2_from = barcode_size;
let mut r2_to = r2_seq.len();
let overlap_size = 12;
let adapter_seq = &r2_seq[(r2_seq.len()-overlap_size)..(r2_seq.len())];
let adapter_seq_rc = trim_pairwise::revcomp_n(&adapter_seq);
let adapter_pos = find_subsequence_mismatch(r1_seq,adapter_seq_rc.as_slice(),1);
if let Some(adapter_pos) = adapter_pos {
self.num_adapt_trim1 += 1;
let insert_size = r2_seq.len() + adapter_pos;
if insert_size<barcode_size {
return (false, "".to_string(), ReadPair{r1: r1_seq.to_vec(), r2: r2_seq.to_vec(), q1: r1_qual.to_vec(), q2: r2_qual.to_vec(), umi: vec![].to_vec()});
}
let max_r1 = insert_size - barcode_size;
r1_to = min(r1_to, max_r1);
r2_to = min(r2_to,insert_size);
}
self.num_reads_pass += 1;
(true, bc, ReadPair{
r1: r1_seq[r1_from..r1_to].to_vec(), r2: r2_seq[r2_from..r2_to].to_vec(),
q1: r1_qual[r1_from..r1_to].to_vec(),
q2: r2_qual[r2_from..r2_to].to_vec(),
umi: vec![].to_vec()})
} else {
self.num_reads_fail += 1;
(false, "".to_string(), ReadPair{r1: r1_seq.to_vec(), r2: r2_seq.to_vec(), q1: r1_qual.to_vec(), q2: r2_qual.to_vec(), umi: vec![].to_vec()})
}
}
}
pub fn find_subsequence<T>(haystack: &[T], needle: &[T])
-> Option<usize>
where for<'a> &'a [T]: PartialEq {
haystack.windows(needle.len()).position(|window| window == needle)
}
fn find_subsequence_mismatch(haystack: &[u8], needle: &[u8], allow_mismatches: u8) -> Option<usize> {
for pos in 0..(haystack.len()-needle.len()) {
let mut mismatch=0;
let mut is_match = true;
for i in 0..needle.len() {
if needle[i] != haystack[pos+i] {
mismatch += 1;
if mismatch>allow_mismatches {
is_match = false;
break;
}
}
}
if is_match {
return Some(pos);
}
}
None
}