alv(2): new avl implementation

This commit is contained in:
spew 2016-12-22 16:47:41 -06:00
parent 3bf89ed825
commit 0885ed1e80
5 changed files with 383 additions and 549 deletions

View file

@ -1,26 +1,23 @@
#pragma lib "libavl.a" #pragma lib "libavl.a"
#pragma src "/sys/src/libavl" #pragma src "/sys/src/libavl"
typedef struct Avl Avl; typedef struct Avl Avl;
typedef struct Avltree Avltree; typedef struct Avltree Avltree;
typedef struct Avlwalk Avlwalk;
#pragma incomplete Avltree struct Avl {
#pragma incomplete Avlwalk Avl *c[2];
Avl *p;
struct Avl schar balance;
{
Avl *p; /* parent */
Avl *n[2]; /* children */
int bal; /* balance bits */
}; };
Avl *avlnext(Avlwalk *walk); struct Avltree {
Avl *avlprev(Avlwalk *walk); int (*cmp)(Avl*, Avl*);
Avlwalk *avlwalk(Avltree *tree); Avl *root;
void deleteavl(Avltree *tree, Avl *key, Avl **oldp); };
void endwalk(Avlwalk *walk);
void insertavl(Avltree *tree, Avl *new, Avl **oldp); Avltree *avlcreate(int(*cmp)(Avl*, Avl*));
Avl *lookupavl(Avltree *tree, Avl *key); Avl *avllookup(Avltree*, Avl*);
Avltree *mkavltree(int(*cmp)(Avl*, Avl*)); Avl *avldelete(Avltree*, Avl*);
Avl* searchavl(Avltree *tree, Avl *key, int neighbor); Avl *avlinsert(Avltree*, Avl*);
Avl *avlnext(Avl*);
Avl *avlprev(Avl*);

View file

