use std::ops::Deref;
use gtars_core::models::region_set::IntervalSetOps;
use gtars_core::models::{Region, RegionSet};
use crate::multi_chrom_overlapper::{build_indexed_overlapper, MultiChromOverlapper};
use crate::OverlapperType;
pub struct IndexedRegionSet {
source: RegionSet,
index: MultiChromOverlapper<u32, ()>,
}
impl IndexedRegionSet {
pub fn new(regions: RegionSet) -> Self {
Self::with_overlapper_type(regions, OverlapperType::AIList)
}
pub fn with_overlapper_type(regions: RegionSet, overlapper_type: OverlapperType) -> Self {
let index = build_indexed_overlapper(®ions, overlapper_type);
Self {
source: regions,
index,
}
}
fn coord_lookup(&self) -> std::collections::HashMap<(&str, u32, u32), Vec<usize>> {
let mut map: std::collections::HashMap<(&str, u32, u32), Vec<usize>> =
std::collections::HashMap::new();
for (i, r) in self.source.regions.iter().enumerate() {
map.entry((r.chr.as_str(), r.start, r.end)).or_default().push(i);
}
map
}
pub fn regions(&self) -> &RegionSet {
&self.source
}
pub fn into_regions(self) -> RegionSet {
self.source
}
pub fn intersect_all(&self, query: &RegionSet) -> RegionSet {
let mut hits: std::collections::BTreeSet<usize> = std::collections::BTreeSet::new();
let hit_lists = self.find_overlaps(query, None);
for list in hit_lists {
for idx in list {
hits.insert(idx);
}
}
let kept: Vec<Region> = hits
.into_iter()
.map(|idx| self.source.regions[idx].clone())
.collect();
RegionSet::from(kept)
}
pub fn subset_by_overlaps(&self, query: &RegionSet, min_overlap: Option<i32>) -> RegionSet {
let mut hits: std::collections::BTreeSet<usize> = std::collections::BTreeSet::new();
for list in self.find_overlaps(query, min_overlap) {
for idx in list {
hits.insert(idx);
}
}
let kept: Vec<Region> = hits
.into_iter()
.map(|idx| self.source.regions[idx].clone())
.collect();
RegionSet::from(kept)
}
pub fn count_overlaps(&self, query: &RegionSet, min_overlap: Option<i32>) -> Vec<usize> {
self.index.count_overlaps(query, min_overlap)
}
pub fn any_overlaps(&self, query: &RegionSet, min_overlap: Option<i32>) -> Vec<bool> {
self.index.any_overlaps(query, min_overlap)
}
pub fn find_overlaps(&self, query: &RegionSet, min_overlap: Option<i32>) -> Vec<Vec<usize>> {
let lookup = self.coord_lookup();
let region_hits = self.index.find_overlaps_regions(query, min_overlap);
region_hits
.into_iter()
.map(|regions| {
let mut idxs: Vec<usize> = Vec::new();
for r in regions {
if let Some(src_idxs) = lookup.get(&(r.chr.as_str(), r.start, r.end)) {
idxs.extend_from_slice(src_idxs);
}
}
idxs.sort_unstable();
idxs.dedup();
idxs
})
.collect()
}
pub fn closest(&self, other: &RegionSet) -> Vec<(usize, usize, i64)> {
self.source.closest(other)
}
pub fn union(&self, other: &RegionSet) -> RegionSet {
self.source.union(other)
}
pub fn cluster(&self, max_gap: u32) -> Vec<u32> {
self.source.cluster(max_gap)
}
}
impl IntervalSetOps for IndexedRegionSet {
fn setdiff(&self, other: &RegionSet) -> RegionSet {
self.index.setdiff(other)
}
fn intersect(&self, other: &RegionSet) -> RegionSet {
self.index.intersect(other)
}
fn jaccard(&self, other: &RegionSet) -> f64 {
self.index.jaccard(other)
}
fn coverage(&self, other: &RegionSet) -> f64 {
self.index.coverage(other)
}
fn overlap_coefficient(&self, other: &RegionSet) -> f64 {
self.index.overlap_coefficient(other)
}
fn closest(&self, other: &RegionSet) -> Vec<(usize, usize, i64)> {
self.source.closest(other)
}
}
impl Deref for IndexedRegionSet {
type Target = RegionSet;
fn deref(&self) -> &Self::Target {
&self.source
}
}
impl From<RegionSet> for IndexedRegionSet {
fn from(regions: RegionSet) -> Self {
Self::new(regions)
}
}
impl From<IndexedRegionSet> for RegionSet {
fn from(indexed: IndexedRegionSet) -> Self {
indexed.into_regions()
}
}
#[cfg(test)]
mod tests {
use super::*;
use gtars_core::models::Region;
fn make_region(chr: &str, start: u32, end: u32) -> Region {
Region {
chr: chr.to_string(),
start,
end,
rest: None,
}
}
#[test]
fn test_new_and_regions() {
let rs = RegionSet::from(vec![
make_region("chr1", 100, 200),
make_region("chr1", 300, 400),
]);
let indexed = IndexedRegionSet::new(rs.clone());
assert_eq!(indexed.regions().len(), rs.len());
}
#[test]
fn test_with_overlapper_type() {
let rs = RegionSet::from(vec![make_region("chr1", 100, 200)]);
let indexed_ailist = IndexedRegionSet::new(rs.clone());
let indexed_bits = IndexedRegionSet::with_overlapper_type(rs, OverlapperType::Bits);
let query = RegionSet::from(vec![make_region("chr1", 150, 250)]);
assert_eq!(
indexed_ailist.count_overlaps(&query, None),
indexed_bits.count_overlaps(&query, None)
);
}
#[test]
fn test_into_regions() {
let rs = RegionSet::from(vec![make_region("chr1", 100, 200)]);
let indexed = IndexedRegionSet::new(rs.clone());
let rs_back = indexed.into_regions();
assert_eq!(rs_back.len(), rs.len());
}
#[test]
fn test_intersect_all() {
let reference = RegionSet::from(vec![
make_region("chr1", 100, 200),
make_region("chr1", 300, 400),
make_region("chr2", 500, 600),
]);
let indexed = IndexedRegionSet::new(reference);
let query = RegionSet::from(vec![
make_region("chr1", 150, 250), make_region("chr2", 550, 650), ]);
let result = indexed.intersect_all(&query);
assert_eq!(result.regions.len(), 2);
assert_eq!(result.regions[0].start, 100);
assert_eq!(result.regions[0].end, 200);
assert_eq!(result.regions[1].start, 500);
assert_eq!(result.regions[1].end, 600);
}
#[test]
fn test_count_overlaps() {
let reference = RegionSet::from(vec![
make_region("chr1", 100, 200),
make_region("chr1", 150, 250),
make_region("chr1", 300, 400),
]);
let indexed = IndexedRegionSet::new(reference);
let query = RegionSet::from(vec![make_region("chr1", 180, 220)]);
let counts = indexed.count_overlaps(&query, None);
assert_eq!(counts, vec![2]); }
#[test]
fn test_any_overlaps() {
let reference = RegionSet::from(vec![make_region("chr1", 100, 200)]);
let indexed = IndexedRegionSet::new(reference);
let query = RegionSet::from(vec![
make_region("chr1", 150, 250), make_region("chr1", 300, 400), ]);
let any = indexed.any_overlaps(&query, None);
assert_eq!(any, vec![true, false]);
}
#[test]
fn test_find_overlaps() {
let reference = RegionSet::from(vec![
make_region("chr1", 100, 200), make_region("chr1", 300, 400), ]);
let indexed = IndexedRegionSet::new(reference);
let query = RegionSet::from(vec![make_region("chr1", 150, 350)]);
let indices = indexed.find_overlaps(&query, None);
assert_eq!(indices.len(), 1);
let mut sorted = indices[0].clone();
sorted.sort();
assert_eq!(sorted, vec![0, 1]);
}
#[test]
fn test_deref_to_regionset() {
let rs = RegionSet::from(vec![
make_region("chr1", 100, 200),
make_region("chr2", 300, 400),
]);
let indexed = IndexedRegionSet::new(rs);
assert_eq!(indexed.len(), 2);
assert!(!indexed.is_empty());
let chroms: Vec<_> = indexed.iter_chroms().collect();
assert_eq!(chroms.len(), 2);
}
#[test]
fn test_from_into_conversions() {
let rs = RegionSet::from(vec![make_region("chr1", 100, 200)]);
let indexed: IndexedRegionSet = rs.clone().into();
assert_eq!(indexed.len(), 1);
let rs_back: RegionSet = indexed.into();
assert_eq!(rs_back.len(), 1);
}
#[test]
fn test_empty_indexed_regionset() {
let rs = RegionSet::from(vec![]);
let indexed = IndexedRegionSet::new(rs);
assert!(indexed.is_empty());
assert_eq!(indexed.len(), 0);
let query = RegionSet::from(vec![make_region("chr1", 100, 200)]);
assert_eq!(indexed.count_overlaps(&query, None), vec![0]);
assert_eq!(indexed.any_overlaps(&query, None), vec![false]);
assert_eq!(indexed.find_overlaps(&query, None), vec![vec![] as Vec<usize>]);
assert_eq!(indexed.intersect_all(&query).regions.len(), 0);
}
#[test]
fn test_empty_query() {
let reference = RegionSet::from(vec![make_region("chr1", 100, 200)]);
let indexed = IndexedRegionSet::new(reference);
let query = RegionSet::from(vec![]);
assert_eq!(indexed.count_overlaps(&query, None), Vec::<usize>::new());
assert_eq!(indexed.any_overlaps(&query, None), Vec::<bool>::new());
assert_eq!(indexed.find_overlaps(&query, None), Vec::<Vec<usize>>::new());
assert_eq!(indexed.intersect_all(&query).regions.len(), 0);
}
#[test]
fn test_multi_chrom() {
let reference = RegionSet::from(vec![
make_region("chr1", 100, 200),
make_region("chr2", 100, 200),
make_region("chr3", 100, 200),
]);
let indexed = IndexedRegionSet::new(reference);
let query = RegionSet::from(vec![
make_region("chr1", 150, 250),
make_region("chr2", 150, 250),
make_region("chr4", 150, 250), ]);
let counts = indexed.count_overlaps(&query, None);
assert_eq!(counts, vec![1, 1, 0]);
let any = indexed.any_overlaps(&query, None);
assert_eq!(any, vec![true, true, false]);
}
}