;; tree-add1.ss

(define-datatype StackFrame StackFrame?
  [identity-k]
  [left-k (val number?)
          (right box?)]
  [right-k (val number?)
           (left box?)])

(define HEAP-SIZE 24)

(define stack (make-vector 20 (identity-k)))
(define stack-ptr 0)

(define heap (make-vector HEAP-SIZE 'unused))
(define heap-ptr 0)

(define (alloc/empty-tree)
  (if (< heap-ptr HEAP-SIZE)
      (begin0
        heap-ptr
        (vector-set! heap heap-ptr 'empty-tree)
        (set! heap-ptr (add1 heap-ptr)))
      (error 'alloc/empty-tree "out of memory!")))

(define (alloc/node val lchild rchild)
  (if (< (+ heap-ptr 3) HEAP-SIZE)
      (begin0
        heap-ptr
        (vector-set! heap heap-ptr 'node)
        (vector-set! heap (+ 1 heap-ptr) val)
        (vector-set! heap (+ 2 heap-ptr) lchild)
        (vector-set! heap (+ 3 heap-ptr) rchild)
        (set! heap-ptr (+ 4 heap-ptr)))
      (error 'alloc/node "out of memory!")))

(define (empty-tree? loc)
  (eq? (vector-ref heap loc) 'empty-tree))

(define (node? loc)
  (eq? (vector-ref heap loc) 'node))

(define (node-val loc)
  (vector-ref heap (add1 loc)))

(define (node-left loc)
  (vector-ref heap (+ 2 loc)))

(define (node-right loc)
  (vector-ref heap (+ 3 loc)))

(define empty-tree alloc/empty-tree)
(define node alloc/node)

(define (get-stack-roots)
  (let loop ([i (sub1 stack-ptr)] [roots empty])
    (if (> i 0)
      (cases StackFrame (vector-ref stack i)
        [identity-k () (loop (sub1 i) roots)]
        [left-k (val right) (loop (sub1 i) (cons right roots))]
        [right-k (val left) (loop (sub1 i) (cons left roots))])
      roots)))

(define (push frame)
  (vector-set! stack stack-ptr frame)
  (set! stack-ptr (add1 stack-ptr)))

(define (pop a-tree)
  (set! stack-ptr (sub1 stack-ptr))
  (cases StackFrame (vector-ref stack stack-ptr)
    [identity-k () a-tree]
    [left-k (val right)
            (push (right-k val (box a-tree)))
            (tree-add1/k (unbox right))]
    [right-k (val left)
             (pop
              (node (add1 val) (unbox left) a-tree))]))
            
(define (tree-add1 a-tree)
  (push (identity-k))
  (tree-add1/k a-tree))

(define (tree-add1/k a-tree)
  (cond
    [(empty-tree? a-tree)
     (pop (empty-tree))]
    [(node? a-tree)
     (push (left-k (node-val a-tree)
                   (box (node-right a-tree))))
     (tree-add1/k (node-left a-tree))]))

(define (location->tree loc)
  (cond
    [(empty-tree? loc) 'mt]
    [(node? loc) (list 'node (node-val loc)
                       (location->tree (node-left loc))
                       (location->tree (node-right loc)))]))
