KiCad PCB EDA Suite
Loading...
Searching...
No Matches
union_find.h
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#ifndef KICAD_CORE_UNION_FIND_H
21#define KICAD_CORE_UNION_FIND_H
22
23#include <atomic>
24#include <cstddef>
25#include <cstdint>
26#include <limits>
27#include <stdexcept>
28#include <utility>
29#include <vector>
30
48{
49public:
50 explicit KI_UNION_FIND( size_t aCount ) { Reset( aCount ); }
51
52 KI_UNION_FIND( const KI_UNION_FIND& ) = delete;
54
56 void Reset( size_t aCount )
57 {
58 // Parents are uint32_t to keep the array dense, so a larger set would alias index
59 // 2^32 onto index zero
60 if( aCount > static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) )
61 throw std::length_error( "KI_UNION_FIND exceeds its 2^32 element limit" );
62
63 std::vector<std::atomic<uint32_t>> fresh( aCount );
64 m_parent.swap( fresh );
65
66 for( size_t ii = 0; ii < aCount; ++ii )
67 m_parent[ii].store( static_cast<uint32_t>( ii ), std::memory_order_relaxed );
68
69 m_components.store( aCount, std::memory_order_relaxed );
70 }
71
72 size_t Size() const { return m_parent.size(); }
73
75 size_t ComponentCount() const { return m_components.load( std::memory_order_relaxed ); }
76
82 bool Unite( size_t aA, size_t aB )
83 {
84 uint32_t u = static_cast<uint32_t>( aA );
85 uint32_t v = static_cast<uint32_t>( aB );
86 uint32_t pu = m_parent[u].load( std::memory_order_relaxed );
87 uint32_t pv = m_parent[v].load( std::memory_order_relaxed );
88
89 while( pu != pv )
90 {
91 // Work on the side with the larger parent so that a root always links downwards
92 if( pu < pv )
93 {
94 std::swap( u, v );
95 std::swap( pu, pv );
96 }
97
98 if( u == pu )
99 {
100 // u is a root and pv is below it, so this link is legal
101 if( m_parent[u].compare_exchange_strong( pu, pv, std::memory_order_acq_rel,
102 std::memory_order_relaxed ) )
103 {
104 m_components.fetch_sub( 1, std::memory_order_relaxed );
105 return true;
106 }
107
108 // The exchange reloaded pu with the value that won, so try again
109 }
110 else
111 {
112 // Lift u one level and climb. A lost exchange costs only the shortcut, so
113 // the result is ignored
114 uint32_t grandparent = m_parent[pu].load( std::memory_order_relaxed );
115 uint32_t expected = pu;
116
117 m_parent[u].compare_exchange_strong( expected, grandparent,
118 std::memory_order_acq_rel,
119 std::memory_order_relaxed );
120
121 u = pu;
122 pu = m_parent[u].load( std::memory_order_relaxed );
123 }
124 }
125
126 return false;
127 }
128
130 size_t Find( size_t aX ) const
131 {
132 uint32_t x = static_cast<uint32_t>( aX );
133 uint32_t p = m_parent[x].load( std::memory_order_relaxed );
134
135 while( p != x )
136 {
137 x = p;
138 p = m_parent[x].load( std::memory_order_relaxed );
139 }
140
141 return x;
142 }
143
150 size_t FindCompress( size_t aX )
151 {
152 uint32_t x = static_cast<uint32_t>( aX );
153 uint32_t p = m_parent[x].load( std::memory_order_relaxed );
154
155 while( p != x )
156 {
157 uint32_t grandparent = m_parent[p].load( std::memory_order_relaxed );
158
159 m_parent[x].store( grandparent, std::memory_order_relaxed );
160 x = p;
161 p = grandparent;
162 }
163
164 return x;
165 }
166
167 bool Connected( size_t aA, size_t aB ) const { return Find( aA ) == Find( aB ); }
168
169private:
170 std::vector<std::atomic<uint32_t>> m_parent;
171 std::atomic<size_t> m_components{ 0 };
172};
173
174#endif // KICAD_CORE_UNION_FIND_H
size_t FindCompress(size_t aX)
Shorten the path from aX to its root so that later queries walk less of it.
Definition union_find.h:150
size_t ComponentCount() const
Definition union_find.h:75
bool Connected(size_t aA, size_t aB) const
Definition union_find.h:167
KI_UNION_FIND(size_t aCount)
Definition union_find.h:50
KI_UNION_FIND & operator=(const KI_UNION_FIND &)=delete
Discard all unions and resize the set to aCount single-element components.
size_t Size() const
Definition union_find.h:72
void Reset(size_t aCount)
Definition union_find.h:56
std::vector< std::atomic< uint32_t > > m_parent
Definition union_find.h:170
bool Unite(size_t aA, size_t aB)
Merge the components that hold aA and aB.
Definition union_find.h:82
size_t Find(size_t aX) const
Definition union_find.h:130
std::atomic< size_t > m_components
Definition union_find.h:171
KI_UNION_FIND(const KI_UNION_FIND &)=delete
void Reset() override
VECTOR3I expected(15, 30, 45)