use rust_lapper::{Interval as LapperInterval, Lapper};
use crate::intervals::{Interval, Intervals};
pub struct Overlapper<T: Clone + Send + Sync + Eq> {
trees: Vec<Option<Lapper<u32, T>>>,
}
impl<T: Clone + Send + Sync + Eq> Overlapper<T> {
pub fn new(intervals: impl Iterator<Item = (usize, u32, u32, T)>) -> Self {
let mut by_contig: Vec<Vec<LapperInterval<u32, T>>> = Vec::new();
for (ref_id, start, stop, val) in intervals {
if ref_id >= by_contig.len() {
by_contig.resize_with(ref_id + 1, Vec::new);
}
by_contig[ref_id].push(LapperInterval { start, stop, val });
}
let trees: Vec<Option<Lapper<u32, T>>> = by_contig
.into_iter()
.map(|ivs| if ivs.is_empty() { None } else { Some(Lapper::new(ivs)) })
.collect();
Self { trees }
}
#[must_use]
pub fn from_intervals(intervals: &Intervals) -> Overlapper<Interval> {
Overlapper::<Interval>::new(
intervals.iter().map(|iv| (iv.ref_id, iv.start, iv.end, iv.clone())),
)
}
pub fn get_overlaps(&self, ref_id: usize, start: u32, end: u32) -> impl Iterator<Item = &T> {
self.trees
.get(ref_id)
.and_then(Option::as_ref)
.into_iter()
.flat_map(move |lapper| lapper.find(start, end).map(|iv| &iv.val))
}
pub fn overlaps_any(&self, ref_id: usize, start: u32, end: u32) -> bool {
self.trees
.get(ref_id)
.and_then(Option::as_ref)
.is_some_and(|lapper| lapper.find(start, end).next().is_some())
}
#[must_use]
pub fn contains_position(&self, ref_id: usize, pos: u32) -> bool {
self.overlaps_any(ref_id, pos, pos + 1)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_overlap() {
let overlapper = Overlapper::new(
vec![(0_usize, 10_u32, 20_u32, "A"), (0, 30, 40, "B"), (1, 5, 15, "C")].into_iter(),
);
let hits: Vec<&&str> = overlapper.get_overlaps(0, 15, 25).collect();
assert_eq!(hits, vec![&"A"]);
let hits: Vec<&&str> = overlapper.get_overlaps(0, 15, 35).collect();
assert_eq!(hits.len(), 2);
let hits: Vec<&&str> = overlapper.get_overlaps(1, 0, 10).collect();
assert_eq!(hits, vec![&"C"]);
let hits: Vec<&&str> = overlapper.get_overlaps(2, 0, 100).collect();
assert!(hits.is_empty());
}
#[test]
fn test_overlaps_any() {
let overlapper = Overlapper::new(vec![(0_usize, 10_u32, 20_u32, 1)].into_iter());
assert!(overlapper.overlaps_any(0, 15, 25));
assert!(overlapper.overlaps_any(0, 5, 15));
assert!(!overlapper.overlaps_any(0, 20, 30)); assert!(!overlapper.overlaps_any(0, 0, 10)); assert!(!overlapper.overlaps_any(1, 10, 20)); }
#[test]
fn test_contains_position() {
let overlapper = Overlapper::new(vec![(0_usize, 10_u32, 20_u32, "X")].into_iter());
assert!(!overlapper.contains_position(0, 9));
assert!(overlapper.contains_position(0, 10));
assert!(overlapper.contains_position(0, 19));
assert!(!overlapper.contains_position(0, 20)); }
#[test]
fn test_empty_overlapper() {
let overlapper: Overlapper<i32> = Overlapper::new(std::iter::empty());
assert!(!overlapper.overlaps_any(0, 0, 100));
assert!(overlapper.get_overlaps(0, 0, 100).next().is_none());
}
#[test]
fn test_sparse_contigs() {
let overlapper = Overlapper::new(vec![(5_usize, 100_u32, 200_u32, "Z")].into_iter());
assert!(!overlapper.overlaps_any(0, 0, 1000));
assert!(!overlapper.overlaps_any(4, 0, 1000));
assert!(overlapper.overlaps_any(5, 150, 160));
}
}