@ -1,147 +1,119 @@
.TH AVL 2 .TH AVL 2
.SH NAME .SH NAME
mkavltree, insertavl, lookupavl, deleteavl, avlwalk, avlnext, avlprev, endwalk - AVL tree routines avlcreate,
avlinsert,
avldelete,
avllookup,
avlnext,
avlprev \- Balanced binary search tree routines
.SH SYNOPSIS .SH SYNOPSIS
.\" .ta 0.75i 1.5i 2.25i 3i 3.75i 4.5i .ta 0.75i 1.5i 2.25i 3i 3.75i 4.5i
.ta 0.7i +0.7i +0.7i +0.7i +0.7i +0.7i +0.7i .\" .ta 0.7i +0.7i +0.7i +0.7i +0.7i +0.7i +0.7i
.EX .EX
#include <u.h> #include <u.h>
#include <libc.h> #include <libc.h>
#include <avl.h> #include <avl.h>
.sp 0.3v
typedef struct Avl Avl; typedef struct Avl Avl;
struct Avl typedef struct Avltree Avltree;
{
Avl *p; /* parent */ struct Avl {
Avl *n[2]; /* children */ Avl *c[2]; /* children */
int bal; /* balance bits */ Avl *p; /* parent */
schar balance; /* balance factor */
}; };
.sp 0.3v struct Avltree {
Avl *avlnext(Avlwalk *walk); int (*cmp)(Avl*, Avl*);
Avl *avlprev(Avlwalk *walk); Avl *root;
Avlwalk *avlwalk(Avltree *tree); };
void deleteavl(Avltree *tree, Avl *key, Avl **oldp);
void endwalk(Avlwalk *walk); Avltree *avlcreate(int(*cmp)(Avl*, Avl*));
void insertavl(Avltree *tree, Avl *new, Avl **oldp); Avl *avlinsert(Avltree *tree, Avl *new);
Avl *lookupavl(Avltree *tree, Avl *key); Avl *avldelete(Avltree *tree, Avl *key);
Avl *searchavl(Avltree *tree, Avl *key, int neighbor); Avl *avllookup(Avltree *tree, Avl *key);
Avltree *mkavltree(int(*cmp)(Avl*, Avl*)); Avl *avlnext(Avl *n);
Avl *avlprev(Avl *n);
.EE .EE
.SH DESCRIPTION .SH DESCRIPTION
An AVL tree is a self-balancing binary search tree. These routines allow creation and maintenance of in-memory balanced
These routines allow creation and maintenance of in-memory AVL trees. binary search trees.
.PP .PP
An empty AVL tree is created by calling An empty tree is created by calling
.I mkavltree .I avlcreate
with a comparison function as argument. with a comparison function as an argument.
This function should take two pointers to The comparison function must take two pointers to
.B Avl .B Avl
objects and return -1, 0 or 1 as the first is structures and return an integer less than, equal to, or
greater than 0 as the first is
respectively less than, respectively less than,
equal to, or greater than, equal to, or greater than the second.
the second. .PP
.I Insertavl .I Avlinsert
adds a adds a new
.I new node into the tree and returns an existing
tree node into node with the same key that has been removed
.IR tree . from the tree and may be freed.
If .I Avllookup
.I oldp
is non-nil upon return,
it points to storage for an old node
with the same key that may now be freed.
.I Lookupavl
returns the returns the
.I tree node that matches the key or
node that matches
.I key
by
.IR tree 's
comparison function,
or
.B nil .B nil
if none. if no node matches.
.I Avldelete
removes the node matching the key from the tree and returns
it. It returns nil of no matching key is found.
.PP .PP
.I Searchavl
returns the
.I tree
node that matches
.I key
by
.IR tree 's
comparison function, if it exists.
If it does not, and
.I neighbor
is positive, it returns the nearest node whose
.I key
is greater or
.B nil
if there is none and, if
.I neighbor
is negative, it returns the nearest node whose
.I key
is less or
.B nil
if there is none.
It is an error to set
.I neighbor
to values other than \-1, 0, or +1.
.PP
.I Deleteavl
removes the node matching
.I key
from
.IR tree ;
.I oldp
is handled as per
.IR insertavl .
.PP
.I Avlwalk
returns a pointer to a newly-allocated
.B Avlwalk
object.
.I Endwalk
frees such an object.
.I Avlnext .I Avlnext
returns the next
.B Avl
node in an in-order walk of the AVL tree
and and
.I avlprev .I avlprev
walk the tree associated with returns the previous node.
.IR walk ,
returning the next
(respectively, previous)
tree node in the comparison order
defined by the comparison function
associated with the tree associated with
.IR walk .
.SH EXAMPLES .SH EXAMPLES
Intended usage seems to be to make an anonymous Intended usage is to embed the
.B Avl .B Avl
the first member of the application's tree-node structure, structure anonymously.
then pass these routines tree-node pointers instead of For example, the following will create a key-value store
.BR Avl* s. with strings as keys and integers as values.
.IP .IP
.EX .EX
typedef struct Node { typedef struct Node {
Avl; Avl;
uchar score[VtScoreSize]; char *key;
int type; int val;
} Node; } Node;
.sp 0.3v
Avltree *tree; int
Avl *res; nodecmp(Avl *la, Avl *lb)
Node *np; {
Node *a, *b;
a = (Node*)la;
b = (Node*)lb;
return strcmp(a->key, b->key);
}
int
get(char *key)
{
Node *h, n;
n.key = key;
h = (Node*)avllookup(&n);
return h ? h->val : -1;
}
\fI\&...\fP \fI\&...\fP
res = lookupavl(tree, np); Avltree *t = avlcreate(AVL, nodecmp);
.EE .EE
.SH SOURCE .SH SOURCE
.B /sys/src/libavl .B /sys/src/libavl
.SH SEE ALSO .SH SEE ALSO
G. M. Adelson-Velsky, .nf
E. M. Landis, Donald Knuth, ``The Art of Computer Programming'', Volume 3. Section 6.2.3
``An algorithm for the organization of information'',
.IR "Soviet Mathematics" ,
Vol. 3, pp. 1256—1263.
.SH DIAGNOSTICS .SH DIAGNOSTICS
Functions returning pointers return .I Avlcreate
.B nil returns nil on error.
on error. .SH HISTORY
This implementation was written for 9front (Dec, 2016).

View file

