use super::create::{gdata_t, gdata0_t, parse_bed};
use gtars_core::consts::{BED_FILE_EXTENSION, IGD_FILE_EXTENSION};
use gtars_core::utils::get_dynamic_reader;
use byteorder::{LittleEndian, ReadBytesExt};
use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader, Error, Read, Seek, SeekFrom};
use std::path::{Path, PathBuf};
#[derive(Default)]
pub struct igd_t_from_disk {
pub nFiles: i32,
pub file_info: Vec<info_t>,
pub filename: String,
pub nbp: i32, pub gType: i32, pub nCtg: i32, pub cName: Vec<String>,
pub nTile: Vec<i32>,
pub nCnt: Vec<Vec<i32>>,
pub tIdx: Vec<Vec<i64>>,
}
impl igd_t_from_disk {
pub fn new() -> Self {
Self::default()
}
}
#[derive(Default)]
pub struct info_t {
pub fileName: String, pub nr: i32, pub md: f64, }
#[allow(unused_variables)]
pub fn igd_search(database_path: &str, query_file_path: &str) -> Result<Vec<String>, String> {
let mode = 1;
let mut hash_table: HashMap<String, i32> = HashMap::new();
let mut final_string_vec = Vec::new();
match check_file_extension(database_path, IGD_FILE_EXTENSION) {
Ok(_) => (),
Err(e) => return Err(e),
}
match check_file_extension(query_file_path, BED_FILE_EXTENSION) {
Ok(_) => (),
Err(e) => {
return Err(e);
}
}
let mut IGD: igd_t_from_disk =
get_igd_info(database_path, &mut hash_table).expect("Could not open IGD");
let tsv_path = get_tsv_path(database_path).unwrap();
get_file_info_tsv(tsv_path, &mut IGD).unwrap();
let nfiles = IGD.nFiles;
let mut hits: Vec<i64> = vec![0; nfiles as usize];
match mode {
1 => {
if IGD.gType == 0 {
println!("gType = 0");
} else {
getOverlaps(
&IGD,
database_path,
query_file_path,
&mut hits,
&mut hash_table,
);
}
println!("index\t number of regions\t number of hits\t File_name");
let format_string =
"index\t number of regions\t number of hits\t File_name".to_string();
final_string_vec.push(format_string);
let mut total: i64 = 0;
for (i, hit) in hits.iter().enumerate() {
if *hit > 0 {
println!(
"{}\t{}\t{}\t{}",
i, IGD.file_info[i].nr, hit, IGD.file_info[i].fileName
);
let format_string = format!(
"{}\t{}\t{}\t{}",
i, IGD.file_info[i].nr, hit, IGD.file_info[i].fileName
);
final_string_vec.push(format_string);
}
total += hit;
}
println!("Total: {}", total);
}
_ => {
println!("Invalid mode selected, exiting");
return Err(String::from("Invalid mode selected, exiting"));
}
}
println!("FINISHED");
Ok(final_string_vec)
}
#[allow(unused_variables)]
pub fn getOverlaps(
IGD: &igd_t_from_disk,
database_path: &str,
query_file: &str,
hits: &mut [i64],
hash_table: &mut HashMap<String, i32>,
) -> i32 {
let mut start = 0;
let mut end = 0;
let mut va = 0;
let mut ols = 0;
let mut preChr = -6;
let mut preIdx = -8;
let path = Path::new(query_file);
let reader = get_dynamic_reader(path).unwrap();
let parent_path = database_path;
let dbpath = std::path::Path::new(&parent_path);
let db_file = OpenOptions::new()
.create(true)
.append(true)
.read(true)
.open(dbpath)
.unwrap();
let mut db_reader = BufReader::new(db_file);
for line in reader.lines() {
let line = line.unwrap();
let ctg = parse_bed(&line, &mut start, &mut end, &mut va);
match ctg {
Some(ctg) => {
let nl = get_overlaps(
IGD,
ctg,
start,
end,
hits,
hash_table,
&mut preChr,
&mut preIdx,
path,
&mut db_reader,
);
ols += nl;
}
None => continue,
}
}
ols
}
#[allow(unused_variables, clippy::too_many_arguments)]
fn get_overlaps(
IGD: &igd_t_from_disk,
ctg: String,
query_start: i32,
query_end: i32,
hits: &mut [i64],
hash_table: &mut HashMap<String, i32>,
preChr: &mut i32,
preIdx: &mut i32,
query_path: &Path,
db_reader: &mut BufReader<File>,
) -> i32 {
let ichr = get_id(ctg, hash_table);
if ichr < 0 {
return 0;
}
let n1 = query_start / IGD.nbp;
let mut n2 = (query_end - 1) / IGD.nbp;
let i: i32;
let j: i32;
let ni: i32;
let tE: i32;
let mut tS: i32;
let mut tL: i32;
let mut tR: i32;
let mut tM: i32;
let mut tmpi: i32;
let mut tmpi1: i32;
let mlen: i32;
let nols = 0;
let mTile = IGD.nTile[ichr as usize] - 1;
if n1 > mTile {
return 0;
}
n2 = if n2 < mTile { n2 } else { mTile };
tmpi = IGD.nCnt[ichr as usize][n1 as usize];
tmpi1 = tmpi - 1;
if tmpi > 0 {
db_reader
.seek(SeekFrom::Start(IGD.tIdx[ichr as usize][n1 as usize] as u64))
.unwrap();
let mut gData: Vec<gdata_t> = Vec::new();
for j in 0..tmpi {
gData.push(gdata_t::default())
}
for i in 0..tmpi {
let mut buf = [0u8; 16];
let n = db_reader.read(&mut buf).unwrap();
if n == 0 {
break;
} else if n != 16 {
break;
}
let mut rdr = &buf[..] as &[u8];
let idx = rdr.read_i32::<LittleEndian>().unwrap();
let start = rdr.read_i32::<LittleEndian>().unwrap();
let end = rdr.read_i32::<LittleEndian>().unwrap();
let value = rdr.read_i32::<LittleEndian>().unwrap();
gData[i as usize] = gdata_t {
idx,
start,
end,
value,
};
*preIdx = n1;
*preChr = ichr;
}
if query_end > gData[0].start {
tL = 0;
tR = tmpi1;
while tL < tR - 1 {
tM = (tL + tR) / 2; if gData[tM as usize].start < query_end {
tL = tM; } else {
tR = tM; }
}
if gData[tR as usize].start < query_end {
tL = tR;
}
for i in (0..=tL).rev() {
if gData[i as usize].end > query_start {
hits[gData[i as usize].idx as usize] += 1;
}
}
}
if n2 > n1 {
let mut bd = IGD.nbp * (n1 + 1); for j in (n1 + 1)..=n2 {
tmpi = IGD.nCnt[ichr as usize][j as usize];
tmpi1 = tmpi - 1;
if tmpi > 0 {
let mut gData: Vec<gdata_t> = Vec::with_capacity(tmpi as usize);
if j != *preIdx || ichr != *preChr {
db_reader
.seek(SeekFrom::Start(IGD.tIdx[ichr as usize][j as usize] as u64))
.unwrap();
for i in 0..tmpi {
let mut buf = [0u8; 16];
let n = db_reader.read(&mut buf).unwrap();
if n == 0 {
break;
} else if n != 16 {
break;
}
let mut rdr = &buf[..] as &[u8];
let idx = rdr.read_i32::<LittleEndian>().unwrap();
let start = rdr.read_i32::<LittleEndian>().unwrap();
let end = rdr.read_i32::<LittleEndian>().unwrap();
let value = rdr.read_i32::<LittleEndian>().unwrap();
gData.push(gdata_t {
idx,
start,
end,
value,
});
*preIdx = j;
*preChr = ichr;
}
}
if query_end > gData[0].start {
tS = 0;
while tS < tmpi && gData[tS as usize].start < bd {
tS += 1;
}
tL = 0;
tR = tmpi1;
while tL < tR - 1 {
tM = (tL + tR) / 2;
if gData[tM as usize].start < query_end {
tL = tM;
} else {
tR = tM;
}
}
if gData[tR as usize].start < query_end {
tL = tR;
}
for i in (tS..=tL).rev() {
if gData[i as usize].end > query_start {
hits[gData[i as usize].idx as usize] += 1;
}
}
}
}
bd += IGD.nbp;
}
}
}
nols }
#[allow(unused_variables)]
fn get_id(ctg: String, hash_table: &mut HashMap<String, i32>) -> i32 {
let key_check = hash_table.contains_key(&ctg);
if !key_check {
-1
} else {
let value = hash_table.get(&ctg).unwrap();
*value
}
}
pub fn get_tsv_path(igd_path: &str) -> Option<PathBuf> {
let igd_path = Path::new(igd_path);
let stem = igd_path.file_stem()?;
let mut tsv_path = igd_path.with_file_name(stem);
tsv_path.set_extension("tsv");
Some(tsv_path)
}
#[allow(unused_variables)]
pub fn get_igd_info(
database_path: &str,
hash_table: &mut HashMap<String, i32>,
) -> Result<igd_t_from_disk, Error> {
let mut igd = igd_t_from_disk::new();
let parent_path = database_path;
let dbpath = std::path::Path::new(&parent_path);
let temp_tile_file = match OpenOptions::new()
.create(true)
.append(true)
.read(true)
.open(dbpath)
{
Ok(temp_tile_file) => temp_tile_file,
Err(err) => {
println!("Error opening file: {}", err);
return Err(err);
}
};
let mut reader = BufReader::new(temp_tile_file);
let mut buffer = [0u8; 4];
reader.read_exact(&mut buffer)?;
let nbp = i32::from_le_bytes(buffer);
reader.read_exact(&mut buffer)?;
let gType = i32::from_le_bytes(buffer);
reader.read_exact(&mut buffer)?;
let nCtg = i32::from_le_bytes(buffer);
println!("Found:\n nbp:{} gtype: {} nCtg: {}", nbp, gType, nCtg);
igd.nbp = nbp;
igd.gType = gType;
igd.nCtg = nCtg;
let gdsize = if gType == 0 {
std::mem::size_of::<gdata0_t>()
} else {
std::mem::size_of::<gdata_t>()
};
let tileS = igd.nCtg;
let m = igd.nCtg;
let mut n_Tile: Vec<i32> = Vec::with_capacity(m as usize);
for _ in 0..m {
reader.read_exact(&mut buffer)?;
let tile_value = i32::from_le_bytes(buffer);
n_Tile.push(tile_value);
}
igd.nTile = n_Tile.clone();
let mut chr_loc = (12 + 44 * m) as i64;
for n in 0..m {
chr_loc += (n_Tile[n as usize] as i64) * 4;
}
let mut nCnt: Vec<Vec<i32>> = Vec::new();
for _ in 0..n_Tile.len() {
nCnt.push(Vec::new());
}
let mut tIdx: Vec<Vec<i64>> = Vec::new();
for _ in 0..n_Tile.len() {
tIdx.push(Vec::new());
}
for i in 0..m {
let k = igd.nTile[i as usize];
let mut cnt = vec![0; k as usize]; for kdx in 0..k {
cnt[kdx as usize] = reader.read_i32::<LittleEndian>()?;
}
nCnt[i as usize] = cnt;
let idx = vec![0; k as usize];
tIdx[i as usize] = idx;
tIdx[i as usize][0] = chr_loc;
for j in 1..k {
tIdx[i as usize][j as usize] = tIdx[i as usize][j as usize - 1]
+ (nCnt[i as usize][j as usize - 1] as i64) * gdsize as i64;
}
chr_loc = tIdx[i as usize][k as usize - 1]
+ nCnt[i as usize][k as usize - 1] as i64 * gdsize as i64;
}
igd.nCnt = nCnt;
igd.tIdx = tIdx;
let mut c_name = Vec::with_capacity(m as usize);
for _ in 0..m {
let mut buf = [0u8; 40];
reader.read_exact(&mut buf)?;
let name = String::from_utf8(buf.to_vec()).unwrap(); let name = name.trim_matches('\0');
c_name.push(String::from(name)); }
igd.cName = c_name.clone();
for (i, name) in igd.cName.iter().enumerate() {
hash_table.insert(name.to_string(), i as i32);
}
Ok(igd)
}
pub fn get_file_info_tsv(tsv_path: PathBuf, igd: &mut igd_t_from_disk) -> Result<(), Error> {
let path = Path::new(&tsv_path);
let tsv_file = match OpenOptions::new()
.create(true)
.append(true)
.read(true)
.open(path)
{
Ok(temp_tile_file) => temp_tile_file,
Err(err) => {
println!("Error opening file: {}", err);
return Err(err);
}
};
let reader = BufReader::new(tsv_file);
let mut lines = reader.lines();
lines.next();
let mut infos: Vec<info_t> = Vec::new();
let mut count = 0;
for line in lines {
count += 1;
let line = line?;
let fields: Vec<&str> = line.split('\t').collect();
let info = info_t {
fileName: fields[1].to_string(),
nr: fields[2].to_string().as_str().trim().parse().unwrap(),
md: fields[3].to_string().as_str().trim().parse().unwrap(),
};
infos.push(info);
}
igd.nFiles = count;
igd.file_info = infos;
Ok(())
}
fn check_file_extension(path: &str, expected_extension: &str) -> Result<(), String> {
let path = Path::new(path);
let actual_extension = path
.extension()
.and_then(|ext| ext.to_str())
.ok_or_else(|| format!("Invalid file path: {}", path.display()))?;
if actual_extension != expected_extension {
return Err(format!(
"Incorrect file extension. Expected: {}, got: {}",
expected_extension, actual_extension
));
}
Ok(())
}