Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 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
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
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;

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();
}
}
24 changes: 24 additions & 0 deletions src/org/sosy_lab/common/collect/AbstractImmutableUnionFind.java
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;

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();
}
}
17 changes: 17 additions & 0 deletions src/org/sosy_lab/common/collect/PackageSanityTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
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;

public class PackageSanityTest extends AbstractPackageSanityTests {
Expand All @@ -24,4 +26,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() {}
}
48 changes: 48 additions & 0 deletions src/org/sosy_lab/common/collect/PersistentSortedUnionFind.java
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;

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);
}
48 changes: 48 additions & 0 deletions src/org/sosy_lab/common/collect/PersistentUnionFind.java
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;

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);
}
179 changes: 179 additions & 0 deletions src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// 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;

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

/**
* An implementation of {@link SortedUnionFind} using a {@link HashMap} of {@link TreeSet}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. Must be {@link Comparable} to ensure correct
* ordering.
*/
public class SortedTreeSetUnionFind<T extends Comparable<T>> implements SortedUnionFind<T> {

private final Map<T, NavigableSet<T>> setOfSets;

/** Generates an empty {@link SortedTreeSetUnionFind}. */
public SortedTreeSetUnionFind() {
setOfSets = new HashMap<>();
}

/**
* 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 (NavigableSet<T> current : setOfSets.values()) {
if (current.contains(e)) {
for (T element : current) {
if (setOfSets.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
*/
@Override
public void union(T e1, T e2) {

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

if (e1.equals(e2)) {
addElementAsNewSet(e1);
} else {
Set<T> canonicalElements = setOfSets.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 {
addElementAsNewSet(e1);
addElementToExistingSet(e2, e1);
}
}
}

private void addElementAsNewSet(T e) {

if (!contains(e)) {
NavigableSet<T> newSet = new TreeSet<>();
newSet.add(e);
setOfSets.put(e, newSet);
}
}

private void addElementToExistingSet(T e, T canon) {

if (!contains(e)) {
for (NavigableSet<T> currentSet : setOfSets.values()) {
if (currentSet.contains(canon)) {
currentSet.add(e);
setOfSets.replace(canon, currentSet);
break;
}
}
} else {
mergeExistingSets(e, canon);
}
}

private void mergeExistingSets(T e1, T e2) {

@Var NavigableSet<T> set1 = null;
@Var NavigableSet<T> set2 = null;

for (NavigableSet<T> current : setOfSets.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();

if (size1 > size2) {
set1.addAll(set2);
setOfSets.remove(e2);
} else {
set2.addAll(set1);
setOfSets.remove(e1);
}
}

/**
* Provides a {@link Collection} containing all current subsets.
*
* @return {@link Collection} containing all current subsets
*/
@Override
public Collection<? extends Set<T>> getAllSubsets() {
return setOfSets.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 (NavigableSet<T> current : setOfSets.values()) {
if (current.contains(e)) {
return true;
}
}
return false;
}
}
Loading