use std::cell::Cell;
use std::cmp::Ordering::{self, *};
use std::collections::{BTreeMap, HashMap};
use std::fmt::Debug;
use crate::cc_traits::{Keyed, Map, MapIter, MapMut};
use crate::collections::{ArrayMap, OptionMap, SingletonMap, VecMap};
use crate::{Atomize, DeepReveal, IsBot, IsTop, LatticeFrom, LatticeOrd, Max, Merge, Min};
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct UnionFind<Map>(Map);
impl<Map> UnionFind<Map> {
pub fn new(val: Map) -> Self {
Self(val)
}
pub fn new_from(val: impl Into<Map>) -> Self {
Self::new(val.into())
}
pub fn as_reveal_ref(&self) -> &Map {
&self.0
}
pub fn as_reveal_mut(&mut self) -> &mut Map {
&mut self.0
}
pub fn into_reveal(self) -> Map {
self.0
}
}
impl<Map> DeepReveal for UnionFind<Map> {
type Revealed = Map;
fn deep_reveal(self) -> Self::Revealed {
self.0
}
}
impl<Map, K> UnionFind<Map>
where
Map: MapMut<K, Cell<K>, Key = K, Item = Cell<K>>,
K: Copy + Eq,
{
pub fn union(&mut self, a: K, b: K) -> Min<bool> {
let a_root = self.find(a);
let b_root = self.find(b);
if a_root == b_root {
Min::new(false)
} else {
self.0.insert(b_root, Cell::new(a_root));
Min::new(true)
}
}
}
impl<MapSelf, K> UnionFind<MapSelf>
where
MapSelf: Map<K, Cell<K>, Key = K, Item = Cell<K>>,
K: Copy + Eq,
{
pub fn same(&self, a: K, b: K) -> Max<bool> {
Max::new(a == b || self.find(a) == self.find(b))
}
fn find(&self, mut item: K) -> K {
let mut root = item;
while let Some(parent) = self.0.get(&root) {
if parent.get() == root {
break;
}
if parent.get() == item {
parent.set(root);
break;
}
root = parent.get();
}
while item != root {
item = self.0.get(&item).unwrap().replace(root);
}
item
}
}
impl<MapSelf, MapOther, K> Merge<UnionFind<MapOther>> for UnionFind<MapSelf>
where
MapSelf: MapMut<K, Cell<K>, Key = K, Item = Cell<K>>,
MapOther: IntoIterator<Item = (K, Cell<K>)>,
K: Copy + Eq,
{
fn merge(&mut self, other: UnionFind<MapOther>) -> bool {
let mut changed = false;
for (item, parent) in other.0 {
changed |= self.union(item, parent.get()).into_reveal();
}
changed
}
}
impl<MapSelf, MapOther, K> LatticeFrom<UnionFind<MapOther>> for UnionFind<MapSelf>
where
MapSelf: Keyed<Key = K, Item = Cell<K>> + FromIterator<(K, Cell<K>)>,
MapOther: IntoIterator<Item = (K, Cell<K>)>,
K: Copy + Eq,
{
fn lattice_from(other: UnionFind<MapOther>) -> Self {
Self(other.0.into_iter().collect())
}
}
impl<MapSelf, MapOther, K> PartialOrd<UnionFind<MapOther>> for UnionFind<MapSelf>
where
MapSelf: MapMut<K, Cell<K>, Key = K, Item = Cell<K>> + MapIter,
MapOther: MapMut<K, Cell<K>, Key = K, Item = Cell<K>> + MapIter,
K: Copy + Eq,
{
fn partial_cmp(&self, other: &UnionFind<MapOther>) -> Option<Ordering> {
let self_any_greater = self
.0
.iter()
.any(|(item, parent)| !other.same(*item, parent.get()).into_reveal());
let other_any_greater = other
.0
.iter()
.any(|(item, parent)| !self.same(*item, parent.get()).into_reveal());
match (self_any_greater, other_any_greater) {
(true, true) => None,
(true, false) => Some(Greater),
(false, true) => Some(Less),
(false, false) => Some(Equal),
}
}
}
impl<MapSelf, MapOther> LatticeOrd<UnionFind<MapOther>> for UnionFind<MapSelf> where
Self: PartialOrd<UnionFind<MapOther>>
{
}
impl<MapSelf, MapOther, K> PartialEq<UnionFind<MapOther>> for UnionFind<MapSelf>
where
MapSelf: MapMut<K, Cell<K>, Key = K, Item = Cell<K>> + MapIter,
MapOther: MapMut<K, Cell<K>, Key = K, Item = Cell<K>> + MapIter,
K: Copy + Eq,
{
fn eq(&self, other: &UnionFind<MapOther>) -> bool {
!(self
.0
.iter()
.any(|(item, parent)| !other.same(*item, parent.get()).into_reveal())
|| other
.0
.iter()
.any(|(item, parent)| !self.same(*item, parent.get()).into_reveal()))
}
}
impl<Map> Eq for UnionFind<Map> where Self: PartialEq {}
impl<Map, K> IsBot for UnionFind<Map>
where
Map: MapIter<Key = K, Item = Cell<K>>,
K: Copy + Eq,
{
fn is_bot(&self) -> bool {
self.0.iter().all(|(a, b)| *a == b.get())
}
}
impl<Map> IsTop for UnionFind<Map> {
fn is_top(&self) -> bool {
false
}
}
impl<Map, K> Atomize for UnionFind<Map>
where
Map: 'static + MapMut<K, Cell<K>, Key = K, Item = Cell<K>> + IntoIterator<Item = (K, Cell<K>)>,
K: 'static + Copy + Eq,
{
type Atom = UnionFindSingletonMap<K>;
type AtomIter = Box<dyn Iterator<Item = Self::Atom>>;
fn atomize(self) -> Self::AtomIter {
Box::new(
self.0
.into_iter()
.filter(|(a, b)| *a != b.get())
.map(UnionFindSingletonMap::new_from),
)
}
}
pub type UnionFindHashMap<K> = UnionFind<HashMap<K, Cell<K>>>;
pub type UnionFindBTreeMap<K> = UnionFind<BTreeMap<K, Cell<K>>>;
pub type UnionFindVec<K> = UnionFind<VecMap<K, Cell<K>>>;
pub type UnionFindArrayMap<K, const N: usize> = UnionFind<ArrayMap<K, Cell<K>, N>>;
pub type UnionFindSingletonMap<K> = UnionFind<SingletonMap<K, Cell<K>>>;
pub type UnionFindOptionMap<K> = UnionFind<OptionMap<K, Cell<K>>>;
#[cfg(test)]
mod test {
use super::*;
use crate::test::{check_all, check_atomize_each};
#[test]
fn test_basic() {
let mut my_uf_a = <UnionFindHashMap<char>>::default();
let my_uf_b = <UnionFindSingletonMap<char>>::new(SingletonMap('c', Cell::new('a')));
let my_uf_c = UnionFindSingletonMap::new_from(('c', Cell::new('b')));
assert!(!my_uf_a.same('c', 'a').into_reveal());
assert!(!my_uf_a.same('c', 'b').into_reveal());
assert!(!my_uf_a.same('a', 'b').into_reveal());
assert!(!my_uf_a.same('a', 'z').into_reveal());
assert_eq!('z', my_uf_a.find('z'));
my_uf_a.merge(my_uf_b);
assert!(my_uf_a.same('c', 'a').into_reveal());
assert!(!my_uf_a.same('c', 'b').into_reveal());
assert!(!my_uf_a.same('a', 'b').into_reveal());
assert!(!my_uf_a.same('a', 'z').into_reveal());
assert_eq!('z', my_uf_a.find('z'));
my_uf_a.merge(my_uf_c);
assert!(my_uf_a.same('c', 'a').into_reveal());
assert!(my_uf_a.same('c', 'b').into_reveal());
assert!(my_uf_a.same('a', 'b').into_reveal());
assert!(!my_uf_a.same('a', 'z').into_reveal());
assert_eq!('z', my_uf_a.find('z'));
}
#[test]
fn test_malformed() {
{
let my_uf = <UnionFindBTreeMap<char>>::new_from([
('a', Cell::new('b')),
('b', Cell::new('c')),
('c', Cell::new('a')),
]);
println!("{:?}", my_uf);
assert!(my_uf.same('a', 'b').into_reveal());
println!("{:?}", my_uf);
}
{
let my_uf = <UnionFindBTreeMap<char>>::new_from([
('a', Cell::new('b')),
('b', Cell::new('c')),
('c', Cell::new('d')),
('d', Cell::new('a')),
]);
println!("{:?}", my_uf);
assert!(my_uf.same('a', 'b').into_reveal());
println!("{:?}", my_uf);
}
}
#[test]
fn consistency_atomize() {
let items = &[
<UnionFindHashMap<char>>::default(),
<UnionFindHashMap<_>>::new_from([('a', Cell::new('a'))]),
<UnionFindHashMap<_>>::new_from([('a', Cell::new('a')), ('b', Cell::new('a'))]),
<UnionFindHashMap<_>>::new_from([('b', Cell::new('a'))]),
<UnionFindHashMap<_>>::new_from([('b', Cell::new('a')), ('c', Cell::new('b'))]),
<UnionFindHashMap<_>>::new_from([('b', Cell::new('a')), ('c', Cell::new('b'))]),
<UnionFindHashMap<_>>::new_from([('d', Cell::new('b'))]),
<UnionFindHashMap<_>>::new_from([
('b', Cell::new('a')),
('c', Cell::new('b')),
('d', Cell::new('a')),
]),
<UnionFindHashMap<_>>::new_from([
('b', Cell::new('a')),
('c', Cell::new('b')),
('d', Cell::new('d')),
]),
];
check_all(items);
check_atomize_each(items);
}
}