geop_core_math/disjoint_set/
mod.rs1use crate::{scalars::Scalar, vector::Vector};
13
14pub trait Mergeable: Copy {
18 fn could_be_equal(&self, other: &Self) -> bool;
19 fn union(&self, other: &Self) -> Self;
20}
21
22impl Mergeable for Scalar {
23 fn could_be_equal(&self, other: &Self) -> bool {
24 Scalar::could_be_equal(*self, *other)
25 }
26 fn union(&self, other: &Self) -> Self {
27 Scalar::union(*self, *other)
28 }
29}
30
31impl<const N: usize> Mergeable for Vector<N> {
32 fn could_be_equal(&self, other: &Self) -> bool {
33 Vector::could_be_equal(self, other)
34 }
35 fn union(&self, other: &Self) -> Self {
36 Vector::union(self, other)
37 }
38}
39
40impl<A: Mergeable, B: Mergeable> Mergeable for (A, B) {
41 fn could_be_equal(&self, other: &Self) -> bool {
42 self.0.could_be_equal(&other.0) && self.1.could_be_equal(&other.1)
43 }
44 fn union(&self, other: &Self) -> Self {
45 (self.0.union(&other.0), self.1.union(&other.1))
46 }
47}
48
49#[derive(Clone, Debug)]
52pub struct DisjointSet<T: Mergeable> {
53 items: Vec<T>,
54}
55
56impl<T: Mergeable> DisjointSet<T> {
57 pub fn new() -> Self {
58 Self { items: Vec::new() }
59 }
60
61 pub fn len(&self) -> usize {
63 self.items.len()
64 }
65
66 pub fn is_empty(&self) -> bool {
67 self.items.is_empty()
68 }
69
70 pub fn iter(&self) -> impl Iterator<Item = &T> {
71 self.items.iter()
72 }
73
74 pub fn insert(&mut self, mut candidate: T) {
84 let mut at: Option<usize> = None;
85 while let Some(i) = self
86 .items
87 .iter()
88 .position(|item| item.could_be_equal(&candidate))
89 {
90 candidate = candidate.union(&self.items.remove(i));
91 at = Some(at.map_or(i, |a| a.min(i)));
92 }
93 match at {
94 Some(i) => self.items.insert(i, candidate),
95 None => self.items.push(candidate),
96 }
97 }
98
99 pub fn into_vec(self) -> Vec<T> {
100 self.items
101 }
102}
103
104impl<T: Mergeable> Default for DisjointSet<T> {
105 fn default() -> Self {
106 Self::new()
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::DisjointSet;
113 use crate::scalars::Scalar;
114
115 #[test]
116 fn disjoint_scalars_stay_separate() {
117 let mut set = DisjointSet::new();
118 set.insert(Scalar::from_f64(0.1));
119 set.insert(Scalar::from_f64(0.9));
120 assert_eq!(set.len(), 2);
121 }
122
123 #[test]
124 fn overlapping_scalars_merge() {
125 let mut set = DisjointSet::new();
126 set.insert(Scalar::from_f64(0.5));
127 set.insert(Scalar::from_f64(0.5));
128 assert_eq!(set.len(), 1);
129 }
130
131 #[test]
135 fn bridging_candidate_joins_two_solutions() {
136 let mut set = DisjointSet::new();
137 set.insert(Scalar::from_f64(0.0));
138 set.insert(Scalar::from_f64(1.0));
139 assert_eq!(set.len(), 2);
140
141 let bridge = Scalar::from_f64(-0.5).union(Scalar::from_f64(1.5));
144 set.insert(bridge);
145 assert_eq!(
146 set.len(),
147 1,
148 "a candidate overlapping both existing entries should merge them into one"
149 );
150 }
151}