| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // ====================================================================== | ||
| 2 | // \title MaxHeap.cpp | ||
| 3 | // \author dinkel | ||
| 4 | // \brief An implementation of a stable max heap data structure. Items | ||
| 5 | // popped off the heap are guaranteed to be in order of decreasing | ||
| 6 | // "value" (max removed first). Items of equal "value" will be | ||
| 7 | // popped off in FIFO order. The performance of both push and pop | ||
| 8 | // is O(log(n)). | ||
| 9 | // | ||
| 10 | // \copyright | ||
| 11 | // Copyright 2009-2015, by the California Institute of Technology. | ||
| 12 | // ALL RIGHTS RESERVED. United States Government Sponsorship | ||
| 13 | // acknowledged. | ||
| 14 | // | ||
| 15 | // ====================================================================== | ||
| 16 | |||
| 17 | #include "Os/Generic/Types/MaxHeap.hpp" | ||
| 18 | #include <Fw/FPrimeBasicTypes.hpp> | ||
| 19 | #include <Fw/Logger/Logger.hpp> | ||
| 20 | #include <cstdio> | ||
| 21 | #include "Fw/LanguageHelpers.hpp" | ||
| 22 | #include "Fw/Types/Assert.hpp" | ||
| 23 | |||
| 24 | // Macros for traversing the heap: | ||
| 25 | #define LCHILD(x) (2 * x + 1) | ||
| 26 | #define RCHILD(x) (2 * x + 2) | ||
| 27 | #define PARENT(x) ((x - 1) / 2) | ||
| 28 | |||
| 29 | namespace Types { | ||
| 30 | |||
| 31 | 28 | MaxHeap::MaxHeap() { | |
| 32 | 28 | this->m_capacity = 0; | |
| 33 | 28 | this->m_heap = nullptr; | |
| 34 | 28 | this->m_size = 0; | |
| 35 | 28 | this->m_order = 0; | |
| 36 | 28 | } | |
| 37 | |||
| 38 | 28 | MaxHeap::~MaxHeap() { | |
| 39 | 28 | this->m_heap = nullptr; | |
| 40 | 28 | } | |
| 41 | |||
| 42 | 28 | void MaxHeap::create(FwSizeType capacity, Fw::ByteArray heap_allocation) { | |
| 43 | 28 | FW_ASSERT(this->m_heap == nullptr); | |
| 44 | 28 | FW_ASSERT((heap_allocation.size / sizeof(Node)) >= capacity, static_cast<FwAssertArgType>(capacity), | |
| 45 | static_cast<FwAssertArgType>(heap_allocation.size)); | ||
| 46 | 28 | FW_ASSERT(heap_allocation.bytes != nullptr); | |
| 47 | // Loop bounds will overflow if capacity set to the max allowable value | ||
| 48 | 28 | FW_ASSERT(capacity < std::numeric_limits<FwSizeType>::max()); | |
| 49 | 28 | this->m_heap = Fw::arrayPlacementNew<Node>(heap_allocation, capacity); | |
| 50 | 28 | this->m_capacity = capacity; | |
| 51 | 28 | } | |
| 52 | |||
| 53 | 28 | void MaxHeap::teardown() { | |
| 54 | // Only destroy the heap if it is still allocated | ||
| 55 |
1/2✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
|
28 | if (this->m_heap != nullptr) { |
| 56 | 28 | Fw::arrayPlacementDestruct<Node>(this->m_heap, this->m_capacity); | |
| 57 | } | ||
| 58 | // Reset the capacity and heap so that the provider of memory | ||
| 59 | 28 | this->m_capacity = 0; | |
| 60 | 28 | this->m_heap = nullptr; | |
| 61 | 28 | this->m_size = 0; | |
| 62 | 28 | this->m_order = 0; | |
| 63 | 28 | } | |
| 64 | |||
| 65 | 6945 | bool MaxHeap::push(FwQueuePriorityType value, FwSizeType id) { | |
| 66 | // If the queue is full, return false: | ||
| 67 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6944 times.
|
6945 | if (this->isFull()) { |
| 68 | ✗ | return false; | |
| 69 | } | ||
| 70 | |||
| 71 | // Heap indexes: | ||
| 72 | FwSizeType parent; | ||
| 73 | 6944 | FwSizeType index = this->m_size; | |
| 74 | |||
| 75 | // Max loop bounds for bit flip protection: | ||
| 76 | 6944 | const FwSizeType maxIter = this->m_size + 1; | |
| 77 | 6944 | FW_ASSERT(maxIter != 0); | |
| 78 | // Start at the bottom of the heap and work our ways | ||
| 79 | // upwards until we find a parent that has a value | ||
| 80 | // greater than ours. | ||
| 81 | 6944 | FwSizeType i = 0; | |
| 82 |
3/4✓ Branch 0 taken 6944 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1348 times.
✓ Branch 3 taken 5596 times.
|
6944 | for (i = 0; (i < maxIter) && (index != 0); i++) { |
| 83 | // Get the parent index: | ||
| 84 | 1348 | parent = PARENT(index); | |
| 85 | // The parent index should ALWAYS be less than the | ||
| 86 | // current index. Let's verify that. | ||
| 87 | 1348 | FW_ASSERT(parent < index, static_cast<FwAssertArgType>(parent), static_cast<FwAssertArgType>(index)); | |
| 88 | // If the current value is less than the parent, | ||
| 89 | // then the current index is in the correct place, | ||
| 90 | // so break out of the loop: | ||
| 91 |
1/2✓ Branch 0 taken 1348 times.
✗ Branch 1 not taken.
|
1348 | if (value <= this->m_heap[parent].value) { |
| 92 | 1348 | break; | |
| 93 | } | ||
| 94 | // Swap the parent and child: | ||
| 95 | ✗ | this->m_heap[index] = this->m_heap[parent]; | |
| 96 | ✗ | index = parent; | |
| 97 | } | ||
| 98 | |||
| 99 | // Check for programming errors or bit flips: | ||
| 100 | 6944 | FW_ASSERT(i < maxIter, static_cast<FwAssertArgType>(i), static_cast<FwAssertArgType>(maxIter)); | |
| 101 | 6944 | FW_ASSERT(index <= this->m_size, static_cast<FwAssertArgType>(index)); | |
| 102 | |||
| 103 | // Set the values of the new element: | ||
| 104 | 6937 | this->m_heap[index].value = value; | |
| 105 | 6937 | this->m_heap[index].order = m_order; | |
| 106 | 6937 | this->m_heap[index].id = id; | |
| 107 | |||
| 108 | 6937 | ++this->m_size; | |
| 109 | 6937 | ++this->m_order; | |
| 110 | 6937 | return true; | |
| 111 | } | ||
| 112 | |||
| 113 | 6920 | bool MaxHeap::pop(FwQueuePriorityType& value, FwSizeType& id) { | |
| 114 | // If there is nothing in the heap then | ||
| 115 | // return false: | ||
| 116 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6922 times.
|
6920 | if (this->isEmpty()) { |
| 117 | ✗ | return false; | |
| 118 | } | ||
| 119 | 6922 | FW_ASSERT(this->m_heap != nullptr); | |
| 120 | |||
| 121 | // Set the return values to the top (max) of | ||
| 122 | // the heap: | ||
| 123 | 6922 | value = this->m_heap[0].value; | |
| 124 | 6922 | id = this->m_heap[0].id; | |
| 125 | |||
| 126 | // Now place the last element on the heap in | ||
| 127 | // the root position, and resize the heap. | ||
| 128 | // This will put the smallest value in the | ||
| 129 | // heap on the top, violating the heap property. | ||
| 130 | 6922 | FwSizeType index = this->m_size - 1; | |
| 131 | 6922 | this->m_heap[0] = this->m_heap[index]; | |
| 132 | 6922 | --this->m_size; | |
| 133 | |||
| 134 | // Now that the heap property is violated, we | ||
| 135 | // need to reorganize the heap to restore it's | ||
| 136 | // heapy-ness. | ||
| 137 | 6922 | this->heapify(); | |
| 138 | 6908 | return true; | |
| 139 | } | ||
| 140 | |||
| 141 | // Is the heap full: | ||
| 142 | 20795 | bool MaxHeap::isFull() { | |
| 143 | 20795 | return (this->m_size == this->m_capacity); | |
| 144 | } | ||
| 145 | |||
| 146 | // Is the heap empty: | ||
| 147 | 26621 | bool MaxHeap::isEmpty() { | |
| 148 | 26621 | return (this->m_size == 0); | |
| 149 | } | ||
| 150 | |||
| 151 | // Get the current size of the heap: | ||
| 152 | 6923 | FwSizeType MaxHeap::getSize() const { | |
| 153 | 6923 | return this->m_size; | |
| 154 | } | ||
| 155 | |||
| 156 | // A non-recursive heapify method. | ||
| 157 | // Note: This method had an additional property, such that | ||
| 158 | // items pushed of the same priority will be popped in FIFO | ||
| 159 | // order. | ||
| 160 | 6906 | void MaxHeap::heapify() { | |
| 161 | 6906 | FwSizeType index = 0; | |
| 162 | FwSizeType left; | ||
| 163 | FwSizeType right; | ||
| 164 | FwSizeType largest; | ||
| 165 | |||
| 166 | // Max loop bounds for bit flip protection: | ||
| 167 | 6906 | const FwSizeType maxIter = this->m_size + 1; | |
| 168 | 6906 | FwSizeType i = 0; | |
| 169 | |||
| 170 |
2/4✓ Branch 0 taken 8488 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8488 times.
✗ Branch 3 not taken.
|
8488 | for (i = 0; (i < maxIter) && (index <= this->m_size); i++) { |
| 171 | // Get the children indexes for this node: | ||
| 172 | 8488 | left = LCHILD(index); | |
| 173 | 8488 | right = RCHILD(index); | |
| 174 | 8488 | FW_ASSERT(left > index, static_cast<FwAssertArgType>(left), static_cast<FwAssertArgType>(index)); | |
| 175 | 8488 | FW_ASSERT(right > left, static_cast<FwAssertArgType>(right), static_cast<FwAssertArgType>(left)); | |
| 176 | |||
| 177 | // If the left node is bigger than the heap | ||
| 178 | // size, we have reached the end of the heap | ||
| 179 | // so we can stop: | ||
| 180 |
2/2✓ Branch 0 taken 6782 times.
✓ Branch 1 taken 1708 times.
|
8490 | if (left >= this->m_size) { |
| 181 | 6782 | break; | |
| 182 | } | ||
| 183 | |||
| 184 | // Initialize the largest node to the current | ||
| 185 | // node: | ||
| 186 | 1708 | largest = index; | |
| 187 | |||
| 188 | // Which one is larger, the current node or | ||
| 189 | // the left node?: | ||
| 190 | 1708 | largest = this->max(left, largest); | |
| 191 | |||
| 192 | // Make sure the right node exists before checking it: | ||
| 193 |
2/2✓ Branch 0 taken 1489 times.
✓ Branch 1 taken 219 times.
|
1708 | if (right < this->m_size) { |
| 194 | // Which one is larger, the current largest | ||
| 195 | // node or the right node? | ||
| 196 | 1489 | largest = this->max(right, largest); | |
| 197 | } | ||
| 198 | |||
| 199 | // If the largest node is the current node | ||
| 200 | // then we are done heapifying: | ||
| 201 |
2/2✓ Branch 0 taken 126 times.
✓ Branch 1 taken 1582 times.
|
1708 | if (largest == index) { |
| 202 | 126 | break; | |
| 203 | } | ||
| 204 | |||
| 205 | // Swap the largest node with the current node: | ||
| 206 | 1582 | this->swap(index, largest); | |
| 207 | |||
| 208 | // Set the new index to whichever child was larger: | ||
| 209 | 1582 | index = largest; | |
| 210 | } | ||
| 211 | |||
| 212 | // Check for programming errors or bit flips: | ||
| 213 | 6908 | FW_ASSERT(i < maxIter, static_cast<FwAssertArgType>(i), static_cast<FwAssertArgType>(maxIter)); | |
| 214 | 6908 | FW_ASSERT(index <= this->m_size, static_cast<FwAssertArgType>(index)); | |
| 215 | 6908 | } | |
| 216 | |||
| 217 | // Return the maximum priority index between two nodes. If their | ||
| 218 | // priorities are equal, return the oldest to keep the heap stable | ||
| 219 | 3197 | FwSizeType MaxHeap::max(FwSizeType a, FwSizeType b) { | |
| 220 | static_assert(not std::numeric_limits<FwSizeType>::is_signed, "FwSizeType must be unsigned"); | ||
| 221 | 3197 | FW_ASSERT(a < this->m_size, static_cast<FwAssertArgType>(a), static_cast<FwAssertArgType>(this->m_size)); | |
| 222 | 3197 | FW_ASSERT(b < this->m_size, static_cast<FwAssertArgType>(b), static_cast<FwAssertArgType>(this->m_size)); | |
| 223 | |||
| 224 | // Extract the priorities: | ||
| 225 | 3197 | FwQueuePriorityType aValue = this->m_heap[a].value; | |
| 226 | 3197 | FwQueuePriorityType bValue = this->m_heap[b].value; | |
| 227 | |||
| 228 | // If the priorities are equal, the "larger" one will be | ||
| 229 | // the "older" one as determined by order pushed on to the | ||
| 230 | // heap. Using this secondary ordering technique makes the | ||
| 231 | // heap stable (ie. FIFO for equal priority elements). | ||
| 232 | // Note: We check this first, because it is the most common | ||
| 233 | // case. Let's save as many ticks as we can... | ||
| 234 |
1/2✓ Branch 0 taken 3197 times.
✗ Branch 1 not taken.
|
3197 | if (aValue == bValue) { |
| 235 | 3197 | FwSizeType aAge = this->m_order - this->m_heap[a].order; | |
| 236 | 3197 | FwSizeType bAge = this->m_order - this->m_heap[b].order; | |
| 237 |
2/2✓ Branch 0 taken 1999 times.
✓ Branch 1 taken 1198 times.
|
3197 | if (aAge > bAge) { |
| 238 | 1999 | return a; | |
| 239 | } | ||
| 240 | 1198 | return b; | |
| 241 | } | ||
| 242 | |||
| 243 | // Which priority is larger?: | ||
| 244 | ✗ | if (aValue > bValue) { | |
| 245 | ✗ | return a; | |
| 246 | } | ||
| 247 | // B is larger: | ||
| 248 | ✗ | return b; | |
| 249 | } | ||
| 250 | |||
| 251 | // Swap two nodes in the heap: | ||
| 252 | 1582 | void MaxHeap::swap(FwSizeType a, FwSizeType b) { | |
| 253 | 1582 | FW_ASSERT(a < this->m_size, static_cast<FwAssertArgType>(a), static_cast<FwAssertArgType>(this->m_size)); | |
| 254 | 1582 | FW_ASSERT(b < this->m_size, static_cast<FwAssertArgType>(b), static_cast<FwAssertArgType>(this->m_size)); | |
| 255 | 1582 | Node temp = this->m_heap[a]; | |
| 256 | 1582 | this->m_heap[a] = this->m_heap[b]; | |
| 257 | 1582 | this->m_heap[b] = temp; | |
| 258 | 1582 | } | |
| 259 | |||
| 260 | } // namespace Types | ||
| 261 |