import java.util.*;
import java.io.*;

class Heap implements SeqStructure {

  protected TreeCell t;
  protected TreeCell finger;//a cursor into the data structure
  protected TreeCell previous;//previous is one step behind finger
  protected int size;
  protected int nComparisons;

  public Heap() {
    t = null;
    size = 0;
    nComparisons = 0;
  }

  public String toString() {
    return "Tree has " + size + " elements:" + t;
  }

  public int numComparisons() {
    return nComparisons;
  }

  public boolean isEmpty() {
    return size == 0;
  }

  public int size() {
    return this.size;
  }

  public Object get() {

    //check for empty heap
    if (isEmpty()) {
      System.out.println("Attempt to get from empty heap");
      return null;
    }

    //extract the maximum guy
    Comparable max = (Comparable)(t.getDatum());

    //if empty tree remains after deletion, update is easy
    if (size == 1) {
      t =  null;
      size = 0;
      return max;
    }

    //non-empty tree will remain after deletion of root
    //move down the tree to PARENT of node whose number is size
    //easy to show that number of this parent node is size/2
    heapDecoder d = new heapDecoder(size/2, 2);
    finger = t;
    while (d.hasMoreDigits()) 
      if (d.getNextDigit() == 0) 
	finger = finger.getLeft();
      else 
	finger = finger.getRight();

    //set root value to value in node whose number is size
    //delete node whose number is size
    if (size%2 == 0) { //left child
      t.setDatum(finger.getLeft().getDatum());
      finger.setLeft(null);
    }
    else {
      t.setDatum(finger.getRight().getDatum());
      finger.setRight(null);
    }
    
    //now walk down the tree from the root restoring the heap property
    TreeCell dNode = t; //the disturbed node
    TreeCell cNode; //max child of dNode
    while (true) {
      //check the left and right children for heap property
      TreeCell l = dNode.getLeft();
      TreeCell r = dNode.getRight();

      //find the max kid
      if (l == null) break; //d must be a leaf node
      if (r == null) cNode = l;
      else {//dNode has two children
	if (((Comparable)l.getDatum()).compareTo(r.getDatum()) < 0) 
	  cNode = r;
	else cNode = l;
	nComparisons++;
      }

      //swap maxkid with parent maybe?
      nComparisons++;
      if (((Comparable)dNode.getDatum()).compareTo(cNode.getDatum()) < 0) {
	//swap parent and child data fields and update dNode
	Object o = dNode.getDatum();
	dNode.setDatum(cNode.getDatum());
	cNode.setDatum(o);
	dNode = cNode;
      }
      else //no need to go further down heap
	break;
    }
    size = size - 1;
    return max;
  }


  public void put(Object o) {

    Comparable p = (Comparable)o;
    size = size + 1;
    if (t == null) {
      t = new TreeCell(p);
      return;
    }

    //otherwise move down the tree to parent of node whose number is size
    //along the way, compare values to maintain heap property
    heapDecoder d = new heapDecoder(size/2,2);
    finger = t;
    while (true) {
      if (p.compareTo(finger.getDatum()) > 0) {
	//swap
	Comparable temp = (Comparable) finger.getDatum();
	finger.setDatum(p);
	p = temp;
      }
      nComparisons++;

      if (! d.hasMoreDigits()) {
	TreeCell n = new TreeCell(p);
	if (size%2 == 0) //set left
	  finger.setLeft(n);
	else
	  finger.setRight(n);
	break;
      }
      else {
	if (d.getNextDigit() == 0) //go left
	  finger = finger.getLeft();
	else //go right
	  finger = finger.getRight();
      }
    }
  }

  //inner class that implements an iterator for translating heap position numbers
  //into paths down the tree from root to that position
  //base will usually be 2 (binary heaps) but it can be set arbitrarily
  //for more general n-ary heaps. cursor points to bit/digit position to be returned
  //next. For example, consider a binary heap and the position 6 = 110 in binary. 
  //cursor will be set to 1 by the constructor. The first call to getNextDigit 
  //will return 1 and decrement this cursor to 0, and the next call after that will 
  //return 0.

 protected class heapDecoder {
    protected int position;
    protected int base;
    protected int cursor;

    public heapDecoder(int pos, int base){
      position = pos;
      this.base = base;
      cursor = (int)(Math.log(pos)/Math.log(base)) - 1;
    }
    
    public int getNextDigit() {
        return (int)(position/Math.pow(base,cursor--)) % base ;

    }

    public boolean hasMoreDigits() {
	return (cursor >= 0);
    }
 }

    //This method tests if a TreeCell t is the root of a heap
 public static boolean isHeap(TreeCell t) {
   if (t == null) return true;
   boolean OK = isHeap(t.getLeft()) && isHeap(t.getRight());
   Comparable datum = (Comparable)(t.getDatum());
   if (t.getLeft() != null) 
       OK = (datum.compareTo(t.getLeft().getDatum()) >= 0) && OK;
   if (t.getRight() != null) 
       OK = (datum.compareTo(t.getRight().getDatum()) >= 0) && OK;
   return OK;
 }
}


class testHeapSort {

  public static void main(String[] args) {

    TreeCell t = null;
    System.out.println(Heap.isHeap(t));
    t = new TreeCell(new Integer(7));
    System.out.println(Heap.isHeap(t));
    t.setLeft(new TreeCell(new Integer (8)));
    System.out.println(Heap.isHeap(t));
    t.setLeft(new TreeCell(new Integer (6)));
    System.out.println(Heap.isHeap(t));  

    Integer[] ar = new Integer[6];
    ar[0] = new Integer(4);
    ar[1] = new Integer(3);
    ar[2] = new Integer(8);
    ar[3] = new Integer(7);
    ar[4] = new Integer(2);
    ar[5] = new Integer(9);    

    printArray(ar);
    int nC = heapSort(ar);
    printArray(ar);
    System.out.println("Number of Comparisons:" + nC);

    //create a random array
    Random rand = new Random();
    ar = new Integer[30];
    for (int i = 0; i < ar.length; i++) 
	ar[i] = new Integer((int) (rand.nextFloat() * ar.length) + 1);
    printArray(ar);
     nC = heapSort(ar);
    printArray(ar);
    System.out.println("Number of Comparisons:" + nC);
  }

  public static int heapSort(Object[] ar) {
    //create a heap
    Heap uriah = new Heap();

    for (int i = 0; i < ar.length; i++)
      uriah.put(ar[i]);

    System.out.println("Number of comparisons for put:" + uriah.numComparisons());

    for (int i = 0; i< ar.length; i++) {
      ar[i] = uriah.get();
      System.out.println("Number of comparisons:" + uriah.numComparisons());
    }


    return uriah.numComparisons();
  }

  public static void printArray(Object[] foo) {
    for (int i = 0; i < foo.length; i++) 
      System.out.print (foo[i] + " ");
    System.out.println();
  }
}