"""
 heap_v1.py

 a implementation of a priority queue
 as a binary heap including heapsort

 ** IN PROGRESS **

 version 1 : 
    * methods to find parent & child indeces
    * a recursive method to print the heap as a tree
 
 See https://runestone.academy/runestone/books/published/pythonds/Trees/BinaryHeapImplementation.html
 
 We'll do at least these operations.
 
  - create
  - add an element
  - remve an element

The heap stores these indeces

   0 1 2 3 4 5 6 

as these places in a binary tree

               0
          1        2
        3   4     5   6          

and we can find the index of parents and children
with these formulas.

     left_child        2*i + 1     (for parent at i)
     right_child       2*i + 2

     parent           (n-1)//2   (for child at n)

     check : 
     (5 - 1) // 2       =>   2
     (6 - 1) // 2  = 5 //2 =  round_down(2.5) => 2

-------------------------------------------------------

 $ python heap_v1.py 
 -- heap initial test case as tree --
 5
    9
       14
       18
    11

 TODO : 

   * implement the insert and pull methods (see below & the pythonds notes)
   * write some test cases and verify that things are working
   * use the Heap to implement heapsort(array)

Jim Mahoney | cs.bennington.college | MIT License | March 2022
"""

# -- utility functions --

def parent(i):
    """ return the index of the parent of node with index i """
    # Remember that // is the "integer division" operator in python.
    return (i-1) // 2

def left(i):
    """ return index of the left child of the node with index i """
    return 2*i + 1

def right(i):
    """ return index of the left child of the node with index i """
    return 2*i + 2

# -- utility function tests --
# The binary tree indices are arranged like this :
#        0
#     1     2
#    3 4   5 6
# ... so here are a few of the index relationships.
assert parent(3) == 1
assert parent(6) == 2
assert right(1) == 4
assert left(2) == 5

class Heap:

    """ An implementation of a minimum priority queue with a binary heap """
    
    def __init__(self):
        self.array = []
        
    def init_test_case(self):
        # a valid heap that we can play with;
        # same numbers as in the pythonds material.
        #        5
        #     9     11
        #   14  18
        self.array = [5, 9, 11, 14, 18]

    def valid_index(self, i):
        """ True iff i is a valid index """
        return 0 <= i and i < len(self.array)

    def tree_string(self, i=0, indent=0):
        """ return binary heap as a tree string, indented values by depth """
        padding = 3  # amount of whitespace to indent each next depth
        if not self.valid_index(i):
            return ''
        else:
            result = ' ' * indent + str(self.array[i]) + '\n'
            for child in (left(i), right(i)):
                if self.valid_index(child):
                    result += self.tree_string(child, indent + padding)
            return result

        
    def insert(self, value):
        """ add a new priority value to the heap """
        # TODO
        pass

    def pull(self):
        """ remove and return the smallest value from the heap """
        # TODO
        return None
    
        
def main():
    heap = Heap()
    heap.init_test_case()
    print("-- heap initial test case as tree --")
    print(heap.tree_string())
    
main()