use std::cmp::Reverse;
use std::collections::BinaryHeap;
pub struct SeekableMergeIterator<T: Ord, I: Iterator<Item = T>> {
streams: Vec<I>,
heap: BinaryHeap<Reverse<(T, usize)>>,
upper_bound: Option<T>,
}
impl<T: Ord, I: Iterator<Item = T>> SeekableMergeIterator<T, I> {
pub fn new<S: IntoIterator<Item = I>>(streams: S) -> Self {
let mut streams: Vec<I> = streams.into_iter().collect();
let mut heap = BinaryHeap::with_capacity(streams.len());
for (i, s) in streams.iter_mut().enumerate() {
if let Some(v) = s.next() {
heap.push(Reverse((v, i)));
}
}
Self {
streams,
heap,
upper_bound: None,
}
}
pub fn set_upper_bound(&mut self, bound: T) {
self.upper_bound = Some(bound);
}
pub fn clear_upper_bound(&mut self) {
self.upper_bound = None;
}
pub fn peek(&self) -> Option<&T> {
let head = self.heap.peek().map(|Reverse((value, _))| value)?;
match &self.upper_bound {
Some(hi) if head >= hi => None,
_ => Some(head),
}
}
pub fn live_streams(&self) -> usize {
self.heap.len()
}
pub fn num_streams(&self) -> usize {
self.streams.len()
}
pub fn seek(&mut self, target: &T) {
let mut new_heads: Vec<(T, usize)> = Vec::new();
while let Some(Reverse((value, idx))) = self.heap.peek() {
if value < target {
let Reverse((_value, idx)) = self.heap.pop().unwrap();
let mut found: Option<T> = None;
for v in self.streams[idx].by_ref() {
if &v >= target {
found = Some(v);
break;
}
}
if let Some(v) = found {
new_heads.push((v, idx));
}
} else {
let _ = idx;
break;
}
}
for (v, i) in new_heads {
self.heap.push(Reverse((v, i)));
}
}
}
impl<T: Ord, I: Iterator<Item = T>> Iterator for SeekableMergeIterator<T, I> {
type Item = T;
fn next(&mut self) -> Option<T> {
self.peek()?;
let Reverse((value, idx)) = self.heap.pop()?;
if let Some(next_value) = self.streams[idx].next() {
self.heap.push(Reverse((next_value, idx)));
}
Some(value)
}
}
#[cfg(test)]
#[path = "seek_tests.rs"]
mod tests;