Path: blob/master/thirdparty/manifold/src/disjoint_sets.h
9903 views
// from https://github.com/wjakob/dset, changed to add connected component1// computation2//3// Copyright (c) 2015 Wenzel Jakob <[email protected]>4//5// This software is provided 'as-is', without any express or implied6// warranty. In no event will the authors be held liable for any damages7// arising from the use of this software.8//9// Permission is granted to anyone to use this software for any purpose,10// including commercial applications, and to alter it and redistribute it11// freely, subject to the following restrictions:12//13// 1. The origin of this software must not be misrepresented; you must not14// claim that you wrote the original software. If you use this software15// in a product, an acknowledgment in the product documentation would be16// appreciated but is not required.17// 2. Altered source versions must be plainly marked as such, and must not be18// misrepresented as being the original software.19// 3. This notice may not be removed or altered from any source distribution.20//21#pragma once22#include <atomic>23#include <cstddef>24#include <cstdint>25#include <unordered_map>26#include <vector>2728class DisjointSets {29public:30DisjointSets(uint32_t size) : mData(size) {31for (uint32_t i = 0; i < size; ++i) mData[i] = (uint32_t)i;32}3334uint32_t find(uint32_t id) const {35while (id != parent(id)) {36uint64_t value = mData[id];37uint32_t new_parent = parent((uint32_t)value);38uint64_t new_value = (value & 0xFFFFFFFF00000000ULL) | new_parent;39/* Try to update parent (may fail, that's ok) */40if (value != new_value) mData[id].compare_exchange_weak(value, new_value);41id = new_parent;42}43return id;44}4546bool same(uint32_t id1, uint32_t id2) const {47for (;;) {48id1 = find(id1);49id2 = find(id2);50if (id1 == id2) return true;51if (parent(id1) == id1) return false;52}53}5455uint32_t unite(uint32_t id1, uint32_t id2) {56for (;;) {57id1 = find(id1);58id2 = find(id2);5960if (id1 == id2) return id1;6162uint32_t r1 = rank(id1), r2 = rank(id2);6364if (r1 > r2 || (r1 == r2 && id1 < id2)) {65std::swap(r1, r2);66std::swap(id1, id2);67}6869uint64_t oldEntry = ((uint64_t)r1 << 32) | id1;70uint64_t newEntry = ((uint64_t)r1 << 32) | id2;7172if (!mData[id1].compare_exchange_strong(oldEntry, newEntry)) continue;7374if (r1 == r2) {75oldEntry = ((uint64_t)r2 << 32) | id2;76newEntry = ((uint64_t)(r2 + 1) << 32) | id2;77/* Try to update the rank (may fail, retry if rank = 0) */78if (!mData[id2].compare_exchange_strong(oldEntry, newEntry) && r2 == 0)79continue;80}8182break;83}84return id2;85}8687uint32_t size() const { return (uint32_t)mData.size(); }8889uint32_t rank(uint32_t id) const {90return ((uint32_t)(mData[id] >> 32)) & 0x7FFFFFFFu;91}9293uint32_t parent(uint32_t id) const { return (uint32_t)mData[id]; }9495int connectedComponents(std::vector<int>& components) {96components.resize(mData.size());97int lonelyNodes = 0;98std::unordered_map<uint32_t, int> toLabel;99for (size_t i = 0; i < mData.size(); ++i) {100// we optimize for connected component of size 1101// no need to put them into the hashmap102auto iParent = find(i);103if (rank(iParent) == 0) {104components[i] = static_cast<int>(toLabel.size()) + lonelyNodes++;105continue;106}107auto iter = toLabel.find(iParent);108if (iter == toLabel.end()) {109auto s = static_cast<uint32_t>(toLabel.size()) + lonelyNodes;110toLabel.insert(std::make_pair(iParent, s));111components[i] = s;112} else {113components[i] = iter->second;114}115}116return toLabel.size() + lonelyNodes;117}118119mutable std::vector<std::atomic<uint64_t>> mData;120};121122123