Login
7 branches 0 tags
Ben (Win10) Fixed funcalls for anonymous fn's that are defined in the top-level 90f1116 3 years ago 754 Commits
;; An avl tree is a balanced binary tree that stores a set of ordered keys.
;; It is represented as an array #[root-node compare-func]
;; An avl node with a key is represented as an array #[height key left-child right-child]
;; An empty avl node is represented as just the symbol :e.
;; The comparison function is called with two keys [cmp a b] and is expected to return
;;  -1 if a < b
;;  +1 if a > b
;;   0 if a = b.

[def avl/empty :e]

[defn avl/empty? [n] [= :e n]]
[export default-cmp [defn avl/default-cmp [x y] [if [< x y] -1 [if [> x y] 1 0]]]]
[defn avl/typecheck [r k]
      [or [avl/empty? [avl/root r]]
          [= [type-of k] [type-of [avl/key [avl/root r]]]]
          [throw [list :type-error "AVL trees can only contains keys of a single type" k [current-lambda]]]]]
[export tree [defn avl/tree [cmp]
                   #[avl/empty [or cmp avl/default-cmp]]]]

[defn avl/height [n] [if [avl/empty? n] 0 [array/ref n 0]]]
[defn avl/key    [n] [array/ref n 1]]
[defn avl/left   [n] [array/ref n 2]]
[defn avl/right  [n] [array/ref n 3]]

[defn avl/root   [r] [array/ref r 0]]
[defn avl/cmp    [r] [array/ref r 1]]

[defn avl/min-node [n]
      [if [avl/empty? n]
          avl/empty
          [let [[l [avl/left n]]]
               [if [avl/empty? l]
                   n
                   [avl/min-mode l]]]]]

[defn avl/update-left   [n l] [array/set! [array/dup n] 2 l]]
[defn avl/update-right  [n r] [array/set! [array/dup n] 3 r]]
[defn avl/update-key    [n k] [array/set! [array/dup n] 1 k]]

[defn avl/update-root   [t r] [array/set! [array/dup t] 0 r]]

[defn avl/update-height [n]
      [array/set! [array/dup n] 0
                  [+ 1 [max [avl/height [avl/left n]]
                            [avl/height [avl/right n]]]]]]

;;        y               x
;;       / \             / \
;;      /   \           /   \
;;     x    T3         T1    y
;;    / \                   / \
;;   /   \                 /   \
;;  T1    T2              T2    T3
;;
;; rotate-left  transforms the left node configuration to the right.
;; rotate-right transforms the right node configuration to the left.

[defn avl/rotate-right [y]
      [let [[x [avl/left y]]]
           [avl/update-height [avl/update-right x [avl/update-height [avl/update-left y [avl/right x]]]]]]]

[defn avl/rotate-left [x]
      [let [[y [avl/right x]]]
           [avl/update-height [avl/update-left y [avl/update-height [avl/update-right x [avl/left y]]]]]]]

[defn avl/balance [n]
      [if [avl/empty? n]
          0
          [- [avl/height [avl/left n]] [avl/height [avl/right n]]]]]

[defn avl/insert-rebalance [n cmp v]
      [let [[b [avl/balance n]]]
           [cond
            [[> b 1]
             [case [cmp v [avl/key [avl/left n]]]
                   [-1 [avl/rotate-right n]]
                   [1  [avl/rotate-right [avl/update-left n [avl/rotate-left [avl/left n]]]]]
                   [0  n]]]
            [[< b -1]
             [case [cmp v [avl/key [avl/right n]]]
                   [1  [avl/rotate-left n]]
                   [-1 [avl/rotate-left [avl/update-right n [avl/rotate-right [avl/right n]]]]]
                   [0  n]]]
            [#t n]]]]

[defn avl/node-insert [n cmp v]
      [if [avl/empty? n]
          #[1 v avl/empty avl/empty]
          [case [cmp v [avl/key n]]
                [-1 [avl/insert-rebalance [avl/update-height [avl/update-left  n [avl/node-insert [avl/left  n] cmp v]]] cmp v]]
                [1  [avl/insert-rebalance [avl/update-height [avl/update-right n [avl/node-insert [avl/right n] cmp v]]] cmp v]]
                [0  [avl/update-key n v]]]]]

[export insert [defn avl/insert [t v]
                     "Insert key V into tree T.  If a node with an equivalent key already exists, its key is updated to V"
                     [avl/typecheck t v]
                     [avl/update-root t [avl/node-insert [avl/root t] [avl/cmp t] v]]]]

[defn avl/node-get [n cmp v]
      [if [avl/empty? n]
          #nil
          [case [cmp v [avl/key n]]
                [0  [avl/key n]]
                [-1 [avl/node-get [avl/left  n] cmp v]]
                [1  [avl/node-get [avl/right n] cmp v]]]]]

[export get [defn avl/get [t v]
                  "Retrieve the key V from tree T, or #nil if V is not in it"
                  [if [or [avl/empty? [avl/root t]]
                          [not= [type-of v] [type-of [avl/key [avl/root t]]]]]
                      #nil
                      [avl/node-get [avl/root t] [avl/cmp t] v]]]]

[export from-list [defn avl/from-list [l cmp]
                        "Create a new avl tree using the keys in L and the comparison function CMP"
                        [list/reduce l avl/insert [avl/tree cmp]]]]

[defn avl/remove-rebalance [n]
      [if [avl/empty? n]
          n
          [let [[b [avl/balance n]]
                [l [avl/left n]]
                [r [avl/right n]]]
               [cond
                [[> b 1]
                 [if [>= [avl/balance l] 0]
                     [avl/rotate-right n]
                     [avl/rotate-right [avl/update-left n [avl/rotate-left l]]]]]
                [[< b -1]
                 [if [<= [avl/balance r] 0]
                     [avl/rotate-left n]
                     [avl/rotate-left [avl/update-right n [avl/rotate-right r]]]]]
                [#t n]]]]]

[defn avl/node-remove [n cmp v]
      [if [avl/empty? n]
          n
          [let [[root
                    [case [cmp v [avl/key n]]
                          [-1 [avl/update-left  n [avl/node-remove [avl/left  n] cmp v]]]
                          [1  [avl/update-right n [avl/node-remove [avl/right n] cmp v]]]
                          [0  [cond
                               [[avl/empty? [avl/left  n]] [avl/right n]]
                               [[avl/empty? [avl/right n]] [avl/left  n]]
                               [#t [let [[k [avl/key [avl/min-node [avl/right n]]]]]
                                        [avl/update-key [avl/update-right [avl/right n] [avl/node-remove [avl/right n] cmp v]] k]]]]]]]]
               [set! root [avl/update-height root]]
               [avl/remove-rebalance root]]]]

[export remove [defn avl/remove [t v]
                     "Remove the key V from tree T if it is contained within it"
                     [avl/update-root t [avl/node-remove [avl/root t] [avl/cmp t] v]]]]

[defn avl/equal-node? [a b]
      [if [avl/empty? a]
          [avl/empty? b]
          [and [equal? [avl/key a] [avl/key b]]
               [avl/equal-node? [avl/left a] [avl/left b]]
               [avl/equal-node? [avl/right a] [avl/right b]]]]]

[export equal? [defn avl/equal? [a b]
                     "Test if two avl trees are equal"
                     [avl/equal-node? [avl/root a] [avl/root b]]]]

[defn avl/reduce-node [node o s]
      [if [avl/empty? node]
          s
          [o [avl/key node]
             [avl/reduce-node [avl/right node] o
                              [avl/reduce-node [avl/left node] o s]]]]]

[export reduce [defn avl/reduce [t o s]
                     "Reduce T in-order with a reducer O taking a key and the result of the reductions of one subtree"
                     [avl/reduce-node [avl/root t] o s]]]

[defn avl/reduce-node-bin [n o s]
      [if [avl/empty? n]
          s
          [o [o [avl/key n]
                [avl/reduce-node-bin [avl/left n] o s]]
             [avl/reduce-node-bin [avl/right n] o s]]]]

[export reduce-bin [defn avl/reduce-bin [t o s]
                         "Reduce T with a reducer O taking a key and the result of the reductions of both subtrees"
                         [avl/reduce-node-bin [avl/root t] o s]]]

[export map [defn avl/map [t f]
                  "Create a new avl tree by mapping each key in T using F, using the same comparison function as T"
                  [avl/reduce t [fn [x acc] [avl/insert acc [f x]]] [avl/tree [avl/cmp t]]]]]

[export map-to [defn avl/map-to [t f cmp]
                     "Create a new avl tree by mapping each key in in T using F, using the comparison function CMP, which may be different from the comparison used in T"
                     [avl/reduce t [fn [x acc] [avl/insert acc [f x]]] [avl/tree cmp]]]]

[export to-list [defn avl/to-list [t]
                      [avl/reduce t cons #nil]]]

[deftest 1    [avl/get [avl/from-list '[1 2 3]] 1]]
[deftest 4    [avl/get [avl/from-list '[1 2 3 4 5]] 4]]
[deftest 15   [avl/get [avl/from-list '[23 42 100 10 15 64 101]] 15]]
[deftest 100  [avl/get [avl/from-list '[23 42 100 100 101 100]] 100]]
[deftest 101  [avl/get [avl/from-list '[23 42 100 100 101 100]] 101]]
[deftest 100  [avl/get [avl/remove [avl/from-list '[23 42 100 10 64 101]] 15] 100]]
[deftest #nil [avl/get [avl/from-list '[1 2 3]] 4]]
[deftest #nil [avl/get [avl/from-list #nil] 1]]
[deftest #nil [avl/get [avl/remove [avl/from-list '[23 42 100 10 15 64 101]] 15] 15]]
[deftest #nil [avl/get [avl/remove [avl/from-list '[23 42 100 10 64 101]] 15] 15]]
[deftest 355  [avl/reduce [avl/from-list '[23 42 100 10 15 64 101]] + 0]]
[deftest 355  [avl/reduce-bin [avl/from-list '[23 42 100 10 15 64 101]] + 0]]
[deftest -19  [avl/reduce [avl/from-list '[23 42 100 10 15 64 101]] - 0]]
[deftest 125  [avl/reduce-bin [avl/from-list '[23 42 100 10 15 64 101]] - 0]]
[deftest '[42 100 101 64 15 23 10] [avl/to-list [avl/from-list '[23 42 100 10 15 64 101]]]]
[deftest 200   [avl/get [avl/map [avl/from-list '[23 42 100 10 15 64 101]] [fn [x] [* x 2]]] 200]]
[deftest #nil  [avl/get [avl/map [avl/from-list '[23 42 100 10 15 64 101]] [fn [x] [* x 2]]] 100]]
[deftest 100   [avl/get [avl/insert [avl/map [avl/remove [avl/from-list '[23 42 100 10 15 64 101]] 100] [fn [x] [* x 2]]] 100] 100]]
[deftest "100" [avl/get [avl/map [avl/from-list '[23 42 100 10 15 64 101]] string] "100"]]
[deftest "100" [avl/get [avl/map-to [avl/from-list '[23 42 100 10 15 64 101]] string [fn [x y] [if [< x y] -1 [if [> x y] 1 0]]]] "100"]]
[deftest "a"   [avl/get [avl/from-list '["a" "b" "c"]] "a"]]
[deftest "ein" [avl/get [avl/from-list '["Dies" "ist" "ein" "Test"]] "ein"]]
[deftest #nil  [avl/get [avl/from-list '["a" "b" "c"]] "d"]]
[deftest #nil  [avl/get [avl/from-list '["a" "b" "c"]] :a]]
[deftest :c    [avl/get [avl/from-list '[:a :b :c]] :c]]
[deftest #nil  [avl/get [avl/from-list '[:a :b :c]] :d]]
[deftest #nil  [avl/get [avl/from-list '[:a :b :c]] "c"]]
[deftest 'c    [avl/get [avl/from-list '[c b a]] 'c]]
[deftest 'c    [avl/get [avl/from-list '[b c a]] 'c]]
[deftest 'c    [avl/get [avl/from-list '[b c b b b a]] 'c]]
[deftest '[b c a] [avl/to-list [avl/from-list '[b c b b b a]] 'c]]
[deftest #nil [avl/get [avl/from-list '[c b a]] 'd]]
[deftest :type-error [try car [avl/from-list '[c :b a]]]]
[deftest :type-error [try car [avl/from-list '["c" b a]]]]
[deftest :type-error [try car [avl/from-list '[123 b a]]]]