// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Copyright (C) 2016 Red Hat Inc. */ #pragma once #include #include #include #include #include "assert.h" namespace crimson { using IntruHeapData = size_t; // T = type of data in heap; I = functor that returns a non-const // reference to IntruHeapData; C = functor that compares two const // refs and return true if the first precedes the second template class IntruHeap { static_assert( std::is_same::type>::value, "class I must define operator() to take T& and return a IntruHeapData&."); static_assert( std::is_same::type>::value, "class C must define operator() to take two const T& and return a bool."); protected: using index_t = IntruHeapData; std::vector data; index_t count; I intru_data_of; C comparator; public: IntruHeap() : count(0) { // empty } IntruHeap(const IntruHeap& other) : count(other.count) { for (uint i = 0; i < other.count; ++i) { data.push_back(other.data[i]); } } bool empty() const { return 0 == count; } T& top() { return data[0]; } void push(T&& item) { index_t i = count++; intru_data_of(item) = i; data.emplace_back(item); sift_up(i); } void push(const T& item) { T copy(item); push(std::move(copy)); } void pop() { std::swap(data[0], data[--count]); intru_data_of(data[0]) = 0; data.pop_back(); sift_down(0); } void adjust_up(T& item) { sift_up(intru_data_of(item)); } void adjust_down(T& item) { sift_down(intru_data_of(item)); } void adjust(T& item) { sift(intru_data_of(item)); } friend std::ostream& operator<<(std::ostream& out, const IntruHeap& h) { for (uint i = 0; i < h.count; ++i) { out << h.data[i] << ", "; } return out; } std::ostream& display_sorted(std::ostream& out, bool insert_line_breaks = true, std::function filter = all_filter) const { IntruHeap copy = *this; bool first = true; out << "[ "; while(!copy.empty()) { const T& top = copy.top(); if (filter(top)) { if (!first) { out << ", "; } if (insert_line_breaks) { out << std::endl << " "; } out << copy.top(); first = false; } copy.pop(); } out << " ]"; if (insert_line_breaks) { out << std::endl; } return out; } protected: // default value of filter parameter to display_sorted static bool all_filter(const T& data) { return true; } // when i is negative? static inline index_t parent(index_t i) { assert(0 != i); return (i - 1) / 2; } static inline index_t lhs(index_t i) { return 2*i + 1; } static inline index_t rhs(index_t i) { return 2*i + 2; } void sift_up(index_t i) { while (i > 0) { index_t pi = parent(i); if (!comparator(data[i], data[pi])) { break; } std::swap(data[i], data[pi]); intru_data_of(data[i]) = i; intru_data_of(data[pi]) = pi; i = pi; } } // sift_up void sift_down(index_t i) { while (i < count) { index_t li = lhs(i); index_t ri = rhs(i); if (li < count) { if (comparator(data[li], data[i])) { if (ri < count && comparator(data[ri], data[li])) { std::swap(data[i], data[ri]); intru_data_of(data[i]) = i; intru_data_of(data[ri]) = ri; i = ri; } else { std::swap(data[i], data[li]); intru_data_of(data[i]) = i; intru_data_of(data[li]) = li; i = li; } } else if (ri < count && comparator(data[ri], data[i])) { std::swap(data[i], data[ri]); intru_data_of(data[i]) = i; intru_data_of(data[ri]) = ri; i = ri; } else { break; } } else { break; } } } // sift_down void sift(index_t i) { if (i == 0) { // if we're at top, can only go down sift_down(i); } else { index_t pi = parent(i); if (comparator(data[i], data[pi])) { // if we can go up, we will sift_up(i); } else { // otherwise we'll try to go down sift_down(i); } } } // sift }; // class IntruHeap } // namespace crimson