3 * Basic utilities for splay 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 void xyla__splay_split(struct xyla_bt_nodecls *cls,
34 struct xyla_bt_node **left_inout,
35 struct xyla_bt_node **right_inout,
36 struct xyla_splay_node *parent, unsigned side)
38 /* Splay the tree at a possibly fictional node. The node is promoted to
39 * the tree root, and the child links are updated.
41 * The PARENT and SIDE arguments identify the node's initial position in
42 * the tree, and *LEFT_INOUT and *RIGHT_INOUT identify its child links. It
43 * is valid if the node is already the tree root: in this case, PARENT is
44 * null and SIDE is ignored; otherwise SIDE must be one of the constants
45 * `SPLAY_LEFT' or `SPLAY_RIGHT'.
47 * The `xyla__splay_splay' function below calls this with a real node,
48 * determining the PARENT and SIDE appropriately; `xyla_splay_insert' uses
49 * this to prepare the tree before the new node is linked in;
50 * `xyla_splay_split' uses this to handle both its full and empty path
54 struct xyla_splay_node *p, *n, *g, *l, *r, *t;
55 xyla_bt_updfn *updfn = cls ? cls->ops->upd : 0;
58 l = SPLAY_NODE(*left_inout); r = SPLAY_NODE(*right_inout);
59 n = parent; trail = side;
61 /* Ascend the tree two links at a time. */
64 p = n->spl.parent; if (!p) break;
66 trail |= (p->bt.left == &n->bt ? SPLAY_LEFT : SPLAY_RIGHT) << 1;
69 trail |= (g->bt.left == &p->bt ? SPLAY_LEFT : SPLAY_RIGHT) << 0;
72 #define SPLAY_ZZ(left, LEFT, l, right, RIGHT, r) \
73 case (SPLAY_##LEFT << 2) | (SPLAY_##LEFT << 1): \
74 /* Zig-zig case: left child of a left child. \
86 V( if (xyla__verbout && (xyla__verbose&XYLA__VF_SPLAY)) \
87 fputs("XYLA-SPLAY SPLAY zig-zig " \
88 #left "/" #left " child\n", xyla__verbout); ) \
89 t = SPLAY_NODE(n->bt.right); \
90 p->bt.left = t ? &t->bt : 0; if (t) t->spl.parent = p; \
91 n->bt.right = &p->bt; p->spl.parent = n; \
92 n->bt.left = r ? &r->bt : 0; if (r) r->spl.parent = n; \
95 case (SPLAY_##RIGHT << 2) | (SPLAY_##LEFT << 1): \
96 /* Zig-zag case: right child of a left child. \
102 * / \ c ---> (*) (*) \
108 V( if (xyla__verbout && (xyla__verbose&XYLA__VF_SPLAY)) \
109 fputs("XYLA-SPLAY SPLAY zig-zag " \
110 #left "/" #right " child\n", xyla__verbout); ) \
111 p->bt.left = r ? &r->bt : 0; if (r) r->spl.parent = p; \
112 n->bt.right = l ? &l->bt : 0; if (l) l->spl.parent = n; \
116 SPLAY_ZZ(left, LEFT, l, right, RIGHT, r);
117 SPLAY_ZZ(right, RIGHT, r, left, LEFT, l);
122 /* Update the node and its parent. The ascending child still hasn't
123 * found its level yet.
125 if (updfn) { updfn(cls, &p->bt); updfn(cls, &n->bt); }
127 /* Ascend the tree. */
131 /* Fix up the remaining link from the (old) root. */
132 if ((trail&1) == SPLAY_LEFT)
133 #define SPLAY_Z(left, l, right, r) do { \
134 /* Zig case: left child of the root. \
144 V( if (xyla__verbout && (xyla__verbose&XYLA__VF_SPLAY)) \
145 fputs("XYLA-SPLAY SPLAY zig " \
146 #left " child\n", xyla__verbout); ) \
147 n->bt.left = r ? &r->bt : 0; if (r) r->spl.parent = n; \
150 SPLAY_Z(left, l, right, r);
152 SPLAY_Z(right, r, left, l);
155 if (updfn) updfn(cls, &n->bt);
159 *left_inout = l ? &l->bt : 0; *right_inout = r ? &r->bt : 0;
162 void xyla__splay_splay(struct xyla_bt_nodecls *cls,
163 struct xyla_splay_node *node)
165 /* Splay the tree at NODE. The NODE becomes the new tree root; updating
166 * this is the caller's responsibility. The NODE is /not/ updated, since
167 * the caller may want to make further changes before it's ready.
170 struct xyla_splay_node *parent, *left, *right;
172 /* Determine the node's parent. */
173 parent = node->spl.parent;
176 /* There's a parent, so the tree needs updating. */
178 /* Split the tree and update the links. */
179 xyla__splay_split(cls, &node->bt.left, &node->bt.right, parent,
180 parent->bt.left == &node->bt ?
181 SPLAY_LEFT : SPLAY_RIGHT);
183 /* Fix the parent links in the node and left and right subtree roots. */
184 left = SPLAY_NODE(node->bt.left); if (left) left->spl.parent = node;
185 right = SPLAY_NODE(node->bt.right); if (right) right->spl.parent = node;
186 node->spl.parent = 0;
190 void *xyla__splay_join(struct xyla_bt_nodecls *cls,
191 struct xyla_splay_node *left,
192 struct xyla_splay_node *right)
194 /* Join the two trees LEFT and RIGHT, without an intervening node. The
195 * input tree roots' parent links need not be cleared before calling.
198 struct xyla_splay_node *next, *node;
199 xyla_bt_updfn *updfn = cls ? cls->ops->upd : 0;
202 /* There's no left tree, so the combined tree is simply the right tree.
203 * If the right tree isn't empty, then clear its parent link.
206 if (right) right->spl.parent = 0;
209 /* There's no right tree, so the combined tree is simply the left tree.
210 * The left tree is definitely not empty, or we'd have taken the previous
214 left->spl.parent = 0;
217 /* Both trees are non-empty. Splay the right tree at its leftmost node.
218 * It's left link becomes null, so hook the left tree in its place.
221 /* Clear the root's parent link, and find the leftmost node. */
222 node = right; node->spl.parent = 0;
224 next = SPLAY_NODE(node->bt.left); if (!next) break;
228 /* Splay, and hook the left tree in place. */
229 xyla__splay_splay(cls, node);
230 node->bt.left = &left->bt; left->spl.parent = node;
232 /* Update the new root. */
233 if (updfn) updfn(cls, &node->bt);
238 unsigned xyla__splay_resolve_path(struct xyla_splay_path *path)
240 /* The PATH must be empty -- i.e., refer to a null link rather than a real
241 * node -- and nontrivial -- so the tree must not be empty. If the tree
242 * has been reorganized since the path was constructed, then the `gap'
243 * referred to by the path may have moved, so that the link referenced
244 * isn't actually null any more. Modify the PATH so that it refers to the
245 * actual null link at the same position in the tree's ordering, and return
246 * `SPLAY_LEFT' or `SPLAY_RIGHT' suitable for calling `xyla__splay_split'.
249 struct xyla_splay_node *node = path->node, *next;
252 if (path->f&XYLA_SPF_LEFT)
253 #define RESOLVE(left, LEFT, right, RIGHT) do { \
254 /* The original link is on the left of the node. */ \
256 if (!node->bt.left) \
257 /* The left link is still null. Just roll with it. */ \
259 side = SPLAY_##LEFT; \
261 /* Descend into to the left subtree and work all the way \
265 node = SPLAY_NODE(node->bt.left); \
267 next = SPLAY_NODE(node->bt.right); \
271 side = SPLAY_##RIGHT; path->node = node; \
272 path->f = XYLA_SPF_##RIGHT; \
275 RESOLVE(left, LEFT, right, RIGHT);
277 RESOLVE(right, RIGHT, left, LEFT);
282 void xyla_splay_splay(struct xyla_bt_nodecls *cls,
283 struct xyla_splay_path *path)
285 /* Promote the node designated by the PATH to the root. Call this after
286 * `xyla_splay_probe' if you're not about to do anything else with the
290 struct xyla_splay_node *node;
291 xyla_bt_updfn *updfn = cls ? cls->ops->upd : 0;
295 xyla__splay_splay(cls, node); *path->root = &node->bt;
296 if (updfn) updfn(cls, &node->bt);
300 /*----- That's all, folks -------------------------------------------------*/