use std::{cmp, sync::Arc};
use super::{atomic_int::AtomicInt, buffer::Buffer};
pub(crate) struct ConcurrentUnionFind<T> {
data: Arc<Buffer<T>>,
}
impl<T> Clone for ConcurrentUnionFind<T> {
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
}
}
}
impl<T: AtomicInt> Default for ConcurrentUnionFind<T> {
fn default() -> Self {
Self::with_capacity(32)
}
}
impl<T: AtomicInt> ConcurrentUnionFind<T> {
pub fn deep_copy(&self) -> Self {
ConcurrentUnionFind {
data: Arc::new(Buffer::clone(&*self.data)),
}
}
}
impl<T: AtomicInt> ConcurrentUnionFind<T> {
pub fn reset(&self) {
self.data.re_init(T::from_usize);
}
pub fn with_capacity(capacity: usize) -> ConcurrentUnionFind<T> {
let data = Arc::new(Buffer::new(capacity, T::from_usize));
ConcurrentUnionFind { data }
}
pub fn find(&self, elt: T::Underlying) -> T::Underlying {
self.data.with_access(
T::as_usize(elt) + 1,
|buf| Self::find_impl(buf, elt),
T::from_usize,
)
}
pub fn same_set(&self, l: T::Underlying, r: T::Underlying) -> bool {
let max_elt = cmp::max(l, r);
self.data.with_access(
T::as_usize(max_elt) + 1,
|buf| {
let mut l = Self::find_impl(buf, l);
let mut r = Self::find_impl(buf, r);
while l != r {
let next = buf[T::as_usize(l)].load();
if next == l {
return false;
}
l = Self::find_impl(buf, l);
r = Self::find_impl(buf, r);
}
true
},
T::from_usize,
)
}
pub fn merge(
&self,
l: T::Underlying,
r: T::Underlying,
) -> (
T::Underlying, /* parent */
T::Underlying, /* child */
) {
self.data.with_access(
T::as_usize(cmp::max(l, r)) + 1,
|buf| {
let mut l = l;
let mut r = r;
loop {
l = Self::find_impl(buf, l);
r = Self::find_impl(buf, r);
if l != r {
let parent = cmp::min(l, r);
let child = cmp::max(l, r);
match buf[T::as_usize(child)].cas(child, parent) {
Ok(_) => return (parent, child),
Err(_) => continue,
}
} else {
return (l, l);
}
}
},
T::from_usize,
)
}
fn find_impl(buf: &[T], elt: T::Underlying) -> T::Underlying {
macro_rules! load {
($x:expr) => {
buf[T::as_usize($x)].load()
};
}
let mut cur = elt;
let mut next = load!(cur);
let mut grand = load!(next);
while next != grand {
let _ = buf[T::as_usize(cur)].cas(next, grand);
cur = next;
next = load!(cur);
grand = load!(next);
}
next
}
}