#lang racket

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

;;; Procedure:
;;;   get
;;; Parameters:
;;;   heap, a heap
;;; Purpose:
;;;   Remove the highest-priority element in the heap
;;; Produces:
;;;   The first element in the heap while also removing it and storing a 0 in the new spot
(define get
  (lambda (heap)
    (let ([ret (first heap)]) ;ret = (first heap)
      (define new (make-vector (max 0 (- (vector-ref heap 0) 1)))) ;new vector of smaller length
      (when (> (vector-ref heap 0) 0) ;if elements are left in vector
        (begin (vector-set! heap 0 
                            (- (vector-ref heap 0) 1)) ;decrement first value in heap
               (vector-copy! new 0 ;copy old vector to new vector
                             (vector-ref heap 1) 1 
                             (+ (vector-ref heap 0) 1)))) 
      (vector-copy! (vector-ref heap 1) 0 
                    new 0 (vector-length new)) ;copy new vector into heap vector
      (vector-set! (vector-ref heap 1) 
                   (vector-ref heap 0) 0) ;replace lost value with 0
      ret))) ;return ret

;;; Procedure:
;;;   swap
;;; Parameters:
;;;   i, an index
;;;   j, an index
;;;   vector, a vector
;;; Purpose:
;;;   Swap two values in a vector at indices i and j
;;; Produces:
;;;   A vector with the values at the two indices swapped
(define swap
  (lambda (i j vector)
    (let ([temp (vector-ref vector i)])
      (vector-set! vector i (vector-ref vector j))
      (vector-set! vector j temp)
      temp)
    vector))

;;; Procedure:
;;;   heapify
;;; Parameters:
;;;   heap, a heap
;;;   index, a non-negative integer
;;; Purpose:
;;;   Reorder heap so values are in correct position, starting at index
;;; Produces:
;;;   A properly-sorted heap
(define heapify
  (lambda (heap index)
    (define temp 0)
    (cond ((odd? index)
           (set! temp (- index 1))) ;reassign temp to be divisible by 2
          ((even? index)
           (set! temp index)))
    (when ((vector-ref heap 2) (vector-ref (vector-ref heap 1) index) ;when current index and parent are backward
                               (vector-ref (vector-ref heap 1) (/ temp 2)))
      (begin (swap index (/ temp 2) (vector-ref heap 1)) ;swap index and parent
             (heapify heap (/ temp 2)))))) ;continue swapping until heap is properly ordered

;;; Procedure:
;;;   add
;;; Parameters:
;;;   heap, a heap
;;;   value, an integer
;;; Purpose:
;;;   Add a value to a heap
;;; Produces:
;;;   A heap with the given value added to it in it's proper position
(define add
  (lambda (heap value)
    ;if vector is full, create a new one of double size
    ;replace heap with new heap containing bigger vector
    (when (equal? (vector-ref heap 0) (vector-length (vector-ref heap 1)))
      (begin (define new (make-vector ;double vector length
                          (max 1 (* (vector-ref heap 0) 2)))) ;if prior empty, make length = one
             (vector-copy! new 0 ;copy old vector to new vector
                           (vector-ref heap 1) 0
                           (vector-ref heap 0))
             (vector-set! heap 1 new))) ;store newvec contents in heap
    ;place value at end of vector and compare/swap with parent nodes to see when it is in correct position
    (vector-set! (vector-ref heap 1) (vector-ref heap 0) value) ;place value at end of heap
    (vector-set! heap 0 (+ 1 (vector-ref heap 0))) ;add one to heap length
    (heapify heap (- (vector-ref heap 0) 1)) ;reorder heap
    heap))