// heap.cc
#include "heap.h"

bool minHeapCompare (void *x, void *y) {
   return ((TreeObject *)x)->value() < ((TreeObject *)y)->value();
}

bool maxHeapCompare (void *x, void *y) {
   return ((TreeObject *)x)->value() > ((TreeObject *)y)->value();
}

Cell::Cell (TreeObject *obj, Cell *ln, Cell *prnt) {
   object   = obj;
   lefttree = NULL;
   rghttree = NULL;
   leftnext = ln;
   rghtnext = NULL;
   parent   = prnt;
}
   
Cell::Cell (TreeObject *obj, Cell *ln) {
   object   = obj;
   leftnext = ln;
   rghtnext = NULL;
   parent   = NULL;
   lefttree = NULL;
   rghttree = NULL;
}

TreeObject *BinTree::getRoot() {
   TreeObject *t = head->object;
   head->object = NULL;
   return t;
}

void BinTree::putRoot(TreeObject *t) {
   if (t == NULL) return;
   if (head == NULL) return;
   head->object = t;
}

/***--------------------------------------------------------------------***/
/***       Remove the last Cell from a tree and return its object       ***/
/***--------------------------------------------------------------------***/
TreeObject *BinTree::popNode() {
   if (head == NULL) return NULL;
   if (tail->leftnext == NULL && tail->lefttree == NULL) {
      TreeObject *t = head->object;
      delete head;
      head = tail = NULL;
      return t;
   } else if (tail->lefttree != NULL) {
      TreeObject *t = tail->lefttree->object;
      tail->lefttree->leftnext->rghtnext = NULL;
      delete tail->lefttree;
      tail->lefttree = NULL;
      return t;
   } else {
      tail = tail->leftnext;
      TreeObject *t = tail->rghttree->object;
      tail->lefttree->rghtnext = NULL;
      delete tail->rghttree;
      tail->rghttree = NULL;
      return t;
   }
}

/***--------------------------------------------------------------------***/
/***  Add a Cell to the end of a heap and insert an object in that Cell ***/
/***--------------------------------------------------------------------***/
void BinTree::addNode(TreeObject *t) {
   if (t == NULL) return;
   if (head == NULL) { 
      head = tail = new Cell(t, NULL, NULL);
   } else if (tail->lefttree == NULL) {
      if (tail->leftnext == NULL) {
         tail->lefttree = new Cell(t, tail, tail);
         tail->rghtnext = tail->lefttree;
      } else {
         Cell *last = tail->leftnext->rghttree;
         tail->lefttree = new Cell(t, last, tail);
         last->rghtnext = tail->lefttree;
      }
   } else {
      Cell *last = tail->lefttree;
      tail->rghttree = new Cell(t, last, tail);
      last->rghtnext = tail->rghttree;
      tail = tail->rghtnext;
   }
}

ostream & operator<< (ostream & out, BinTree & bt) {
   Cell *t;
   if (bt.head == NULL) { out << "(empty)\n";  return out; }
   for (t = bt.head ; t != NULL ; t = t->rghtnext)
      t->object->display(out);
   out << "\n";
   return out;
}

/***--------------------------------------------------------------------***/
/***  Heapify Down -                                                    ***/
/***     let t be a Cell with an object                                 ***/
/***     let h be the child Cell of t with object of least value        ***/
/***     if no such child Cell exists, stop                             ***/
/***     otherwise, if t's object value > h's object value              ***/
/***     then swap t's object with h's object, set t = h, and repeat    ***/
/***--------------------------------------------------------------------***/
void Heap::heapifyDown() {
   Cell *t = head, *h;
   TreeObject *obj;

   if (head == NULL) return;

   while (1) {
      if (t->lefttree == NULL && t->rghttree == NULL)
         break;
      else
      if (t->rghttree == NULL)
         h = t->lefttree;
      else
      if (cmp(t->lefttree->object, t->rghttree->object))
         h = t->lefttree;
      else
         h = t->rghttree;

      if (cmp(h->object, t->object)) {
         obj = t->object;
         t->object = h->object;
         h->object = obj;
         t = h;
      } else
         break;
   }
}

/***--------------------------------------------------------------------***/
/***  Heapify Up -                                                      ***/
/***     let h be the last Cell of the tree                             ***/
/***     if no such Cell exists, stop                                   ***/
/***     otherwise, if h's object value < h's parent's object value     ***/
/***     then swap h's object with h's parent's object,                 ***/
/***     set h = h's parent,                                            ***/
/***     and repeat                                                     ***/
/***--------------------------------------------------------------------***/
void Heap::heapifyUp() {
   Cell *h;
   TreeObject *obj;

   if (head == NULL || (head == tail && tail->lefttree == NULL)) return;
   if (tail->rghttree == NULL && tail->lefttree == NULL)
      h = tail->leftnext->rghttree;
   else
      h = tail->lefttree;

   while (1) {
      if (h == head) break;
      if (cmp(h->object, h->parent->object)) {
         obj = h->object;
         h->object = h->parent->object;
         h->parent->object = obj;
         h = h->parent;
      } else
         break;
   }
}

void Heap::heapifyUpFrom (Cell *cell) {
   Cell *h;
   TreeObject *obj;

   if (head == NULL || (head == tail && tail->lefttree == NULL)) return;
   h = cell;

   while (1) {
      if (h == head) break;
      if (cmp(h->object, h->parent->object)) {
         obj = h->object;
         h->object = h->parent->object;
         h->parent->object = obj;
         h = h->parent;
      } else
         break;
   }
}

/***--------------------------------------------------------------------***/
/***                   Basic function for building a heap               ***/
/***--------------------------------------------------------------------***/
void Heap::addNode(TreeObject *t) {
   BinTree::addNode(t);
   heapifyUp();
}

void Heap::insert(TreeObject *t) {
   BinTree::addNode(t);
   heapifyUp();
}

ostream & Heap::display(ostream & out) {
   int i=0, j=1; Cell *t;

   if (head == NULL) { out << "(empty)\n";  return out; }
   for (t = head ; t != NULL ; t = t->rghtnext) {
      if ((1 << i) == j++) {
         out << "\n";
         int place = 40 - (int)(j*(1.5));
         for (int p=0 ; p < place ; p++) out << " ";
         i++;
      }
      t->object->display(out);
   }
   out << "\n";
   return out;
}

ostream & operator<<(ostream & out, Heap & heap) {
   return heap.display(out);
}

/***--------------------------------------------------------------------***/
/***   Perform the heapsort on a binary tree T that is assumed a heap   ***/
/***--------------------------------------------------------------------***/
void Heap::heapSort() {
   while (!empty()) {
      displayRoot();
      putRoot(popNode());
      heapifyDown();
   }
}

TreeObject *Heap::popRoot() {
   TreeObject *t = getRoot();
   TreeObject *e = popNode();
   putRoot(e);
   heapifyDown();
   return t;
}

TreeObject *Heap::dequeue() {
   TreeObject *t = getRoot();
   TreeObject *e = popNode();
   putRoot(e);
   heapifyDown();
   return t;
}