@ -19,7 +19,7 @@ uchar zeroscore[VtScoreSize]; /* all zeros */
typedef struct ScoreTree ScoreTree; typedef struct ScoreTree ScoreTree;
struct ScoreTree struct ScoreTree
{ {
Avl avl; Avl;
uchar score[VtScoreSize]; uchar score[VtScoreSize];
int type; int type;
}; };
@ -51,21 +51,20 @@ havevisited(uchar score[VtScoreSize], int type)
return 0; return 0;
memmove(a.score, score, VtScoreSize); memmove(a.score, score, VtScoreSize);
a.type = type; a.type = type;
return lookupavl(scoretree, &a.avl) != nil; return avllookup(scoretree, &a) != nil;
} }
static void static void
markvisited(uchar score[VtScoreSize], int type) markvisited(uchar score[VtScoreSize], int type)
{ {
ScoreTree *a; ScoreTree *a;
Avl *old;
if(scoretree == nil) if(scoretree == nil)
return; return;
a = binalloc(&scorebin, sizeof *a, 1); a = binalloc(&scorebin, sizeof *a, 1);
memmove(a->score, score, VtScoreSize); memmove(a->score, score, VtScoreSize);
a->type = type; a->type = type;
insertavl(scoretree, &a->avl, &old); avlinsert(scoretree, a);
} }
void void
@ -196,7 +195,7 @@ main(int argc, char *argv[])
ignoreerrors = 1; ignoreerrors = 1;
break; break;
case 'm': case 'm':
scoretree = mkavltree(scoretreecmp); scoretree = avlcreate(scoretreecmp);
break; break;
case 'r': case 'r':
if(ignoreerrors) if(ignoreerrors)

View file

