]> git.wh0rd.org Git - fontconfig.git/blob - src/fcavl.c
More autoconf cleanup for fontconfig
[fontconfig.git] / src / fcavl.c
1 /*
2  * $XFree86: $
3  *
4  * Copyright © 2002 Keith Packard, member of The XFree86 Project, Inc.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of Keith Packard not be used in
11  * advertising or publicity pertaining to distribution of the software without
12  * specific, written prior permission.  Keith Packard makes no
13  * representations about the suitability of this software for any purpose.  It
14  * is provided "as is" without express or implied warranty.
15  *
16  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  */
24
25 /*
26  * Semi-Balanced trees (avl).  This only contains two
27  * routines - insert and delete.  Searching is
28  * reserved for the client to write.
29  */
30
31 #include        "fcint.h"
32 #include        "fcavl.h"
33
34 static FcBool
35 FcAvlRebalanceRight (FcAvlNode **);
36
37 static FcBool
38 FcAvlRebalanceLeft (FcAvlNode **);
39
40 /*
41  * insert a new node
42  *
43  * this routine returns FcTrue if the tree has grown
44  * taller
45  */
46
47 FcBool
48 FcAvlInsert (FcAvlMore more, FcAvlNode **treep, FcAvlNode *new)
49 {
50     if (!(*treep)) 
51     {
52         new->left = 0;
53         new->right = 0;
54         new->balance = 0;
55         *treep = new;
56         return FcTrue;
57     } 
58     else 
59     {
60         if ((*more) (new, *treep))
61         {
62             if (FcAvlInsert (more, &(*treep)->right, new))
63                 switch (++(*treep)->balance) {
64                 case 0:
65                     return FcFalse;
66                 case 1:
67                     return FcTrue;
68                 case 2:
69                     (void) FcAvlRebalanceRight (treep);
70                 }
71         }
72         else
73         {
74             if (FcAvlInsert (more, &(*treep)->left, new))
75                 switch (--(*treep)->balance) {
76                 case 0:
77                     return FcFalse;
78                 case -1:
79                     return FcTrue;
80                 case -2:
81                     (void) FcAvlRebalanceLeft (treep);
82                 }
83         }
84     }
85     return 0;
86 }
87
88 /*
89  * delete a node from a tree
90  *
91  * this routine return FcTrue if the tree has been shortened
92  */
93
94 FcBool
95 FcAvlDelete (FcAvlMore more, FcAvlNode **treep, FcAvlNode *old)
96 {
97     if (!*treep)
98         return FcFalse; /* node not found */
99     if (old == *treep)
100     {
101         FcAvlNode       *replacement;
102         FcAvlNode       *replacement_parent;
103         int             replacement_direction;
104         int             delete_direction;
105         FcAvlNode       *swap_temp;
106         int             balance_temp;
107
108         /*
109          * find an empty down pointer (if any)
110          * and rehook the tree
111          */
112         if (!old->right) {
113             (*treep) = old->left;
114             return FcTrue;
115         } else if (!old->left) {
116             (*treep) = old->right;
117             return FcTrue;
118         } else {
119             /* 
120              * if both down pointers are full, then
121              * move a node from the bottom of the tree up here.
122              *
123              * This builds an incorrect tree -- the replacement
124              * node and the old node will not
125              * be in correct order.  This doesn't matter as
126              * the old node will obviously not leave
127              * this routine alive.
128              */
129             /*
130              * if the tree is left heavy, then go left
131              * else go right
132              */
133             replacement_parent = old;
134             if (old->balance == -1) {
135                 delete_direction = -1;
136                 replacement_direction = -1;
137                 replacement = old->left;
138                 while (replacement->right) {
139                     replacement_parent = replacement;
140                     replacement_direction = 1;
141                     replacement = replacement->right;
142                 }
143             } else {
144                 delete_direction = 1;
145                 replacement_direction = 1;
146                 replacement = old->right;
147                 while (replacement->left) {
148                     replacement_parent = replacement;
149                     replacement_direction = -1;
150                     replacement = replacement->left;
151                 }
152             }
153             /*
154              * swap the replacement node into
155              * the tree where the node is to be removed
156              * 
157              * this would be faster if only the data
158              * element was swapped -- but that
159              * won't work for kalypso.  The alternate
160              * code would be:
161              data_temp = old->data;
162              to _be_deleted->data = replacement->data;
163              replacement->data = data_temp;
164              */
165             swap_temp = old->left;
166             old->left = replacement->left;
167             replacement->left = swap_temp;
168             
169             swap_temp = old->right;
170             old->right = replacement->right;
171             replacement->right = swap_temp;
172             
173             balance_temp = old->balance;
174             old->balance = replacement->balance;
175             replacement->balance = balance_temp;
176             /*
177              * if the replacement node is directly below
178              * the to-be-removed node, hook the old
179              * node below it (instead of below itself!)
180              */
181             if (replacement_parent == old)
182                 replacement_parent = replacement;
183             if (replacement_direction == -1)
184                 replacement_parent->left = old;
185             else
186                 replacement_parent->right = old;
187             (*treep) = replacement;
188             /*
189              * delete the node from the sub-tree
190              */
191             if (delete_direction == -1) 
192             {
193                 if (FcAvlDelete (more, &(*treep)->left, old)) 
194                 {
195                     switch (++(*treep)->balance) {
196                     case 2:
197                         abort ();
198                     case 1:
199                         return FcFalse;
200                     case 0:
201                         return FcTrue;
202                     }
203                 }
204                 return 0;
205             }
206             else 
207             {
208                 if (FcAvlDelete (more, &(*treep)->right, old)) 
209                 {
210                     switch (--(*treep)->balance) {
211                     case -2:
212                         abort ();
213                     case -1:
214                         return FcFalse;
215                     case 0:
216                         return FcTrue;
217                     }
218                 }
219                 return 0;
220             }
221         }
222     }
223     else if ((*more) (old, *treep))
224     {
225         if (FcAvlDelete (more, &(*treep)->right, old))
226         {
227             /*
228              * check the balance factors
229              * Note that the conditions are
230              * inverted from the insertion case
231              */
232             switch (--(*treep)->balance) {
233             case 0:
234                 return FcFalse;
235             case -1:
236                 return FcTrue;
237             case -2:
238                 return FcAvlRebalanceLeft (treep);
239             }
240         }
241         return 0;
242     }
243     else 
244     {
245         if (FcAvlDelete (more, &(*treep)->left, old))
246         {
247             switch (++(*treep)->balance) {
248             case 0:
249                 return FcTrue;
250             case 1:
251                 return FcFalse;
252             case 2:
253                 return FcAvlRebalanceRight (treep);
254             }
255         }
256         return 0;
257     }
258     /*NOTREACHED*/
259 }
260
261 /*
262  * two routines to rebalance the tree.
263  *
264  * rebalance_right -- the right sub-tree is too long
265  * rebalance_left --  the left sub-tree is too long
266  *
267  * These routines are the heart of avl trees, I've tried
268  * to make their operation reasonably clear with comments,
269  * but some study will be necessary to understand the
270  * algorithm.
271  *
272  * these routines return FcTrue if the resultant
273  * tree is shorter than the un-balanced version.  This
274  * is only of interest to the delete routine as the
275  * balance after insertion can never actually shorten
276  * the tree.
277  */
278
279 static FcBool
280 FcAvlRebalanceRight (FcAvlNode **treep)
281 {
282     FcAvlNode   *temp;
283     /*
284      * rebalance the tree
285      */
286     if ((*treep)->right->balance == -1) {
287         /* 
288          * double whammy -- the inner sub-sub tree
289          * is longer than the outer sub-sub tree
290          *
291          * this is the "double rotation" from
292          * knuth.  Scheme:  replace the tree top node
293          * with the inner sub-tree top node and
294          * adjust the maze of pointers and balance
295          * factors accordingly.
296          */
297         temp = (*treep)->right->left;
298         (*treep)->right->left = temp->right;
299         temp->right = (*treep)->right;
300         switch (temp->balance) {
301         case -1:
302             temp->right->balance = 1;
303             (*treep)->balance = 0;
304             break;
305         case 0:
306             temp->right->balance = 0;
307             (*treep)->balance = 0;
308             break;
309         case 1:
310             temp->right->balance = 0;
311             (*treep)->balance = -1;
312             break;
313         }
314         temp->balance = 0;
315         (*treep)->right = temp->left;
316         temp->left = (*treep);
317         (*treep) = temp;
318         return FcTrue;
319     } else {
320         /*
321          * a simple single rotation
322          *
323          * Scheme:  replace the tree top node
324          * with the sub-tree top node 
325          */
326         temp = (*treep)->right->left;
327         (*treep)->right->left = (*treep);
328         (*treep) = (*treep)->right;
329         (*treep)->left->right = temp;
330         /*
331          * only two possible configurations --
332          * if the right sub-tree was balanced, then
333          * *both* sides of it were longer than the
334          * left side, so the resultant tree will
335          * have a long leg (the left inner leg being
336                             * the same length as the right leg)
337          */
338         if ((*treep)->balance == 0) {
339             (*treep)->balance = -1;
340             (*treep)->left->balance = 1;
341             return FcFalse;
342         } else {
343             (*treep)->balance = 0;
344             (*treep)->left->balance = 0;
345             return FcTrue;
346         }
347     }
348 }
349
350 static FcBool
351 FcAvlRebalanceLeft (FcAvlNode **treep)
352 {
353     FcAvlNode   *temp;
354     /*
355      * rebalance the tree
356      */
357     if ((*treep)->left->balance == 1) {
358         /* 
359          * double whammy -- the inner sub-sub tree
360          * is longer than the outer sub-sub tree
361          *
362          * this is the "double rotation" from
363          * knuth.  Scheme:  replace the tree top node
364          * with the inner sub-tree top node and
365          * adjust the maze of pointers and balance
366          * factors accordingly.
367          */
368         temp = (*treep)->left->right;
369         (*treep)->left->right = temp->left;
370         temp->left = (*treep)->left;
371         switch (temp->balance) {
372         case 1:
373             temp->left->balance = -1;
374             (*treep)->balance = 0;
375             break;
376         case 0:
377             temp->left->balance = 0;
378             (*treep)->balance = 0;
379             break;
380         case -1:
381             temp->left->balance = 0;
382             (*treep)->balance = 1;
383             break;
384         }
385         temp->balance = 0;
386         (*treep)->left = temp->right;
387         temp->right = (*treep);
388         (*treep) = temp;
389         return FcTrue;
390     } else {
391         /*
392          * a simple single rotation
393          *
394          * Scheme:  replace the tree top node
395          * with the sub-tree top node 
396          */
397         temp = (*treep)->left->right;
398         (*treep)->left->right = (*treep);
399         (*treep) = (*treep)->left;
400         (*treep)->right->left = temp;
401         /*
402          * only two possible configurations --
403          * if the left sub-tree was balanced, then
404          * *both* sides of it were longer than the
405          * right side, so the resultant tree will
406          * have a long leg (the right inner leg being
407          * the same length as the left leg)
408          */
409         if ((*treep)->balance == 0) {
410             (*treep)->balance = 1;
411             (*treep)->right->balance = -1;
412             return FcTrue;
413         } else {
414             (*treep)->balance = 0;
415             (*treep)->right->balance = 0;
416             return FcFalse;
417         }
418     }
419 }