Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
63a45e6
Add first draft for potential MutableUnionFind interface
May 25, 2026
137ad6e
Rename MutableUnionFind interface to simply UnionFind
Jun 7, 2026
a6820bf
Update methods specified in interface UnionFind
Jun 7, 2026
42aa243
Merge branch 'main' into 49-add-union-find-data-structure
Jun 7, 2026
cb5d31d
Add class MutableUnionFind including empty method bodies and construc…
Jun 7, 2026
27a7728
Implement getAllSubsets in MutableUnionFind
Jun 7, 2026
7d0afc7
Implement addElementToNewSet in MutableUnionFind and capture current …
Jun 7, 2026
33e3326
Rename MutableUnionFind to UnsortedUnionFind and alter uses of HashSe…
Jun 7, 2026
b301753
Implement find as HashMap version in UnsortedUnionFind; contains type…
Jun 8, 2026
4c52fc0
Declare type for whole classes and add rough implementation of union …
Jun 8, 2026
5baa93d
Continue implementing several methods in UnsortedUnionFind and add ne…
Jun 9, 2026
24e376b
Add addNewSet(Set<T> input) to UnsortedUnionFind; might end up replac…
Jun 9, 2026
00d285d
Add notes for further work to UnsortedUnionFind
Jun 9, 2026
6e04f27
Add new class SortedUnionFind; still missing all method bodies
Jun 9, 2026
a7959db
Change all uses of put() to putIfAbsent() in UnsortedUnionFind to ens…
Jun 9, 2026
831ec68
Remove superfluous methods from interface UnionFind
Jun 11, 2026
cb0ca0e
Implement large portion of methods in SortedUnionFind
Jun 11, 2026
b2902e2
Rename SortedUnionFind to SortedTreeSetUnionFind
Jun 11, 2026
fdcd004
Add new interface SortedUnionFind
Jun 11, 2026
eaadc94
Add contains() to UnionFind
Jun 11, 2026
b12540f
Change SortedTreeSetUnionFind to implement SortedUnionFind instead of…
Jun 11, 2026
d9b5dc3
Adapt UnsortedUnionFind to interface alterations
Jun 11, 2026
41ae904
Delete UnsortedUnionFind as currently not planning on using it
Jun 11, 2026
188b203
Implement find() in SortedTreeSetUnionFind and refactor mergeExisting…
Jun 11, 2026
88f484d
Revert back to direct constructor call instead of getEmptyInstanceOf(…
Jun 12, 2026
b432119
Make SortedTreeSetUnionFind's constructor public
Jun 12, 2026
94f1d26
Fix compilation errors due to forbidden types etc. in SortedTreeSetUn…
Jun 17, 2026
97b7bd3
Add SortedUnionFindTest with setup and first few tests; needs further…
Jun 17, 2026
2bafeee
Add test to assure union keeps correct canonical element after union …
Jun 17, 2026
3e1f07a
Make test name more specific in SortedUnionFindTest
Jun 17, 2026
9504ebb
Add test that covers mergeExistingSets() in SortedUnionFindTest
Jun 17, 2026
15e7096
A couple small style fixes
Jun 17, 2026
f4c5213
Change variable declarations from classes to interfaces where needed …
Jun 18, 2026
0ee0893
Change variable declarations from classes to interfaces where needed …
Jun 18, 2026
71b0f16
Add missing edge case in union() in SortedTreeSetUnionFind
Jun 18, 2026
d07c875
Fix bug in addToExistingSet() in SortedTreeSetUnionFind
Jun 19, 2026
0a8f5ec
Add testUnion_InsertingDuplicateElementFails() in SortedUnionFindTest
Jun 19, 2026
469685f
Remove testUnion_InsertingDuplicateElementFails() in SortedUnionFindT…
Jun 19, 2026
3e42ae7
Add testUnion_ConstantCanonicalElementDuringNonlinearInsertion() in S…
Jun 19, 2026
f765a9c
Switch usage of Set to Map to facilitate tracking each subset's canon…
Jun 19, 2026
6221788
Implement changes to interface (exchange Set for Map) in SortedTreeSe…
Jun 19, 2026
7a17558
Remove resolved TODO notes in SortedTreeSetUnionFind
Jun 19, 2026
a3ce540
Add documentation to UnionFind
Jun 23, 2026
493a220
Add documentation to SortedUnionFind
Jun 23, 2026
7370d47
Add documentation to SortedTreeSetUnionFind
Jun 23, 2026
61437f3
Declare type T to be of Comparable in SortedUnionFind and SortedTreeS…
Jun 23, 2026
352907c
Add AbstractImmutableUnionFind
Jun 23, 2026
92088a1
Add AbstractImmutableSortedUnionFind
Jun 23, 2026
d2234c8
Add fix to stop SortedTreeSetUnionFind from failing "test nulls"; not…
Jun 23, 2026
fca3102
Remove find() from deprecated methods; shouldn't have ended up there …
Jun 25, 2026
4dd0356
Add interface PersistentSortedUnionFind
Jun 25, 2026
f7ebd96
Add interface PersistentUnionFind
Jun 25, 2026
89e3a79
Add documentation to PersistentUnionFind and PersistentSortedUnionFind
Jun 25, 2026
f452274
Check input values are not null in SortedTreeSetUnionFind
Jun 26, 2026
a0dacef
Refactor union() to not through unnecessary exceptions in SortedTreeS…
Jun 26, 2026
56133be
Refactor union() to not through unnecessary exceptions in SortedTreeS…
Jun 26, 2026
2f01c62
Merge branch 'main' into 49-add-union-find-data-structure
Jul 3, 2026
efc2bcb
Add beginnings of additional tests in SortedUnionFindTest; not done y…
Jul 5, 2026
9f939ba
Remove previously commited nested classes from SortedUnionFindTest as…
Jul 7, 2026
2f4969d
Add testUnion_Strings() to SortedUnionFindTest
Jul 7, 2026
0782511
Correct test syntax and add more variety to test cases in testUnion_S…
Jul 7, 2026
37770d5
Correct test syntax in SortedUnionFindTest
Jul 7, 2026
9334994
Make result comparison more straightforward in SortedUnionFindTest
Jul 9, 2026
ea44c05
Attempt to fix (but not yet succeed) subset removal bug in SortedTree…
Jul 9, 2026
ee6fb37
Mark new bug discovery in SortedUnionFindTest
Jul 9, 2026
9c86ef7
Fix bug in SortedUnionFindTest (was comparing Integer to String)
Jul 10, 2026
9b27c60
Fix union bug in SortedTreeSetUnionFind
Jul 10, 2026
5917c15
Add separate test class for benchmarking UnionFind called UnionFindSi…
Jul 10, 2026
d67006e
Reduce method calls in UnionFindSimpleBenchmarkTest
Jul 10, 2026
69f5fa6
Fix UnionFindSimpleBenchmarkTest that it finishes within a reasonable…
Jul 10, 2026
1898af1
Replace recursion with iterative approach in UnionFindSimpleBenchmark…
Jul 10, 2026
52571a8
Correct calculations in UnionFindSimpleBenchmarkTest
Jul 14, 2026
5d6cca6
Increase magnitudes slightly in UnionFindSimpleBenchmarkTest
Jul 14, 2026
2dab552
Move all UnionFind classes to new subpackage union_find
Jul 16, 2026
43b4aaa
Rename variable as previous name was misleading
Jul 16, 2026
61278c7
Ignore benchmark test as it is not intended as part of the regular Un…
Jul 16, 2026
919356e
Add AbstractGenericUnionFind as basis for further union-find implemen…
Jul 16, 2026
6bee232
Ignore whole benchmark test class instead of just test method
Jul 16, 2026
9f69580
Move AbstractGenericUnionFind to union_find package
Jul 16, 2026
af996d2
Fic bug causing build to fail (incorrect warning suppression)
Jul 16, 2026
344cd5f
Add package-info.java to union_find package
Jul 16, 2026
761acec
Make SortedTreeSetUnionFind extend AbstractGenericUnionFind
Jul 16, 2026
e9e53d7
Correct package info
Jul 17, 2026
eb1c8b8
Rework how AbstractGenericUnionFind gets correct types from subclasses
Jul 17, 2026
599310b
Remove superfluous super() call in SortedTreeSetUnionFind constructor
Jul 17, 2026
4de8043
format-source
Jul 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/org/sosy_lab/common/collect/PackageSanityTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
package org.sosy_lab.common.collect;

import com.google.common.testing.AbstractPackageSanityTests;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.sosy_lab.common.Classes;
import org.sosy_lab.common.collect.union_find.SortedTreeSetUnionFind;

public class PackageSanityTest extends AbstractPackageSanityTests {

Expand All @@ -24,4 +27,19 @@ public class PackageSanityTest extends AbstractPackageSanityTests {
OurSortedMap.class, OurSortedMap.EmptyImmutableOurSortedMap.of(), singletonMap);
ignoreClasses(Classes.IS_GENERATED);
}

{
setDefault(SortedTreeSetUnionFind.class, new SortedTreeSetUnionFind<>());
// ignoreClasses(Classes.IS_GENERATED);

try {
setDefault(Constructor.class, PackageSanityTest.class.getConstructor());
setDefault(Method.class, PackageSanityTest.class.getDeclaredMethod("defaultMethod"));
} catch (NoSuchMethodException e) {
throw new AssertionError(e);
}
}

@SuppressWarnings("unused")
private static void defaultMethod() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
// This file is part of SoSy-Lab Common,
// a library of useful utilities:
// https://github.com/sosy-lab/java-common-lib
//
// SPDX-FileCopyrightText: 2026 Dirk Beyer <https://www.sosy-lab.org>
//
// SPDX-License-Identifier: Apache-2.0

package org.sosy_lab.common.collect.union_find;

import com.google.common.base.Preconditions;
import com.google.errorprone.annotations.Var;
import java.util.Collection;
import java.util.Map;
import java.util.Set;

/**
* An abstract, generic implementation of {@link UnionFind} using a {@link Map} of {@link Set}s. In
* order to represent subsets by canonical elements, each one is mapped to its representative
* canonical element. This is always the first element added to the subset, unless it has changed
* due to union operations. The union is implemented as union by size.
*
* @param <T> type of elements added to the Union-Find.
*/
public abstract class AbstractGenericUnionFind<T, S extends Set<T>, M extends Map<T, S>>
implements UnionFind<T> {

protected final M mapOfSets;

/**
* Takes an empty map of the desired kind and allocates it to the variable mapOfSets. This enables
* child classes to simply pass an object of the desired kind without having to modify the
* constructor and methods.
*/
@SuppressWarnings("unchecked")
public AbstractGenericUnionFind() {
mapOfSets = (M) getEmptyMap();
}

/**
* Returns the canonical element of the set containing the provided element.
*
* @param e element for which set is to be found
* @return canonical element of the found set
* @throws IllegalArgumentException if element is not contained in any subset
*/
@Override
public T find(T e) {

Preconditions.checkNotNull(e);
for (S current : mapOfSets.values()) {
if (current.contains(e)) {
for (T element : current) {
if (mapOfSets.containsKey(element)) {
return element;
}
}
}
}

throw new IllegalArgumentException("Element not contained");
}

/**
* Merges the sets represented by the two input values according to standard Union-Find behaviour.
*
* <p>USES: Add new element as new set: pass it as both e1 and e2. Add new element to existing
* set: one input value is the new element, the other the canonical element of the set to be added
* to. Merge two existing sets: e1, e2 canonical elements of sets to be merged.
*
* @param e1 first element
* @param e2 second element
*/
@SuppressWarnings("unchecked")
@Override
public void union(T e1, T e2) {

Preconditions.checkNotNull(e1);
Preconditions.checkNotNull(e2);

if (e1.equals(e2)) {
addElementAsNewSet(e1);
} else {
S canonicalElements = (S) mapOfSets.keySet();

if (canonicalElements.contains(e1)) {
if (canonicalElements.contains(e2)) {
mergeExistingSets(e1, e2);
} else {
addElementToExistingSet(e2, e1);
}
} else if (canonicalElements.contains(e2)) {
addElementToExistingSet(e1, e2);
} else {

if (contains(e1)) {
if (contains(e2)) {
mergeExistingSets(find(e1), find(e2));
} else {
addElementToExistingSet(e2, find(e1));
}
} else {
addElementAsNewSet(e1);
addElementToExistingSet(e2, e1);
}
}
}
}

@SuppressWarnings("unchecked")
private void addElementAsNewSet(T e) {

if (!contains(e)) {
S newSet = (S) getEmptySet();
newSet.add(e);
mapOfSets.put(e, newSet);
}
}

private void addElementToExistingSet(T e, T canon) {

if (!contains(e)) {
for (S currentSet : mapOfSets.values()) {
if (currentSet.contains(canon)) {
assert mapOfSets.remove(canon, currentSet);
currentSet.add(e);
mapOfSets.put(canon, currentSet);
break;
}
}
} else {
mergeExistingSets(find(e), canon);
}
}

private void mergeExistingSets(T e1, T e2) {

@Var S set1 = null;
@Var S set2 = null;

for (S current : mapOfSets.values()) {
if (current.contains(e1)) {
set1 = current;
} else if (current.contains(e2)) {
set2 = current;
}
}

assert set1 != null;
assert set2 != null;

int size1 = set1.size();
int size2 = set2.size();

assert mapOfSets.remove(e1, set1);
assert mapOfSets.remove(e2, set2);

if (size1 > size2) {
set1.addAll(set2);
mapOfSets.put(e1, set1);
} else {
set2.addAll(set1);
mapOfSets.put(e2, set2);
}
}

/**
* Provides a {@link Collection} containing all current subsets.
*
* @return {@link Collection} containing all current subsets
*/
@Override
public Collection<S> getAllSubsets() {
return mapOfSets.values();
}

/**
* Checks whether the provided element is contained in any current subset and returns true or
* false accordingly.
*
* @param e element to be searched for
* @return true if contained, false if not
*/
@Override
public boolean contains(T e) {

Preconditions.checkNotNull(e);

for (S current : mapOfSets.values()) {
if (current.contains(e)) {
return true;
}
}
return false;
}

protected abstract Set<T> getEmptySet();

protected abstract Map<T, Set<T>> getEmptyMap();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// This file is part of SoSy-Lab Common,
// a library of useful utilities:
// https://github.com/sosy-lab/java-common-lib
//
// SPDX-FileCopyrightText: 2026 Dirk Beyer <https://www.sosy-lab.org>
//
// SPDX-License-Identifier: Apache-2.0

package org.sosy_lab.common.collect.union_find;

import com.google.errorprone.annotations.DoNotCall;

public abstract class AbstractImmutableSortedUnionFind<T extends Comparable<T>>
implements SortedUnionFind<T> {
/**
* @throws UnsupportedOperationException Always.
* @deprecated Unsupported operation.
*/
@Deprecated
@Override
@DoNotCall
public final void union(T e1, T e2) {
throw new UnsupportedOperationException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// This file is part of SoSy-Lab Common,
// a library of useful utilities:
// https://github.com/sosy-lab/java-common-lib
//
// SPDX-FileCopyrightText: 2026 Dirk Beyer <https://www.sosy-lab.org>
//
// SPDX-License-Identifier: Apache-2.0

package org.sosy_lab.common.collect.union_find;

import com.google.errorprone.annotations.DoNotCall;

public abstract class AbstractImmutableUnionFind<T> implements UnionFind<T> {
/**
* @throws UnsupportedOperationException Always.
* @deprecated Unsupported operation.
*/
@Deprecated
@Override
@DoNotCall
public final void union(T e1, T e2) {
throw new UnsupportedOperationException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// This file is part of SoSy-Lab Common,
// a library of useful utilities:
// https://github.com/sosy-lab/java-common-lib
//
// SPDX-FileCopyrightText: 2026 Dirk Beyer <https://www.sosy-lab.org>
//
// SPDX-License-Identifier: Apache-2.0

package org.sosy_lab.common.collect.union_find;

import com.google.errorprone.annotations.CheckReturnValue;
import com.google.errorprone.annotations.DoNotCall;
import com.google.errorprone.annotations.Immutable;
import java.util.Map;
import java.util.NavigableSet;

/**
* Interface for a persistent and sorted union-find. A persistent data structure is immutable, but
* provides cheap copy-and-write operations. Thus, all write operations ({@link #union(Comparable,
* Comparable)}) will not modify the current instance, but return a new instance instead.
*
* <p>All modifying operations inherited from {@link SortedUnionFind} are not supported and will
* always throw {@link UnsupportedOperationException}.
*
* @param <T> The type of values.
*/
@Immutable(containerOf = "T")
public interface PersistentSortedUnionFind<T extends Comparable<T>> extends SortedUnionFind<T> {

/**
* Replacement for {@link #union(Comparable, Comparable)} that returns a fresh new instance.
*
* @param e1 first element
* @param e2 second element
* @return new instance that the desired changes have been applied to
*/
@CheckReturnValue
Map<T, NavigableSet<T>> unionAndCopy(T e1, T e2);

/**
* @throws UnsupportedOperationException Always.
* @deprecated Unsupported operation.
*/
@Deprecated
@Override
@DoNotCall
void union(T e1, T e2);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// This file is part of SoSy-Lab Common,
// a library of useful utilities:
// https://github.com/sosy-lab/java-common-lib
//
// SPDX-FileCopyrightText: 2026 Dirk Beyer <https://www.sosy-lab.org>
//
// SPDX-License-Identifier: Apache-2.0

package org.sosy_lab.common.collect.union_find;

import com.google.errorprone.annotations.CheckReturnValue;
import com.google.errorprone.annotations.DoNotCall;
import com.google.errorprone.annotations.Immutable;
import java.util.Map;
import java.util.NavigableSet;

/**
* Interface for a persistent union-find. A persistent data structure is immutable, but provides
* cheap copy-and-write operations. Thus, all write operations ({@link #union(Object, Object)}) will
* not modify the current instance, but return a new instance instead.
*
* <p>All modifying operations inherited from {@link UnionFind} are not supported and will always
* throw {@link UnsupportedOperationException}.
*
* @param <T> The type of values.
*/
@Immutable(containerOf = "T")
public interface PersistentUnionFind<T> extends UnionFind<T> {

/**
* Replacement for {@link #union(Object, Object)} that returns a fresh new instance.
*
* @param e1 first element
* @param e2 second element
* @return new instance that the desired changes have been applied to
*/
@CheckReturnValue
Map<T, NavigableSet<T>> unionAndCopy(T e1, T e2);

/**
* @throws UnsupportedOperationException Always.
* @deprecated Unsupported operation.
*/
@Deprecated
@Override
@DoNotCall
void union(T e1, T e2);
}
Loading