3 * Path 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 /* A path is much simpler than in other trees, again because we have parent
34 * pointers. We just keep track of the address of the root link, a node, and
35 * some flags. If the path designates to a node, then the path just
36 * maintains a pointer to that node. If it designates a null link, then
37 * there are two cases: if the tree is empty, then the null root link is
38 * indicated by a null node pointer; otherwise, the path holds the a pointer
39 * to the parent node and a set flag indicating which of its child links is
43 void *xyla_splay_current(const struct xyla_splay_path *path)
45 /* Return the node currently designated by PATH, or null if PATH is
49 struct xyla_splay_node *node = path->node;
51 return (node && !path->f ? node : 0);
54 void xyla_splay_copypath(struct xyla_splay_path *newpath,
55 const struct xyla_splay_path *path)
57 /* Make a copy of PATH as NEWPATH. This has the same effect as simply
58 * assigning the path structures.
64 #define DEF_EXTRMPATH(op, OP, dir) \
65 void *xyla_splay_##op(struct xyla_bt_node **root, \
66 struct xyla_splay_path *path) \
68 /* Return the first or last node in the tree, together with a PATH \
69 * to the requested node, which can be used to remove them, or to \
70 * navigate to other nodes in the tree. Return null if the tree \
74 struct xyla_splay_node *next, \
75 *node = SPLAY_NODE(*root); \
77 /* Save the root link address. */ \
80 /* Find the leftmost or rightmost node if there is one. */ \
83 next = SPLAY_NODE(node->bt.dir); \
88 /* All done. The flags must be clear here: either the tree is \
89 * empty, and the `node' pointer is null, or the tree contains at \
90 * least one node, and we find the leftmost. \
92 path->node = node; path->f = 0; return (node); \
95 DEF_EXTRMPATH(firstpath, FIRSTPATH, left)
96 DEF_EXTRMPATH(lastpath, LASTPATH, right)
100 #define DEF_STEPPATH(op, OP, dir, DIR, opp, OPP) \
101 void *xyla_splay_##op(struct xyla_splay_path *path) \
103 /* Return the next or previous node in the tree, and update the \
104 * PATH to the requested node, which can be used to remove them, \
105 * or to navigate to other nodes in the tree. \
107 * If the path is full, i.e., refers to an existing node, then the \
108 * functions return that node's successor or predecessor. If the \
109 * path is empty, i.e., refers to a space between nodes where a \
110 * new node can be inserted, then the functions return the node at \
111 * the upper or lower end of the gap, unless the `gap' is actually \
112 * at an extreme of the tree and no such node exists. In the \
113 * latter case, a null pointer is returned. The path is modified \
114 * to refer to the new node, if any, or to the gap at the \
115 * appropriate extreme of the tree. \
118 struct xyla_splay_node *next, *old, *node = path->node; \
120 /* Resolve an empty path. An intervening splay might have \
121 * rearranged the links, but the appropriate actions are clear. \
122 * If there's no node, then the tree must have been empty \
123 * initially. If the gap is on the far side of the node, then we \
124 * return to the node. If it's on the same side, then we want the \
125 * node's successor anyway. \
127 if (!node) return (0); \
128 else if (path->f&XYLA_SPF_##OPP) { path->f = 0; return (node); } \
130 next = SPLAY_NODE(node->bt.dir); \
132 /* The node has a subtree in the appropriate direction, so \
138 next = SPLAY_NODE(node->bt.opp); \
141 /* Ascend the tree and find an ancestor on the correct side. */ \
144 old = node; node = node->spl.parent; \
145 if (!node) { path->f = XYLA_SPF_##DIR; return (0); } \
146 if (node->bt.opp == &old->bt) break; \
149 path->node = node; path->f = 0; return (node); \
152 DEF_STEPPATH(nextpath, NEXTPATH, right, RIGHT, left, LEFT)
153 DEF_STEPPATH(prevpath, PREVPATH, left, LEFT, right, RIGHT)
157 #define DEF_NUDGEPATH(op, OP, dir, DIR) \
158 void xyla_splay_##op(struct xyla_splay_path *path) \
160 /* Advance the PATH to just before or after the current node. The \
161 * path thereby becomes empty, suitable to insert an element or \
162 * split the tree just before or after the previously selected \
166 /* Very lazy: just designate the appropriate link of the current \
167 * node, and expect everything else to cope. If anyone asks, \
168 * pretend that we did everything properly, but the tree was \
169 * splayed afterwards. \
171 XYLA__ASSERT(path->node && !path->f); path->f = XYLA_SPF_##DIR; \
174 DEF_NUDGEPATH(beforepath, BEFOREPATH, left, LEFT)
175 DEF_NUDGEPATH(afterpath, AFTERPATH, right, RIGHT)
179 void *xyla_splay_rootpath(struct xyla_bt_node **root,
180 struct xyla_splay_path *path)
182 /* Initialize PATH to refer to the tree root, given the address ROOT of the
183 * root link, and return this root node. The resulting path will be empty
184 * if and only if the tree is empty.
187 struct xyla_bt_node *node;
189 node = *root; path->node = SPLAY_NODE(node);
190 path->f = 0; return (node);
193 void *xyla_splay_uppath(unsigned *pos_out, struct xyla_splay_path *path)
195 /* Update the PATH to refer to the parent of the link currently designated;
196 * return this parent node, and set *POS_OUT to the `XYLA_BTPOS_...'
197 * constant describing the link's relation to the parent. If the path
198 * initially designated the tree's root link, then return null, set
199 * *POS_OUT to `XYLA_BTPOS_ROOT', and leave the path unchanged; otherwise,
200 * the tree becomes full.
203 struct xyla_splay_node *node, *parent;
207 { *pos_out = XYLA_BTPOS_ROOT; return (0); }
209 node = path->node; f = path->f;
210 if (f&XYLA_SPF_LEFT) {
212 { path->f = 0; *pos_out = XYLA_BTPOS_LEFT; return (node); }
214 dir = xyla__splay_resolve_path(path);
215 *pos_out = dir == SPLAY_LEFT ? XYLA_BTPOS_LEFT : XYLA_BTPOS_RIGHT;
216 path->f = 0; return (path->node);
218 } else if (f&XYLA_SPF_RIGHT) {
220 { path->f = 0; *pos_out = XYLA_BTPOS_RIGHT; return (node); }
222 dir = xyla__splay_resolve_path(path);
223 *pos_out = dir == SPLAY_LEFT ? XYLA_BTPOS_LEFT : XYLA_BTPOS_RIGHT;
224 path->f = 0; return (path->node);
227 parent = node->spl.parent;
229 { *pos_out = XYLA_BTPOS_ROOT; return (0); }
230 else if (parent->bt.left == &node->bt) {
231 *pos_out = XYLA_BTPOS_LEFT; path->node = parent;
234 *pos_out = XYLA_BTPOS_RIGHT; path->node = parent;
241 #define DEF_CHILDPATH(dir, DIR) \
242 void *xyla_splay_##dir##path(struct xyla_splay_path *path) \
244 /* Update the PATH to refer to the left (if DIR is `left') or \
245 * right (if DIR is `right') child of the node currently \
246 * designated, and set NODE to be this child node. The PATH must \
247 * initially have been full, and may become empty as a result. \
250 struct xyla_splay_node *node; \
252 node = path->node; XYLA__ASSERT(node); XYLA__ASSERT(!path->f); \
253 node = SPLAY_NODE(node->bt.dir); \
254 if (!node) path->f = XYLA_SPF_##DIR; \
255 else path->node = node; \
259 DEF_CHILDPATH(left, LEFT)
260 DEF_CHILDPATH(right, RIGHT)
264 void xyla_splay_replace(const struct xyla_splay_path *path,
265 struct xyla_splay_node *node)
267 /* Replace the node identified by the PATH with the given NODE. The node's
268 * links need not be initialized. The replacement node's weight is copied
269 * from the original. Paths and iterators are not invalidated. It is the
270 * caller's responsibility to ensure that any ordering invariants are
271 * respected as a result of the change.
274 struct xyla_splay_node *old_node, *left, *right;
276 old_node = path->node; XYLA__ASSERT(old_node);
277 left = SPLAY_NODE(old_node->bt.left);
278 right = SPLAY_NODE(old_node->bt.right);
280 node->bt = old_node->bt;
281 node->spl.parent = old_node->spl.parent;
282 if (left) left->spl.parent = node;
283 if (right) right->spl.parent = node;
286 void xyla_splay_ripple(struct xyla_bt_nodecls *cls,
287 const struct xyla_splay_path *path)
289 /* Update a node's ancestors -- i.e., call the node class `upd' function on
290 * them, in ascending order -- after it has been modified. The PATH
291 * describes a full path to the node that has been changed. The PATH
295 xyla_bt_updfn *updfn = cls ? cls->ops->upd : 0;
296 struct xyla_splay_node *node = path->node;
298 XYLA__ASSERT(node); XYLA__ASSERT(!path->f);
300 node = node->spl.parent; if (!node) break;
301 updfn(cls, &node->bt);
305 int xyla_splay_ascend(xyla_bt_ascendfn *fn,
306 const struct xyla_splay_path *path, void *arg)
308 /* Ascend the tree along the PATH, allowing FN to accumulate summary data
309 * from the nodes to the root; see `XYLA_BT_ASCEND' for details. If FN
310 * returns nonzero, then stop and return the nonzero value; otherwise
311 * return zero. The PATH remains valid.
314 struct xyla_splay_node *node, *parent;
315 struct xyla_splay_path mypath;
321 rc = fn(0, 0, 0, XYLA_BTPOS_ROOT, arg);
324 mypath = *path; xyla__splay_resolve_path(&mypath); node = mypath.node;
325 if (mypath.f&XYLA_SPF_LEFT)
326 rc = fn(0, &node->bt, node->bt.right, XYLA_BTPOS_LEFT, arg);
328 rc = fn(0, &node->bt, node->bt.left, XYLA_BTPOS_RIGHT, arg);
333 parent = node->spl.parent; if (!parent) break;
334 if (&node->bt == parent->bt.left)
335 rc = fn(&node->bt, &parent->bt, parent->bt.right,
336 XYLA_BTPOS_LEFT, arg);
338 rc = fn(&node->bt, &parent->bt, parent->bt.left,
339 XYLA_BTPOS_RIGHT, arg);
344 rc = fn(&node->bt, 0, 0, XYLA_BTPOS_ROOT, arg);
349 /*----- That's all, folks -------------------------------------------------*/