|
|
- #include "priority_queue.h"
- #include "erl_nif.h"
- #include <stdlib.h>
- #include <string.h>
-
- // Algorithm described at : http://robin-thomas.github.io/min-heap/
-
- #define left(x) 2 * x + 1
- #define right(x) 2 * x + 2
- #define parent(x) (x - 1) / 2
-
- #define less(a, b) less_(heap_[a], heap_[b])
-
- PriorityQueue::PriorityQueue(LessFun ls, UpdatePositionFun upd, DestroyElementFun dtor) :
- capacity_(0),
- length_(0),
- heap_(NULL),
- less_(ls),
- update_pos_fun_(upd),
- item_dtor_(dtor) { }
-
- PriorityQueue::~PriorityQueue()
- {
- if(heap_)
- {
- for(int i = 0; i < length_; i++)
- item_dtor_(heap_[i]);
-
- enif_free(heap_);
- }
- }
-
- bool PriorityQueue::insert(void* item)
- {
- int pos;
-
- if (length_ == capacity_)
- {
- int new_capacity = (length_+1) * 2;
- void** new_heap = reinterpret_cast<void**>(enif_alloc(sizeof(void*) * new_capacity));
-
- if (!new_heap)
- return false;
-
- memcpy(new_heap, heap_, sizeof(void*)*length_);
- enif_free(heap_);
- heap_ = new_heap;
- capacity_ = new_capacity;
- }
-
- pos = (length_)++;
- set(pos, item);
- bubble_down(pos);
- return true;
- }
-
- bool PriorityQueue::remove(void* item, int pos)
- {
- if (pos >= length_)
- return false;
-
- if(heap_[pos] != item)
- return false;
-
- return remove(pos) == item;
- }
-
- void* PriorityQueue::remove(int pos)
- {
- if (pos >= length_)
- return NULL;
-
- void* item = heap_[pos];
- length_--;
-
- int ls = less(pos, length_);
-
- set(pos, heap_[length_]);
-
- if(ls)
- bubble_up(pos);
- else
- bubble_down(pos);
-
- // todo: resize down the heap in case we have a lot of empty slots
-
- update_pos_fun_(item, -1);
- return item;
- }
-
- void* PriorityQueue::peek()
- {
- if(length_ == 0)
- return NULL;
-
- return heap_[0];
- }
-
- // private
-
- void PriorityQueue::set(int pos, void* item)
- {
- heap_[pos] = item;
- update_pos_fun_(item, pos);
- }
-
- void PriorityQueue::pos_swap(int pos1, int pos2)
- {
- void* tmp = heap_[pos1];
- set(pos1, heap_[pos2]);
- set(pos2, tmp);
- }
-
- void PriorityQueue::bubble_down(int pos)
- {
- while(true)
- {
- int parent = parent(pos);
-
- if (pos == 0 || less(parent, pos))
- return;
-
- pos_swap(pos, parent);
- pos = parent;
- }
- }
-
- void PriorityQueue::bubble_up(int pos)
- {
- while (true)
- {
- int left = left(pos);
- int right = right(pos);
- int smallest = pos;
-
- if (left < length_ && less(left, smallest))
- smallest = left;
-
- if (right < length_ && less(right, smallest))
- smallest = right;
-
- if (smallest == pos)
- return;
-
- pos_swap(pos, smallest);
- pos = smallest;
- }
- }
|