F´ Flight Software - C/C++ Documentation  devel
A framework for building embedded system applications to NASA flight quality standards.
MaxHeap.cpp
Go to the documentation of this file.
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 
18 #include <FpConfig.hpp>
19 #include "Fw/Types/Assert.hpp"
20 #include <Fw/Logger/Logger.hpp>
21 
22 #include <new>
23 #include <cstdio>
24 
25 // Macros for traversing the heap:
26 #define LCHILD(x) (2 * x + 1)
27 #define RCHILD(x) (2 * x + 2)
28 #define PARENT(x) ((x - 1) / 2)
29 
30 namespace Os {
31 
33  // Initialize the heap:
34  this->m_capacity = 0;
35  this->m_heap = nullptr;
36  this->m_size = 0;
37  this->m_order = 0;
38  }
39 
41  delete [] this->m_heap;
42  this->m_heap = nullptr;
43  }
44 
46  {
47  // The heap has already been created.. so delete
48  // it and try again.
49  if( nullptr != this->m_heap ) {
50  delete [] this->m_heap;
51  this->m_heap = nullptr;
52  }
53 
54  this->m_heap = new(std::nothrow) Node[capacity];
55  if( nullptr == this->m_heap ) {
56  return false;
57  }
58  this->m_capacity = capacity;
59  return true;
60  }
61 
63  // If the queue is full, return false:
64  if(this->isFull()) {
65  return false;
66  }
67 
68  // Heap indexes:
69  NATIVE_UINT_TYPE parent;
70  NATIVE_UINT_TYPE index = this->m_size;
71 
72  // Max loop bounds for bit flip protection:
73  NATIVE_UINT_TYPE maxIter = this->m_size+1;
74  NATIVE_UINT_TYPE maxCount = 0;
75 
76  // Start at the bottom of the heap and work our ways
77  // upwards until we find a parent that has a value
78  // greater than ours.
79  while(index && maxCount < maxIter) {
80  // Get the parent index:
81  parent = PARENT(index);
82  // The parent index should ALWAYS be less than the
83  // current index. Let's verify that.
84  FW_ASSERT(parent < index, static_cast<FwAssertArgType>(parent), static_cast<FwAssertArgType>(index));
85  // If the current value is less than the parent,
86  // then the current index is in the correct place,
87  // so break out of the loop:
88  if(value <= this->m_heap[parent].value) {
89  break;
90  }
91  // Swap the parent and child:
92  this->m_heap[index] = this->m_heap[parent];
93  index = parent;
94  ++maxCount;
95  }
96 
97  // Check for programming errors or bit flips:
98  FW_ASSERT(maxCount < maxIter, static_cast<FwAssertArgType>(maxCount), static_cast<FwAssertArgType>(maxIter));
99  FW_ASSERT(index <= this->m_size, static_cast<FwAssertArgType>(index));
100 
101  // Set the values of the new element:
102  this->m_heap[index].value = value;
103  this->m_heap[index].order = m_order;
104  this->m_heap[index].id = id;
105 
106  ++this->m_size;
107  ++this->m_order;
108  return true;
109  }
110 
112  // If there is nothing in the heap then
113  // return false:
114  if(this->isEmpty()) {
115  return false;
116  }
117 
118  // Set the return values to the top (max) of
119  // the heap:
120  value = this->m_heap[0].value;
121  id = this->m_heap[0].id;
122 
123  // Now place the last element on the heap in
124  // the root position, and resize the heap.
125  // This will put the smallest value in the
126  // heap on the top, violating the heap property.
127  NATIVE_UINT_TYPE index = this->m_size-1;
128  // Fw::Logger::logMsg("Putting on top: i: %u v: %d\n", index, this->m_heap[index].value);
129  this->m_heap[0]= this->m_heap[index];
130  --this->m_size;
131 
132  // Now that the heap property is violated, we
133  // need to reorganize the heap to restore it's
134  // heapy-ness.
135  this->heapify();
136  return true;
137  }
138 
139  // Is the heap full:
141  return (this->m_size == this->m_capacity);
142  }
143 
144  // Is the heap empty:
146  return (this->m_size == 0);
147  }
148 
149  // Get the current size of the heap:
151  return this->m_size;
152  }
153 
154  // A non-recursive heapify method.
155  // Note: This method had an additional property, such that
156  // items pushed of the same priority will be popped in FIFO
157  // order.
158  void MaxHeap::heapify() {
159  NATIVE_UINT_TYPE index = 0;
160  NATIVE_UINT_TYPE left;
161  NATIVE_UINT_TYPE right;
162  NATIVE_UINT_TYPE largest;
163 
164  // Max loop bounds for bit flip protection:
165  NATIVE_UINT_TYPE maxIter = this->m_size+1;
166  NATIVE_UINT_TYPE maxCount = 0;
167 
168  while(index <= this->m_size && maxCount < maxIter) {
169  // Get the children indexes for this node:
170  left = LCHILD(index);
171  right = RCHILD(index);
172  FW_ASSERT(left > index, static_cast<FwAssertArgType>(left), static_cast<FwAssertArgType>(index));
173  FW_ASSERT(right > left, static_cast<FwAssertArgType>(right), static_cast<FwAssertArgType>(left));
174 
175  // If the left node is bigger than the heap
176  // size, we have reached the end of the heap
177  // so we can stop:
178  if (left >= this->m_size) {
179  break;
180  }
181 
182  // Initialize the largest node to the current
183  // node:
184  largest = index;
185 
186  // Which one is larger, the current node or
187  // the left node?:
188  largest = this->max(left, largest);
189 
190  // Make sure the right node exists before checking it:
191  if (right < this->m_size) {
192  // Which one is larger, the current largest
193  // node or the right node?
194  largest = this->max(right, largest);
195  }
196 
197  // If the largest node is the current node
198  // then we are done heapifying:
199  if (largest == index)
200  {
201  break;
202  }
203 
204  // Swap the largest node with the current node:
205  // Fw::Logger::logMsg("Swapping: i: %u v: %d with i: %u v: %d\n",
206  // index, this->m_heap[index].value,
207  // largest, this->m_heap[largest].value);
208  this->swap(index, largest);
209 
210  // Set the new index to whichever child was larger:
211  index = largest;
212  }
213 
214  // Check for programming errors or bit flips:
215  FW_ASSERT(maxCount < maxIter, static_cast<FwAssertArgType>(maxCount), static_cast<FwAssertArgType>(maxIter));
216  FW_ASSERT(index <= this->m_size, static_cast<FwAssertArgType>(index));
217  }
218 
219  // Return the maximum priority index between two nodes. If their
220  // priorities are equal, return the oldest to keep the heap stable
222  FW_ASSERT(a < this->m_size, static_cast<FwAssertArgType>(a), static_cast<FwAssertArgType>(this->m_size));
223  FW_ASSERT(b < this->m_size, static_cast<FwAssertArgType>(b), static_cast<FwAssertArgType>(this->m_size));
224 
225  // Extract the priorities:
226  NATIVE_INT_TYPE aValue = this->m_heap[a].value;
227  NATIVE_INT_TYPE bValue = this->m_heap[b].value;
228 
229  // If the priorities are equal, the "larger" one will be
230  // the "older" one as determined by order pushed on to the
231  // heap. Using this secondary ordering technique makes the
232  // heap stable (ie. FIFO for equal priority elements).
233  // Note: We check this first, because it is the most common
234  // case. Let's save as many ticks as we can...
235  if(aValue == bValue) {
236  NATIVE_UINT_TYPE aAge = this->m_order - this->m_heap[a].order;
237  NATIVE_UINT_TYPE bAge = this->m_order - this->m_heap[b].order;
238  if(aAge > bAge) {
239  return a;
240  }
241  return b;
242  }
243 
244  // Which priority is larger?:
245  if( aValue > bValue ) {
246  return a;
247  }
248  // B is larger:
249  return b;
250  }
251 
252  // Swap two nodes in the heap:
253  void MaxHeap::swap(NATIVE_UINT_TYPE a, NATIVE_UINT_TYPE b) {
254  FW_ASSERT(a < this->m_size, static_cast<FwAssertArgType>(a), static_cast<FwAssertArgType>(this->m_size));
255  FW_ASSERT(b < this->m_size, static_cast<FwAssertArgType>(b), static_cast<FwAssertArgType>(this->m_size));
256  Node temp = this->m_heap[a];
257  this->m_heap[a] = this->m_heap[b];
258  this->m_heap[b] = temp;
259  }
260 
261  // Print heap, for debugging purposes only:
262  void MaxHeap::print() {
263  NATIVE_UINT_TYPE index = 0;
264  NATIVE_UINT_TYPE left;
265  NATIVE_UINT_TYPE right;
266  Fw::Logger::logMsg("Printing Heap of Size: %d\n", this->m_size);
267  while(index < this->m_size) {
268  left = LCHILD(index);
269  right = RCHILD(index);
270 
271  if( left >= m_size && index == 0) {
272  Fw::Logger::logMsg("i: %u v: %d d: %u -> (NULL, NULL)\n",
273  index, static_cast<POINTER_CAST>(this->m_heap[index].value), this->m_heap[index].id);
274  }
275  else if( right >= m_size && left < m_size ) {
276  Fw::Logger::logMsg("i: %u v: %d d: %u -> (i: %u v: %d d: %u, NULL)\n",
277  index, static_cast<POINTER_CAST>(this->m_heap[index].value), this->m_heap[index].id,
278  left, static_cast<POINTER_CAST>(this->m_heap[left].value), this->m_heap[left].id);
279  }
280  else if( right < m_size && left < m_size ) {
281  Fw::Logger::logMsg("i: %u v: %d d: %u -> (i: %u v: %d d: %u, i: %u v: %d d: %u)\n",
282  index, static_cast<POINTER_CAST>(this->m_heap[index].value), this->m_heap[index].id,
283  left, static_cast<POINTER_CAST>(this->m_heap[left].value),this->m_heap[left].id,
284  right, static_cast<POINTER_CAST>(this->m_heap[right].value), this->m_heap[right].id);
285  }
286 
287  ++index;
288  }
289  }
290 }
#define FW_ASSERT(...)
Definition: Assert.hpp:14
PlatformPointerCastType POINTER_CAST
Definition: BasicTypes.h:53
PlatformIntType NATIVE_INT_TYPE
Definition: BasicTypes.h:51
PlatformUIntType NATIVE_UINT_TYPE
Definition: BasicTypes.h:52
PlatformAssertArgType FwAssertArgType
Definition: FpConfig.h:34
C++-compatible configuration header for fprime configuration.
#define LCHILD(x)
Definition: MaxHeap.cpp:26
#define PARENT(x)
Definition: MaxHeap.cpp:28
#define RCHILD(x)
Definition: MaxHeap.cpp:27
static void logMsg(const char *fmt, POINTER_CAST a0=0, POINTER_CAST a1=0, POINTER_CAST a2=0, POINTER_CAST a3=0, POINTER_CAST a4=0, POINTER_CAST a5=0, POINTER_CAST a6=0, POINTER_CAST a7=0, POINTER_CAST a8=0, POINTER_CAST a9=0)
Definition: Logger.cpp:18
bool isFull()
Is the heap full?
Definition: MaxHeap.cpp:140
~MaxHeap()
MaxHeap deconstructor.
Definition: MaxHeap.cpp:40
bool push(NATIVE_INT_TYPE value, NATIVE_UINT_TYPE id)
Push an item onto the heap.
Definition: MaxHeap.cpp:62
MaxHeap()
MaxHeap constructor.
Definition: MaxHeap.cpp:32
bool create(NATIVE_UINT_TYPE capacity)
MaxHeap creation.
Definition: MaxHeap.cpp:45
void print()
Print the contents of the heap to stdout.
Definition: MaxHeap.cpp:262
bool pop(NATIVE_INT_TYPE &value, NATIVE_UINT_TYPE &id)
Pop an item from the heap.
Definition: MaxHeap.cpp:111
bool isEmpty()
Is the heap empty?
Definition: MaxHeap.cpp:145
NATIVE_UINT_TYPE getSize()
Get the current number of elements on the heap.
Definition: MaxHeap.cpp:150