use std::cmp::Ordering;
use crate::index::Index;
use crate::scalar::Scalar;
#[derive(Debug, Clone, Copy)]
pub struct Score<V: Scalar, I: Index> {
pub order: u32,
pub idx: I,
pub val: V,
}
#[derive(Debug)]
pub struct MaxHeap<V: Scalar, I: Index> {
capacity: usize,
init: V,
n_set: u32,
heap: Vec<Score<V, I>>,
}
impl<V: Scalar, I: Index> MaxHeap<V, I> {
pub fn new(capacity: usize, threshold: V) -> Self {
let heap = (0..capacity)
.map(|_| Score {
order: u32::MAX,
idx: I::zero(),
val: threshold,
})
.collect();
Self {
capacity,
init: threshold,
n_set: 0,
heap,
}
}
pub fn reset(&mut self) -> V {
self.n_set = 0;
for entry in &mut self.heap {
entry.order = u32::MAX;
entry.idx = I::zero();
entry.val = self.init;
}
self.init
}
pub fn n_set(&self) -> usize {
(self.n_set as usize).min(self.capacity)
}
pub fn push_pop(&mut self, idx: I, val: V) -> V {
let last = self.capacity - 1;
self.heap.swap(0, last);
sift_down(&mut self.heap, 0, last);
self.heap[last] = Score {
order: self.n_set,
idx,
val,
};
self.n_set = self.n_set.saturating_add(1);
sift_up(&mut self.heap, last);
self.heap[0].val
}
pub fn sort_by_insertion_order(&mut self) {
self.heap.sort_by_key(|s| s.order);
}
pub fn sort_by_value_desc(&mut self) {
self.heap
.sort_by(|a, b| b.val.partial_cmp(&a.val).unwrap_or(Ordering::Equal));
}
pub fn entries(&self) -> &[Score<V, I>] {
&self.heap[..self.n_set()]
}
}
fn sift_down<V: Scalar, I: Index>(heap: &mut [Score<V, I>], mut i: usize, len: usize) {
loop {
let left = 2 * i + 1;
let right = 2 * i + 2;
let mut smallest = i;
if left < len && lt(heap[left].val, heap[smallest].val) {
smallest = left;
}
if right < len && lt(heap[right].val, heap[smallest].val) {
smallest = right;
}
if smallest == i {
return;
}
heap.swap(i, smallest);
i = smallest;
}
}
fn sift_up<V: Scalar, I: Index>(heap: &mut [Score<V, I>], mut i: usize) {
while i > 0 {
let parent = (i - 1) / 2;
if lt(heap[i].val, heap[parent].val) {
heap.swap(i, parent);
i = parent;
} else {
return;
}
}
}
#[inline]
fn lt<V: Scalar>(a: V, b: V) -> bool {
a.partial_cmp(&b) == Some(Ordering::Less)
}
#[cfg(test)]
mod tests {
use super::*;
use num_traits::{NumCast, ToPrimitive};
fn v<V: Scalar>(x: u8) -> V {
<V as NumCast>::from(x).unwrap()
}
fn back<V: Scalar>(x: V) -> u8 {
<V as ToPrimitive>::to_u8(&x).unwrap()
}
fn push_guarded<V: Scalar, I: Index>(
heap: &mut MaxHeap<V, I>,
min: &mut V,
idx: usize,
val: V,
) {
if val.partial_cmp(min) == Some(Ordering::Greater) {
*min = heap.push_pop(I::from_usize(idx), val);
}
}
fn run_push_pop_orders_top_n<V: Scalar, I: Index>() {
let mut heap = MaxHeap::<V, I>::new(3, v::<V>(0));
let mut min = v::<V>(0);
for (i, x) in [1u8, 5, 3, 8, 2, 7].iter().enumerate() {
push_guarded(&mut heap, &mut min, i, v::<V>(*x));
}
heap.sort_by_value_desc();
let got: Vec<u8> = heap.entries().iter().map(|s| back(s.val)).collect();
assert_eq!(got, vec![8, 7, 5]);
}
fn run_threshold_filters<V: Scalar, I: Index>() {
let threshold = v::<V>(4);
let mut heap = MaxHeap::<V, I>::new(3, threshold);
let mut min = threshold;
for (i, x) in [1u8, 5, 3].iter().enumerate() {
push_guarded(&mut heap, &mut min, i, v::<V>(*x));
}
heap.sort_by_value_desc();
let got: Vec<u8> = heap.entries().iter().map(|s| back(s.val)).collect();
assert_eq!(got, vec![5]);
}
fn run_insertion_order_sort_preserves_column_order<V: Scalar, I: Index>() {
let mut heap = MaxHeap::<V, I>::new(3, v::<V>(0));
let mut min = v::<V>(0);
for (idx, val) in [(10usize, 5u8), (2, 3), (7, 8)].iter() {
push_guarded(&mut heap, &mut min, *idx, v::<V>(*val));
}
heap.sort_by_insertion_order();
let got: Vec<usize> = heap.entries().iter().map(|s| s.idx.to_usize()).collect();
assert_eq!(got, vec![10, 2, 7]);
}
fn run_reset_clears_state<V: Scalar, I: Index>() {
let mut heap = MaxHeap::<V, I>::new(2, v::<V>(0));
let mut min = v::<V>(0);
for (i, x) in [5u8, 3].iter().enumerate() {
push_guarded(&mut heap, &mut min, i, v::<V>(*x));
}
heap.sort_by_value_desc();
assert_eq!(heap.n_set(), 2);
let returned_init = heap.reset();
assert_eq!(heap.n_set(), 0);
assert_eq!(returned_init.partial_cmp(&v::<V>(0)), Some(Ordering::Equal));
let mut min2 = returned_init;
for (i, x) in [9u8, 1, 7].iter().enumerate() {
push_guarded(&mut heap, &mut min2, i + 100, v::<V>(*x));
}
heap.sort_by_value_desc();
let got: Vec<u8> = heap.entries().iter().map(|s| back(s.val)).collect();
assert_eq!(got, vec![9, 7]);
}
fn run_capacity_one<V: Scalar, I: Index>() {
let mut heap = MaxHeap::<V, I>::new(1, v::<V>(0));
let mut min = v::<V>(0);
for (i, x) in [3u8, 7, 5].iter().enumerate() {
push_guarded(&mut heap, &mut min, i, v::<V>(*x));
}
heap.sort_by_value_desc();
let got: Vec<u8> = heap.entries().iter().map(|s| back(s.val)).collect();
assert_eq!(got, vec![7]);
}
macro_rules! dtype_tests {
($mod_name:ident, $V:ty, $I:ty) => {
mod $mod_name {
use super::*;
#[test]
fn push_pop_orders_top_n() {
run_push_pop_orders_top_n::<$V, $I>();
}
#[test]
fn threshold_filters() {
run_threshold_filters::<$V, $I>();
}
#[test]
fn insertion_order_sort_preserves_column_order() {
run_insertion_order_sort_preserves_column_order::<$V, $I>();
}
#[test]
fn reset_clears_state() {
run_reset_clears_state::<$V, $I>();
}
#[test]
fn capacity_one() {
run_capacity_one::<$V, $I>();
}
}
};
}
dtype_tests!(f32_i32, f32, i32);
dtype_tests!(f32_i64, f32, i64);
dtype_tests!(f64_i32, f64, i32);
dtype_tests!(f64_i64, f64, i64);
dtype_tests!(i32_i32, i32, i32);
dtype_tests!(i32_i64, i32, i64);
dtype_tests!(i64_i32, i64, i32);
dtype_tests!(i64_i64, i64, i64);
}