(module avl mzscheme (provide avl avl-introspect avl? avl-from-avl avl-nodes avl-empty? avl-insert! avl-remove! avl+ avl- avl-find avl-exists? avl-for-each avl-map avl-filter avl-min avl-max avl-atomic) ;;; Node definition ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;=head1 AVL Trees ;;; ;;;This module provides a thread safe implementation of AVL Trees. ;;;AVL Trees are binary trees that have the nice property that they ;;;are kept balanced. Actually, unlike normal binary trees, all insert, ;;;delete and find operations on AVL Trees are guarantied O(log n). ;;;See L<http://wikipedia.org/wiki/AVL_tree|http://wikipedia.org/wiki/AVL_tree> ;;;for more information on AVL Trees. ;;; ;;;This implementation has been derived from a C++ implementation of ;;;William A. McKee (google: avl tree algorithm McKee will get you ;;;to his homepage). ;;; ;;;It starts out with an avl algorithm implementation on the tree nodes, ;;;after which the algorithm on the nodes is wrapped as a whole avl tree. ;;;Refer to the ;;;L<avl tree documentation|#avl_tree_interface> for the ;;;provided AVL Tree functions. ;;; ;;;=head2 On thread safety ;;; ;;;Although the basic operations on avl trees are thread safe, ;;;if multiple avl trees are combined into one operation, e.g. ;;;an avl-map function, thread safety will not be guaranteed for ;;;all trees. ;;; ;;;Thread safety for one tree is provided using ;;;a recursion enabled critical section. ;;; ;;;=head2 Supportive macros ;;; ;;;These macros provide access to the avl tree node datastructure. ;;;The 'get-set' macro defines a getter and a setter macro on a ;;;vector. All getters and setters are defined using this get-set ;;;macro. ;;; ;;;=verbatim scm,8 (define-syntax get-set (syntax-rules () ((_ name-get name-set index) (begin (define-syntax name-get (syntax-rules () ((name-get avl-node) (vector-ref avl-node index)))) (define-syntax name-set (syntax-rules () ((name-set avl-node value) (begin (vector-set! avl-node index value) avl-node)))))) ((_ name-get name-set index get-vector-from-struct) (begin (define-syntax name-get (syntax-rules () ((name-get avl-node) (vector-ref (get-vector-from-struct avl-node) index)))) (define-syntax name-set (syntax-rules () ((name-set avl-node value) (begin (vector-set! (get-vector-from-struct avl-node) index value) avl-node)))))) )) (get-set data data! 0) (get-set left left! 1) (get-set right right! 2) (get-set height height! 3) ;;;=verbatim ;;; ;;;=head2 AVL Node Implementation ;;; ;;;The AVL Tree Node implementation is not hard to understand. ;;;AVL Trees are being balanced by keeping a balance factor in ;;;each node. A node with balance factor -1, 0 or 1 is considered ;;;balanced. All other (integer) values will make the node unbalanced. ;;;In this implementation, the balance factor is the 'height' factor. ;;; ;;;Compute height, computes this factor for a node. ;;; ;;;=verbatim scm,8 (define (compute-height node) (let ((h 0)) (if (not (eq? (left node) 'nil)) (if (> (height (left node)) h) (set! h (height (left node))))) (if (not (eq? (right node) 'nil)) (if (> (height (right node)) h) (set! h (height (right node))))) (height! node (+ h 1)))) ;;;=verbatim ;;; ;;;The (new-node obj) function will make a new AVL Tree node with ;;;the data part set to obj. ;;; ;;;=verbatim scm,8 (define (new-node data) (vector data 'nil 'nil 1)) ;;;=verbatim ;;; ;;;(insert-node is-less? node ndata) is used to insert a new node ;;;in the tree. It will use the 'is-less?' function to determine ;;;the right order in the tree. Insert-node will traverse the tree ;;;and insert a new node at the point in the tree that puts the ;;;ndata argument in the right order of the tree. After inserting ;;;the new node, the tree is rebalanced. ;;; ;;;=verbatim scm,8 (define (insert-node is-less? node ndata) (if (eq? node 'nil) (new-node ndata) (begin (if (is-less? ndata (data node)) (left! node (insert-node is-less? (left node) ndata)) (right! node (insert-node is-less? (right node) ndata))) (balance node)))) ;;;=verbatim ;;; ;;;(find-node is-equal? is-less? node fdata) finds fdata in the ;;;tree. It will use the 'is-less?' and the 'is-equal?' functions ;;;to determine how to traverse the tree and to determine the data ;;;of a node equals the given fdata. ;;; ;;;=verbatim scm,8 (define (find-node is-equal? is-less? node fdata) (if (eq? node 'nil) 'nil (if (is-equal? fdata (data node)) node (if (is-less? fdata (data node)) (find-node is-equal? is-less? (left node) fdata) (find-node is-equal? is-less? (right node) fdata))))) ;;;=verbatim ;;; ;;;(remove-node is-equal? is-less? node rdata) recursively locates the node to ;;;be removed in the avl tree, removes the node (using move-down-righthand-side) ;;;and rebalances the tree as needed all the way back up the recursion. ;;; ;;;=verbatim scm,8 (define (remove-node is-equal? is-less? node rdata decreaser) (if (eq? node 'nil) 'nil (if (is-equal? rdata (data node)) (begin (decreaser) (move-down-righthand-side (left node) (right node))) (begin (if (is-less? rdata (data node)) (left! node (remove-node is-equal? is-less? (left node) rdata decreaser)) (right! node (remove-node is-equal? is-less? (right node) rdata decreaser))) (balance node))))) (define (move-down-righthand-side node rhs) (if (eq? node 'nil) rhs (begin (right! node (move-down-righthand-side (right node) rhs)) (balance node)))) ;;;=verbatim ;;; ;;;(node-for-each level node function) works in ascending order through the whole ;;;tree and calls function with the data of each node and the level of the node ;;;in the tree. Nothing is done with the result of the function. node-for-each ;;;is all about side effects. ;;; ;;;=verbatim scm,8 (define (node-for-each level node function) (if (eq? node 'nil) 'nil (begin (node-for-each (+ level 1) (left node) function) (function (data node) level) (node-for-each (+ level 1) (right node) function)))) ;;;=verbatim ;;; ;;;(node-map level newroot node function) works in ascending order through the ;;;whole avl tree and calls function with the data of each node and the level ;;;of the node int the tree. The result of the function is inserted into ;;;newroot. ;;; ;;;=verbatim scm,8 (define (node-map level newroot node function) (if (eq? node 'nil) 'nil (begin (node-map (+ level 1) newroot (left node) function) (avl-insert! newroot (function (data node) level)) (node-map (+ level 1) newroot (right node) function)))) ;;;=verbatim ;;; ;;;(node-filter level newroot node function) works in ascending order through the ;;;whole avl tree and calls function with the data and the level of each node. ;;;The function is expected to be a boolean function. If function returns #t, ;;;the current node is inserted into newroot, otherwise not. ;;; ;;;=verbatim scm,8 (define (node-filter level newroot node function) (if (eq? node 'nil) 'nil (begin (node-filter (+ level 1) newroot (left node) function) (if (function (data node) level) (avl-insert! newroot (data node))) (node-filter (+ level 1) newroot (right node) function)))) ;;;=verbatim ;;; ;;;(balance node) rebalances a subtree, by rotating nodes. It does this ;;;only, if the difference-in-height between the left hand side and ;;;the right hand side of a node is E<lt> -1 or E<gt> 1. ;;; ;;;=verbatim scm,8 (define (balance node) (define (exchange-left node parent) (right! parent (left node)) (left! node (balance parent)) (balance node)) (define (exchange-right node parent) (left! parent (right node)) (right! node (balance parent)) (balance node)) (define (rotate-left node) (exchange-left (right node) node)) (define (rotate-right node) (exchange-right (left node) node)) (define (difference-in-height node) (let ((lh (if (eq? (left node) 'nil) 0 (height (left node)))) (rh (if (eq? (right node) 'nil) 0 (height (right node))))) (- lh rh))) (let ((d (difference-in-height node))) (if (or (< d -1) (> d 1)) (if (< d 0) (begin (if (> (difference-in-height (right node)) 0) (right! node (rotate-right (right node)))) (rotate-left node)) (begin (if (< (difference-in-height (left node)) 0) (left! node (rotate-left (left node)))) (rotate-right node))) (begin (compute-height node) node)))) ;;;=verbatim ;;; ;;;C<(node-min node)> returns the left most node in the avl tree (which will ;;;hold the minimum data). This function is used by C<avl-min>. ;;; ;;;=verbatim scm,8 (define (node-min node) (if (eq? (left node) 'nil) node (node-min (left node)))) ;;;=verbatim ;;; ;;;C<(node-max node)> returns the right most node in the avl tree (which will ;;;hold the maximum data). This function is used by C<avl-max>. ;;; ;;;=verbatim scm,8 (define (node-max node) (if (eq? (right node) 'nil) node (node-max (right node)))) ;;;=verbatim ;;; ;;;=head2 AVL Tree Wrapper Supportive Macros ;;; ;;;These macros provide access to the AVL Tree data structure, and ;;;implement thread safety, using a monitor section. ;;; ;;;=verbatim scm,8 (define-struct %avl (vect)) (get-set is-equal is-equal! 2 %avl-vect) (get-set is-less is-less! 3 %avl-vect) (get-set root root! 1 %avl-vect) (get-set nodes nodes! 4 %avl-vect) (get-set sem sem! 5 %avl-vect) (get-set me me! 6 %avl-vect) (define-syntax protect (syntax-rules () ((_ %avl body) (let ((sem-set ; Conditional semaphore locking, ; to provide recursive protection (if (not (eq? (me %avl) (current-thread))) (begin (semaphore-wait (sem %avl)) (me! %avl (current-thread)) #t) #f))) (let ((result body)) (if sem-set (begin (me! %avl 'me-done) (semaphore-post (sem %avl)))) result))))) ;;;=verbatim ;;; ;;;=cut ;;; ;;; AVL implementation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;;=head2 Avl Tree Interface ;;; ;;;=head3 C<(avl is-equal? is-less?) : avl-tree> ;;; ;;;Given a function 'is-equal?' that determines if two objects in an ;;;avl tree are equal, and a function 'is-less' that determines wheter ;;;one object is 'less than' an other object, a new avl-tree ;;;is created with the 'avl' function. ;;; ;;;=verbatim scm,8 (define (avl is-equal? is-less?) (make-%avl (vector 'avl 'nil is-equal? is-less? 0 (make-semaphore 1) 'me))) (define (avl-introspect avl) (%avl-vect avl)) ;;;=verbatim ;;; ;;;=head3 C<(avl-from-avl avl-tree-donator) : avl-tree> ;;; ;;;The 'avl-from-avl' function creates a new avl-tree from a given ;;;avl tree, which 'donates' the 'is-equal?' and 'is-less?' functions. ;;; ;;;=verbatim scm,8 (define (avl-from-avl troot) (avl (is-equal troot) (is-less troot))) ;;;=verbatim ;;; ;;;=head3 C<(avl? obj) : boolean> ;;; ;;;Returns #t, if obj is an avl-tree; #f, otherwise. Note! all objects that ;;;the predicate 'avl?' is true for, will also have predicate 'vector?'. ;;; ;;;=verbatim scm,8 (define (avl? obj) (%avl? obj)) ;;;=verbatim ;;; ;;;=head3 C<(avl-insert! avl obj) : avl-tree> ;;; ;;;Inserts obj in avl by calling insert-node. Returns avl. ;;;This is not a functional approach, as the avl tree ;;;is updated in place. ;;; ;;;=verbatim scm,8 (define (avl-insert! avl obj) (protect avl (begin (root! avl (insert-node (is-less avl) (root avl) obj)) (nodes! avl (+ (nodes avl) 1)) avl))) ;;;=verbatim ;;; ;;;=head3 C<(avl-remove! avl obj) : avl-tree> ;;; ;;;Removes obj from avl (if obj exists in avl). Returns avl. ;;;This is not a functional approach, as the avl tree ;;;is updated in place. ;;; ;;;=verbatim scm,8 (define (avl-remove! avl obj) (protect avl (root! avl (remove-node (is-equal avl) (is-less avl) (root avl) obj (lambda () (nodes! avl (- (nodes avl) 1))))))) ;;;=verbatim ;;; ;;;=head3 C<(avl+ avl obj) : avl-tree, (avl- avl obj) : avl-tree> ;;; ;;;avl+ and avl- are macros that wrap the avl-insert! and avl-remove! functions. ;;;avl+ wraps avl-insert!. avl- wraps avl-remove!. ;;; ;;;=verbatim scm,8 (define-syntax avl+ (syntax-rules () ((_ avl obj) (avl-insert! avl obj)))) (define-syntax avl- (syntax-rules () ((_ avl obj) (avl-remove! avl obj)))) ;;;=verbatim ;;; ;;;=head3 C<(avl-find avl obj not-found-func) : obj | result of not-found-func> ;;; ;;;avl-find looks up obj in avl by calling find-node. If no node containing ;;;obj is found, not-founc-func, which must be a function with no arguments, is called. ;;;Either (data node) is returned, or the result of not-found-func. ;;; ;;;Example of use: C<(avl-find avl obj (lambda () #f))>. ;;; ;;;=verbatim scm,8 (define (avl-find avl obj not-found-func) (protect avl (let ((r (find-node (is-equal avl) (is-less avl) (root avl) obj))) (if (eq? r 'nil) (not-found-func) (data r))))) ;;;=verbatim ;;; ;;;=head3 C<(avl-exists? avl obj) : boolean> ;;; ;;;Checks wheter obj exists in avl. Returns #t, if so, returns #f, otherwise. ;;;Be carefull, when using this function on an avl tree in a threaded environment. The result ;;;of this function may not be valid anymore because an other thread may have ;;;inserted or removed an object from the tree. ;;; ;;;=verbatim scm,8 (define (avl-exists? avl obj) (let ((found #t)) (avl-find avl obj (lambda () (set! found #f) #f)) found)) ;;;=verbatim ;;; ;;;=head3 C<(avl-map avl function . is-equal-and-is-less) : new avl-tree> ;;; ;;;Constructs a new avl tree from avl mapping function on the data and the ;;;level in the tree of each node (in ascending order). (i.e., ascending order ;;;in terms of the is-less? function). Function is a function that takes ;;;two arguments: data and level, e.g.: C<(avl-map avl (lambda (obj level) (+ obj level)))>. ;;; ;;;If is-equal-and-is-less is the empty list, the new avl tree will be ;;;constructed from avl. Otherwise, a new avl tree will be constructed, ;;;with is-equal? as (car is-equal-and-is-less) and is-less? as (cadr is-equal-and-is-less). ;;; ;;;=verbatim scm,8 (define (avl-map troot function . iseq-isless) (protect troot (let ((newroot (avl (if (null? iseq-isless) (is-equal troot) (car iseq-isless)) (if (null? iseq-isless) (is-less troot) (cadr iseq-isless))))) (node-map 0 newroot (root troot) function) newroot))) ;;;=verbatim ;;; ;;;=head3 C<(avl-for-each avl function) : avl-tree> ;;; ;;;Calls function for each node of avl. Function is a function that takes ;;;the data and the level of a node as arguments (see avl-map). ;;; ;;;=verbatim scm,8 (define (avl-for-each avl function) (protect avl (begin (node-for-each 0 (root avl) function) avl))) ;;;=verbatim ;;; ;;;=head3 C<(avl-filter avl function) : new avl-tree> ;;; ;;;Constructs a new avl tree from C<avl>, inserting all nodes of C<avl> for which ;;;C<function>, which is a function that takes the data and level of each node ;;;as arguments, returns #t. The result of C<function> must be of type boolean. ;;; ;;;Example: C<(avl-filter avl (lambda (obj level) (E<gt> obj 0)))> filters out all ;;;nodes for which C<data> has a value E<gt> 0. ;;; ;;;=verbatim scm,8 (define (avl-filter troot filter-function-boolean) (protect troot (let ((newroot (avl (is-equal troot) (is-less troot)))) (node-filter 0 newroot (root troot) filter-function-boolean) newroot))) ;;;=verbatim ;;; ;;;=head3 C<(avl-nodes avl) : number> ;;; ;;;Returns the number of nodes in avl. ;;; ;;;=verbatim scm,8 (define (avl-nodes avl) (nodes avl)) ;;;=verbatim ;;; ;;;=head3 C<(avl-empty? avl) : boolean> ;;; ;;;Returns #t, if the avl tree has no nodes; #f otherwise. ;;; ;;;=verbatim scm,8 (define (avl-empty? avl) (= (nodes avl) 0)) ;;;=verbatim ;;; ;;;=head3 C<(avl-min avl) : data> ;;; ;;;Will return the minimum data in the avl tree C<avl>, or ;;;C<'avl-no-nodes>, if C<(avl-nodes avl)> equals 0. ;;; ;;;=verbatim scm,8 (define (avl-min troot) (protect troot (if (= (avl-nodes troot) 0) 'avl-no-nodes (data (node-min (root troot)))))) ;;;=verbatim ;;; ;;;=head3 C<(avl-max avl) : data> ;;; ;;;Will return the maximum data in the avl tree C<avl>, or ;;;C<'avl-no-nodes>, if C<(avl-nodes avl)> equals 0. ;;; ;;;=verbatim scm,8 (define (avl-max troot) (protect troot (if (= (avl-nodes troot) 0) 'avl-no-nodes (data (node-max (root troot)))))) ;;;=verbatim ;;; ;;;=head3 C<(avl-atomic avl function) : <result of function>> ;;; ;;;This function will call C<function> with C<avl> in a critical section, ;;;i.e., make the function call atomic. ;;; ;;;Example of use: ;;; ;;; (avl-atomic avl (lambda (avl) ;;; (if (avl-empty? avl) ;;; #f ;;; (begin ;;; (avl-remove! avl (avl-min avl)) ;;; #t)))) ;;; ;;;Implementation: ;;; ;;;=verbatim scm,8 (define (avl-atomic avl func) (protect avl (func avl))) ;;;=verbatim ;;; ;;;=head2 Info ;;; ;;;S<C<Author(s):>> Hans Oesterholt (hansatelementalprogrammingdotorgextension).E<lb> ;;;S<C<Copyright:>> (c) 2005.E<lb> ;;;S<C<License :>> L<Elemental Programming License|http://www.elemental-programming.org/epwiki/ep_license.html>.E<lb> ;;;S<C<File :>> avl.scm $Revision: 1.3 $ ;;; ;;;=cut )