3 * Insertion and removal for red-black trees
5 * (c) 2024 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Xyla, a library of binary trees.
12 * Xyla is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Lesser General Public License as published by the
14 * Free Software Foundation; either version 3 of the License, or (at your
15 * option) any later version.
17 * Xyla is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
20 * License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with Xyla. If not, see <https://www.gnu.org/licenses/>.
26 /*----- Header files ------------------------------------------------------*/
31 /*----- Main code ---------------------------------------------------------*/
33 int xyla_rb_insert(struct xyla_bt_nodecls *cls,
34 struct xyla_rb_path *path, struct xyla_rb_node *node)
36 /* Add a new node to the tree, in the place determined by the empty PATH.
37 * It's the caller's responsibility to ensure that inserting the node here
38 * respects any ordering invariants. Returns `XYLA_RC_HTCHG' if the tree
39 * has increased in height, or `XYLA_RC_OK' if its height remains
42 * The new node is not copied, and its storage must remain valid until the
43 * node is removed or the tree is discarded.
46 struct xyla_rb_node *p, *n, *c, *l, *r;
47 struct xyla_bt_node **clink, **nlink, **plink;
48 xyla_bt_updfn *updfn = cls ? cls->ops->upd : 0;
51 /* Check that the path is well-formed and describes a failed probe. */
52 clink = path->p[k]; XYLA__ASSERT(!*clink);
54 /* Initialize the new node's links and hook it onto the tree. */
55 node->bt.left = node->bt.right = 0; *clink = &node->bt;
57 /* If the tree was empty then the new node is going to be the root of the
58 * tree, which must be black.
61 node->rb.f &= ~XYLA_RBF_RED;
62 if (updfn) updfn(cls, &node->bt);
63 return (XYLA_RC_HTCHG);
66 /* Otherwise we'll provisionally paint the new node red and try to fix
69 c = node; c->rb.f |= XYLA_RBF_RED;
71 /* The child node c is known to be red. Our focus node n is the child's
72 * parent, and it might also be red; this is the only violation of the
73 * tree invariants, and we're trying to fix it. In particular, c's
74 * children, if any, are black. Neither c nor n has been updated yet.
75 * We'll also be interested in the focus node's parent p. The focus
76 * node's sibling, denoted s in the diagrams below, is also interesting
77 * to the theory, but not named explicitly in the code itself.
80 /* Retrieve the focus node, which must exist because c is red and the
81 * tree root is black. If the focus node is black then we're done.
83 nlink = path->p[--k]; n = RB_NODE(*nlink);
84 if (!(n->rb.f&XYLA_RBF_RED)) {
85 if (updfn) { updfn(cls, &c->bt); updfn(cls, &n->bt); }
89 /* Retrieve the focus node's parent p, which must also exist for the same
90 * reason. Note that p is definitely black, since it has at least one
93 plink = path->p[--k]; p = RB_NODE(*plink);
95 /* If the parent has two children and they're both red, then blacken them
96 * both. (This approach avoids dealing with all of the symmetry in the
97 * loop.) If the parent is the root then we're done, and the tree black-
98 * height has increased by one; otherwise, redden it and ascend.
103 * ( ) ( ) ---> (*) (*)
104 * c / \ / \ c / \ / \
108 l = RB_NODE(p->bt.left); r = RB_NODE(p->bt.right);
109 if (l && r && (l->rb.f&r->rb.f&XYLA_RBF_RED)) {
110 V( if (xyla__verbout && (xyla__verbose&XYLA__VF_INSERT))
111 fprintf(xyla__verbout, "XYLA-RB INSERT red sibling: %s\n",
112 k ? "ascend" : "extend tree"); )
113 l->rb.f &= ~XYLA_RBF_RED; r->rb.f &= ~XYLA_RBF_RED;
116 updfn(cls, &c->bt); updfn(cls, &n->bt);
119 return (XYLA_RC_HTCHG);
121 if (updfn) { updfn(cls, &c->bt); updfn(cls, &n->bt); }
122 p->rb.f |= XYLA_RBF_RED; c = p; clink = plink; continue;
125 /* Otherwise, we have some links to rewrite. */
126 if (nlink == &p->bt.left)
127 #define FIXUP_INSERT(left, l, right, r) do { \
128 /* The focus node is its parent's left child. There are two \
129 * cases, depending on whether c is the focus node's left or \
130 * right child. It's safe to redden p here: if the focus node \
131 * has a red sibling then we'd have ascended the tree above. \
134 if (clink == &n->bt.left) { \
144 V( if (xyla__verbout && (xyla__verbose&XYLA__VF_INSERT)) \
145 fputs("XYLA-RB INSERT zig-zig (" #left " child)\n", \
147 p->bt.left = n->bt.right; n->bt.right = &p->bt; \
148 n->rb.f &= ~XYLA_RBF_RED; p->rb.f |= XYLA_RBF_RED; \
150 updfn(cls, &c->bt); updfn(cls, &p->bt); updfn(cls, &n->bt); \
163 V( if (xyla__verbout && (xyla__verbose&XYLA__VF_INSERT)) \
164 fputs("XYLA-RB INSERT zig-zag (" #left " child)\n", \
166 n->bt.right = c->bt.left; c->bt.left = &n->bt; \
167 p->bt.left = c->bt.right; c->bt.right = &p->bt; \
168 c->rb.f &= ~XYLA_RBF_RED; p->rb.f |= XYLA_RBF_RED; \
170 updfn(cls, &n->bt); updfn(cls, &p->bt); \
171 updfn(cls, &c->bt); \
176 FIXUP_INSERT(left, l, right, r);
178 FIXUP_INSERT(right, r, left, l);
181 /* Whatever happened above, we're now done: the parent is still black. */
185 /* Follow the rest of the path up to the root, updating the nodes. */
186 if (updfn) while (k) updfn(cls, *path->p[--k]);
188 /* We're done. The overall black-height only changes if we reach the root,
189 * and we'd have returned already if that happened.
194 int xyla_rb_remove(struct xyla_bt_nodecls *cls, struct xyla_rb_path *path)
196 /* Remove the node identified by the PATH. The node was allocated by the
197 * caller, and should be freed by the caller in whatever way is
198 * appropriate. Returns `XYLA_RC_HTCHG' if the tree has decreased in
199 * height, or `XYLA_RC_OK' if its height remains unchanged.
202 struct xyla_rb_node *n, *p, *s, *t, *l, *r;
203 struct xyla_bt_node *del, *subst, *replace, **nlink, **plink;
204 xyla_bt_updfn *updfn = cls ? cls->ops->upd : 0;
208 /* Unlink the node from the tree. */
209 rc = xyla_bt_remove(&del, &subst, &replace,
210 path->p, &path->k, XYLA_RB_HTMAX);
212 t = RB_NODE(del); f = t->rb.f; s = RB_NODE(subst); n = RB_NODE(replace);
215 /* If the node was substituted, then fix up the colouring. */
217 { ff = f; f = s->rb.f; s->rb.f = (f&~XYLA_RBF_RED) | (ff&XYLA_RBF_RED); }
219 /* We've cut out out a node t, and replaced it with one of its `children' n
220 * -- which might, in fact, be null. If t was black, then, to maintain the
221 * invariant, we (mentally) assign an additional dose of blackness to n.
222 * Our task will be to take one of these blackness doses and move it
223 * somewhere safe -- either a red ancestor, or the root, where we can just
224 * safely forget about it.
226 if (f&XYLA_RBF_RED) {
227 /* If the node we've cut out was actually red then we can just forget
231 if (updfn && n) updfn(cls, &n->bt);
232 } else if (n && (n->rb.f&XYLA_RBF_RED)) {
233 /* If n is actually a real node, and it's red, then we can just blacken
237 n->rb.f &= ~XYLA_RBF_RED;
238 if (updfn && n) updfn(cls, &n->bt);
240 /* It looks like we have some actual work to do. */
244 /* The node n is doubly black, and we must offload one of the doses of
245 * blackness somewhere else.
249 /* The node we're looking at is, in fact, the root. The extra
250 * blackness affects all paths to the leaves equally, so we can can
251 * just throw it away without anyone noticing, except that the tree
252 * black-height has decreased.
255 V( if (xyla__verbout && (xyla__verbose&XYLA__VF_REMOVE))
256 fputs("XYLA-RB REMOVE shorten tree\n", xyla__verbout); )
257 return (XYLA_RC_HTCHG);
260 /* Not the root, so we can find a parent node. */
261 plink = path->p[--k]; p = RB_NODE(*plink);
263 /* In fact, because we have at least one blackness dose, we must have a
264 * live sibling, with a subtree containing at least one black node.
265 * Split into two main cases according to whether we're on the left or
268 if (nlink == &p->bt.left)
269 #define FIXUP_REMOVE(left, l, right, r) do { \
270 /* We're the left child. */ \
272 s = RB_NODE(p->bt.right); \
273 if (s->rb.f&XYLA_RBF_RED) { \
274 /* Our sibling is red. The parent must then be black, and \
275 * the sibling must have two black children, our node's \
276 * niblings, though we're only interested in the left-hand \
277 * one, which we call t. \
280 t = RB_NODE(s->bt.left); \
281 l = RB_NODE(t->bt.left); r = RB_NODE(t->bt.right); \
283 if (r && r->rb.f&XYLA_RBF_RED) { \
284 /* The left nibling has a red right child. The following \
285 * rearrangement discharges the surplus black dose. \
290 * (* *) ( ) ---> ( ) \
291 * / \ t / \ p / \ r \
298 V( if (xyla__verbout && (xyla__verbose&XYLA__VF_REMOVE)) \
299 fputs("XYLA-RB REMOVE red sibling, " \
300 "outer red great-nibling (" #left " child)\n", \
302 p->bt.right = l ? &l->bt : 0; \
303 t->bt.left = &p->bt; s->bt.left = &t->bt; \
304 s->rb.f &= ~XYLA_RBF_RED; r->rb.f &= ~XYLA_RBF_RED; \
305 t->rb.f |= XYLA_RBF_RED; \
307 updfn(cls, &p->bt); updfn(cls, &t->bt); \
308 updfn(cls, &s->bt); \
310 } else if (l && l->rb.f&XYLA_RBF_RED) { \
311 /* The left nibling has a red left child. The following \
312 * rearrangement discharges the surplus black dose. \
317 * (* *) ( ) ---> ( ) \
318 * / \ t / \ p / \ t \
325 V( if (xyla__verbout && (xyla__verbose&XYLA__VF_REMOVE)) \
326 fputs("XYLA-RB REMOVE red sibling, " \
327 "inner red great-nibling (" #left " child)\n", \
329 p->bt.right = l->bt.left; l->bt.left = &p->bt; \
330 t->bt.left = l->bt.right; l->bt.right = &t->bt; \
331 s->bt.left = &l->bt; s->rb.f &= ~XYLA_RBF_RED; \
333 updfn(cls, &p->bt); updfn(cls, &t->bt); \
334 updfn(cls, &l->bt); updfn(cls, &s->bt); \
337 /* The left nibling has no red children. The following \
338 * rearrangement discharges the surplus black dose: \
339 * reddening t is safe because it has no red children. \
344 * (* *) ( ) ---> (*) \
345 * / \ t / \ n / \ t \
350 V( if (xyla__verbout && (xyla__verbose&XYLA__VF_REMOVE)) \
351 fputs("XYLA-RB REMOVE red sibling, " \
352 "no red great-nibling (" #left " child)\n", \
354 p->bt.right = &t->bt; s->bt.left = &p->bt; \
355 s->rb.f &= ~XYLA_RBF_RED; t->rb.f |= XYLA_RBF_RED; \
356 if (updfn) { updfn(cls, &p->bt); updfn(cls, &s->bt); } \
359 /* Adjust the parent link. In all cases, the invariant has \
360 * been properly restored. \
362 *plink = &s->bt; goto update; \
364 /* Our sibling is black. We can't deduce the colour of the \
368 l = RB_NODE(s->bt.left); r = RB_NODE(s->bt.right); \
370 if (r && r->rb.f&XYLA_RBF_RED) { \
371 /* The sibling has a red right child. The following \
372 * rearrangement discharges the surplus black dose. \
377 * (* *) (*) ---> (*) (*) \
378 * / \ / \ r n / \ / \ \
383 V( if (xyla__verbout && (xyla__verbose&XYLA__VF_REMOVE)) \
384 fputs("XYLA-RB REMOVE black sibling, " \
385 "outer red nibling (" #left " child)\n", \
387 p->bt.right = l ? &l->bt : 0; s->bt.left = &p->bt; \
388 s->rb.f |= p->rb.f&XYLA_RBF_RED; \
389 p->rb.f &= ~XYLA_RBF_RED; r->rb.f &= ~XYLA_RBF_RED; \
390 if (updfn) { updfn(cls, &p->bt); updfn(cls, &s->bt); } \
391 *plink = &s->bt; goto update; \
392 } else if (l && l->rb.f&XYLA_RBF_RED) { \
393 /* The sibling has a red left child. The following \
394 * rearrangement discharges the surplus black dose. \
399 * (* *) (*) ---> (*) (*) \
400 * / \ l / \ n / \ / \ \
405 V( if (xyla__verbout && (xyla__verbose&XYLA__VF_REMOVE)) \
406 fputs("XYLA-RB REMOVE black sibling, " \
407 "inner red nibling (" #left " child)\n", \
409 p->bt.right = l->bt.left; l->bt.left = &p->bt; \
410 s->bt.left = l->bt.right; l->bt.right = &s->bt; \
411 l->rb.f = (l->rb.f&~XYLA_RBF_RED) | (p->rb.f&XYLA_RBF_RED); \
412 p->rb.f &= ~XYLA_RBF_RED; \
414 updfn(cls, &p->bt); updfn(cls, &s->bt); \
415 updfn(cls, &l->bt); \
417 *plink = &l->bt; goto update; \
419 /* The sibling has no red children. That means that it's \
420 * safe to make it red, so we can push the extra black dose \
421 * up into the parent. If the parent was previously red, \
422 * then we're done; otherwise, we ascend the tree. \
427 * (* *) (*) ---> (*) ( ) \
431 V( if (xyla__verbout && (xyla__verbose&XYLA__VF_REMOVE)) \
432 fprintf(xyla__verbout, "XYLA-RB REMOVE " \
433 "black sibling, %s parent, no red nibling " \
434 "(" #left " child)\n", \
435 p->rb.f&XYLA_RBF_RED ? "red" : "black"); ) \
436 s->rb.f |= XYLA_RBF_RED; \
437 if (updfn) updfn(cls, &p->bt); \
438 if (p->rb.f&XYLA_RBF_RED) \
439 { p->rb.f &= ~XYLA_RBF_RED; goto update; } \
440 n = p; nlink = plink; \
444 FIXUP_REMOVE(left, l, right, r);
446 FIXUP_REMOVE(right, r, left, l);
451 /* Follow the rest of the path up to the root, updating the nodes. */
453 if (updfn) while (k) updfn(cls, *path->p[--k]);
457 /*----- That's all, folks -------------------------------------------------*/