exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / gl_rbtree_ordered.h
bloba79aa27c0559fdfd81103998fe6bb940939b1d5d
1 /* Ordered {set,map} data type implemented by a binary tree.
2 Copyright (C) 2006-2007, 2009-2024 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2006.
5 This file is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as
7 published by the Free Software Foundation; either version 2.1 of the
8 License, or (at your option) any later version.
10 This file is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
18 /* A red-black tree is a binary tree where every node is colored black or
19 red such that
20 1. The root is black.
21 2. No red node has a red parent.
22 Or equivalently: No red node has a red child.
23 3. All paths from the root down to any NULL endpoint contain the same
24 number of black nodes.
25 Let's call this the "black-height" bh of the tree. It follows that every
26 such path contains exactly bh black and between 0 and bh red nodes. (The
27 extreme cases are a path containing only black nodes, and a path colored
28 alternately black-red-black-red-...-black-red.) The height of the tree
29 therefore is >= bh, <= 2*bh.
32 /* Color of a node. */
33 typedef enum color { BLACK, RED } color_t;
35 /* Tree node implementation, valid for this file only. */
36 struct NODE_IMPL
38 struct NODE_IMPL *left; /* left branch, or NULL */
39 struct NODE_IMPL *right; /* right branch, or NULL */
40 /* Parent pointer, or NULL. The parent pointer is not needed for most
41 operations. It is needed so that a NODE_T can be returned without
42 memory allocation, on which the functions <container>_remove_node,
43 <container>_add_before, <container>_add_after can be implemented. */
44 struct NODE_IMPL *parent;
45 color_t color; /* node's color */
46 NODE_PAYLOAD_FIELDS
48 typedef struct NODE_IMPL * NODE_T;
50 /* Concrete CONTAINER_IMPL type, valid for this file only. */
51 struct CONTAINER_IMPL
53 struct CONTAINER_IMPL_BASE base;
54 struct NODE_IMPL *root; /* root node or NULL */
55 size_t count; /* number of nodes */
58 /* A red-black tree of height h has a black-height bh >= ceil(h/2) and
59 therefore at least 2^ceil(h/2) - 1 elements. So, h <= 116 (because a tree
60 of height h >= 117 would have at least 2^59 - 1 elements, and because even
61 on 64-bit machines,
62 sizeof (NODE_IMPL) * (2^59 - 1) > 2^64
63 this would exceed the address space of the machine. */
64 #define MAXHEIGHT 116
66 /* Rotates left a subtree.
68 B D
69 / \ / \
70 A D --> B E
71 / \ / \
72 C E A C
74 Changes the tree structure, updates the branch sizes.
75 The caller must update the colors and register D as child of its parent. */
76 static NODE_T
77 rotate_left (NODE_T b_node, NODE_T d_node)
79 NODE_T c_node = d_node->left;
81 b_node->right = c_node;
82 d_node->left = b_node;
84 d_node->parent = b_node->parent;
85 b_node->parent = d_node;
86 if (c_node != NULL)
87 c_node->parent = b_node;
89 return d_node;
92 /* Rotates right a subtree.
94 D B
95 / \ / \
96 B E --> A D
97 / \ / \
98 A C C E
100 Changes the tree structure, updates the branch sizes.
101 The caller must update the colors and register B as child of its parent. */
102 static NODE_T
103 rotate_right (NODE_T b_node, NODE_T d_node)
105 NODE_T c_node = b_node->right;
107 d_node->left = c_node;
108 b_node->right = d_node;
110 b_node->parent = d_node->parent;
111 d_node->parent = b_node;
112 if (c_node != NULL)
113 c_node->parent = d_node;
115 return b_node;
118 /* Ensures the tree is balanced, after an insertion operation.
119 Also assigns node->color.
120 parent is the given node's parent, known to be non-NULL. */
121 static void
122 rebalance_after_add (CONTAINER_T container, NODE_T node, NODE_T parent)
124 for (;;)
126 /* At this point, parent = node->parent != NULL.
127 Think of node->color being RED (although node->color is not yet
128 assigned.) */
129 NODE_T grandparent;
130 NODE_T uncle;
132 if (parent->color == BLACK)
134 /* A RED color for node is acceptable. */
135 node->color = RED;
136 return;
139 grandparent = parent->parent;
140 /* Since parent is RED, we know that
141 grandparent is != NULL and colored BLACK. */
143 if (grandparent->left == parent)
144 uncle = grandparent->right;
145 else if (grandparent->right == parent)
146 uncle = grandparent->left;
147 else
148 abort ();
150 if (uncle != NULL && uncle->color == RED)
152 /* Change grandparent from BLACK to RED, and
153 change parent and uncle from RED to BLACK.
154 This makes it acceptable for node to be RED. */
155 node->color = RED;
156 parent->color = uncle->color = BLACK;
157 node = grandparent;
159 else
161 /* grandparent and uncle are BLACK. parent is RED. node wants
162 to be RED too.
163 In this case, recoloring is not sufficient. Need to perform
164 one or two rotations. */
165 NODE_T *grandparentp;
167 if (grandparent->parent == NULL)
168 grandparentp = &container->root;
169 else if (grandparent->parent->left == grandparent)
170 grandparentp = &grandparent->parent->left;
171 else if (grandparent->parent->right == grandparent)
172 grandparentp = &grandparent->parent->right;
173 else
174 abort ();
176 if (grandparent->left == parent)
178 if (parent->right == node)
180 /* Rotation between node and parent. */
181 grandparent->left = rotate_left (parent, node);
182 node = parent;
183 parent = grandparent->left;
185 /* grandparent and uncle are BLACK. parent and node want to be
186 RED. parent = grandparent->left. node = parent->left.
188 grandparent parent
189 bh+1 bh+1
190 / \ / \
191 parent uncle --> node grandparent
192 bh bh bh bh
193 / \ / \
194 node C C uncle
195 bh bh bh bh
197 *grandparentp = rotate_right (parent, grandparent);
198 parent->color = BLACK;
199 node->color = grandparent->color = RED;
201 else /* grandparent->right == parent */
203 if (parent->left == node)
205 /* Rotation between node and parent. */
206 grandparent->right = rotate_right (node, parent);
207 node = parent;
208 parent = grandparent->right;
210 /* grandparent and uncle are BLACK. parent and node want to be
211 RED. parent = grandparent->right. node = parent->right.
213 grandparent parent
214 bh+1 bh+1
215 / \ / \
216 uncle parent --> grandparent node
217 bh bh bh bh
218 / \ / \
219 C node uncle C
220 bh bh bh bh
222 *grandparentp = rotate_left (grandparent, parent);
223 parent->color = BLACK;
224 node->color = grandparent->color = RED;
226 return;
229 /* Start again with a new (node, parent) pair. */
230 parent = node->parent;
232 if (parent == NULL)
234 /* Change node's color from RED to BLACK. This increases the
235 tree's black-height. */
236 node->color = BLACK;
237 return;
242 /* Ensures the tree is balanced, after a deletion operation.
243 CHILD was a grandchild of PARENT and is now its child. Between them,
244 a black node was removed. CHILD is also black, or NULL.
245 (CHILD can also be NULL. But PARENT is non-NULL.) */
246 static void
247 rebalance_after_remove (CONTAINER_T container, NODE_T child, NODE_T parent)
249 for (;;)
251 /* At this point, we reduced the black-height of the CHILD subtree by 1.
252 To make up, either look for a possibility to turn a RED to a BLACK
253 node, or try to reduce the black-height tree of CHILD's sibling
254 subtree as well. */
255 NODE_T *parentp;
257 if (parent->parent == NULL)
258 parentp = &container->root;
259 else if (parent->parent->left == parent)
260 parentp = &parent->parent->left;
261 else if (parent->parent->right == parent)
262 parentp = &parent->parent->right;
263 else
264 abort ();
266 if (parent->left == child)
268 NODE_T sibling = parent->right;
269 /* sibling's black-height is >= 1. In particular,
270 sibling != NULL.
272 parent
274 child sibling
275 bh bh+1
278 if (sibling->color == RED)
280 /* sibling is RED, hence parent is BLACK and sibling's children
281 are non-NULL and BLACK.
283 parent sibling
284 bh+2 bh+2
285 / \ / \
286 child sibling --> parent SR
287 bh bh+1 bh+1 bh+1
288 / \ / \
289 SL SR child SL
290 bh+1 bh+1 bh bh+1
292 *parentp = rotate_left (parent, sibling);
293 parent->color = RED;
294 sibling->color = BLACK;
296 /* Concentrate on the subtree of parent. The new sibling is
297 one of the old sibling's children, and known to be BLACK. */
298 parentp = &sibling->left;
299 sibling = parent->right;
301 /* Now we know that sibling is BLACK.
303 parent
305 child sibling
306 bh bh+1
308 if (sibling->right != NULL && sibling->right->color == RED)
311 parent sibling
312 bh+1|bh+2 bh+1|bh+2
313 / \ / \
314 child sibling --> parent SR
315 bh bh+1 bh+1 bh+1
316 / \ / \
317 SL SR child SL
318 bh bh bh bh
320 *parentp = rotate_left (parent, sibling);
321 sibling->color = parent->color;
322 parent->color = BLACK;
323 sibling->right->color = BLACK;
324 return;
326 else if (sibling->left != NULL && sibling->left->color == RED)
329 parent parent
330 bh+1|bh+2 bh+1|bh+2
331 / \ / \
332 child sibling --> child SL
333 bh bh+1 bh bh+1
334 / \ / \
335 SL SR SLL sibling
336 bh bh bh bh
337 / \ / \
338 SLL SLR SLR SR
339 bh bh bh bh
341 where SLL, SLR, SR are all black.
343 parent->right = rotate_right (sibling->left, sibling);
344 /* Change sibling from BLACK to RED and SL from RED to BLACK. */
345 sibling->color = RED;
346 sibling = parent->right;
347 sibling->color = BLACK;
349 /* Now do as in the previous case. */
350 *parentp = rotate_left (parent, sibling);
351 sibling->color = parent->color;
352 parent->color = BLACK;
353 sibling->right->color = BLACK;
354 return;
356 else
358 if (parent->color == BLACK)
360 /* Change sibling from BLACK to RED. Then the entire
361 subtree at parent has decreased its black-height.
362 parent parent
363 bh+2 bh+1
364 / \ / \
365 child sibling --> child sibling
366 bh bh+1 bh bh
368 sibling->color = RED;
370 child = parent;
372 else
374 /* Change parent from RED to BLACK, but compensate by
375 changing sibling from BLACK to RED.
376 parent parent
377 bh+1 bh+1
378 / \ / \
379 child sibling --> child sibling
380 bh bh+1 bh bh
382 parent->color = BLACK;
383 sibling->color = RED;
384 return;
388 else if (parent->right == child)
390 NODE_T sibling = parent->left;
391 /* sibling's black-height is >= 1. In particular,
392 sibling != NULL.
394 parent
396 sibling child
397 bh+1 bh
400 if (sibling->color == RED)
402 /* sibling is RED, hence parent is BLACK and sibling's children
403 are non-NULL and BLACK.
405 parent sibling
406 bh+2 bh+2
407 / \ / \
408 sibling child --> SR parent
409 bh+1 ch bh+1 bh+1
410 / \ / \
411 SL SR SL child
412 bh+1 bh+1 bh+1 bh
414 *parentp = rotate_right (sibling, parent);
415 parent->color = RED;
416 sibling->color = BLACK;
418 /* Concentrate on the subtree of parent. The new sibling is
419 one of the old sibling's children, and known to be BLACK. */
420 parentp = &sibling->right;
421 sibling = parent->left;
423 /* Now we know that sibling is BLACK.
425 parent
427 sibling child
428 bh+1 bh
430 if (sibling->left != NULL && sibling->left->color == RED)
433 parent sibling
434 bh+1|bh+2 bh+1|bh+2
435 / \ / \
436 sibling child --> SL parent
437 bh+1 bh bh+1 bh+1
438 / \ / \
439 SL SR SR child
440 bh bh bh bh
442 *parentp = rotate_right (sibling, parent);
443 sibling->color = parent->color;
444 parent->color = BLACK;
445 sibling->left->color = BLACK;
446 return;
448 else if (sibling->right != NULL && sibling->right->color == RED)
451 parent parent
452 bh+1|bh+2 bh+1|bh+2
453 / \ / \
454 sibling child --> SR child
455 bh+1 bh bh+1 bh
456 / \ / \
457 SL SR sibling SRR
458 bh bh bh bh
459 / \ / \
460 SRL SRR SL SRL
461 bh bh bh bh
463 where SL, SRL, SRR are all black.
465 parent->left = rotate_left (sibling, sibling->right);
466 /* Change sibling from BLACK to RED and SL from RED to BLACK. */
467 sibling->color = RED;
468 sibling = parent->left;
469 sibling->color = BLACK;
471 /* Now do as in the previous case. */
472 *parentp = rotate_right (sibling, parent);
473 sibling->color = parent->color;
474 parent->color = BLACK;
475 sibling->left->color = BLACK;
476 return;
478 else
480 if (parent->color == BLACK)
482 /* Change sibling from BLACK to RED. Then the entire
483 subtree at parent has decreased its black-height.
484 parent parent
485 bh+2 bh+1
486 / \ / \
487 sibling child --> sibling child
488 bh+1 bh bh bh
490 sibling->color = RED;
492 child = parent;
494 else
496 /* Change parent from RED to BLACK, but compensate by
497 changing sibling from BLACK to RED.
498 parent parent
499 bh+1 bh+1
500 / \ / \
501 sibling child --> sibling child
502 bh+1 bh bh bh
504 parent->color = BLACK;
505 sibling->color = RED;
506 return;
510 else
511 abort ();
513 /* Start again with a new (child, parent) pair. */
514 parent = child->parent;
516 #if 0 /* Already handled. */
517 if (child != NULL && child->color == RED)
519 child->color = BLACK;
520 return;
522 #endif
524 if (parent == NULL)
525 return;
529 static NODE_T
530 gl_tree_nx_add_first (CONTAINER_T container, NODE_PAYLOAD_PARAMS)
532 /* Create new node. */
533 NODE_T new_node =
534 (struct NODE_IMPL *) malloc (sizeof (struct NODE_IMPL));
536 if (new_node == NULL)
537 return NULL;
539 new_node->left = NULL;
540 new_node->right = NULL;
541 NODE_PAYLOAD_ASSIGN(new_node)
543 /* Add it to the tree. */
544 if (container->root == NULL)
546 new_node->color = BLACK;
547 container->root = new_node;
548 new_node->parent = NULL;
550 else
552 NODE_T node;
554 for (node = container->root; node->left != NULL; )
555 node = node->left;
557 node->left = new_node;
558 new_node->parent = node;
560 /* Color and rebalance. */
561 rebalance_after_add (container, new_node, node);
564 container->count++;
565 return new_node;
568 /* Adds the already allocated NEW_NODE to the tree, right before NODE. */
569 static void
570 gl_tree_add_node_before (CONTAINER_T container, NODE_T node, NODE_T new_node)
572 new_node->left = NULL;
573 new_node->right = NULL;
575 /* Add it to the tree. */
576 if (node->left == NULL)
577 node->left = new_node;
578 else
580 for (node = node->left; node->right != NULL; )
581 node = node->right;
582 node->right = new_node;
584 new_node->parent = node;
586 /* Color and rebalance. */
587 rebalance_after_add (container, new_node, node);
589 container->count++;
592 static NODE_T
593 gl_tree_nx_add_before (CONTAINER_T container, NODE_T node, NODE_PAYLOAD_PARAMS)
595 /* Create new node. */
596 NODE_T new_node =
597 (struct NODE_IMPL *) malloc (sizeof (struct NODE_IMPL));
599 if (new_node == NULL)
600 return NULL;
602 NODE_PAYLOAD_ASSIGN(new_node)
604 gl_tree_add_node_before (container, node, new_node);
605 return new_node;
608 /* Adds the already allocated NEW_NODE to the tree, right after NODE. */
609 static void
610 gl_tree_add_node_after (CONTAINER_T container, NODE_T node, NODE_T new_node)
612 new_node->left = NULL;
613 new_node->right = NULL;
615 /* Add it to the tree. */
616 if (node->right == NULL)
617 node->right = new_node;
618 else
620 for (node = node->right; node->left != NULL; )
621 node = node->left;
622 node->left = new_node;
624 new_node->parent = node;
626 /* Color and rebalance. */
627 rebalance_after_add (container, new_node, node);
629 container->count++;
632 static NODE_T
633 gl_tree_nx_add_after (CONTAINER_T container, NODE_T node, NODE_PAYLOAD_PARAMS)
635 /* Create new node. */
636 NODE_T new_node =
637 (struct NODE_IMPL *) malloc (sizeof (struct NODE_IMPL));
639 if (new_node == NULL)
640 return NULL;
642 NODE_PAYLOAD_ASSIGN(new_node)
644 gl_tree_add_node_after (container, node, new_node);
645 return new_node;
648 static void
649 gl_tree_remove_node_no_free (CONTAINER_T container, NODE_T node)
651 NODE_T parent = node->parent;
653 if (node->left == NULL)
655 /* Replace node with node->right. */
656 NODE_T child = node->right;
658 if (child != NULL)
660 child->parent = parent;
661 /* Since node->left == NULL, child must be RED and of height 1,
662 hence node must have been BLACK. Recolor the child. */
663 child->color = BLACK;
665 if (parent == NULL)
666 container->root = child;
667 else
669 if (parent->left == node)
670 parent->left = child;
671 else /* parent->right == node */
672 parent->right = child;
674 if (child == NULL && node->color == BLACK)
675 rebalance_after_remove (container, child, parent);
678 else if (node->right == NULL)
680 /* It is not absolutely necessary to treat this case. But the more
681 general case below is more complicated, hence slower. */
682 /* Replace node with node->left. */
683 NODE_T child = node->left;
685 child->parent = parent;
686 /* Since node->right == NULL, child must be RED and of height 1,
687 hence node must have been BLACK. Recolor the child. */
688 child->color = BLACK;
689 if (parent == NULL)
690 container->root = child;
691 else
693 if (parent->left == node)
694 parent->left = child;
695 else /* parent->right == node */
696 parent->right = child;
699 else
701 /* Replace node with the rightmost element of the node->left subtree. */
702 NODE_T subst;
703 NODE_T subst_parent;
704 NODE_T child;
705 color_t removed_color;
707 for (subst = node->left; subst->right != NULL; )
708 subst = subst->right;
710 subst_parent = subst->parent;
712 child = subst->left;
714 removed_color = subst->color;
716 /* The case subst_parent == node is special: If we do nothing special,
717 we get confusion about node->left, subst->left and child->parent.
718 subst_parent == node
719 <==> The 'for' loop above terminated immediately.
720 <==> subst == subst_parent->left
721 [otherwise subst == subst_parent->right]
722 In this case, we would need to first set
723 child->parent = node; node->left = child;
724 and later - when we copy subst into node's position - again
725 child->parent = subst; subst->left = child;
726 Altogether a no-op. */
727 if (subst_parent != node)
729 if (child != NULL)
730 child->parent = subst_parent;
731 subst_parent->right = child;
734 /* Copy subst into node's position.
735 (This is safer than to copy subst's value into node, keep node in
736 place, and free subst.) */
737 if (subst_parent != node)
739 subst->left = node->left;
740 subst->left->parent = subst;
742 subst->right = node->right;
743 subst->right->parent = subst;
744 subst->color = node->color;
745 subst->parent = parent;
746 if (parent == NULL)
747 container->root = subst;
748 else if (parent->left == node)
749 parent->left = subst;
750 else /* parent->right == node */
751 parent->right = subst;
753 if (removed_color == BLACK)
755 if (child != NULL && child->color == RED)
756 /* Recolor the child. */
757 child->color = BLACK;
758 else
759 /* Rebalancing starts at child's parent, that is subst_parent -
760 except when subst_parent == node. In this case, we need to use
761 its replacement, subst. */
762 rebalance_after_remove (container, child,
763 subst_parent != node ? subst_parent : subst);
767 container->count--;
770 static bool
771 gl_tree_remove_node (CONTAINER_T container, NODE_T node)
773 gl_tree_remove_node_no_free (container, node);
774 NODE_PAYLOAD_DISPOSE (container, node)
775 free (node);
776 return true;
779 /* For debugging. */
780 static unsigned int
781 check_invariants (NODE_T node, NODE_T parent, size_t *counterp)
783 unsigned int left_blackheight =
784 (node->left != NULL ? check_invariants (node->left, node, counterp) : 0);
785 unsigned int right_blackheight =
786 (node->right != NULL ? check_invariants (node->right, node, counterp) : 0);
788 if (!(node->parent == parent))
789 abort ();
790 if (!(node->color == BLACK || node->color == RED))
791 abort ();
792 if (parent == NULL && !(node->color == BLACK))
793 abort ();
794 if (!(left_blackheight == right_blackheight))
795 abort ();
797 (*counterp)++;
799 return left_blackheight + (node->color == BLACK ? 1 : 0);