@ -1,436 +1,306 @@
#include <u.h> #include <u.h>
#include <libc.h> #include <libc.h>
#include <bio.h>
#include <avl.h> #include <avl.h>
/* /* See Knuth Volume 3, 6.2.3 */
* In-memory database stored as self-balancing AVL tree.
* See Lewis & Denenberg, Data Structures and Their Algorithms.
*/
static void Avl *avllookup(Avltree*, Avl*);
singleleft(Avl **tp, Avl *p) Avl *avldelete(Avltree*, Avl*);
{ Avl *avlinsert(Avltree*, Avl*);
int l, r2;
Avl *a, *c;
a = *tp;
c = a->n[1];
r2 = c->bal;
l = (r2 > 0? r2: 0)+1 - a->bal;
if((a->n[1] = c->n[0]) != nil)
a->n[1]->p = a;
if((c->n[0] = a) != nil)
c->n[0]->p = c;
if((*tp = c) != nil)
(*tp)->p = p;
a->bal = -l;
c->bal = r2 - ((l > 0? l: 0)+1);
}
static void
singleright(Avl **tp, Avl *p)
{
int l2, r;
Avl *a, *c;
a = *tp;
c = a->n[0];
l2 = - c->bal;
r = a->bal + ((l2 > 0? l2: 0)+1);
if((a->n[0] = c->n[1]) != nil)
a->n[0]->p = a;
if((c->n[1] = a) != nil)
c->n[1]->p = c;
if((*tp = c) != nil)
(*tp)->p = p;
a->bal = r;
c->bal = ((r > 0? r: 0)+1) - l2;
}
static void
doublerightleft(Avl **tp, Avl *p)
{
singleright(&(*tp)->n[1], *tp);
singleleft(tp, p);
}
static void
doubleleftright(Avl **tp, Avl *p)
{
singleleft(&(*tp)->n[0], *tp);
singleright(tp, p);
}
static void
balance(Avl **tp, Avl *p)
{
switch((*tp)->bal){
case -2:
if((*tp)->n[0]->bal <= 0)
singleright(tp, p);
else if((*tp)->n[0]->bal == 1)
doubleleftright(tp, p);
else
assert(0);
break;
case 2:
if((*tp)->n[1]->bal >= 0)
singleleft(tp, p);
else if((*tp)->n[1]->bal == -1)
doublerightleft(tp, p);
else
assert(0);
break;
}
}
static int
canoncmp(int cmp)
{
if(cmp < 0)
return -1;
else if(cmp > 0)
return 1;
return 0;
}
static int
_insertavl(Avl **tp, Avl *p, Avl *r, int (*cmp)(Avl*,Avl*), Avl **rfree)
{
int i, ob;
if(*tp == nil){
r->bal = 0;
r->n[0] = nil;
r->n[1] = nil;
r->p = p;
*tp = r;
return 1;
}
ob = (*tp)->bal;
if((i = canoncmp(cmp(r, *tp))) != 0){
(*tp)->bal += i * _insertavl(&(*tp)->n[(i+1)/2], *tp, r, cmp,
rfree);
balance(tp, p);
return ob == 0 && (*tp)->bal != 0;
}
/* install new entry */
*rfree = *tp; /* save old node for freeing */
*tp = r; /* insert new node */
**tp = **rfree; /* copy old node's Avl contents */
if(r->n[0]) /* fix node's children's parent pointers */
r->n[0]->p = r;
if(r->n[1])
r->n[1]->p = r;
return 0;
}
static int
successor(Avl **tp, Avl *p, Avl **r)
{
int ob;
if((*tp)->n[0] == nil){
*r = *tp;
*tp = (*r)->n[1];
if(*tp)
(*tp)->p = p;
return -1;
}
ob = (*tp)->bal;
(*tp)->bal -= successor(&(*tp)->n[0], *tp, r);
balance(tp, p);
return -(ob != 0 && (*tp)->bal == 0);
}
static int
_deleteavl(Avl **tp, Avl *p, Avl *rx, int(*cmp)(Avl*,Avl*), Avl **del,
void (*predel)(Avl*, void*), void *arg)
{
int i, ob;
Avl *r, *or;
if(*tp == nil)
return 0;
ob = (*tp)->bal;
if((i=canoncmp(cmp(rx, *tp))) != 0){
(*tp)->bal += i * _deleteavl(&(*tp)->n[(i+1)/2], *tp, rx, cmp,
del, predel, arg);
balance(tp, p);
return -(ob != 0 && (*tp)->bal == 0);
}
if(predel)
(*predel)(*tp, arg);
or = *tp;
if(or->n[i=0] == nil || or->n[i=1] == nil){
*tp = or->n[1-i];
if(*tp)
(*tp)->p = p;
*del = or;
return -1;
}
/* deleting node with two kids, find successor */
or->bal += successor(&or->n[1], or, &r);
r->bal = or->bal;
r->n[0] = or->n[0];
r->n[1] = or->n[1];
*tp = r;
(*tp)->p = p;
/* node has changed; fix children's parent pointers */
if(r->n[0])
r->n[0]->p = r;
if(r->n[1])
r->n[1]->p = r;
*del = or;
balance(tp, p);
return -(ob != 0 && (*tp)->bal == 0);
}
static void
checkparents(Avl *a, Avl *p)
{
if(a == nil)
return;
if(a->p != p)
print("bad parent\n");
checkparents(a->n[0], a);
checkparents(a->n[1], a);
}
struct Avltree
{
Avl *root;
int (*cmp)(Avl*, Avl*);
Avlwalk *walks;
};
struct Avlwalk
{
int started;
int moved;
Avlwalk *next;
Avltree *tree;
Avl *node;
};
Avltree* Avltree*
mkavltree(int (*cmp)(Avl*, Avl*)) avlcreate(int (*cmp)(Avl*, Avl*))
{ {
Avltree *t; Avltree *t;
t = malloc(sizeof *t); t = malloc(sizeof(*t));
if(t == nil) if(t == nil)
return nil; return nil;
memset(t, 0, sizeof *t);
t->cmp = cmp; t->cmp = cmp;
t->root = nil;
return t; return t;
} }
void Avl*
insertavl(Avltree *t, Avl *new, Avl **oldp) avllookup(Avltree *t, Avl *k)
{ {
*oldp = nil; Avl *h;
_insertavl(&t->root, nil, new, t->cmp, oldp); int c;
}
static Avl* h = t->root;
findpredecessor(Avl *a) while(h != nil){
{ c = (t->cmp)(k, h);
if(a == nil) if(c < 0){
return nil; h = h->c[0];
continue;
if(a->n[0] != nil){ }
/* predecessor is rightmost descendant of left child */ if(c > 0){
for(a = a->n[0]; a->n[1]; a = a->n[1]) h = h->c[1];
; continue;
return a; }
}else{ return h;
/* we're at a leaf, successor is a parent we enter from the right */
while(a->p && a->p->n[0] == a)
a = a->p;
return a->p;
} }
return nil;
} }
static Avl* static int insert(int (*)(Avl*, Avl*), Avl*, Avl**, Avl*, Avl**);
findsuccessor(Avl *a)
{
if(a == nil)
return nil;
if(a->n[1] != nil){ Avl*
/* successor is leftmost descendant of right child */ avlinsert(Avltree *t, Avl *k)
for(a = a->n[1]; a->n[0]; a = a->n[0]) {
; Avl *old;
return a;
}else{ old = nil;
/* we're at a leaf, successor is a parent we enter from the left going up */ insert(t->cmp, nil, &t->root, k, &old);
while(a->p && a->p->n[1] == a) return old;
a = a->p; }
return a->p;
static int insertfix(int, Avl**);
static int
insert(int (*cmp)(Avl*, Avl*), Avl *p, Avl **qp, Avl *k, Avl **oldp)
{
Avl *q;
int fix, c;
q = *qp;
if(q == nil) {
k->c[0] = nil;
k->c[1] = nil;
k->balance = 0;
k->p = p;
*qp = k;
return 1;
} }
c = cmp(k, q);
c = c > 0 ? 1 : c < 0 ? -1: 0;
if(c == 0) {
*oldp = q;
*k = *q;
if(q->c[0] != nil)
q->c[0]->p = k;
if(q->c[1] != nil)
q->c[1]->p = k;
*qp = k;
return 0;
}
fix = insert(cmp, q, q->c + (c+1)/2, k, oldp);
if(fix)
return insertfix(c, qp);
return 0;
}
static Avl *singlerot(int, Avl*);
static Avl *doublerot(int, Avl*);
static int
insertfix(int c, Avl **t)
{
Avl *s;
s = *t;
if(s->balance == 0) {
s->balance = c;
return 1;
}
if(s->balance == -c) {
s->balance = 0;
return 0;
}
if(s->c[(c+1)/2]->balance == c)
s = singlerot(c, s);
else
s = doublerot(c, s);
*t = s;
return 0;
}
static int delete(int (*cmp)(Avl*, Avl*), Avl**, Avl*, Avl**);
static int deletemin(Avl**, Avl**);
static int deletefix(int, Avl**);
Avl*
avldelete(Avltree *t, Avl *k)
{
Avl *old;
if(t->root == nil)
return nil;
old = nil;
delete(t->cmp, &t->root, k, &old);
return old;
}
static int
delete(int (*cmp)(Avl*, Avl*), Avl **qp, Avl *k, Avl **oldp)
{
Avl *q, *e;
int c, fix;
q = *qp;
if(q == nil)
return 0;
c = cmp(k, q);
c = c > 0 ? 1 : c < 0 ? -1: 0;
if(c == 0) {
*oldp = q;
if(q->c[1] == nil) {
*qp = q->c[0];
if(*qp != nil)
(*qp)->p = q->p;
return 1;
}
fix = deletemin(q->c+1, &e);
*e = *q;
if(q->c[0] != nil)
q->c[0]->p = e;
if(q->c[1] != nil)
q->c[1]->p = e;
*qp = e;
if(fix)
return deletefix(-1, qp);
return 0;
}
fix = delete(cmp, q->c + (c+1)/2, k, oldp);
if(fix)
return deletefix(-c, qp);
return 0;
}
static int
deletemin(Avl **qp, Avl **oldp)
{
Avl *q;
int fix;
q = *qp;
if(q->c[0] == nil) {
*oldp = q;
*qp = q->c[1];
if(*qp != nil)
(*qp)->p = q->p;
return 1;
}
fix = deletemin(q->c, oldp);
if(fix)
return deletefix(1, qp);
return 0;
}
static Avl *rotate(int, Avl*);
static int
deletefix(int c, Avl **t)
{
Avl *s;
int a;
s = *t;
if(s->balance == 0) {
s->balance = c;
return 0;
}
if(s->balance == -c) {
s->balance = 0;
return 1;
}
a = (c+1)/2;
if(s->c[a]->balance == 0) {
s = rotate(c, s);
s->balance = -c;
*t = s;
return 0;
}
if(s->c[a]->balance == c)
s = singlerot(c, s);
else
s = doublerot(c, s);
*t = s;
return 1;
} }
static Avl* static Avl*
_lookupavl(Avl *t, Avl *r, int (*cmp)(Avl*,Avl*), int neighbor) singlerot(int c, Avl *s)
{
s->balance = 0;
s = rotate(c, s);
s->balance = 0;
return s;
}
static Avl*
doublerot(int c, Avl *s)
{
Avl *r, *p;
int a;
a = (c+1)/2;
r = s->c[a];
s->c[a] = rotate(-c, s->c[a]);
p = rotate(c, s);
assert(r->p == p);
assert(s->p == p);
if(p->balance == c) {
s->balance = -c;
r->balance = 0;
} else if(p->balance == -c) {
s->balance = 0;
r->balance = c;
} else
s->balance = r->balance = 0;
p->balance = 0;
return p;
}
static Avl*
rotate(int c, Avl *s)
{
Avl *r, *n;
int a;
a = (c+1)/2;
r = s->c[a];
s->c[a] = n = r->c[a^1];
if(n != nil)
n->p = s;
r->c[a^1] = s;
r->p = s->p;
s->p = r;
return r;
}
static Avl *walk1(int, Avl*);
Avl*
avlprev(Avl *q)
{
return walk1(0, q);
}
Avl*
avlnext(Avl *q)
{
return walk1(1, q);
}
static Avl*
walk1(int a, Avl *q)
{ {
int i;
Avl *p; Avl *p;
p = nil; if(q == nil)
if(t == nil)
return nil; return nil;
do{
assert(t->p == p);
if((i = canoncmp(cmp(r, t))) == 0)
return t;
p = t;
t = t->n[(i+1)/2];
}while(t);
if(neighbor == 0)
return nil;
if(neighbor < 0)
return i > 0 ? p : findpredecessor(p);
return i < 0 ? p : findsuccessor(p);
}
Avl* if(q->c[a] != nil){
searchavl(Avltree *t, Avl *key, int neighbor) for(q = q->c[a]; q->c[a^1] != nil; q = q->c[a^1])
{
return _lookupavl(t->root, key, t->cmp, neighbor);
}
Avl*
lookupavl(Avltree *t, Avl *key)
{
return _lookupavl(t->root, key, t->cmp, 0);
}
static void
walkdel(Avl *a, void *v)
{
Avl *p;
Avlwalk *w;
Avltree *t;
if(a == nil)
return;
p = findpredecessor(a);
t = v;
for(w = t->walks; w; w = w->next){
if(w->node == a){
/* back pointer to predecessor; not perfect but adequate */
w->moved = 1;
w->node = p;
if(p == nil)
w->started = 0;
}
}
}
void
deleteavl(Avltree *t, Avl *key, Avl **oldp)
{
*oldp = nil;
_deleteavl(&t->root, nil, key, t->cmp, oldp, walkdel, t);
}
Avlwalk*
avlwalk(Avltree *t)
{
Avlwalk *w;
w = malloc(sizeof *w);
if(w == nil)
return nil;
memset(w, 0, sizeof *w);
w->tree = t;
w->next = t->walks;
t->walks = w;
return w;
}
Avl*
avlnext(Avlwalk *w)
{
Avl *a;
if(w->started==0){
for(a = w->tree->root; a && a->n[0]; a = a->n[0])
; ;
w->node = a; return q;
w->started = 1;
}else{
a = findsuccessor(w->node);
if(a == w->node)
abort();
w->node = a;
} }
return w->node; for(p = q->p; p != nil && p->c[a] == q; p = p->p)
} q = p;
return p;
Avl*
avlprev(Avlwalk *w)
{
Avl *a;
if(w->started == 0){
for(a = w->tree->root; a && a->n[1]; a = a->n[1])
;
w->node = a;
w->started = 1;
}else if(w->moved){
w->moved = 0;
return w->node;
}else{
a = findpredecessor(w->node);
if(a == w->node)
abort();
w->node = a;
}
return w->node;
}
void
endwalk(Avlwalk *w)
{
Avltree *t;
Avlwalk **l;
t = w->tree;
for(l = &t->walks; *l; l = &(*l)->next){
if(*l == w){
*l = w->next;
break;
}
}
free(w);
}
static void
walkavl(Avl *t, void (*f)(Avl*, void*), void *v)
{
if(t == nil)
return;
walkavl(t->n[0], f, v);
f(t, v);
walkavl(t->n[1], f, v);
} }

View file

@ -1,15 +1,11 @@
</$objtype/mkfile </$objtype/mkfile
LIB=/$objtype/lib/libavl.a LIB=/$objtype/lib/libavl.a
OFILES=\ OFILES=\
avl.$O\ avl.$O\
HFILES=/sys/include/avl.h HFILES=\
/sys/include/avl.h\
UPDATE=\
mkfile\
$HFILES\
${OFILES:%.$O=%.c}\
${LIB:/$objtype/%=/386/%}\
</sys/src/cmd/mksyslib </sys/src/cmd/mksyslib