#lang racket
;;; Worked in collaboration with Jonah Zimmermann, Blake Rish, Maddie Gouldman, Julia Fay

;;; Procedure:
;;;   heap-new
;;; Parameters:
;;;   higher-priority?, a binary predicate
;;;   size, a positive integer
;;; Purpose:
;;;   Create a new heap.
;;; Produces:
;;;   heap, a heap of length size
(define heap-new
  (lambda (higher-priority?)
    (vector 0 (make-vector 3) higher-priority?)))

;;; Procedure:
;;;   first
;;; Parameters:
;;;   heap, a heap
;;; Purpose:
;;;   returns the highest-priority element in heap
;;; Produces:
;;;   first-element, the highest-priority element in the heap
(define first 
  (lambda (heap)
    (vector-ref (vector-ref heap 1) 0)))

;;; Procedure:
;;;   swap!
;;; Parameters:
;;;   heap, a heap
;;;   p0, an integer position
;;;   p1, an integer position
;;; Purpose:
;;;   swaps the elements at the two input position 
;;; Produces:
;;;   side effect
(define swap!
  (lambda (heap p0 p1)
    (let ([tmp (vector-ref heap p0)])
      (vector-set! heap p0 (vector-ref heap p1))
      (vector-set! heap p1 tmp))))

;;; Procedure:
;;;   percolate-up
;;; Parameters:
;;;   heap, a heap
;;;   pos, an integer position
;;; Purpose:
;;;   moves a higher-priority element of a heap to its correct position to satisfy ;;;   the heap invariant 
;;; Produces:
;;;   void
(define percolate-up
  (lambda (heap pos)
    (let ([elements (vector-ref heap 1)]
          [higher-priority? (vector-ref heap 2)])
      (let kernel ([pos-new pos])
        (cond
          [(positive? pos-new)
           (if (higher-priority? (vector-ref elements pos-new)
                                 (vector-ref elements (quotient pos-new 2)))
               (swap! elements (quotient pos-new 2) pos-new)            
               (void))
           (kernel (quotient pos-new 2))])))))

(define percolate-down
  (lambda (heap pos)
    (let([elements (vector-ref heap 1)]
         [higher-priority? (vector-ref heap 2)]
         [sz (vector-ref heap 0)])
      (let kernel ([parent pos]
                   [left (+ 1 (* pos 2))]
                   [right (+ 2 (* pos 2))])
        (cond 
          [(and (<= left sz) (> right sz) (higher-priority? (vector-ref elements left) (vector-ref elements parent)))
           (swap! heap left parent)]
          [(and (higher-priority? (vector-ref elements left) (vector-ref elements right)))
           (swap! heap left parent)
           (kernel left left right)]
          [(and (higher-priority? (vector-ref elements right) (vector-ref elements parent))
                (higher-priority? (vector-ref elements right) (vector-ref elements left)))
           (swap! heap right parent)
           (kernel right left right)]
          [else
           (void)])))))



(define move-up
  (lambda (heap p0 p1)
    (cond
      [(not(zero? (vector-ref heap p1)))
       (swap! heap p0 p1)
       (move-up heap (+ p0 1) (+ p1 1))])))

;;; Procedure:
;;;   get
;;; Parameters:
;;;   heap, a heap
;;; Purpose:
;;;   returns and removes the highest-priority in the heap
;;; Produces:
;;;   elem, the highest-priority element in the heap
(define get
  (lambda (heap)
    (letrec ([vec (vector-ref heap 1)]
             [cursize (vector-ref heap 0)])
      (let ([highest (vector-ref vec 0)])
        (vector-set! vec 0 0)
        (vector-set! heap 0 (- cursize 1))
        (move-up vec 0 1)
        (+ highest 0)))))

;;; Procedure:
;;;   add
;;; Parameters:
;;;   heap, a heap
;;;   value, a number
;;; Purpose:
;;;   Adds value to the heap, then properly sorts it 
;;; Produces:
;;;   heap, a heap
(define add
  (lambda (heap value)
    (let ([elem (vector-ref heap 1)]
          [size (vector-ref heap 0)])
      (cond
        [(not(= size (vector-length elem)))
         (vector-set! elem size value)
         (vector-set! heap 0 (+ size 1))
         (percolate-up heap size)]
        [else
         (let ([new-heap (make-vector (* 2 size))])
           (vector-copy! new-heap 0 elem)
           (vector-set! heap 1 new-heap)
           (vector-set! new-heap size value)
           (vector-set! heap 0 (+ size 1))
           (percolate-up heap size))]))))