use core::cmp::Ordering;
use crate::{DoubleEndedHeap, Heap};
const DEFAULT_HEAP_CAPACITY: usize = 16;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MinMaxBinaryArrayDoubleEndedHeap<T> {
values: Vec<T>,
}
impl<T: Ord> MinMaxBinaryArrayDoubleEndedHeap<T> {
#[must_use]
pub fn new() -> Self {
Self::with_capacity(DEFAULT_HEAP_CAPACITY)
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
values: Vec::with_capacity(capacity),
}
}
#[must_use]
pub fn from_vec(values: Vec<T>) -> Self {
let mut heap = Self { values };
heap.heapify();
heap
}
}
impl<T: Ord> Default for MinMaxBinaryArrayDoubleEndedHeap<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Ord> FromIterator<T> for MinMaxBinaryArrayDoubleEndedHeap<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Self::from_vec(iter.into_iter().collect())
}
}
impl<T: Ord> Extend<T> for MinMaxBinaryArrayDoubleEndedHeap<T> {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
for value in iter {
self.push(value);
}
}
}
impl<T: Ord> MinMaxBinaryArrayDoubleEndedHeap<T> {
#[must_use]
pub fn peek_max(&self) -> Option<&T> {
self.max_index().map(|index| &self.values[index])
}
pub fn pop_max(&mut self) -> Option<T> {
let index = self.max_index()?;
let value = self.values.swap_remove(index);
if index < self.values.len() {
self.fix_down_max(index);
}
Some(value)
}
#[must_use]
pub fn into_vec(self) -> Vec<T> {
self.values
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.values.iter()
}
fn heapify(&mut self) {
for index in (0..self.values.len() / 2).rev() {
self.fix_down(index);
}
}
fn max_index(&self) -> Option<usize> {
match self.values.len() {
0 => None,
1 => Some(0),
2 => Some(1),
_ => {
if self.values[2] > self.values[1] {
Some(2)
} else {
Some(1)
}
}
}
}
fn on_min_level(index: usize) -> bool {
(index + 1).ilog2().is_multiple_of(2)
}
fn fix_up(&mut self, index: usize) {
if index == 0 {
return;
}
let parent = (index - 1) / 2;
if Self::on_min_level(index) {
if self.values[index] > self.values[parent] {
self.values.swap(index, parent);
self.bubble_up_max(parent);
} else {
self.bubble_up_min(index);
}
} else if self.values[index] < self.values[parent] {
self.values.swap(index, parent);
self.bubble_up_min(parent);
} else {
self.bubble_up_max(index);
}
}
fn bubble_up_min(&mut self, mut index: usize) {
while index >= 3 {
let grandparent = (index - 3) / 4;
if self.values[index] >= self.values[grandparent] {
return;
}
self.values.swap(index, grandparent);
index = grandparent;
}
}
fn bubble_up_max(&mut self, mut index: usize) {
while index >= 3 {
let grandparent = (index - 3) / 4;
if self.values[index] <= self.values[grandparent] {
return;
}
self.values.swap(index, grandparent);
index = grandparent;
}
}
fn fix_down(&mut self, index: usize) {
if Self::on_min_level(index) {
self.fix_down_min(index);
} else {
self.fix_down_max(index);
}
}
fn fix_down_min(&mut self, mut index: usize) {
while let Some(candidate) = self.best_descendant(index, Ordering::Less) {
let first_grandchild = index
.checked_mul(4)
.and_then(|value| value.checked_add(3))
.unwrap_or(self.values.len());
if candidate >= first_grandchild {
if self.values[candidate] >= self.values[index] {
return;
}
self.values.swap(index, candidate);
let parent = (candidate - 1) / 2;
if self.values[candidate] > self.values[parent] {
self.values.swap(candidate, parent);
}
index = candidate;
} else {
if self.values[candidate] < self.values[index] {
self.values.swap(index, candidate);
}
return;
}
}
}
fn fix_down_max(&mut self, mut index: usize) {
while let Some(candidate) = self.best_descendant(index, Ordering::Greater) {
let first_grandchild = index
.checked_mul(4)
.and_then(|value| value.checked_add(3))
.unwrap_or(self.values.len());
if candidate >= first_grandchild {
if self.values[candidate] <= self.values[index] {
return;
}
self.values.swap(index, candidate);
let parent = (candidate - 1) / 2;
if self.values[candidate] < self.values[parent] {
self.values.swap(candidate, parent);
}
index = candidate;
} else {
if self.values[candidate] > self.values[index] {
self.values.swap(index, candidate);
}
return;
}
}
}
fn best_descendant(&self, index: usize, wanted: Ordering) -> Option<usize> {
let first_child = index.checked_mul(2)?.checked_add(1)?;
if first_child >= self.values.len() {
return None;
}
let second_child = first_child + 1;
let first_grandchild = index.checked_mul(4).and_then(|value| value.checked_add(3));
let mut best = first_child;
let candidates = [second_child].into_iter().chain(
first_grandchild
.into_iter()
.flat_map(|first| first..first.saturating_add(4)),
);
for candidate in candidates.filter(|&candidate| candidate < self.values.len()) {
if self.values[candidate].cmp(&self.values[best]) == wanted {
best = candidate;
}
}
Some(best)
}
}
impl<T: Ord> IntoIterator for MinMaxBinaryArrayDoubleEndedHeap<T> {
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.into_vec().into_iter()
}
}
impl<'a, T: Ord> IntoIterator for &'a MinMaxBinaryArrayDoubleEndedHeap<T> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.values.iter()
}
}
impl<T: Ord> Heap<T> for MinMaxBinaryArrayDoubleEndedHeap<T> {
fn push(&mut self, value: T) {
self.values.push(value);
self.fix_up(self.values.len() - 1);
}
fn peek(&self) -> Option<&T> {
self.values.first()
}
fn pop(&mut self) -> Option<T> {
if self.values.is_empty() {
return None;
}
let value = self.values.swap_remove(0);
if !self.values.is_empty() {
self.fix_down(0);
}
Some(value)
}
fn len(&self) -> usize {
self.values.len()
}
fn clear(&mut self) {
self.values.clear();
}
}
impl<T: Ord> DoubleEndedHeap<T> for MinMaxBinaryArrayDoubleEndedHeap<T> {
fn peek_max(&self) -> Option<&T> {
Self::peek_max(self)
}
fn pop_max(&mut self) -> Option<T> {
Self::pop_max(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn heapify_and_alternating_removals_work_at_all_small_sizes() {
for size in 0..128 {
let mut heap =
MinMaxBinaryArrayDoubleEndedHeap::from_vec((0..size).rev().collect::<Vec<_>>());
let mut minimum = 0;
let mut maximum = size;
let mut remove_minimum = true;
while minimum < maximum {
if remove_minimum {
assert_eq!(heap.pop(), Some(minimum));
minimum += 1;
} else {
maximum -= 1;
assert_eq!(heap.pop_max(), Some(maximum), "initial size {size}");
}
remove_minimum = !remove_minimum;
}
assert!(heap.is_empty());
}
}
#[test]
fn incremental_alternating_removals_work_at_all_small_sizes() {
for size in 0..128 {
let mut heap = MinMaxBinaryArrayDoubleEndedHeap::new();
for value in (0..size).rev() {
heap.push(value);
}
let mut minimum = 0;
let mut maximum = size;
while minimum < maximum {
assert_eq!(heap.pop(), Some(minimum));
minimum += 1;
if minimum < maximum {
maximum -= 1;
assert_eq!(heap.pop_max(), Some(maximum));
}
}
assert!(heap.is_empty());
}
}
}