OpenSTA/include/sta/BoundedHeap.hh

258 lines
7.6 KiB
C++
Raw Normal View History

// OpenSTA, Static Timing Analyzer
// Copyright (c) 2025, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
#pragma once
#include <vector>
#include <algorithm>
#include <functional>
namespace sta {
// BoundedHeap: A container that maintains the top N elements using a min-heap.
// This provides O(log n) insertion when the heap is full, O(1) when not full,
// and O(n log n) extraction of all elements. Useful for maintaining top K
// elements without storing all elements.
//
// The heap maintains the "worst" (minimum according to Compare) element at
// the root, so new elements that are better than the worst can replace it.
// For example, with Compare = std::greater<int>, this maintains the N largest
// values (greater values are "better").
//
// Template parameters:
// T: The element type
// Compare: Comparison function object type (default: std::less<T>)
// For top N largest, use std::greater<T>
// For top N smallest, use std::less<T>
template <typename T, typename Compare = std::less<T>>
class BoundedHeap {
public:
using value_type = T;
using size_type = size_t;
using const_reference = const T&;
using compare_type = Compare;
// Constructors
explicit BoundedHeap(size_type max_size,
const Compare& comp = Compare()) :
max_size_(max_size),
comp_(comp),
min_heap_comp_(comp)
{
}
// Copy constructor
BoundedHeap(const BoundedHeap& other) :
heap_(other.heap_),
max_size_(other.max_size_),
comp_(other.comp_),
min_heap_comp_(other.comp_)
{}
// Assignment operator
BoundedHeap& operator=(const BoundedHeap& other)
{
if (this != &other) {
heap_ = other.heap_;
max_size_ = other.max_size_;
comp_ = other.comp_;
min_heap_comp_ = MinHeapCompare(other.comp_);
}
return *this;
}
// Move constructor
BoundedHeap(BoundedHeap&& other) noexcept :
heap_(std::move(other.heap_)),
max_size_(other.max_size_),
comp_(std::move(other.comp_)),
min_heap_comp_(comp_)
{}
// Move assignment operator
BoundedHeap& operator=(BoundedHeap&& other) noexcept
{
if (this != &other) {
heap_ = std::move(other.heap_);
max_size_ = other.max_size_;
comp_ = std::move(other.comp_);
min_heap_comp_ = MinHeapCompare(comp_);
}
return *this;
}
void
setMaxSize(size_t max_size)
{
max_size_ = max_size;
}
void
reserve(size_t size)
{
heap_.reserve(size);
}
// Insert an element into the heap.
// If the heap is not full, the element is added.
// If the heap is full and the new element is better than the worst element,
// the worst element is replaced. Otherwise, the element is ignored.
// Returns true if the element was inserted, false if it was ignored.
bool
insert(const T& value) {
if (heap_.size() < max_size_) {
heap_.push_back(value);
std::push_heap(heap_.begin(), heap_.end(), min_heap_comp_);
return true;
}
else if (!heap_.empty()) {
// When keeping N worst (smallest) values: if new value is smaller than worst,
// we should keep it and remove the largest element to make room.
// If new value is larger than worst, we reject it (already have worse values).
// comp_(value, worst) is true when value < worst (value is smaller/worse)
if (comp_(value, heap_.front())) {
// New value is smaller than worst - find and replace the largest element
auto max_it = std::max_element(heap_.begin(), heap_.end(), comp_);
*max_it = value;
// Rebuild heap since we modified an internal element
std::make_heap(heap_.begin(), heap_.end(), min_heap_comp_);
return true;
}
// Otherwise, new value is >= worst, so we already have worse values - reject it
}
return false;
}
// Insert an element using move semantics
bool insert(T&& value)
{
if (heap_.size() < max_size_) {
heap_.push_back(std::move(value));
std::push_heap(heap_.begin(), heap_.end(), min_heap_comp_);
return true;
}
else if (!heap_.empty()) {
// When keeping N worst (smallest) values: if new value is smaller than worst,
// we should keep it and remove the largest element to make room.
// If new value is larger than worst, we reject it (already have worse values).
// comp_(value, worst) is true when value < worst (value is smaller/worse)
if (comp_(value, heap_.front())) {
// New value is smaller than worst - find and replace the largest element
auto max_it = std::max_element(heap_.begin(), heap_.end(), comp_);
*max_it = std::move(value);
// Rebuild heap since we modified an internal element
std::make_heap(heap_.begin(), heap_.end(), min_heap_comp_);
return true;
}
// Otherwise, new value is >= worst, so we already have worse values - reject it
}
return false;
}
// Extract all elements sorted from best to worst.
// This destroys the heap structure but preserves the elements.
std::vector<T> extract()
{
// Convert heap to sorted vector (best to worst)
std::sort_heap(heap_.begin(), heap_.end(), min_heap_comp_);
std::vector<T> result = std::move(heap_);
heap_.clear();
return result;
}
// Extract all elements sorted from best to worst (const version).
// Creates a copy since we can't modify the heap.
std::vector<T> contents() const
{
std::vector<T> temp_heap = heap_;
std::sort_heap(temp_heap.begin(), temp_heap.end(), min_heap_comp_);
return temp_heap;
}
// Get the worst element (the one that would be replaced next).
// Requires !empty()
const_reference worst() const
{
return heap_.front();
}
// Check if the heap is empty
bool empty() const
{
return heap_.empty();
}
// Get the current number of elements in the heap
size_type size() const
{
return heap_.size();
}
// Get the maximum size of the heap
size_type max_size() const
{
return max_size_;
}
// Check if the heap is full
bool full() const
{
return heap_.size() >= max_size_;
}
// Clear all elements from the heap
void clear()
{
heap_.clear();
}
// Get the comparison function
Compare compare() const
{
return comp_;
}
private:
std::vector<T> heap_;
size_type max_size_;
Compare comp_;
// Helper comparator for min-heap: we want the worst element at root
// so we can easily remove it when adding better elements.
// This is the inverse of the user's comparison.
struct MinHeapCompare
{
Compare comp_;
explicit MinHeapCompare(const Compare& c) : comp_(c) {}
bool operator()(const T& a, const T& b) const {
return comp_(a, b); // comp = less puts largest at root (worst)
}
};
MinHeapCompare min_heap_comp_;
};
} // namespace sta