use std::cmp::Ordering;
#[derive(Debug)]
pub struct ScAIList<T: Clone + Eq + std::fmt::Debug> {
intervals: Vec<Interval<T>>,
num_comps: usize,
comp_lens: Vec<usize>,
comp_idxs: Vec<usize>,
max_ends: Vec<u32>,
}
#[derive(Eq, Debug, Clone, Copy)]
pub struct Interval<T: Clone + Eq + std::fmt::Debug> {
pub start: u32,
pub end: u32,
pub val: T,
}
impl<T: Clone + Eq + std::fmt::Debug> ScAIList<T> {
pub fn new(mut input_intervals: Vec<Interval<T>>, min_cov_len: Option<usize>) -> Self {
let max_comps = (input_intervals.len() as f64).log2().floor() as usize + 1;
let min_cov_len = min_cov_len.unwrap_or(20); let min_cov = min_cov_len / 2;
let num_comps; let mut comp_lens = vec![]; let mut comp_idxs = vec![]; let mut max_ends = vec![];
let min_comp_len = std::cmp::max(64, min_cov_len);
let input_len = input_intervals.len();
input_intervals.sort();
let mut decomposed = vec![];
if input_len <= min_comp_len {
num_comps = 1;
comp_lens.push(input_len);
comp_idxs.push(0);
decomposed.append(&mut input_intervals);
} else {
let mut curr_comp = 0;
while curr_comp < max_comps && input_len - decomposed.len() > min_comp_len {
let mut list1 = vec![];
let mut list2 = vec![];
for i in 0..input_intervals.len() {
let interval = &input_intervals[i];
let mut j = 1;
let mut cov = 1;
while j < min_comp_len && cov < min_cov && j + i < input_intervals.len() {
if input_intervals[j + i].end >= interval.end {
cov += 1;
}
j += 1;
}
if cov < min_cov {
list1.push(input_intervals[i].clone());
} else {
list2.push(input_intervals[i].clone())
}
}
comp_idxs.push(decomposed.len());
comp_lens.push(list1.len());
curr_comp += 1;
if list2.len() <= min_comp_len || curr_comp == max_comps - 2 {
if !list2.is_empty() {
decomposed.append(&mut list1);
comp_idxs.push(decomposed.len());
comp_lens.push(list2.len());
decomposed.append(&mut list2);
curr_comp += 1;
}
} else {
decomposed.append(&mut list1);
input_intervals = list2;
}
}
num_comps = curr_comp;
}
for j in 0..num_comps {
let comp_start = comp_idxs[j];
let comp_end = comp_start + comp_lens[j];
let mut max_end = decomposed[comp_start].end;
max_ends.push(max_end);
for iv in decomposed[comp_start + 1..comp_end].iter() {
if iv.end > max_end {
max_end = iv.end;
}
max_ends.push(max_end);
}
}
ScAIList {
num_comps,
comp_idxs,
comp_lens,
max_ends,
intervals: decomposed,
}
}
#[inline]
pub fn upper_bound(stop: u32, intervals: &[Interval<T>]) -> Option<usize> {
let mut right = intervals.len();
let mut left = 0;
if intervals[right - 1].start < stop {
return Some(right - 1);
} else if intervals[left].start >= stop {
return None;
}
while right > 0 {
let half = right / 2;
let other_half = right - half;
let probe = left + half;
let other_left = left + other_half;
let v = &intervals[probe];
right = half;
left = if v.start < stop { other_left } else { left }
}
if intervals[left].start >= stop {
Some(left - 1)
} else {
Some(left)
}
}
#[inline]
pub fn iter(&self) -> IterScAIList<T> {
IterScAIList {
inner: self,
pos: 0,
}
}
#[inline]
pub fn find(&self, start: u32, stop: u32) -> IterFind<T> {
IterFind {
comp_num: 0,
offset: 0,
inner: self,
start,
stop,
find_offset: true,
breaknow: false,
}
}
}
#[derive(Debug)]
pub struct IterFind<'a, T: Clone + Eq + std::fmt::Debug> {
inner: &'a ScAIList<T>,
offset: usize,
comp_num: usize,
stop: u32,
start: u32,
find_offset: bool,
breaknow: bool,
}
impl<'a, T: Clone + Eq + std::fmt::Debug> Iterator for IterFind<'a, T> {
type Item = &'a Interval<T>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
while self.comp_num < self.inner.num_comps {
let comp_start = self.inner.comp_idxs[self.comp_num];
let comp_end = comp_start + self.inner.comp_lens[self.comp_num];
if self.inner.comp_lens[self.comp_num] > 15 {
if self.find_offset {
self.offset = match ScAIList::upper_bound(
self.stop,
&self.inner.intervals[comp_start..comp_end],
) {
Some(n) => n,
None => {
self.comp_num += 1;
self.find_offset = true;
continue;
}
};
self.offset += comp_start;
self.find_offset = false;
}
while self.offset >= comp_start
&& self.inner.max_ends[self.offset] > self.start
&& !self.breaknow
{
let interval = &self.inner.intervals[self.offset];
self.offset = match self.offset.checked_sub(1) {
Some(n) => n,
None => {
self.breaknow = true;
0
}
};
if interval.end > self.start {
return Some(interval);
}
}
} else {
while self.offset < comp_end {
let interval = &self.inner.intervals[self.offset];
self.offset += 1;
if interval.start < self.stop && interval.end > self.start {
return Some(interval);
}
}
}
self.breaknow = false;
self.find_offset = true;
self.comp_num += 1;
}
None
}
}
pub struct IterScAIList<'a, T>
where
T: Clone + Eq + std::fmt::Debug + 'a,
{
inner: &'a ScAIList<T>,
pos: usize,
}
impl<'a, T: Clone + Eq + std::fmt::Debug> Iterator for IterScAIList<'a, T> {
type Item = &'a Interval<T>;
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.inner.intervals.len() {
None
} else {
self.pos += 1;
Some(&self.inner.intervals[self.pos - 1])
}
}
}
impl<T: Clone + Eq + std::fmt::Debug> Interval<T> {
#[inline]
pub fn intersect(&self, other: &Self) -> u32 {
std::cmp::min(self.end, other.end)
.checked_sub(std::cmp::max(self.start, other.start))
.unwrap_or(0)
}
pub fn intersect_raw(&self, start: u32, end: u32) -> u32 {
std::cmp::min(self.end, end)
.checked_sub(std::cmp::max(self.start, start))
.unwrap_or(0)
}
#[inline]
pub fn overlap(&self, start: u32, end: u32) -> bool {
self.start < end && self.end > start
}
}
impl<T: Clone + Eq + std::fmt::Debug> Ord for Interval<T> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
if self.start < other.start {
Ordering::Less
} else if other.start < self.start {
Ordering::Greater
} else {
self.end.cmp(&other.end)
}
}
}
impl<T: Clone + Eq + std::fmt::Debug> PartialOrd for Interval<T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(&other))
}
}
impl<T: Clone + Eq + std::fmt::Debug> PartialEq for Interval<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.start == other.start && self.end == other.end
}
}
#[cfg(test)]
#[rustfmt::skip]
mod tests {
use super::*;
type Iv = Interval<u32>;
fn setup_nonoverlapping() -> ScAIList<u32> {
let data: Vec<Iv> = (0..100)
.step_by(20)
.map(|x| Iv {
start: x,
end: x + 10,
val: 0,
})
.collect();
let lapper = ScAIList::new(data, Some(4));
lapper
}
fn setup_overlapping() -> ScAIList<u32> {
let data: Vec<Iv> = (0..100)
.step_by(10)
.map(|x| Iv {
start: x,
end: x + 15,
val: 0,
})
.collect();
let lapper = ScAIList::new(data, Some(4));
lapper
}
fn setup_badlapper() -> ScAIList<u32> {
let data: Vec<Iv> = vec![
Iv{start: 70, end: 120, val: 0}, Iv{start: 10, end: 15, val: 0},
Iv{start: 10, end: 15, val: 0}, Iv{start: 12, end: 15, val: 0}, Iv{start: 14, end: 16, val: 0}, Iv{start: 40, end: 45, val: 0},
Iv{start: 50, end: 55, val: 0},
Iv{start: 60, end: 65, val: 0},
Iv{start: 68, end: 71, val: 0}, Iv{start: 70, end: 75, val: 0},
];
let lapper = ScAIList::new(data, Some(4));
lapper
}
fn setup_single() -> ScAIList<u32> {
let data: Vec<Iv> = vec![Iv {
start: 10,
end: 35,
val: 0,
}];
let lapper = ScAIList::new(data, Some(4));
lapper
}
#[test]
fn test_query_end_interval_start() {
let lapper = setup_nonoverlapping();
let mut cursor = 0;
assert_eq!(None, lapper.find(15, 20).next());
}
#[test]
fn test_query_start_interval_end() {
let lapper = setup_nonoverlapping();
assert_eq!(None, lapper.find(30, 35).next());
}
#[test]
fn test_query_overlaps_interval_start() {
let lapper = setup_nonoverlapping();
let expected = Iv {
start: 20,
end: 30,
val: 0,
};
assert_eq!(Some(&expected), lapper.find(15, 25).next());
}
#[test]
fn test_query_overlaps_interval_end() {
let lapper = setup_nonoverlapping();
let expected = Iv {
start: 20,
end: 30,
val: 0,
};
assert_eq!(Some(&expected), lapper.find(25, 35).next());
}
#[test]
fn test_interval_envelops_query() {
let lapper = setup_nonoverlapping();
let expected = Iv {
start: 20,
end: 30,
val: 0,
};
assert_eq!(Some(&expected), lapper.find(22, 27).next());
}
#[test]
fn test_query_envolops_interval() {
let lapper = setup_nonoverlapping();
let expected = Iv {
start: 20,
end: 30,
val: 0,
};
assert_eq!(Some(&expected), lapper.find(15, 35).next());
}
#[test]
fn test_overlapping_intervals() {
let lapper = setup_overlapping();
let e1 = Iv {
start: 0,
end: 15,
val: 0,
};
let e2 = Iv {
start: 10,
end: 25,
val: 0,
};
assert_eq!(vec![&e1, &e2], lapper.find(8, 20).collect::<Vec<&Iv>>());
}
#[test]
fn test_interval_intersects() {
let i1 = Iv{start: 70, end: 120, val: 0}; let i2 = Iv{start: 10, end: 15, val: 0};
let i3 = Iv{start: 10, end: 15, val: 0}; let i4 = Iv{start: 12, end: 15, val: 0}; let i5 = Iv{start: 14, end: 16, val: 0}; let i6 = Iv{start: 40, end: 50, val: 0};
let i7 = Iv{start: 50, end: 55, val: 0};
let i_8 = Iv{start: 60, end: 65, val: 0};
let i9 = Iv{start: 68, end: 71, val: 0}; let i10 = Iv{start: 70, end: 75, val: 0};
assert_eq!(i2.intersect(&i3), 5); assert_eq!(i2.intersect(&i4), 3); assert_eq!(i2.intersect(&i5), 1); assert_eq!(i9.intersect(&i10), 1); assert_eq!(i7.intersect(&i_8), 0); assert_eq!(i6.intersect(&i7), 0); assert_eq!(i1.intersect(&i10), 5); }
#[test]
fn test_find_overlaps_in_large_intervals() {
let data1: Vec<Iv> = vec![
Iv{start: 0, end: 8, val: 0},
Iv{start: 1, end: 10, val: 0},
Iv{start: 2, end: 5, val: 0},
Iv{start: 3, end: 8, val: 0},
Iv{start: 4, end: 7, val: 0},
Iv{start: 5, end: 8, val: 0},
Iv{start: 8, end: 8, val: 0},
Iv{start: 9, end: 11, val: 0},
Iv{start: 10, end: 13, val: 0},
Iv{start: 100, end: 200, val: 0},
Iv{start: 110, end: 120, val: 0},
Iv{start: 110, end: 124, val: 0},
Iv{start: 111, end: 160, val: 0},
Iv{start: 150, end: 200, val: 0},
];
let lapper = ScAIList::new(data1, Some(4));
let found = lapper.find(8, 11).collect::<Vec<&Iv>>();
assert_eq!(found, vec![
&Iv{start: 1, end: 10, val: 0},
&Iv{start: 9, end: 11, val: 0},
&Iv{start: 10, end: 13, val: 0},
]);
let found = lapper.find(145, 151).collect::<Vec<&Iv>>();
assert_eq!(found, vec![
&Iv{start: 100, end: 200, val: 0},
&Iv{start: 111, end: 160, val: 0},
&Iv{start: 150, end: 200, val: 0},
]);
}
#[test]
fn test_find_overlaps_with_self() {
let data1: Vec<Iv> = vec![
Iv{start: 0, end: 8, val: 0}, Iv{start: 1, end: 10, val: 0}, Iv{start: 2, end: 5, val: 0}, Iv{start: 3, end: 8, val: 0}, Iv{start: 4, end: 7, val: 0}, Iv{start: 5, end: 8, val: 0}, Iv{start: 9, end: 11, val: 0}, Iv{start: 10, end: 13, val: 0}, ];
let lapper = ScAIList::new(data1, Some(2));
let mut count = 0;
for iv in lapper.iter() {
count += lapper.find(iv.start, iv.end).count();
}
assert_eq!(count, 40);
}
#[test]
fn test_find_over_behind_first_match() {
let lapper = setup_badlapper();
let e1 = Iv {start: 50, end: 55, val: 0};
let found = lapper.find(50, 55).next();
assert_eq!(found, Some(&e1));
}
#[test]
fn test_bad_skips() {
let data = vec![
Iv{start:25264912, end: 25264986, val: 0},
Iv{start:27273024, end: 27273065 , val: 0},
Iv{start:27440273, end: 27440318 , val: 0},
Iv{start:27488033, end: 27488125 , val: 0},
Iv{start:27938410, end: 27938470 , val: 0},
Iv{start:27959118, end: 27959171 , val: 0},
Iv{start:28866309, end: 33141404 , val: 0},
];
let lapper = ScAIList::new(data, None);
let found = lapper.find(28974798, 33141355).collect::<Vec<&Iv>>();
assert_eq!(found, vec![
&Iv{start:28866309, end: 33141404 , val: 0},
])
}
}