use core::cmp::Ordering;
use crate::storage::Storage;
use crate::tag_type::TagType;
use crate::UnionResult;
pub(crate) fn initialize_storage<Tag: TagType, const FILL_RANKS: bool>(
storage: &mut impl Storage<Tag>,
) {
let lower_bound = storage.lower_bound();
let upper_bound = storage.upper_bound();
let (roots, ranks) = storage.roots_and_ranks();
debug_assert_eq!(upper_bound - lower_bound, roots.len());
debug_assert_eq!(upper_bound - lower_bound, ranks.len());
debug_assert!(Tag::MAX_VAL.as_u() >= upper_bound - 1);
let mut current = Tag::from_u(lower_bound);
for val in roots.iter_mut() {
*val = current;
current = current.plus_one();
}
if FILL_RANKS {
ranks.fill(TagType::ZERO);
}
}
pub(crate) unsafe fn get_root<Tag: TagType>(storage: &mut impl Storage<Tag>, idx: usize) -> Tag {
debug_assert!(storage.lower_bound() <= idx && idx < storage.upper_bound());
let lower_bound = storage.lower_bound();
let mut curr = idx - lower_bound;
let (roots, _) = storage.roots_and_ranks();
loop {
unsafe {
let parent = *roots.get_unchecked(curr);
let pp = *roots.get_unchecked(parent.as_u() - lower_bound);
if parent == pp {
return parent;
}
*roots.get_unchecked_mut(curr) = pp;
curr = parent.as_u() - lower_bound;
}
}
}
pub(crate) unsafe fn union<Tag: TagType>(
storage: &mut impl Storage<Tag>,
idx0: usize,
idx1: usize,
) -> UnionResult {
if idx0 == idx1 {
return UnionResult::AlreadyJoined;
}
let root0 = unsafe {
get_root(storage, idx0)
};
let root1 = unsafe {
get_root(storage, idx1)
};
if root0 == root1 {
return UnionResult::AlreadyJoined;
}
let lower_bound = storage.lower_bound();
let (all_roots, all_ranks) = storage.roots_and_ranks();
let r_idx0 = root0.as_u() - lower_bound;
let r_idx1 = root1.as_u() - lower_bound;
let rank0 = unsafe {
*all_ranks.get_unchecked(r_idx0)
};
let rank1 = unsafe {
*all_ranks.get_unchecked(r_idx1)
};
unsafe {
match rank0.cmp(&rank1) {
Ordering::Less => *all_roots.get_unchecked_mut(r_idx0) = root1,
Ordering::Equal => {
*all_roots.get_unchecked_mut(r_idx1) = root0;
*all_ranks.get_unchecked_mut(r_idx0) = rank1.plus_one();
}
Ordering::Greater => *all_roots.get_unchecked_mut(r_idx1) = root0,
}
}
UnionResult::Success
}
pub(crate) unsafe fn is_united<Tag: TagType>(
storage: &mut impl Storage<Tag>,
idx0: usize,
idx1: usize,
) -> bool {
idx0 == idx1
|| unsafe {
get_root(storage, idx0) == get_root(storage, idx1)
}
}
pub(crate) unsafe fn detach<Tag: TagType>(storage: &mut impl Storage<Tag>, idx: usize) {
debug_assert!(idx >= storage.lower_bound() && idx < storage.upper_bound());
let lower_bound = storage.lower_bound();
let detach_tag = Tag::from_u(idx);
let idx = idx - lower_bound;
let (all_roots, all_ranks) = storage.roots_and_ranks();
let (prev_root, prev_rank) = unsafe {
let prev_root = *all_roots.get_unchecked(idx);
let prev_rank = *all_ranks.get_unchecked(idx);
if prev_rank == Tag::ZERO {
*all_roots.get_unchecked_mut(idx) = detach_tag;
return;
}
(prev_root, prev_rank)
};
let new_root = if prev_root == detach_tag {
let mut new_root_idx = 0;
unsafe {
while *all_roots.get_unchecked(new_root_idx) != detach_tag || new_root_idx == idx {
new_root_idx += 1;
debug_assert!(new_root_idx < all_roots.len());
}
debug_assert!(
all_ranks[new_root_idx] < prev_rank,
"Rank of the child must be smaller",
);
*all_ranks.get_unchecked_mut(new_root_idx) = prev_rank;
}
Tag::from_u(new_root_idx + lower_bound)
} else {
prev_root
};
for v in all_roots.iter_mut() {
if *v == detach_tag {
*v = new_root;
}
}
unsafe {
*all_roots.get_unchecked_mut(idx) = detach_tag;
*all_ranks.get_unchecked_mut(idx) = Tag::ZERO;
}
}
pub(crate) unsafe fn compress_all_paths<Tag: TagType>(storage: &mut impl Storage<Tag>) {
let lower_bound = storage.lower_bound();
let upper_bound = storage.upper_bound();
for idx in lower_bound..upper_bound {
let root = unsafe { get_root(storage, idx) };
let (all_roots, _) = storage.roots_and_ranks();
let root_ref = unsafe {
all_roots.get_unchecked_mut(idx - lower_bound)
};
if *root_ref != root {
*root_ref = root;
}
}
let (all_roots, all_ranks) = storage.roots_and_ranks();
for ((root_tag, &real_tag), rank) in Iterator::zip(
Iterator::zip(
(lower_bound..upper_bound).map(Tag::from_u),
all_roots.iter(),
),
all_ranks.iter_mut(),
) {
if root_tag != real_tag {
*rank = Tag::ZERO;
}
}
}
#[cfg(test)]
mod tests {
use super::{detach, get_root, initialize_storage, is_united, union};
use crate::algorithm::compress_all_paths;
use crate::storage::{BoxStorage, Storage};
use crate::UnionResult;
fn make_storage() -> impl Storage<u8> {
let mut storage = BoxStorage::new(8);
initialize_storage::<_, false>(&mut storage);
storage
}
#[test]
fn test_initialize() {
{
let mut storage = make_storage();
let (roots, ranks) = storage.roots_and_ranks();
assert_eq!(roots, &[0, 1, 2, 3, 4, 5, 6, 7]);
assert_eq!(ranks, &[0; 8]);
}
{
let mut storage = BoxStorage::new(8);
let (roots, ranks) = storage.roots_and_ranks();
roots.fill(128);
ranks.fill(50);
initialize_storage::<u8, true>(&mut storage);
let (roots, ranks) = storage.roots_and_ranks();
assert_eq!(roots, &[0, 1, 2, 3, 4, 5, 6, 7]);
assert_eq!(ranks, &[0; 8]);
}
}
#[test]
fn test_get_root() {
for key in [0, 1, 2, 3, 4, 5, 6, 7] {
let mut storage = make_storage();
let (roots, ranks) = storage.roots_and_ranks();
assert_eq!(roots.len(), 8);
assert_eq!(ranks.len(), 8);
roots.copy_from_slice(&[0, 0, 1, 2, 3, 4, 5, 6]);
unsafe {
assert_eq!(get_root(&mut storage, key), 0);
assert_eq!(get_root(&mut storage, key), 0);
}
}
for (key, value) in [
(0, 2),
(1, 2),
(2, 2),
(3, 3),
(4, 3),
(5, 3),
(6, 6),
(7, 7),
] {
let mut storage = make_storage();
let (roots, ranks) = storage.roots_and_ranks();
assert_eq!(roots.len(), 8);
assert_eq!(ranks.len(), 8);
roots.copy_from_slice(&[1, 2, 2, 3, 3, 4, 6, 7]);
unsafe {
assert_eq!(get_root(&mut storage, key), value);
assert_eq!(get_root(&mut storage, key), value);
}
}
}
#[test]
fn test_ranks() {
let mut storage = make_storage();
for i in 0..8 {
unsafe {
union(&mut storage, 0, i);
}
}
assert_eq!(storage.roots_and_ranks().1, &[1, 0, 0, 0, 0, 0, 0, 0]);
storage = make_storage();
for i in 0..4 {
unsafe {
union(&mut storage, i * 2, i * 2 + 1);
}
}
assert_eq!(storage.roots_and_ranks().1, &[1, 0, 1, 0, 1, 0, 1, 0]);
for i in (0..7).step_by(4) {
unsafe {
union(&mut storage, i, i + 2);
}
}
assert_eq!(storage.roots_and_ranks().1, &[2, 0, 1, 0, 2, 0, 1, 0]);
unsafe {
union(&mut storage, 0, 4);
}
assert_eq!(storage.roots_and_ranks().1, &[3, 0, 1, 0, 2, 0, 1, 0]);
}
#[test]
fn test_unions() {
unsafe {
let mut storage = make_storage();
for f in (0..8).step_by(2) {
let s = f + 1;
assert!(!is_united(&mut storage, f, s));
assert!(matches!(union(&mut storage, f, s), UnionResult::Success));
assert!(is_united(&mut storage, f, s));
}
for f in (0..8).step_by(2) {
let s = f + 1;
assert!(is_united(&mut storage, f, s));
assert!(matches!(
union(&mut storage, f, s),
UnionResult::AlreadyJoined
));
}
assert!(!is_united(&mut storage, 1, 2));
assert!(matches!(union(&mut storage, 0, 3), UnionResult::Success));
assert!(matches!(
union(&mut storage, 1, 2),
UnionResult::AlreadyJoined
));
}
}
#[test]
fn test_detach() {
const ALL_CONNS: &[(usize, usize)] = &[
(0, 1),
(0, 2),
(0, 3),
(1, 2),
(1, 3),
(2, 3),
(4, 5),
(4, 6),
(5, 6),
];
fn check_no_unexpected_conn(djs: &mut impl Storage<u8>, case: &str) {
for i in djs.lower_bound()..djs.upper_bound() {
for j in i + 1..djs.upper_bound() {
if ALL_CONNS.contains(&(i, j)) {
continue;
}
unsafe {
assert!(
!is_united(djs, i, j),
"Unexpected connection after {}",
case
);
}
}
}
}
let make_djs = || unsafe {
let mut djs = make_storage();
for (l, r) in [(0, 1), (2, 3), (0, 2), (4, 5), (4, 6)] {
union(&mut djs, l, r);
}
djs
};
unsafe {
let mut djs = make_djs();
detach(&mut djs, 7);
for i in (djs.lower_bound()..djs.upper_bound()).filter(|&i| i != 7) {
assert!(!is_united(&mut djs, i, 7));
}
for &(i, j) in ALL_CONNS {
assert!(is_united(&mut djs, i, j));
}
check_no_unexpected_conn(&mut djs, "already detached");
}
unsafe {
let mut djs = make_djs();
detach(&mut djs, 3);
for i in (djs.lower_bound()..djs.upper_bound()).filter(|&i| i != 3) {
assert!(!is_united(&mut djs, i, 3));
}
for &(i, j) in ALL_CONNS {
if i == 3 || j == 3 {
continue;
}
assert!(is_united(&mut djs, i, j));
}
check_no_unexpected_conn(&mut djs, "leaf");
}
unsafe {
let mut djs = make_djs();
detach(&mut djs, 2);
for i in (djs.lower_bound()..djs.upper_bound()).filter(|&i| i != 2) {
assert!(!is_united(&mut djs, i, 2));
}
for &(i, j) in ALL_CONNS {
if i == 2 || j == 2 {
continue;
}
assert!(is_united(&mut djs, i, j));
}
check_no_unexpected_conn(&mut djs, "middle");
}
unsafe {
let mut djs = make_djs();
detach(&mut djs, 0);
for i in (djs.lower_bound()..djs.upper_bound()).filter(|&i| i != 0) {
assert!(!is_united(&mut djs, i, 0));
}
for &(i, j) in ALL_CONNS {
if i == 0 || j == 0 {
continue;
}
assert!(is_united(&mut djs, i, j));
}
check_no_unexpected_conn(&mut djs, "root");
}
}
#[test]
fn test_compress_paths() {
let mut djs = make_storage();
unsafe {
union(&mut djs, 0, 2);
union(&mut djs, 3, 4);
union(&mut djs, 0, 3);
union(&mut djs, 1, 5);
union(&mut djs, 1, 6);
}
assert_ne!(djs.roots_and_ranks().0, &[0, 1, 0, 0, 0, 1, 1, 7]);
assert_ne!(djs.roots_and_ranks().1, &[2, 1, 0, 0, 0, 0, 0, 0]);
unsafe {
compress_all_paths(&mut djs);
}
assert_eq!(djs.roots_and_ranks().0, &[0, 1, 0, 0, 0, 1, 1, 7]);
assert_eq!(djs.roots_and_ranks().1, &[2, 1, 0, 0, 0, 0, 0, 0]);
}
}
#[cfg(test)]
mod splitted_storage_tests {
use super::{detach, get_root, initialize_storage, is_united, union};
use crate::algorithm::compress_all_paths;
use crate::storage::{BoxStorage, Storage};
use crate::UnionResult;
#[test]
fn test_initialize() {
unsafe {
let mut storage: BoxStorage<u8> = BoxStorage::new(8);
let (mut left, mut right) = storage.split_at(5);
initialize_storage::<u8, true>(&mut left);
initialize_storage::<u8, true>(&mut right);
let (roots, ranks) = storage.roots_and_ranks();
assert_eq!(roots, &[0, 1, 2, 3, 4, 5, 6, 7]);
assert_eq!(ranks, &[0; 8]);
}
unsafe {
let mut storage = BoxStorage::new(8);
let (roots, ranks) = storage.roots_and_ranks();
roots.fill(128);
ranks.fill(50);
initialize_storage::<u8, true>(&mut storage.split_at(4).0);
let (roots, ranks) = storage.roots_and_ranks();
assert_eq!(roots, &[0, 1, 2, 3, 128, 128, 128, 128]);
assert_eq!(ranks, &[0, 0, 0, 0, 50, 50, 50, 50]);
initialize_storage::<u8, false>(&mut storage.split_at(4).1);
let (roots, ranks) = storage.roots_and_ranks();
assert_eq!(roots, &[0, 1, 2, 3, 4, 5, 6, 7]);
assert_eq!(ranks, &[0, 0, 0, 0, 50, 50, 50, 50]);
}
}
fn make_storage() -> impl Storage<u8> {
let mut storage = BoxStorage::new(8);
initialize_storage::<_, false>(&mut storage);
storage
}
#[test]
fn test_get_root() {
for key in [4, 5, 6, 7] {
let mut storage = make_storage();
let (roots, ranks) = storage.roots_and_ranks();
assert_eq!(roots.len(), 8);
assert_eq!(ranks.len(), 8);
roots.copy_from_slice(&[0, 1, 2, 3, 4, 4, 5, 6]);
let (_, mut part) = unsafe { storage.split_at(4) };
unsafe {
assert_eq!(get_root(&mut part, key), 4);
assert_eq!(get_root(&mut part, key), 4);
}
}
for (key, value) in [(4, 5), (5, 5), (6, 5), (7, 7)] {
let mut storage = make_storage();
let (roots, ranks) = storage.roots_and_ranks();
assert_eq!(roots.len(), 8);
assert_eq!(ranks.len(), 8);
roots.copy_from_slice(&[0, 1, 2, 3, 5, 5, 4, 7]);
let (_, mut part) = unsafe { storage.split_at(4) };
unsafe {
assert_eq!(get_root(&mut part, key), value);
assert_eq!(get_root(&mut part, key), value);
}
}
}
#[test]
fn test_ranks() {
let mut storage = make_storage();
let mut part = unsafe { storage.split_at(4).1 };
for i in 4..8 {
unsafe {
union(&mut part, 4, i);
}
}
assert_eq!(storage.roots_and_ranks().1, &[0, 0, 0, 0, 1, 0, 0, 0]);
storage = make_storage();
let mut part = unsafe { storage.split_at(4).1 };
for i in (part.lower_bound()..part.upper_bound()).step_by(2) {
unsafe {
union(&mut part, i, i + 1);
}
}
assert_eq!(part.roots_and_ranks().1, &[1, 0, 1, 0]);
unsafe {
union(&mut part, 5, 7);
}
assert_eq!(part.roots_and_ranks().1, &[2, 0, 1, 0]);
assert_eq!(storage.roots_and_ranks().1, &[0, 0, 0, 0, 2, 0, 1, 0]);
}
#[test]
fn test_unions() {
unsafe {
let mut st = make_storage();
let (_, mut right) = st.split_at(2);
for f in (right.lower_bound()..right.upper_bound()).step_by(2) {
let s = f + 1;
assert!(!is_united(&mut right, f, s));
assert!(matches!(union(&mut right, f, s), UnionResult::Success));
assert!(is_united(&mut right, f, s));
}
for f in (2..8).step_by(2) {
let s = f + 1;
assert!(is_united(&mut right, f, s));
assert!(matches!(
union(&mut right, f, s),
UnionResult::AlreadyJoined
));
}
assert!(!is_united(&mut right, 3, 4));
assert!(matches!(union(&mut right, 2, 5), UnionResult::Success));
assert!(matches!(
union(&mut right, 3, 4),
UnionResult::AlreadyJoined
));
}
}
#[test]
fn test_detach() {
const ALL_CONNS: &[(usize, usize)] = &[(1, 2), (1, 3), (2, 3), (4, 5), (4, 6), (5, 6)];
fn check_no_unexpected_conn(djs: &mut impl Storage<u8>, case: &str) {
for i in djs.lower_bound()..djs.upper_bound() {
for j in i + 1..djs.upper_bound() {
if ALL_CONNS.contains(&(i, j)) {
continue;
}
unsafe {
assert!(
!is_united(djs, i, j),
"Unexpected connection after {}",
case
);
}
}
}
}
let make_djs = || unsafe {
let mut djs = make_storage();
let mut right = djs.split_at(1).1;
for (l, r) in [(1, 2), (2, 3), (4, 5), (4, 6)] {
union(&mut right, l, r);
}
djs
};
unsafe {
let mut djs = make_djs();
let mut right = djs.split_at(1).1;
detach(&mut right, 7);
for i in (djs.lower_bound()..djs.upper_bound()).filter(|&i| i != 7) {
assert!(!is_united(&mut djs, i, 7));
}
for &(i, j) in ALL_CONNS {
assert!(is_united(&mut djs, i, j));
}
check_no_unexpected_conn(&mut djs, "already detached");
}
unsafe {
let mut djs = make_djs();
let mut right = djs.split_at(1).1;
detach(&mut right, 3);
for i in (djs.lower_bound()..djs.upper_bound()).filter(|&i| i != 3) {
assert!(!is_united(&mut djs, i, 3));
}
for &(i, j) in ALL_CONNS {
if i == 3 || j == 3 {
continue;
}
assert!(is_united(&mut djs, i, j));
}
check_no_unexpected_conn(&mut djs, "leaf");
}
unsafe {
let mut djs = make_djs();
let mut right = djs.split_at(1).1;
detach(&mut right, 2);
for i in (djs.lower_bound()..djs.upper_bound()).filter(|&i| i != 2) {
assert!(!is_united(&mut djs, i, 2));
}
for &(i, j) in ALL_CONNS {
if i == 2 || j == 2 {
continue;
}
assert!(is_united(&mut djs, i, j));
}
check_no_unexpected_conn(&mut djs, "middle");
}
unsafe {
let mut djs = make_djs();
let mut right = djs.split_at(1).1;
detach(&mut right, 1);
for i in (djs.lower_bound()..djs.upper_bound()).filter(|&i| i != 0) {
assert!(!is_united(&mut djs, i, 0));
}
for &(i, j) in ALL_CONNS {
if i == 1 || j == 1 {
continue;
}
assert!(is_united(&mut djs, i, j));
}
check_no_unexpected_conn(&mut djs, "root");
}
}
#[test]
fn test_compress_paths() {
let mut storage = BoxStorage::new(9);
initialize_storage::<u8, false>(&mut storage);
let mut right = unsafe { storage.split_at(1).1 };
unsafe {
union(&mut right, 1, 3);
union(&mut right, 4, 5);
union(&mut right, 1, 4);
union(&mut right, 2, 6);
union(&mut right, 2, 7);
}
assert_ne!(right.roots_and_ranks().0, &[1, 2, 1, 1, 1, 2, 2, 8]);
assert_ne!(right.roots_and_ranks().1, &[2, 1, 0, 0, 0, 0, 0, 0]);
unsafe {
compress_all_paths(&mut right);
}
assert_eq!(right.roots_and_ranks().0, &[1, 2, 1, 1, 1, 2, 2, 8]);
assert_eq!(right.roots_and_ranks().1, &[2, 1, 0, 0, 0, 0, 0, 0]);
assert_eq!(storage.roots_and_ranks().0, &[0, 1, 2, 1, 1, 1, 2, 2, 8]);
assert_eq!(storage.roots_and_ranks().1, &[0, 2, 1, 0, 0, 0, 0, 0, 0]);
}
}