(define tree-label "Tree")
(define node-label "Node")

(define (make-tree less? equal? node-or-leaf)
  (list tree-label node-or-leaf less? equal?))

(define (is-tree? object)
  (and (pair? object)
       (eq? (car object) tree-label)))

(define (tree.node-or-leaf tree)
  (list-ref tree 1))

(define (tree.less? tree)
  (list-ref tree 2))

(define (tree.equal? tree)
  (list-ref tree 3))

(define (make-node value less greater-or-equal)
  ; LESS and GREATER-OR-EQUAL should be either nodes or values
  (list node-label value less greater-or-equal))

(define (is-node? object)
  (and (pair? object)
       (eq? (car object) node-label)))

(define (node.value node)
  (list-ref node 1))

(define (node.less node)
  (list-ref node 2))

(define (node.greater-or-equal node)
  (list-ref node 3))

(define (tree.greatest t)
  (let ((node-or-leaf (tree.node-or-leaf t)))
    (node.greatest node-or-leaf)))

(define (node.greatest n)
  (define (walk node-or-leaf)
    (if (is-node? node-or-leaf)
	(walk (node.greater-or-equal node-or-leaf))
	node-or-leaf))
  (walk n))

(define (tree.least t)
  (let ((node-or-leaf (tree.node-or-leaf t)))
    (node.least node-or-leaf)))

(define (node.least n)
  (define (walk node-or-leaf)
    (if (is-node? node-or-leaf)
	(walk (node.less node-or-leaf))
	node-or-leaf))
  (walk n))

(define (tree.count-nodes t)
  (let ((node-or-leaf (tree.node-or-leaf t)))
    (define (walk node-or-leaf)
      (if (is-node? node-or-leaf)
	  (+ (walk (node.less node-or-leaf))
	     (walk (node.greater-or-equal node-or-leaf)))
	  1))
    (walk node-or-leaf)))

(define (tree.has-value? t value)
  (let ((node-or-leaf (tree.node-or-leaf t))
	(less? (tree.less? t))
	(equal? (tree.equal? t)))
    (define (walk node-or-leaf)
      (cond ((not (is-node? node-or-leaf))
	     (equal? value node-or-leaf))
	    ((less? value (node.value node-or-leaf))
	     (walk (node.less node-or-leaf)))
	    (else (walk (node.greater-or-equal node-or-leaf)))))
    (walk node-or-leaf)))

(define (tree-valid? t)
  (let ((node-or-leaf (tree.node-or-leaf t))
	(less? (tree.less? t)))
    (node-valid? node-or-leaf less?)))

(define (node-valid? node-or-leaf less?)
  (define (walk node-or-leaf)
    (if (is-node? node-or-leaf)
	(let ((smaller (node.less node-or-leaf))
	      (not-smaller (node.greater-or-equal node-or-leaf))) 
	  (and (node-valid? smaller less?)
	       (node-valid? not-smaller less?)
	       (less? (node.greatest smaller)
		      (node.value node-or-leaf))
	       (not (less? (node.least not-smaller)
			   (node.value node-or-leaf)))))
	#T))
  (walk node-or-leaf))

(define test
  (make-tree < =
   (make-node 10
     (make-node 7
       (make-node 5 3 6)
       8)
     10)))

(list (tree.least test)
      (tree.greatest test)
      (tree.count-nodes test)
      (map (lambda (v) (list v (tree.has-value? test v)))
	   (list 1 2 3 4 5 6 7 8 9 10 11 12))
      (tree-valid? test))

