blob: f3530a74608c270dae34c95da44d2375b1c8156b [file] [log] [blame]
Austin Schuh3333ec72022-12-29 16:21:06 -08001/* Copyright (C) 2013-2016, The Regents of The University of Michigan.
2All rights reserved.
3This software was developed in the APRIL Robotics Lab under the
4direction of Edwin Olson, ebolson@umich.edu. This software may be
5available under alternative licensing terms; contact the address above.
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions are met:
81. Redistributions of source code must retain the above copyright notice, this
9 list of conditions and the following disclaimer.
102. Redistributions in binary form must reproduce the above copyright notice,
11 this list of conditions and the following disclaimer in the documentation
12 and/or other materials provided with the distribution.
13THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23The views and conclusions contained in the software and documentation are those
24of the authors and should not be interpreted as representing official policies,
25either expressed or implied, of the Regents of The University of Michigan.
26*/
27
28#pragma once
29
30#include <stdint.h>
31#include <stdlib.h>
32
33typedef struct unionfind unionfind_t;
34
35struct unionfind
36{
37 uint32_t maxid;
38 struct ufrec *data;
39};
40
41struct ufrec
42{
43 // the parent of this node. If a node's parent is its own index,
44 // then it is a root.
45 uint32_t parent;
46
47 // for the root of a connected component, the number of components
48 // connected to it. For intermediate values, it's not meaningful.
49 uint32_t size;
50};
51
52static inline unionfind_t *unionfind_create(uint32_t maxid)
53{
54 unionfind_t *uf = (unionfind_t*) calloc(1, sizeof(unionfind_t));
55 uf->maxid = maxid;
56 uf->data = (struct ufrec*) malloc((maxid+1) * sizeof(struct ufrec));
57 for (uint32_t i = 0; i <= maxid; i++) {
58 uf->data[i].size = 1;
59 uf->data[i].parent = i;
60 }
61 return uf;
62}
63
64static inline void unionfind_destroy(unionfind_t *uf)
65{
66 free(uf->data);
67 free(uf);
68}
69
70/*
71static inline uint32_t unionfind_get_representative(unionfind_t *uf, uint32_t id)
72{
73 // base case: a node is its own parent
74 if (uf->data[id].parent == id)
75 return id;
76
77 // otherwise, recurse
78 uint32_t root = unionfind_get_representative(uf, uf->data[id].parent);
79
80 // short circuit the path. [XXX This write prevents tail recursion]
81 uf->data[id].parent = root;
82
83 return root;
84}
85*/
86
87// this one seems to be every-so-slightly faster than the recursive
88// version above.
89static inline uint32_t unionfind_get_representative(unionfind_t *uf, uint32_t id)
90{
91 uint32_t root = id;
92
93 // chase down the root
94 while (uf->data[root].parent != root) {
95 root = uf->data[root].parent;
96 }
97
98 // go back and collapse the tree.
99 while (uf->data[id].parent != root) {
100 uint32_t tmp = uf->data[id].parent;
101 uf->data[id].parent = root;
102 id = tmp;
103 }
104
105 return root;
106}
107
108static inline uint32_t unionfind_get_set_size(unionfind_t *uf, uint32_t id)
109{
110 uint32_t repid = unionfind_get_representative(uf, id);
111 return uf->data[repid].size;
112}
113
114static inline uint32_t unionfind_connect(unionfind_t *uf, uint32_t aid, uint32_t bid)
115{
116 uint32_t aroot = unionfind_get_representative(uf, aid);
117 uint32_t broot = unionfind_get_representative(uf, bid);
118
119 if (aroot == broot)
120 return aroot;
121
122 // we don't perform "union by rank", but we perform a similar
123 // operation (but probably without the same asymptotic guarantee):
124 // We join trees based on the number of *elements* (as opposed to
125 // rank) contained within each tree. I.e., we use size as a proxy
126 // for rank. In my testing, it's often *faster* to use size than
127 // rank, perhaps because the rank of the tree isn't that critical
128 // if there are very few nodes in it.
129 uint32_t asize = uf->data[aroot].size;
130 uint32_t bsize = uf->data[broot].size;
131
132 // optimization idea: We could shortcut some or all of the tree
133 // that is grafted onto the other tree. Pro: those nodes were just
134 // read and so are probably in cache. Con: it might end up being
135 // wasted effort -- the tree might be grafted onto another tree in
136 // a moment!
137 if (asize > bsize) {
138 uf->data[broot].parent = aroot;
139 uf->data[aroot].size += bsize;
140 return aroot;
141 } else {
142 uf->data[aroot].parent = broot;
143 uf->data[broot].size += asize;
144 return broot;
145 }
146}