3 * Splitting and joining treaps
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_treap_join(struct xyla_bt_nodecls *cls,
34 struct xyla_bt_node **root_out,
35 struct xyla_bt_node **left,
36 struct xyla_bt_node *mid,
37 struct xyla_bt_node **right)
39 /* Concatenate the two trees rooted at LEFT and RIGHT in order; if MID is
40 * not null, then place this node between them. The root of the resulting
41 * tree is stored in *ROOT_OUT. It's the caller's responsibility to ensure
42 * that this respects any ordering invariants.
44 * The ROOT_OUT pointer may equal either LEFT or RIGHT (or, indeed, both,
45 * only if both are empty). If LEFT is distinct from ROOT_OUT then *LEFT
46 * is set null on exit; similarly, if RIGHT is distinct from ROOT_OUT, then
47 * *RIGHT is set null on exit.
49 * Returns `XYLA_RC_OK'.
52 struct xyla_treap_node
53 *l = TREAP_NODE(*left), *m = TREAP_NODE(mid), *r = TREAP_NODE(*right);
54 struct xyla_bt_node **nlink;
56 xyla_bt_updfn *updfn = cls ? cls->ops->upd : 0;
57 struct xyla_treap_path path;
60 /* There's no joining node, and we just have to stitch the two trees
61 * together. Suppose that the left root is lighter than the right root.
62 * Work down the right edge of the left tree until we find a node that's
63 * heavier than the right root, replace the subtree rooted at that
64 * heavier node by the right tree, and switch to joining the now-detached
65 * subtree into the left edge of the right tree.
68 /* Before we really get stuck in, there are some special cases. */
69 if (!r) { *root_out = l ? &l->bt : 0; goto end; }
70 else if (!l) { *root_out = r ? &r->bt : 0; goto end; }
72 /* Set up the path and the weights, and dispatch. */
73 nlink = path.p[0] = root_out; path.k = 0;
74 lwt = l->trp.wt; rwt = r->trp.wt;
75 if (lwt <= rwt) goto stitch_right;
76 else goto stitch_left;
78 #define JOIN_STITCH(right, r, rwt, left, l, lwt) do { \
80 /* The left node is lighter. */ \
82 V( if (xyla__verbout && (xyla__verbose&XYLA__VF_JOIN)) \
83 fputs("XYLA-TREAP JOIN stitch " #right " edge\n", \
86 /* Hook the left tree onto the most recent tree. */ \
89 /* Work along the right edge to find a node heavier than the right \
90 * root, and then switch. If we reach the end, then just hook the \
91 * right tree in entirely. \
94 if (path.k >= XYLA_TREAP_HTMAX) xyla__treap_boundfail(); \
95 nlink = path.p[++path.k] = &l->bt.right; \
96 l = TREAP_NODE(l->bt.right); \
97 if (!l) { *nlink = &r->bt; goto stitch_done; } \
98 lwt = l->trp.wt; if (lwt > rwt) goto stitch_##left; \
101 JOIN_STITCH(right, r, rwt, left, l, lwt);
102 JOIN_STITCH(left, l, lwt, right, r, rwt);
106 /* Follow the rest of the path up to the root, updating the nodes. */
107 if (updfn) while (path.k) updfn(cls, *path.p[--path.k]);
110 /* We have two trees and a joining node. If we pretend that the joining
111 * node is very light, it will serve as the new root of the combined
112 * tree. Then we allow it to sink down to the right level according to
113 * its actual weight. The details are a bit messy, however: this is the
114 * only point in the code where we do a full `downheap' operation where
115 * we need to consider the relative ordering of three different nodes.
118 /* Clear the input trees. */
120 nlink = path.p[0] = root_out; path.k = 0;
122 /* Initial dispatch. */
125 if (r) { rwt = r->trp.wt; goto mid_right; }
129 if (!r) goto mid_left;
130 rwt = r->trp.wt; goto mid_both;
133 /* The current slot has two children. Further dispatching. */
137 #define JOIN_MID(left, l, lwt, right, r, rwt, next) do { \
138 if (mwt <= lwt) goto mid_done; \
140 /* Promote the left child. \
150 V( if (xyla__verbout && (xyla__verbose&XYLA__VF_JOIN)) \
151 fputs("XYLA-TREAP JOIN mid float " #left \
152 " (next " #next ")\n", xyla__verbout); ) \
153 if (path.k >= XYLA_TREAP_HTMAX) xyla__treap_boundfail(); \
154 *nlink = &l->bt; nlink = path.p[++path.k] = &l->bt.right; \
155 l = TREAP_NODE(l->bt.right); \
156 if (!l) goto mid_##next; \
157 else lwt = l->trp.wt; \
159 JOIN_MID(left, l, lwt, right, r, rwt, right);
161 JOIN_MID(right, r, rwt, left, l, lwt, left);
165 /* The current node has only a left child. Either it's lighter than the
166 * left child, in which case we're done, or we rotate. In the latter
167 * case, it's clear from the diagram above that the right subtree of our
168 * focus node remains null.
170 for (;;) JOIN_MID(left, l, lwt, right, r, rwt, done);
173 /* The current node has only a right child. */
174 for (;;) JOIN_MID(right, r, rwt, left, l, lwt, done);
179 /* We've found the level. Hook the joining node into place. */
181 m->bt.left = l ? &l->bt : 0;
182 m->bt.right = r ? &r->bt : 0;
184 /* Follow the rest of the path up to the root, updating the nodes. */
187 while (path.k) updfn(cls, *path.p[--path.k]);
196 int xyla_treap_split(struct xyla_bt_nodecls *cls,
197 struct xyla_bt_node **left_out,
198 struct xyla_bt_node **mid_out,
199 struct xyla_bt_node **right_out,
200 struct xyla_treap_path *path)
202 /* Split a tree tree into two at the indicated place indicated by the PATH.
203 * Store in *LEFT_OUT and *RIGHT_OUT the roots of trees containing every
204 * node respectively preceding and following the PATH.
206 * If the PATH is full, i.e., it denotes a node in the input tree, then
207 * that becomes the `middle node', which does not appear in either output
208 * tree; a pointer to this node is stored in *MID_OUT.
210 * Returns `XYLA_RC_OK'.
213 struct xyla_treap_node *n, *p, *l, *r;
214 struct xyla_bt_node **nlink, **plink;
215 xyla_bt_updfn *updfn = cls ? cls->ops->upd : 0;
218 /* This is essentially the reverse of joining. We pretend that the
219 * selected node -- or an imaginary node, if the path is empty -- has
220 * become very light and let it float all the way up until becomes the tree
221 * root, at which point the tree just falls apart into two pieces. This
222 * doesn't actually involve any weight comparisons!
226 nlink = path->p[k]; n = TREAP_NODE(*nlink);
230 l = TREAP_NODE(n->bt.left); r = TREAP_NODE(n->bt.right);
231 n->bt.left = n->bt.right = 0;
234 /* Float the node up. */
236 plink = path->p[--k]; p = TREAP_NODE(*plink);
238 /* Adjust the links. */
239 if (nlink == &p->bt.left)
240 #define FIXUP_SPLIT(left, l, right, r) do { \
241 /* We're the left child. \
251 V( if (xyla__verbout && (xyla__verbose&XYLA__VF_SPLIT)) \
252 fputs("XYLA-TREAP SPLIT " #left " child\n", \
254 p->bt.left = r ? &r->bt : 0; r = p; \
256 FIXUP_SPLIT(left, l, right, r);
258 FIXUP_SPLIT(right, r, left, l);
261 /* And ascend the tree. */
262 if (updfn) updfn(cls, &p->bt);
266 /* And we're done. */
268 *left_out = l ? &l->bt : 0;
269 *mid_out = n ? &n->bt : 0;
270 *right_out = r ? &r->bt : 0;
274 int xyla_treap_splitat(struct xyla_bt_nodecls *cls,
275 struct xyla_bt_node **left_out,
276 struct xyla_bt_node **mid_out,
277 struct xyla_bt_node **right_out,
278 struct xyla_bt_node **root, const void *key)
280 /* Split the tree rooted at ROOT into two at an indicated KEY. Store in
281 * *LEFT_OUT and *RIGHT_OUT the roots of trees containing every node whose
282 * key is respectively less than and greater than the KEY.
284 * If a node matching the KEY exists in the input tree, then that becomes
285 * the `middle node', which does not appear in either output tree; a
286 * pointer to this node is stored in *MID_OUT.
288 * This operation assumes that the tree traversal ordering matches an
289 * ordering on search keys, which is implemented by the node-class `nav'
292 * Returns `XYLA_RC_OK'.
295 const struct xyla_bt_ordops *ops = ORDER_OPS(cls->ops);
296 struct xyla_bt_node *node;
297 struct xyla_treap_path path;
300 XYLA_BT_PROBE(rc, node, cls, root, ops->ord.nav, UNCONST(void, key),
301 path.p, path.k, XYLA_TREAP_HTMAX); (void)node;
302 if (rc) xyla__treap_boundfail();
303 return (xyla_treap_split(cls, left_out, mid_out, right_out, &path));
306 int xyla_treap_splitroot(struct xyla_bt_node **left_out,
307 struct xyla_bt_node **root_out,
308 struct xyla_bt_node **right_out,
309 struct xyla_bt_node **root)
311 /* Split the tree rooted at ROOT into two at its root. Store in *ROOT_OUT
312 * a pointer to the original root node, or null if the tree was initially
313 * empty; store in *LEFT_OUT and *RIGHT_OUT the root of the original root's
314 * left and right subtrees respectively.
316 * Returns `XYLA_RC_OK'.
319 struct xyla_bt_node *node;
321 node = *root; *root = 0;
323 *left_out = *right_out = 0;
325 *left_out = node->left;
326 *right_out = node->right;
327 node->left = node->right = 0;
333 /*----- That's all, folks -------------------------------------------------*/