#lang racket
(require rackunit
         data/heap)

(define (mkheap) (vector->heap <= (vector 6 2 4 10 8)))

(test-equal? "heap->vector"
  (heap->vector (mkheap))
  '#(2 4 6 8 10))

(test-equal? "heap-add! min"
  (let ([h (mkheap)])
    (heap-add! h 0)
    (heap->vector h))
  '#(0 2 4 6 8 10))

(test-equal? "heap-add! mid"
  (let ([h (mkheap)])
    (heap-add! h 5)
    (heap->vector h))
  '#(2 4 5 6 8 10))

(test-equal? "heap-add! multi"
  (let ([h (mkheap)])
    (heap-add! h 0 5 12)
    (heap->vector h))
  '#(0 2 4 5 6 8 10 12))

(test-equal? "heap-remove-min!"
  (let ([h (mkheap)])
    (heap-remove-min! h)
    (heap->vector h))
  '#(4 6 8 10))

(define (rand-test range count1 count2 count3)
  (let ([h (make-heap <=)]
        [xs null]) ;; mutated
    (define (fill! count)
      (for ([i (in-range count)])
        (let ([x (random range)])
          (heap-add! h x)
          (set! xs (cons x xs))))
      (set! xs (sort xs <)))

    (fill! count1)

    ;; check equal (non-destructive)
    (check-equal? (vector->list (heap->vector h))
                  xs)

    (for ([i (in-range count2)])
      (let ([xl (car xs)]
            [xh (heap-min h)])
        (set! xs (cdr xs))
        (heap-remove-min! h)))

    (fill! count3)

    (for ([x (in-list xs)])
      (check-equal? (heap-min h) x)
      (heap-remove-min! h))

    (check-equal? (heap-count h) 0)))
  
(test-case "heap random sparse"
  (rand-test 1000 100 50 100))

(test-case "heap random dense"
  (rand-test 20 100 50 100))

(test-equal? "in-heap"
  (for/list ([x (in-heap (mkheap))]) x)
  '(2 4 6 8 10))
(test-equal? "post in-heap count"
   (let* ([h (mkheap)]
          [lst (for/list ([x (in-heap h)]) x)])
     (heap-count h))
   (heap-count (mkheap)))
(test-equal? "in-heap/consume!"
  (for/list ([x (in-heap/consume! (mkheap))]) x)
  '(2 4 6 8 10))
(test-equal? "post in-heap/consume! count"
   (let* ([h (mkheap)]
          [lst (for/list ([x (in-heap/consume! h)]) x)])
     (heap-count h))
   0)

(test-equal? "heap-sort"
  (let ([v (vector 3 4 2 5 1)])
    (heap-sort! v <=)
    v)
  '#(1 2 3 4 5))

(test-equal? "heap-sort (old arg order)"
  (let ([v (vector 3 4 2 5 1)])
    (heap-sort! <= v)
    v)
  '#(1 2 3 4 5))

(let* ([l (for/list ([i 1000]) (random 1000))]
       [v (list->vector l)])
  (test-equal? "heap-sort (random)"
    (begin (heap-sort! v <=) (vector->list v))
    (sort l <)))
