From 63a45e6f437a4fce1260d890ff1de7c9b37a9bb9 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 25 May 2026 13:52:23 +0200 Subject: [PATCH 01/55] Add first draft for potential MutableUnionFind interface --- .../common/collect/MutableUnionFind.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/MutableUnionFind.java diff --git a/src/org/sosy_lab/common/collect/MutableUnionFind.java b/src/org/sosy_lab/common/collect/MutableUnionFind.java new file mode 100644 index 000000000..bb6de798a --- /dev/null +++ b/src/org/sosy_lab/common/collect/MutableUnionFind.java @@ -0,0 +1,18 @@ +// 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 +// +// SPDX-License-Identifier: Apache-2.0 + +package org.sosy_lab.common.collect; + +public interface MutableUnionFind { + Object find(Object o); + void union(Object o1, Object o2); + void addNew(Object o); + void addTo(Object o, Object root); +} + +//possibly extend Set or Collection \ No newline at end of file From 137ad6e4ec1b071cf792a24410ba2cd512a9c97c Mon Sep 17 00:00:00 2001 From: Colleen Date: Sun, 7 Jun 2026 16:25:10 +0200 Subject: [PATCH 02/55] Rename MutableUnionFind interface to simply UnionFind --- .../common/collect/{MutableUnionFind.java => UnionFind.java} | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) rename src/org/sosy_lab/common/collect/{MutableUnionFind.java => UnionFind.java} (79%) diff --git a/src/org/sosy_lab/common/collect/MutableUnionFind.java b/src/org/sosy_lab/common/collect/UnionFind.java similarity index 79% rename from src/org/sosy_lab/common/collect/MutableUnionFind.java rename to src/org/sosy_lab/common/collect/UnionFind.java index bb6de798a..b3bfb6de1 100644 --- a/src/org/sosy_lab/common/collect/MutableUnionFind.java +++ b/src/org/sosy_lab/common/collect/UnionFind.java @@ -8,11 +8,9 @@ package org.sosy_lab.common.collect; -public interface MutableUnionFind { +public interface UnionFind { Object find(Object o); void union(Object o1, Object o2); - void addNew(Object o); - void addTo(Object o, Object root); } //possibly extend Set or Collection \ No newline at end of file From a6820bf0d6d074f1a78d301a474a1c6c27ecb8f8 Mon Sep 17 00:00:00 2001 From: Colleen Date: Sun, 7 Jun 2026 16:52:35 +0200 Subject: [PATCH 03/55] Update methods specified in interface UnionFind --- src/org/sosy_lab/common/collect/UnionFind.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/org/sosy_lab/common/collect/UnionFind.java b/src/org/sosy_lab/common/collect/UnionFind.java index b3bfb6de1..3acd860ce 100644 --- a/src/org/sosy_lab/common/collect/UnionFind.java +++ b/src/org/sosy_lab/common/collect/UnionFind.java @@ -8,9 +8,14 @@ package org.sosy_lab.common.collect; +import java.util.Set; + public interface UnionFind { - Object find(Object o); - void union(Object o1, Object o2); -} + T find(T e); + void union(T e1, T e2); -//possibly extend Set or Collection \ No newline at end of file + UnionFind getEmptyUnionFind(); + void addSetOfSets(Set set); + void addElementToNewSet(T e); + Set getAllSubsets(); +} \ No newline at end of file From cb5d31da5309d8efb214db9a9e270b37625a3873 Mon Sep 17 00:00:00 2001 From: Colleen Date: Sun, 7 Jun 2026 17:16:21 +0200 Subject: [PATCH 04/55] Add class MutableUnionFind including empty method bodies and constructor implementation --- .../common/collect/MutableUnionFind.java | 54 +++++++++++++++++++ .../sosy_lab/common/collect/UnionFind.java | 2 +- 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 src/org/sosy_lab/common/collect/MutableUnionFind.java diff --git a/src/org/sosy_lab/common/collect/MutableUnionFind.java b/src/org/sosy_lab/common/collect/MutableUnionFind.java new file mode 100644 index 000000000..9bd4a956d --- /dev/null +++ b/src/org/sosy_lab/common/collect/MutableUnionFind.java @@ -0,0 +1,54 @@ +// 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 +// +// SPDX-License-Identifier: Apache-2.0 + +package org.sosy_lab.common.collect; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +public class MutableUnionFind implements UnionFind { + + private Set setOfSets; + private ArrayList canonicalElements; + + private MutableUnionFind() { + setOfSets = new HashSet<>(); + canonicalElements = new ArrayList<>(); + } + + @Override + public T find(T e) { + //TODO + } + + @Override + public void union(T e1, T e2) { + //TODO + } + + @Override + public UnionFind getEmptyInstanceOf() { + return new MutableUnionFind(); + } + + @Override + public void addSetOfSets(Set set) { + //TODO + } + + @Override + public void addElementToNewSet(T e) { + //TODO + } + + @Override + public Set getAllSubsets() { + //TODO + } +} diff --git a/src/org/sosy_lab/common/collect/UnionFind.java b/src/org/sosy_lab/common/collect/UnionFind.java index 3acd860ce..d8260efc0 100644 --- a/src/org/sosy_lab/common/collect/UnionFind.java +++ b/src/org/sosy_lab/common/collect/UnionFind.java @@ -14,7 +14,7 @@ public interface UnionFind { T find(T e); void union(T e1, T e2); - UnionFind getEmptyUnionFind(); + UnionFind getEmptyInstanceOf(); void addSetOfSets(Set set); void addElementToNewSet(T e); Set getAllSubsets(); From 27a7728be48bbaac7e2d70fb39240668e1286308 Mon Sep 17 00:00:00 2001 From: Colleen Date: Sun, 7 Jun 2026 17:59:53 +0200 Subject: [PATCH 05/55] Implement getAllSubsets in MutableUnionFind --- src/org/sosy_lab/common/collect/MutableUnionFind.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/MutableUnionFind.java b/src/org/sosy_lab/common/collect/MutableUnionFind.java index 9bd4a956d..8684a7c98 100644 --- a/src/org/sosy_lab/common/collect/MutableUnionFind.java +++ b/src/org/sosy_lab/common/collect/MutableUnionFind.java @@ -49,6 +49,6 @@ public void addElementToNewSet(T e) { @Override public Set getAllSubsets() { - //TODO + return setOfSets; } } From 7d0afc71ed3a7d3922b16b8f867bfda8233ce2b7 Mon Sep 17 00:00:00 2001 From: Colleen Date: Sun, 7 Jun 2026 18:45:21 +0200 Subject: [PATCH 06/55] Implement addElementToNewSet in MutableUnionFind and capture current concerns and notes in a comment --- .../common/collect/MutableUnionFind.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/MutableUnionFind.java b/src/org/sosy_lab/common/collect/MutableUnionFind.java index 8684a7c98..de9417f90 100644 --- a/src/org/sosy_lab/common/collect/MutableUnionFind.java +++ b/src/org/sosy_lab/common/collect/MutableUnionFind.java @@ -14,7 +14,16 @@ public class MutableUnionFind implements UnionFind { - private Set setOfSets; + /* + * CURRENT PROBLEMS: + * - trying to keep set type as flexible as possible but finding certain things can't be implemented without deciding on type + * - currently using HashSet + * - considering using set of maps instead --> map each element of a disjoint set to canonical element of set (but then need to ensure duplicate elements do not occur) + * - not sure ArrayList is ideal for canonical elements + * - addSetOfSets might be dangerous as it relies on user providing set with the correct type of values + * - was trying to make one class work for both sorted and unsorted (defined by type of set user provides) but it's looking like they're going to be separate and this will be unsorted + */ + private HashSet setOfSets; private ArrayList canonicalElements; private MutableUnionFind() { @@ -40,11 +49,17 @@ public UnionFind getEmptyInstanceOf() { @Override public void addSetOfSets(Set set) { //TODO + //problem: extracting canonical elements to add to canonicalElements with current HashSet situation } @Override public void addElementToNewSet(T e) { //TODO + HashSet newSet = new HashSet<>(); + newSet.add(e); + + setOfSets.add(newSet); + canonicalElements.add(e); } @Override From 33e33262db68e8917e8e52a229fa12f23e491a70 Mon Sep 17 00:00:00 2001 From: Colleen Date: Sun, 7 Jun 2026 18:57:22 +0200 Subject: [PATCH 07/55] Rename MutableUnionFind to UnsortedUnionFind and alter uses of HashSet for subsets to HashMap --- ...eUnionFind.java => UnsortedUnionFind.java} | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) rename src/org/sosy_lab/common/collect/{MutableUnionFind.java => UnsortedUnionFind.java} (80%) diff --git a/src/org/sosy_lab/common/collect/MutableUnionFind.java b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java similarity index 80% rename from src/org/sosy_lab/common/collect/MutableUnionFind.java rename to src/org/sosy_lab/common/collect/UnsortedUnionFind.java index de9417f90..adfa6f53c 100644 --- a/src/org/sosy_lab/common/collect/MutableUnionFind.java +++ b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java @@ -9,10 +9,11 @@ package org.sosy_lab.common.collect; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.Set; -public class MutableUnionFind implements UnionFind { +public class UnsortedUnionFind implements UnionFind { /* * CURRENT PROBLEMS: @@ -23,11 +24,11 @@ public class MutableUnionFind implements UnionFind { * - addSetOfSets might be dangerous as it relies on user providing set with the correct type of values * - was trying to make one class work for both sorted and unsorted (defined by type of set user provides) but it's looking like they're going to be separate and this will be unsorted */ - private HashSet setOfSets; + private HashSet setOfMaps; private ArrayList canonicalElements; - private MutableUnionFind() { - setOfSets = new HashSet<>(); + private UnsortedUnionFind() { + setOfMaps = new HashSet<>(); canonicalElements = new ArrayList<>(); } @@ -43,7 +44,7 @@ public void union(T e1, T e2) { @Override public UnionFind getEmptyInstanceOf() { - return new MutableUnionFind(); + return new UnsortedUnionFind(); } @Override @@ -54,16 +55,16 @@ public void addSetOfSets(Set set) { @Override public void addElementToNewSet(T e) { - //TODO - HashSet newSet = new HashSet<>(); - newSet.add(e); + //TODO check whether new element already in structure and handle case where it is + HashMap newMap = new HashMap<>(); + newMap.put(e, e); - setOfSets.add(newSet); + setOfMaps.add(newMap); canonicalElements.add(e); } @Override public Set getAllSubsets() { - return setOfSets; + return setOfMaps; } } From b30175384794e35cd59b9579d976b12ee3095747 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 8 Jun 2026 19:38:15 +0200 Subject: [PATCH 08/55] Implement find as HashMap version in UnsortedUnionFind; contains type mismatches that will be sorted out later on if I decide to stick with HashMaps --- .../common/collect/UnsortedUnionFind.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/org/sosy_lab/common/collect/UnsortedUnionFind.java b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java index adfa6f53c..82e4336a2 100644 --- a/src/org/sosy_lab/common/collect/UnsortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java @@ -27,14 +27,25 @@ public class UnsortedUnionFind implements UnionFind { private HashSet setOfMaps; private ArrayList canonicalElements; - private UnsortedUnionFind() { - setOfMaps = new HashSet<>(); - canonicalElements = new ArrayList<>(); + private UnsortedUnionFind() { + setOfMaps = new HashSet>(); + canonicalElements = new ArrayList(); } @Override public T find(T e) { //TODO + + for(HashMap s : setOfMaps){ + if(s.containsValue(e)) { + Set keySet = s.keySet(); + if(keySet.size() >= 2) { + //error as not all elements of set mapped to same canonical element + } else { + return keySet.iterator().next(); + } + } + } } @Override @@ -49,8 +60,8 @@ public UnionFind getEmptyInstanceOf() { @Override public void addSetOfSets(Set set) { - //TODO - //problem: extracting canonical elements to add to canonicalElements with current HashSet situation + //TODO currently laid out for set of sets instead of set of maps + //problem: extracting canonical elements to add to canonicalElements --> could call keySet() on each Map } @Override From 4c52fc036cf2002e47a1b9023807a8af21e1d1c1 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 8 Jun 2026 20:07:25 +0200 Subject: [PATCH 09/55] Declare type for whole classes and add rough implementation of union by size in UnsortedUnionFind --- .../sosy_lab/common/collect/UnionFind.java | 10 ++-- .../common/collect/UnsortedUnionFind.java | 57 +++++++++++++++---- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/org/sosy_lab/common/collect/UnionFind.java b/src/org/sosy_lab/common/collect/UnionFind.java index d8260efc0..99a234a92 100644 --- a/src/org/sosy_lab/common/collect/UnionFind.java +++ b/src/org/sosy_lab/common/collect/UnionFind.java @@ -10,12 +10,12 @@ import java.util.Set; -public interface UnionFind { - T find(T e); - void union(T e1, T e2); +public interface UnionFind { + T find(T e); + void union(T e1, T e2); - UnionFind getEmptyInstanceOf(); + UnionFind getEmptyInstanceOf(); void addSetOfSets(Set set); - void addElementToNewSet(T e); + void addElementToNewSet(T e); Set getAllSubsets(); } \ No newline at end of file diff --git a/src/org/sosy_lab/common/collect/UnsortedUnionFind.java b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java index 82e4336a2..dabd4c928 100644 --- a/src/org/sosy_lab/common/collect/UnsortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java @@ -11,9 +11,10 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.Set; -public class UnsortedUnionFind implements UnionFind { +public class UnsortedUnionFind implements UnionFind { /* * CURRENT PROBLEMS: @@ -24,21 +25,21 @@ public class UnsortedUnionFind implements UnionFind { * - addSetOfSets might be dangerous as it relies on user providing set with the correct type of values * - was trying to make one class work for both sorted and unsorted (defined by type of set user provides) but it's looking like they're going to be separate and this will be unsorted */ - private HashSet setOfMaps; + private HashSet> setOfMaps; private ArrayList canonicalElements; - private UnsortedUnionFind() { - setOfMaps = new HashSet>(); + private UnsortedUnionFind() { + setOfMaps = new HashSet<>(); canonicalElements = new ArrayList(); } @Override - public T find(T e) { - //TODO + public T find(T e) { + //TODO handle edge cases - for(HashMap s : setOfMaps){ + for(HashMap s : setOfMaps){ if(s.containsValue(e)) { - Set keySet = s.keySet(); + Set keySet = s.keySet(); if(keySet.size() >= 2) { //error as not all elements of set mapped to same canonical element } else { @@ -49,13 +50,45 @@ public T find(T e) { } @Override - public void union(T e1, T e2) { + public void union(T e1, T e2) { //TODO + + Iterator> itti = setOfMaps.iterator(); + HashMap map1 = null; + HashMap map2 = null; + + while(itti.hasNext()) { + HashMap current = itti.next(); + Set keySet = current.keySet(); + T e; + + if(keySet.size() >= 2) { + //error as not all elements of set mapped to same canonical element + } else { + e = keySet.iterator().next(); + + if(e.equals(e1)) { + map1 = current; + } else if(e.equals(e2)) { + map2 = current; + } + } + + if(map1!=null && map2!=null) { + break; + } + } + if(map1.size() > map2.size()) { + //TODO add elements of map2 to map1; map them to canonical elem. of map1 + } else { + //TODO other way around (add map1 to map2 + } + //TODO adjust canonical elements list } @Override - public UnionFind getEmptyInstanceOf() { - return new UnsortedUnionFind(); + public UnionFind getEmptyInstanceOf() { + return new UnsortedUnionFind(); } @Override @@ -65,7 +98,7 @@ public void addSetOfSets(Set set) { } @Override - public void addElementToNewSet(T e) { + public void addElementToNewSet(T e) { //TODO check whether new element already in structure and handle case where it is HashMap newMap = new HashMap<>(); newMap.put(e, e); From 5baa93d5be7ec9a5d503addb4cee9f491f1ad060 Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 9 Jun 2026 09:34:06 +0200 Subject: [PATCH 10/55] Continue implementing several methods in UnsortedUnionFind and add new private method contains(T e) --- .../common/collect/UnsortedUnionFind.java | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/src/org/sosy_lab/common/collect/UnsortedUnionFind.java b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java index dabd4c928..5956fe881 100644 --- a/src/org/sosy_lab/common/collect/UnsortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java @@ -11,7 +11,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.Set; public class UnsortedUnionFind implements UnionFind { @@ -19,18 +18,17 @@ public class UnsortedUnionFind implements UnionFind { /* * CURRENT PROBLEMS: * - trying to keep set type as flexible as possible but finding certain things can't be implemented without deciding on type - * - currently using HashSet - * - considering using set of maps instead --> map each element of a disjoint set to canonical element of set (but then need to ensure duplicate elements do not occur) + * - set of maps --> map each element of a disjoint set to canonical element of set (but then need to ensure duplicate elements do not occur) * - not sure ArrayList is ideal for canonical elements * - addSetOfSets might be dangerous as it relies on user providing set with the correct type of values * - was trying to make one class work for both sorted and unsorted (defined by type of set user provides) but it's looking like they're going to be separate and this will be unsorted */ private HashSet> setOfMaps; - private ArrayList canonicalElements; + private ArrayList canonicalElements; //TODO remove if continues to be unnecessary private UnsortedUnionFind() { setOfMaps = new HashSet<>(); - canonicalElements = new ArrayList(); + canonicalElements = new ArrayList<>(); } @Override @@ -41,7 +39,7 @@ public T find(T e) { if(s.containsValue(e)) { Set keySet = s.keySet(); if(keySet.size() >= 2) { - //error as not all elements of set mapped to same canonical element + //TODO error as not all elements of set mapped to same canonical element } else { return keySet.iterator().next(); } @@ -49,21 +47,18 @@ public T find(T e) { } } + //currently only union by size supported for unsorted union-find @Override public void union(T e1, T e2) { - //TODO - - Iterator> itti = setOfMaps.iterator(); HashMap map1 = null; HashMap map2 = null; - while(itti.hasNext()) { - HashMap current = itti.next(); + for(HashMap current : setOfMaps) { Set keySet = current.keySet(); T e; if(keySet.size() >= 2) { - //error as not all elements of set mapped to same canonical element + //TODO error as not all elements of set mapped to same canonical element } else { e = keySet.iterator().next(); @@ -79,11 +74,16 @@ public void union(T e1, T e2) { } } if(map1.size() > map2.size()) { - //TODO add elements of map2 to map1; map them to canonical elem. of map1 + for(T e : map2.values()) { + map1.put(e1, e); + } + canonicalElements.remove(e2); } else { - //TODO other way around (add map1 to map2 + for(T e : map1.values()) { + map2.put(e2, e); + } + canonicalElements.remove(e1); } - //TODO adjust canonical elements list } @Override @@ -98,8 +98,13 @@ public void addSetOfSets(Set set) { } @Override - public void addElementToNewSet(T e) { - //TODO check whether new element already in structure and handle case where it is + public void addElementToNewSet(T e) throws IllegalArgumentException { + + if (contains(e)) { + //throw exception: element already contained + throw new IllegalArgumentException("Element already exists"); + } + HashMap newMap = new HashMap<>(); newMap.put(e, e); @@ -111,4 +116,13 @@ public void addElementToNewSet(T e) { public Set getAllSubsets() { return setOfMaps; } + + private boolean contains(T e) { + for(HashMap current : setOfMaps) { + if(current.containsValue(e)) { + return true; + } + } + return false; + } } From 24e376bfced4702932e7e373f1569564772e3af4 Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 9 Jun 2026 09:46:01 +0200 Subject: [PATCH 11/55] Add addNewSet(Set input) to UnsortedUnionFind; might end up replacing addSetOfSets --- .../sosy_lab/common/collect/UnionFind.java | 8 +- .../common/collect/UnsortedUnionFind.java | 80 ++++++++++++------- 2 files changed, 57 insertions(+), 31 deletions(-) diff --git a/src/org/sosy_lab/common/collect/UnionFind.java b/src/org/sosy_lab/common/collect/UnionFind.java index 99a234a92..624fd37ce 100644 --- a/src/org/sosy_lab/common/collect/UnionFind.java +++ b/src/org/sosy_lab/common/collect/UnionFind.java @@ -10,12 +10,16 @@ import java.util.Set; -public interface UnionFind { +public interface UnionFind { T find(T e); + void union(T e1, T e2); UnionFind getEmptyInstanceOf(); + void addSetOfSets(Set set); + void addElementToNewSet(T e); + Set getAllSubsets(); -} \ No newline at end of file +} diff --git a/src/org/sosy_lab/common/collect/UnsortedUnionFind.java b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java index 5956fe881..3d0f81e3b 100644 --- a/src/org/sosy_lab/common/collect/UnsortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java @@ -13,7 +13,7 @@ import java.util.HashSet; import java.util.Set; -public class UnsortedUnionFind implements UnionFind { +public class UnsortedUnionFind implements UnionFind { /* * CURRENT PROBLEMS: @@ -23,8 +23,8 @@ public class UnsortedUnionFind implements UnionFind { * - addSetOfSets might be dangerous as it relies on user providing set with the correct type of values * - was trying to make one class work for both sorted and unsorted (defined by type of set user provides) but it's looking like they're going to be separate and this will be unsorted */ - private HashSet> setOfMaps; - private ArrayList canonicalElements; //TODO remove if continues to be unnecessary + private HashSet> setOfMaps; + private ArrayList canonicalElements; // TODO remove if continues to be unnecessary private UnsortedUnionFind() { setOfMaps = new HashSet<>(); @@ -33,13 +33,13 @@ private UnsortedUnionFind() { @Override public T find(T e) { - //TODO handle edge cases + // TODO handle edge cases - for(HashMap s : setOfMaps){ - if(s.containsValue(e)) { + for (HashMap s : setOfMaps) { + if (s.containsValue(e)) { Set keySet = s.keySet(); - if(keySet.size() >= 2) { - //TODO error as not all elements of set mapped to same canonical element + if (keySet.size() >= 2) { + // TODO error as not all elements of set mapped to same canonical element } else { return keySet.iterator().next(); } @@ -47,39 +47,39 @@ public T find(T e) { } } - //currently only union by size supported for unsorted union-find + // currently only union by size supported for unsorted union-find @Override public void union(T e1, T e2) { - HashMap map1 = null; - HashMap map2 = null; + HashMap map1 = null; + HashMap map2 = null; - for(HashMap current : setOfMaps) { + for (HashMap current : setOfMaps) { Set keySet = current.keySet(); T e; - if(keySet.size() >= 2) { - //TODO error as not all elements of set mapped to same canonical element + if (keySet.size() >= 2) { + // TODO error as not all elements of set mapped to same canonical element } else { e = keySet.iterator().next(); - if(e.equals(e1)) { + if (e.equals(e1)) { map1 = current; - } else if(e.equals(e2)) { + } else if (e.equals(e2)) { map2 = current; } } - if(map1!=null && map2!=null) { + if (map1 != null && map2 != null) { break; } } - if(map1.size() > map2.size()) { - for(T e : map2.values()) { + if (map1.size() > map2.size()) { + for (T e : map2.values()) { map1.put(e1, e); } canonicalElements.remove(e2); } else { - for(T e : map1.values()) { + for (T e : map1.values()) { map2.put(e2, e); } canonicalElements.remove(e1); @@ -91,21 +91,42 @@ public UnionFind getEmptyInstanceOf() { return new UnsortedUnionFind(); } + public void addNewSet(Set input) throws IllegalArgumentException { + T canon = null; + HashMap newMap = new HashMap<>(); + + for (T current : input) { + if (contains(current)) { + throw new IllegalArgumentException("Element already exists"); + } + + if (canon == null) { + canon = current; + canonicalElements.add(canon); + } + + newMap.put(canon, current); + } + + setOfMaps.add(newMap); + } + @Override public void addSetOfSets(Set set) { - //TODO currently laid out for set of sets instead of set of maps - //problem: extracting canonical elements to add to canonicalElements --> could call keySet() on each Map + // TODO currently laid out for set of sets instead of set of maps + // problem: extracting canonical elements to add to canonicalElements --> could call keySet() on + // each Map } @Override public void addElementToNewSet(T e) throws IllegalArgumentException { - if (contains(e)) { - //throw exception: element already contained - throw new IllegalArgumentException("Element already exists"); - } + if (contains(e)) { + // throw exception: element already contained + throw new IllegalArgumentException("Element already exists"); + } - HashMap newMap = new HashMap<>(); + HashMap newMap = new HashMap<>(); newMap.put(e, e); setOfMaps.add(newMap); @@ -117,9 +138,10 @@ public Set getAllSubsets() { return setOfMaps; } + // consider making public and adding to interface private boolean contains(T e) { - for(HashMap current : setOfMaps) { - if(current.containsValue(e)) { + for (HashMap current : setOfMaps) { + if (current.containsValue(e)) { return true; } } From 00d285d95aa94861d7ba7189f06a76a1d7bbe88b Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 9 Jun 2026 09:53:39 +0200 Subject: [PATCH 12/55] Add notes for further work to UnsortedUnionFind --- src/org/sosy_lab/common/collect/UnsortedUnionFind.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/org/sosy_lab/common/collect/UnsortedUnionFind.java b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java index 3d0f81e3b..60fc70fae 100644 --- a/src/org/sosy_lab/common/collect/UnsortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java @@ -114,8 +114,9 @@ public void addNewSet(Set input) throws IllegalArgumentException { @Override public void addSetOfSets(Set set) { // TODO currently laid out for set of sets instead of set of maps - // problem: extracting canonical elements to add to canonicalElements --> could call keySet() on - // each Map + // could take set of HashMaps and call addNewSet() on each map + // problem: would need to specify type more clearly here but then won't override interface (but + // also can't alter type in interface as it wouldn't be generally applicable anymore) } @Override @@ -134,7 +135,7 @@ public void addElementToNewSet(T e) throws IllegalArgumentException { } @Override - public Set getAllSubsets() { + public Set> getAllSubsets() { return setOfMaps; } From 6e04f27aaaae481184d3c65698f8cfb8b95547a5 Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 9 Jun 2026 10:03:18 +0200 Subject: [PATCH 13/55] Add new class SortedUnionFind; still missing all method bodies --- .../common/collect/SortedUnionFind.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/SortedUnionFind.java diff --git a/src/org/sosy_lab/common/collect/SortedUnionFind.java b/src/org/sosy_lab/common/collect/SortedUnionFind.java new file mode 100644 index 000000000..9fad88156 --- /dev/null +++ b/src/org/sosy_lab/common/collect/SortedUnionFind.java @@ -0,0 +1,45 @@ +// 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 +// +// SPDX-License-Identifier: Apache-2.0 + +package org.sosy_lab.common.collect; + +import java.util.HashSet; +import java.util.Set; + +public class SortedUnionFind implements UnionFind { + + private HashSet setOfSets; // TODO figure out type + + private SortedUnionFind() { + // TODO + } + + @Override + public T find(T e) { + return null; + } + + @Override + public void union(T e1, T e2) {} + + @Override + public UnionFind getEmptyInstanceOf() { + return null; + } + + @Override + public void addSetOfSets(Set set) {} + + @Override + public void addElementToNewSet(T e) {} + + @Override + public Set getAllSubsets() { + return Set.of(); + } +} From a7959db0f8c52e346f84600514ffb54d4ab4ebdb Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 9 Jun 2026 10:20:48 +0200 Subject: [PATCH 14/55] Change all uses of put() to putIfAbsent() in UnsortedUnionFind to ensure maps fulfil set requirement --- src/org/sosy_lab/common/collect/UnsortedUnionFind.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/org/sosy_lab/common/collect/UnsortedUnionFind.java b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java index 60fc70fae..7fb56b2b5 100644 --- a/src/org/sosy_lab/common/collect/UnsortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java @@ -75,12 +75,12 @@ public void union(T e1, T e2) { } if (map1.size() > map2.size()) { for (T e : map2.values()) { - map1.put(e1, e); + map1.putIfAbsent(e1, e); } canonicalElements.remove(e2); } else { for (T e : map1.values()) { - map2.put(e2, e); + map2.putIfAbsent(e2, e); } canonicalElements.remove(e1); } @@ -105,7 +105,7 @@ public void addNewSet(Set input) throws IllegalArgumentException { canonicalElements.add(canon); } - newMap.put(canon, current); + newMap.putIfAbsent(canon, current); } setOfMaps.add(newMap); @@ -128,7 +128,7 @@ public void addElementToNewSet(T e) throws IllegalArgumentException { } HashMap newMap = new HashMap<>(); - newMap.put(e, e); + newMap.putIfAbsent(e, e); setOfMaps.add(newMap); canonicalElements.add(e); From 831ec680f1a428c172b8798c965f704ba3de9d0a Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 11 Jun 2026 15:05:48 +0200 Subject: [PATCH 15/55] Remove superfluous methods from interface UnionFind --- src/org/sosy_lab/common/collect/UnionFind.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/org/sosy_lab/common/collect/UnionFind.java b/src/org/sosy_lab/common/collect/UnionFind.java index 624fd37ce..72b31c565 100644 --- a/src/org/sosy_lab/common/collect/UnionFind.java +++ b/src/org/sosy_lab/common/collect/UnionFind.java @@ -17,9 +17,5 @@ public interface UnionFind { UnionFind getEmptyInstanceOf(); - void addSetOfSets(Set set); - - void addElementToNewSet(T e); - Set getAllSubsets(); } From cb0ca0e8996e5a3653539a0e1f6a992bef7c3bea Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 11 Jun 2026 15:53:49 +0200 Subject: [PATCH 16/55] Implement large portion of methods in SortedUnionFind --- .../common/collect/SortedUnionFind.java | 114 ++++++++++++++++-- 1 file changed, 104 insertions(+), 10 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFind.java b/src/org/sosy_lab/common/collect/SortedUnionFind.java index 9fad88156..55793af52 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFind.java @@ -8,38 +8,132 @@ package org.sosy_lab.common.collect; +import java.util.ArrayList; import java.util.HashSet; import java.util.Set; +import java.util.TreeSet; public class SortedUnionFind implements UnionFind { - private HashSet setOfSets; // TODO figure out type + private HashSet> setOfSets; private SortedUnionFind() { // TODO + + setOfSets = new HashSet<>(); } @Override public T find(T e) { + // TODO return null; } + /* + USE + - add new element to own new set: e1 and e2 both element to be added + - add new element to existing set: one e new element, other e canon. elem. of set to add to + - merge two existing sets: e1 and e2 canon. elem.s of sets to be merged + */ @Override - public void union(T e1, T e2) {} + public void union(T e1, T e2) throws IllegalArgumentException { + if (e1.equals(e2)) { + addElementAsNewSet(e1); + } else { + ArrayList canonicalElements = getListOfCanonicalElements(); - @Override - public UnionFind getEmptyInstanceOf() { - return null; + if (canonicalElements.contains(e1)) { + if (canonicalElements.contains(e2)) { + mergeExistingSets(e1, e2); + } else { + addElementToExistingSet(e2, e1); + } + } else if (canonicalElements.contains(e2)) { + addElementToExistingSet(e1, e2); + } + } } - @Override - public void addSetOfSets(Set set) {} + private void addElementAsNewSet(T e) throws IllegalArgumentException { + if (!contains(e)) { + TreeSet newSet = new TreeSet<>(); + newSet.add(e); + setOfSets.add(newSet); + } else { + throw new IllegalArgumentException("Element already contained"); + } + } + + private void addElementToExistingSet(T e, T canon) throws IllegalArgumentException { + if (!contains(e)) { + for (TreeSet treeSet : setOfSets) { + if (treeSet.first().equals(canon)) { + treeSet.add(e); + break; + } + } + } else { + throw new IllegalArgumentException("Element already contained"); + } + } + + private void mergeExistingSets(T e1, T e2) { + TreeSet set1; + TreeSet set2; + int size1; + int size2; + + for (TreeSet current : setOfSets) { + if (current.first().equals(e1)) { + set1 = current; + size1 = set1.size(); + } else if (current.first().equals(e2)) { + set2 = current; + size2 = set2.size(); + } + } + + // TODO potential problem: this could cause canon elem to not be the same as before (even though it needs to be) + if (size1 > size2) { + for (T current : set2) { + set1.add(current); + } + setOfSets.remove(set2); + } else { + for (T current : set1) { + set2.add(current); + } + setOfSets.remove(set1); + } + } + + private ArrayList getListOfCanonicalElements() { + ArrayList list = new ArrayList<>(); + + for (TreeSet treeSet : setOfSets) { + list.add(treeSet.first()); + } + + return list; + } @Override - public void addElementToNewSet(T e) {} + public UnionFind getEmptyInstanceOf() { + return new SortedUnionFind<>(); + } @Override - public Set getAllSubsets() { - return Set.of(); + public Set> getAllSubsets() { + return setOfSets; + } + + // consider making public and adding to interface + private boolean contains(T e) { + for (TreeSet current : setOfSets) { + if (current.contains(e)) { + return true; + } + } + return false; } } From b2902e2e10e8c6f240d8bf7907422d02d3fbd66b Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 11 Jun 2026 15:55:48 +0200 Subject: [PATCH 17/55] Rename SortedUnionFind to SortedTreeSetUnionFind --- .../{SortedUnionFind.java => SortedTreeSetUnionFind.java} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename src/org/sosy_lab/common/collect/{SortedUnionFind.java => SortedTreeSetUnionFind.java} (95%) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java similarity index 95% rename from src/org/sosy_lab/common/collect/SortedUnionFind.java rename to src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index 55793af52..eba5059ef 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -13,11 +13,11 @@ import java.util.Set; import java.util.TreeSet; -public class SortedUnionFind implements UnionFind { +public class SortedTreeSetUnionFind implements UnionFind { private HashSet> setOfSets; - private SortedUnionFind() { + private SortedTreeSetUnionFind() { // TODO setOfSets = new HashSet<>(); @@ -119,7 +119,7 @@ private ArrayList getListOfCanonicalElements() { @Override public UnionFind getEmptyInstanceOf() { - return new SortedUnionFind<>(); + return new SortedTreeSetUnionFind<>(); } @Override From fdcd004af8e35103de0c6b61089dd8f3f76eebf1 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 11 Jun 2026 16:00:28 +0200 Subject: [PATCH 18/55] Add new interface SortedUnionFind --- .../common/collect/SortedUnionFind.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/SortedUnionFind.java diff --git a/src/org/sosy_lab/common/collect/SortedUnionFind.java b/src/org/sosy_lab/common/collect/SortedUnionFind.java new file mode 100644 index 000000000..c9e1ba04b --- /dev/null +++ b/src/org/sosy_lab/common/collect/SortedUnionFind.java @@ -0,0 +1,23 @@ +// 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 +// +// SPDX-License-Identifier: Apache-2.0 + +package org.sosy_lab.common.collect; + +import java.util.Set; + +public interface SortedUnionFind { + T find(T e); + + void union(T e1, T e2); + + SortedUnionFind getEmptyInstanceOf(); + + Set getAllSubsets(); + + boolean contains(); +} From eaadc94599056b44525f8d12d2b55e72e5b276c0 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 11 Jun 2026 16:00:55 +0200 Subject: [PATCH 19/55] Add contains() to UnionFind --- src/org/sosy_lab/common/collect/UnionFind.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/org/sosy_lab/common/collect/UnionFind.java b/src/org/sosy_lab/common/collect/UnionFind.java index 72b31c565..817bd8ef7 100644 --- a/src/org/sosy_lab/common/collect/UnionFind.java +++ b/src/org/sosy_lab/common/collect/UnionFind.java @@ -18,4 +18,6 @@ public interface UnionFind { UnionFind getEmptyInstanceOf(); Set getAllSubsets(); + + boolean contains(); } From b12540f493869adde2a188d11c66f86b05c2047f Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 11 Jun 2026 16:03:58 +0200 Subject: [PATCH 20/55] Change SortedTreeSetUnionFind to implement SortedUnionFind instead of UnionFind --- .../sosy_lab/common/collect/SortedTreeSetUnionFind.java | 8 ++++---- src/org/sosy_lab/common/collect/SortedUnionFind.java | 2 +- src/org/sosy_lab/common/collect/UnionFind.java | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index eba5059ef..d0da8744c 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -13,7 +13,7 @@ import java.util.Set; import java.util.TreeSet; -public class SortedTreeSetUnionFind implements UnionFind { +public class SortedTreeSetUnionFind implements SortedUnionFind { private HashSet> setOfSets; @@ -118,7 +118,7 @@ private ArrayList getListOfCanonicalElements() { } @Override - public UnionFind getEmptyInstanceOf() { + public SortedUnionFind getEmptyInstanceOf() { return new SortedTreeSetUnionFind<>(); } @@ -127,8 +127,8 @@ public Set> getAllSubsets() { return setOfSets; } - // consider making public and adding to interface - private boolean contains(T e) { + @Override + public boolean contains(T e) { for (TreeSet current : setOfSets) { if (current.contains(e)) { return true; diff --git a/src/org/sosy_lab/common/collect/SortedUnionFind.java b/src/org/sosy_lab/common/collect/SortedUnionFind.java index c9e1ba04b..c87161e50 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFind.java @@ -19,5 +19,5 @@ public interface SortedUnionFind { Set getAllSubsets(); - boolean contains(); + boolean contains(T e); } diff --git a/src/org/sosy_lab/common/collect/UnionFind.java b/src/org/sosy_lab/common/collect/UnionFind.java index 817bd8ef7..15c013299 100644 --- a/src/org/sosy_lab/common/collect/UnionFind.java +++ b/src/org/sosy_lab/common/collect/UnionFind.java @@ -19,5 +19,5 @@ public interface UnionFind { Set getAllSubsets(); - boolean contains(); + boolean contains(T e); } From d9b5dc35f3f3510ec75b7074bb612dddcd5edd18 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 11 Jun 2026 16:05:43 +0200 Subject: [PATCH 21/55] Adapt UnsortedUnionFind to interface alterations --- .../sosy_lab/common/collect/UnsortedUnionFind.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/org/sosy_lab/common/collect/UnsortedUnionFind.java b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java index 7fb56b2b5..629e51d91 100644 --- a/src/org/sosy_lab/common/collect/UnsortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java @@ -111,14 +111,6 @@ public void addNewSet(Set input) throws IllegalArgumentException { setOfMaps.add(newMap); } - @Override - public void addSetOfSets(Set set) { - // TODO currently laid out for set of sets instead of set of maps - // could take set of HashMaps and call addNewSet() on each map - // problem: would need to specify type more clearly here but then won't override interface (but - // also can't alter type in interface as it wouldn't be generally applicable anymore) - } - @Override public void addElementToNewSet(T e) throws IllegalArgumentException { @@ -139,8 +131,8 @@ public Set> getAllSubsets() { return setOfMaps; } - // consider making public and adding to interface - private boolean contains(T e) { + @Override + public boolean contains(T e) { for (HashMap current : setOfMaps) { if (current.containsValue(e)) { return true; From 41ae904ba5ba6df0f29642b098a9797a33a95b5d Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 11 Jun 2026 16:06:57 +0200 Subject: [PATCH 22/55] Delete UnsortedUnionFind as currently not planning on using it --- .../common/collect/UnsortedUnionFind.java | 143 ------------------ 1 file changed, 143 deletions(-) delete mode 100644 src/org/sosy_lab/common/collect/UnsortedUnionFind.java diff --git a/src/org/sosy_lab/common/collect/UnsortedUnionFind.java b/src/org/sosy_lab/common/collect/UnsortedUnionFind.java deleted file mode 100644 index 629e51d91..000000000 --- a/src/org/sosy_lab/common/collect/UnsortedUnionFind.java +++ /dev/null @@ -1,143 +0,0 @@ -// 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 -// -// SPDX-License-Identifier: Apache-2.0 - -package org.sosy_lab.common.collect; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; - -public class UnsortedUnionFind implements UnionFind { - - /* - * CURRENT PROBLEMS: - * - trying to keep set type as flexible as possible but finding certain things can't be implemented without deciding on type - * - set of maps --> map each element of a disjoint set to canonical element of set (but then need to ensure duplicate elements do not occur) - * - not sure ArrayList is ideal for canonical elements - * - addSetOfSets might be dangerous as it relies on user providing set with the correct type of values - * - was trying to make one class work for both sorted and unsorted (defined by type of set user provides) but it's looking like they're going to be separate and this will be unsorted - */ - private HashSet> setOfMaps; - private ArrayList canonicalElements; // TODO remove if continues to be unnecessary - - private UnsortedUnionFind() { - setOfMaps = new HashSet<>(); - canonicalElements = new ArrayList<>(); - } - - @Override - public T find(T e) { - // TODO handle edge cases - - for (HashMap s : setOfMaps) { - if (s.containsValue(e)) { - Set keySet = s.keySet(); - if (keySet.size() >= 2) { - // TODO error as not all elements of set mapped to same canonical element - } else { - return keySet.iterator().next(); - } - } - } - } - - // currently only union by size supported for unsorted union-find - @Override - public void union(T e1, T e2) { - HashMap map1 = null; - HashMap map2 = null; - - for (HashMap current : setOfMaps) { - Set keySet = current.keySet(); - T e; - - if (keySet.size() >= 2) { - // TODO error as not all elements of set mapped to same canonical element - } else { - e = keySet.iterator().next(); - - if (e.equals(e1)) { - map1 = current; - } else if (e.equals(e2)) { - map2 = current; - } - } - - if (map1 != null && map2 != null) { - break; - } - } - if (map1.size() > map2.size()) { - for (T e : map2.values()) { - map1.putIfAbsent(e1, e); - } - canonicalElements.remove(e2); - } else { - for (T e : map1.values()) { - map2.putIfAbsent(e2, e); - } - canonicalElements.remove(e1); - } - } - - @Override - public UnionFind getEmptyInstanceOf() { - return new UnsortedUnionFind(); - } - - public void addNewSet(Set input) throws IllegalArgumentException { - T canon = null; - HashMap newMap = new HashMap<>(); - - for (T current : input) { - if (contains(current)) { - throw new IllegalArgumentException("Element already exists"); - } - - if (canon == null) { - canon = current; - canonicalElements.add(canon); - } - - newMap.putIfAbsent(canon, current); - } - - setOfMaps.add(newMap); - } - - @Override - public void addElementToNewSet(T e) throws IllegalArgumentException { - - if (contains(e)) { - // throw exception: element already contained - throw new IllegalArgumentException("Element already exists"); - } - - HashMap newMap = new HashMap<>(); - newMap.putIfAbsent(e, e); - - setOfMaps.add(newMap); - canonicalElements.add(e); - } - - @Override - public Set> getAllSubsets() { - return setOfMaps; - } - - @Override - public boolean contains(T e) { - for (HashMap current : setOfMaps) { - if (current.containsValue(e)) { - return true; - } - } - return false; - } -} From 188b2039f8a5dd2c652739a783f8e7fec1e56913 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 11 Jun 2026 16:30:14 +0200 Subject: [PATCH 23/55] Implement find() in SortedTreeSetUnionFind and refactor mergeExistingSets a little --- .../collect/SortedTreeSetUnionFind.java | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index d0da8744c..2cd4c1041 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -18,15 +18,18 @@ public class SortedTreeSetUnionFind implements SortedUnionFind { private HashSet> setOfSets; private SortedTreeSetUnionFind() { - // TODO - setOfSets = new HashSet<>(); } @Override - public T find(T e) { - // TODO - return null; + public T find(T e) throws IllegalArgumentException { + for (TreeSet current : setOfSets) { + if (current.contains(e)) { + return current.first(); + } + } + + throw new IllegalArgumentException("Element not contained"); } /* @@ -78,31 +81,31 @@ private void addElementToExistingSet(T e, T canon) throws IllegalArgumentExcepti } private void mergeExistingSets(T e1, T e2) { - TreeSet set1; - TreeSet set2; - int size1; - int size2; + TreeSet set1 = null; + TreeSet set2 = null; + for (TreeSet current : setOfSets) { if (current.first().equals(e1)) { set1 = current; - size1 = set1.size(); } else if (current.first().equals(e2)) { set2 = current; - size2 = set2.size(); } } - // TODO potential problem: this could cause canon elem to not be the same as before (even though it needs to be) + assert set1 != null; + assert set2 != null; + + int size1 = set1.size(); + int size2 = set2.size(); + + // TODO potential problem: this could cause canon elem to not be the same as before (even though + // it needs to be) if (size1 > size2) { - for (T current : set2) { - set1.add(current); - } + set1.addAll(set2); setOfSets.remove(set2); } else { - for (T current : set1) { - set2.add(current); - } + set2.addAll(set1); setOfSets.remove(set1); } } From 88f484d60886e16aab26181d1faa1b1f32c06ebc Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 12 Jun 2026 16:03:40 +0200 Subject: [PATCH 24/55] Revert back to direct constructor call instead of getEmptyInstanceOf() as static method problematic due to variable type --- src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java | 3 +++ src/org/sosy_lab/common/collect/SortedUnionFind.java | 2 +- src/org/sosy_lab/common/collect/UnionFind.java | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index 2cd4c1041..88c1f55bd 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -54,6 +54,7 @@ public void union(T e1, T e2) throws IllegalArgumentException { } else if (canonicalElements.contains(e2)) { addElementToExistingSet(e1, e2); } + //TODO case where neither elements are contained but also not equal } } @@ -120,10 +121,12 @@ private ArrayList getListOfCanonicalElements() { return list; } + /* @Override public SortedUnionFind getEmptyInstanceOf() { return new SortedTreeSetUnionFind<>(); } + */ @Override public Set> getAllSubsets() { diff --git a/src/org/sosy_lab/common/collect/SortedUnionFind.java b/src/org/sosy_lab/common/collect/SortedUnionFind.java index c87161e50..c2e277482 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFind.java @@ -15,7 +15,7 @@ public interface SortedUnionFind { void union(T e1, T e2); - SortedUnionFind getEmptyInstanceOf(); + //SortedUnionFind getEmptyInstanceOf(); Set getAllSubsets(); diff --git a/src/org/sosy_lab/common/collect/UnionFind.java b/src/org/sosy_lab/common/collect/UnionFind.java index 15c013299..e7d6d1deb 100644 --- a/src/org/sosy_lab/common/collect/UnionFind.java +++ b/src/org/sosy_lab/common/collect/UnionFind.java @@ -15,7 +15,7 @@ public interface UnionFind { void union(T e1, T e2); - UnionFind getEmptyInstanceOf(); + //UnionFind getEmptyInstanceOf(); Set getAllSubsets(); From b4321193d43bf76fea33a40a79085b262b0b7df8 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 12 Jun 2026 16:04:55 +0200 Subject: [PATCH 25/55] Make SortedTreeSetUnionFind's constructor public --- src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index 88c1f55bd..e41fbced0 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -17,7 +17,7 @@ public class SortedTreeSetUnionFind implements SortedUnionFind { private HashSet> setOfSets; - private SortedTreeSetUnionFind() { + public SortedTreeSetUnionFind() { setOfSets = new HashSet<>(); } From 94f1d26c237bf1c1d5edd813d1875b7967f9438d Mon Sep 17 00:00:00 2001 From: Colleen Date: Wed, 17 Jun 2026 15:40:43 +0200 Subject: [PATCH 26/55] Fix compilation errors due to forbidden types etc. in SortedTreeSetUnionFind; adapt interfaces accordingly --- .../collect/SortedTreeSetUnionFind.java | 32 ++++++++----------- .../common/collect/SortedUnionFind.java | 4 +-- .../sosy_lab/common/collect/UnionFind.java | 4 +-- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index e41fbced0..7a814d622 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -8,21 +8,23 @@ package org.sosy_lab.common.collect; +import com.google.errorprone.annotations.Var; import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.TreeSet; public class SortedTreeSetUnionFind implements SortedUnionFind { - private HashSet> setOfSets; + private final HashSet> setOfSets; public SortedTreeSetUnionFind() { setOfSets = new HashSet<>(); } @Override - public T find(T e) throws IllegalArgumentException { + public T find(T e) { for (TreeSet current : setOfSets) { if (current.contains(e)) { return current.first(); @@ -39,11 +41,11 @@ public T find(T e) throws IllegalArgumentException { - merge two existing sets: e1 and e2 canon. elem.s of sets to be merged */ @Override - public void union(T e1, T e2) throws IllegalArgumentException { + public void union(T e1, T e2) { if (e1.equals(e2)) { addElementAsNewSet(e1); } else { - ArrayList canonicalElements = getListOfCanonicalElements(); + List canonicalElements = getListOfCanonicalElements(); if (canonicalElements.contains(e1)) { if (canonicalElements.contains(e2)) { @@ -54,11 +56,11 @@ public void union(T e1, T e2) throws IllegalArgumentException { } else if (canonicalElements.contains(e2)) { addElementToExistingSet(e1, e2); } - //TODO case where neither elements are contained but also not equal + // TODO case where neither elements are contained but also not equal } } - private void addElementAsNewSet(T e) throws IllegalArgumentException { + private void addElementAsNewSet(T e) { if (!contains(e)) { TreeSet newSet = new TreeSet<>(); newSet.add(e); @@ -68,7 +70,7 @@ private void addElementAsNewSet(T e) throws IllegalArgumentException { } } - private void addElementToExistingSet(T e, T canon) throws IllegalArgumentException { + private void addElementToExistingSet(T e, T canon) { if (!contains(e)) { for (TreeSet treeSet : setOfSets) { if (treeSet.first().equals(canon)) { @@ -82,9 +84,8 @@ private void addElementToExistingSet(T e, T canon) throws IllegalArgumentExcepti } private void mergeExistingSets(T e1, T e2) { - TreeSet set1 = null; - TreeSet set2 = null; - + @Var TreeSet set1 = null; + @Var TreeSet set2 = null; for (TreeSet current : setOfSets) { if (current.first().equals(e1)) { @@ -111,7 +112,7 @@ private void mergeExistingSets(T e1, T e2) { } } - private ArrayList getListOfCanonicalElements() { + private List getListOfCanonicalElements() { ArrayList list = new ArrayList<>(); for (TreeSet treeSet : setOfSets) { @@ -121,15 +122,8 @@ private ArrayList getListOfCanonicalElements() { return list; } - /* - @Override - public SortedUnionFind getEmptyInstanceOf() { - return new SortedTreeSetUnionFind<>(); - } - */ - @Override - public Set> getAllSubsets() { + public Set> getAllSubsets() { return setOfSets; } diff --git a/src/org/sosy_lab/common/collect/SortedUnionFind.java b/src/org/sosy_lab/common/collect/SortedUnionFind.java index c2e277482..f60205bf4 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFind.java @@ -15,9 +15,7 @@ public interface SortedUnionFind { void union(T e1, T e2); - //SortedUnionFind getEmptyInstanceOf(); - - Set getAllSubsets(); + Set> getAllSubsets(); boolean contains(T e); } diff --git a/src/org/sosy_lab/common/collect/UnionFind.java b/src/org/sosy_lab/common/collect/UnionFind.java index e7d6d1deb..61ac7f2bc 100644 --- a/src/org/sosy_lab/common/collect/UnionFind.java +++ b/src/org/sosy_lab/common/collect/UnionFind.java @@ -15,9 +15,7 @@ public interface UnionFind { void union(T e1, T e2); - //UnionFind getEmptyInstanceOf(); - - Set getAllSubsets(); + Set> getAllSubsets(); boolean contains(T e); } From 97b7bd394baacd9c827114f58daab63c8d15d84e Mon Sep 17 00:00:00 2001 From: Colleen Date: Wed, 17 Jun 2026 16:09:18 +0200 Subject: [PATCH 27/55] Add SortedUnionFindTest with setup and first few tests; needs further work though --- .../common/collect/SortedUnionFindTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/SortedUnionFindTest.java diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java new file mode 100644 index 000000000..f6d347bec --- /dev/null +++ b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java @@ -0,0 +1,47 @@ +// 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 +// +// SPDX-License-Identifier: Apache-2.0 + +package org.sosy_lab.common.collect; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.Range; +import org.junit.BeforeClass; +import org.junit.Test; + +public class SortedUnionFindTest { + + static final Range LOW_NUMS = Range.closed(0, 4); + static final Range HIGH_NUMS = Range.closed(5, 9); + + static SortedUnionFind unionFind = new SortedTreeSetUnionFind<>(); + + @BeforeClass + public static void setup() { + unionFind = new SortedTreeSetUnionFind<>(); + + for (int i = 0; i <= 4; i++) { + unionFind.union(0, i); + } + for (int i = 5; i <= 9; i++) { + unionFind.union(5, i); + } + } + + @Test + public void testFind_ElementNotContained() { + assertThat(LOW_NUMS.contains(unionFind.find(8))).isFalse(); + assertThat(HIGH_NUMS.contains(unionFind.find(2))).isFalse(); + } + + @Test + public void testFind_ElementContained() { + assertThat(LOW_NUMS.contains(unionFind.find(2))).isTrue(); + assertThat(HIGH_NUMS.contains(unionFind.find(8))).isTrue(); + } +} From 2bafeee2e996a919c26788eef558d51dd9eb7124 Mon Sep 17 00:00:00 2001 From: Colleen Date: Wed, 17 Jun 2026 16:20:24 +0200 Subject: [PATCH 28/55] Add test to assure union keeps correct canonical element after union by size in SortedUnionFindTest --- .../sosy_lab/common/collect/SortedUnionFindTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java index f6d347bec..813899b74 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java @@ -44,4 +44,14 @@ public void testFind_ElementContained() { assertThat(LOW_NUMS.contains(unionFind.find(2))).isTrue(); assertThat(HIGH_NUMS.contains(unionFind.find(8))).isTrue(); } + + @Test + public void testUnion_CorrectCanonicalElementAfterUnionBySize() { + for(int i = 0; i <= 4; i++) { + assertThat(unionFind.find(i).equals(0)).isTrue(); + } + for(int i = 5; i <= 9; i++) { + assertThat(unionFind.find(i).equals(5)).isTrue(); + } + } } From 3e1f07a5e3c5b5b5a4ef3410359789fc02842e84 Mon Sep 17 00:00:00 2001 From: Colleen Date: Wed, 17 Jun 2026 16:29:43 +0200 Subject: [PATCH 29/55] Make test name more specific in SortedUnionFindTest --- src/org/sosy_lab/common/collect/SortedUnionFindTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java index 813899b74..83a71a1e3 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java @@ -11,6 +11,7 @@ import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.Range; +import java.util.Set; import org.junit.BeforeClass; import org.junit.Test; @@ -46,7 +47,7 @@ public void testFind_ElementContained() { } @Test - public void testUnion_CorrectCanonicalElementAfterUnionBySize() { + public void testUnion_CorrectCanonicalElementAndCorrectSubsetAfterUnionBySize() { for(int i = 0; i <= 4; i++) { assertThat(unionFind.find(i).equals(0)).isTrue(); } From 9504ebbd81697cfe229f38c08abf33a6facf4fc3 Mon Sep 17 00:00:00 2001 From: Colleen Date: Wed, 17 Jun 2026 17:16:49 +0200 Subject: [PATCH 30/55] Add test that covers mergeExistingSets() in SortedUnionFindTest --- .../collect/SortedTreeSetUnionFind.java | 2 +- .../common/collect/SortedUnionFindTest.java | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index 7a814d622..e23556f3b 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -108,7 +108,7 @@ private void mergeExistingSets(T e1, T e2) { setOfSets.remove(set2); } else { set2.addAll(set1); - setOfSets.remove(set1); + setOfSets.remove(set1); // TODO it seems removal doesn't actually take place though it should } } diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java index 83a71a1e3..d93876834 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java @@ -11,7 +11,6 @@ import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.Range; -import java.util.Set; import org.junit.BeforeClass; import org.junit.Test; @@ -48,11 +47,31 @@ public void testFind_ElementContained() { @Test public void testUnion_CorrectCanonicalElementAndCorrectSubsetAfterUnionBySize() { - for(int i = 0; i <= 4; i++) { + assertThat(unionFind.getAllSubsets().size() == 2).isTrue(); + + for (int i = 0; i <= 4; i++) { assertThat(unionFind.find(i).equals(0)).isTrue(); } - for(int i = 5; i <= 9; i++) { + for (int i = 5; i <= 9; i++) { assertThat(unionFind.find(i).equals(5)).isTrue(); } } + + @Test + public void testUnion_MergeExistingSubsets() { + unionFind.union(0, 5); + + assertThat(unionFind.getAllSubsets().size() == 1).isTrue(); + + boolean canonUnknown = true; + Integer canon = null; + + for (int i = 0; i <= 9; i++) { + if (canonUnknown) { + canon = unionFind.find(i); + canonUnknown = false; + } + assertThat(unionFind.find(i).equals(canon)).isTrue(); + } + } } From 15e7096114b5640771c5898297bd5cffa9016567 Mon Sep 17 00:00:00 2001 From: Colleen Date: Wed, 17 Jun 2026 17:21:04 +0200 Subject: [PATCH 31/55] A couple small style fixes --- src/org/sosy_lab/common/collect/SortedUnionFindTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java index d93876834..df0f0e36d 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java @@ -11,6 +11,7 @@ import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.Range; +import com.google.errorprone.annotations.Var; import org.junit.BeforeClass; import org.junit.Test; @@ -63,8 +64,8 @@ public void testUnion_MergeExistingSubsets() { assertThat(unionFind.getAllSubsets().size() == 1).isTrue(); - boolean canonUnknown = true; - Integer canon = null; + @Var boolean canonUnknown = true; + @Var Integer canon = null; for (int i = 0; i <= 9; i++) { if (canonUnknown) { From f4c52134b5b98390872417ada9d8e78ccc318cc3 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 18 Jun 2026 15:28:53 +0200 Subject: [PATCH 32/55] Change variable declarations from classes to interfaces where needed in SortedTreeSetUnionFind --- .../collect/SortedTreeSetUnionFind.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index e23556f3b..02c3ccb7c 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -12,12 +12,13 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.NavigableSet; import java.util.Set; import java.util.TreeSet; public class SortedTreeSetUnionFind implements SortedUnionFind { - private final HashSet> setOfSets; + private final HashSet> setOfSets; public SortedTreeSetUnionFind() { setOfSets = new HashSet<>(); @@ -25,7 +26,7 @@ public SortedTreeSetUnionFind() { @Override public T find(T e) { - for (TreeSet current : setOfSets) { + for (NavigableSet current : setOfSets) { if (current.contains(e)) { return current.first(); } @@ -62,7 +63,7 @@ public void union(T e1, T e2) { private void addElementAsNewSet(T e) { if (!contains(e)) { - TreeSet newSet = new TreeSet<>(); + NavigableSet newSet = new TreeSet<>(); newSet.add(e); setOfSets.add(newSet); } else { @@ -72,7 +73,7 @@ private void addElementAsNewSet(T e) { private void addElementToExistingSet(T e, T canon) { if (!contains(e)) { - for (TreeSet treeSet : setOfSets) { + for (NavigableSet treeSet : setOfSets) { if (treeSet.first().equals(canon)) { treeSet.add(e); break; @@ -84,10 +85,10 @@ private void addElementToExistingSet(T e, T canon) { } private void mergeExistingSets(T e1, T e2) { - @Var TreeSet set1 = null; - @Var TreeSet set2 = null; + @Var NavigableSet set1 = null; + @Var NavigableSet set2 = null; - for (TreeSet current : setOfSets) { + for (NavigableSet current : setOfSets) { if (current.first().equals(e1)) { set1 = current; } else if (current.first().equals(e2)) { @@ -113,9 +114,9 @@ private void mergeExistingSets(T e1, T e2) { } private List getListOfCanonicalElements() { - ArrayList list = new ArrayList<>(); + List list = new ArrayList<>(); - for (TreeSet treeSet : setOfSets) { + for (NavigableSet treeSet : setOfSets) { list.add(treeSet.first()); } @@ -129,7 +130,7 @@ public Set> getAllSubsets() { @Override public boolean contains(T e) { - for (TreeSet current : setOfSets) { + for (NavigableSet current : setOfSets) { if (current.contains(e)) { return true; } From 0ee08933b109a834682ccc506a82bf1586ea4fb8 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 18 Jun 2026 15:39:44 +0200 Subject: [PATCH 33/55] Change variable declarations from classes to interfaces where needed in SortedTreeSetUnionFind --- src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index 02c3ccb7c..f15645e1e 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -18,7 +18,7 @@ public class SortedTreeSetUnionFind implements SortedUnionFind { - private final HashSet> setOfSets; + private final Set> setOfSets; public SortedTreeSetUnionFind() { setOfSets = new HashSet<>(); From 71b0f1612c9bfab5a4aa0259504cdfe568834112 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 18 Jun 2026 16:48:19 +0200 Subject: [PATCH 34/55] Add missing edge case in union() in SortedTreeSetUnionFind --- src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index f15645e1e..842fb0b5f 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -56,8 +56,10 @@ public void union(T e1, T e2) { } } else if (canonicalElements.contains(e2)) { addElementToExistingSet(e1, e2); + } else { + addElementAsNewSet(e1); + addElementToExistingSet(e2, e1); } - // TODO case where neither elements are contained but also not equal } } From d07c8757db42e538cb3fb1897e602fdf1fc1642d Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 19 Jun 2026 09:04:31 +0200 Subject: [PATCH 35/55] Fix bug in addToExistingSet() in SortedTreeSetUnionFind --- src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index 842fb0b5f..6fa021249 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -77,7 +77,9 @@ private void addElementToExistingSet(T e, T canon) { if (!contains(e)) { for (NavigableSet treeSet : setOfSets) { if (treeSet.first().equals(canon)) { + setOfSets.remove(treeSet); treeSet.add(e); + setOfSets.add(treeSet); break; } } From 0a8f5ec1fe984fda79bdf7cec4b06c15d7ad0708 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 19 Jun 2026 15:17:23 +0200 Subject: [PATCH 36/55] Add testUnion_InsertingDuplicateElementFails() in SortedUnionFindTest --- .../common/collect/SortedUnionFindTest.java | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java index df0f0e36d..c51016aaa 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java @@ -64,8 +64,10 @@ public void testUnion_MergeExistingSubsets() { assertThat(unionFind.getAllSubsets().size() == 1).isTrue(); - @Var boolean canonUnknown = true; - @Var Integer canon = null; + @Var + boolean canonUnknown = true; + @Var + Integer canon = null; for (int i = 0; i <= 9; i++) { if (canonUnknown) { @@ -75,4 +77,28 @@ public void testUnion_MergeExistingSubsets() { assertThat(unionFind.find(i).equals(canon)).isTrue(); } } -} + + @Test + public void testUnion_InsertingDuplicateElementFails() { + @Var + Exception exception = null; + try { + //case: attempting to insert into subset it is already in + unionFind.union(1, 0); + } catch (Exception e) { + exception = e; + } + assertThat(exception).isNotNull(); + assertThat(exception).isInstanceOf(IllegalArgumentException.class); + + exception = null; + try { + //case: attempting to insert into subset it is not in + unionFind.union(1, 5); + } catch (Exception e) { + exception = e; + } + assertThat(exception).isNotNull(); + assertThat(exception).isInstanceOf(IllegalArgumentException.class); + } +} \ No newline at end of file From 469685fddbfa9b67d3ce0cf94238ea81ea9c7b7e Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 19 Jun 2026 16:56:37 +0200 Subject: [PATCH 37/55] Remove testUnion_InsertingDuplicateElementFails() in SortedUnionFindTest for now as it does not meet codestyle requirements and a quick fix is not available at the moment --- .../common/collect/SortedUnionFindTest.java | 32 ++----------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java index c51016aaa..df0f0e36d 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java @@ -64,10 +64,8 @@ public void testUnion_MergeExistingSubsets() { assertThat(unionFind.getAllSubsets().size() == 1).isTrue(); - @Var - boolean canonUnknown = true; - @Var - Integer canon = null; + @Var boolean canonUnknown = true; + @Var Integer canon = null; for (int i = 0; i <= 9; i++) { if (canonUnknown) { @@ -77,28 +75,4 @@ public void testUnion_MergeExistingSubsets() { assertThat(unionFind.find(i).equals(canon)).isTrue(); } } - - @Test - public void testUnion_InsertingDuplicateElementFails() { - @Var - Exception exception = null; - try { - //case: attempting to insert into subset it is already in - unionFind.union(1, 0); - } catch (Exception e) { - exception = e; - } - assertThat(exception).isNotNull(); - assertThat(exception).isInstanceOf(IllegalArgumentException.class); - - exception = null; - try { - //case: attempting to insert into subset it is not in - unionFind.union(1, 5); - } catch (Exception e) { - exception = e; - } - assertThat(exception).isNotNull(); - assertThat(exception).isInstanceOf(IllegalArgumentException.class); - } -} \ No newline at end of file +} From 3e42ae76c815bc7cb1a0f4e17c449fa01c9ca681 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 19 Jun 2026 17:07:19 +0200 Subject: [PATCH 38/55] Add testUnion_ConstantCanonicalElementDuringNonlinearInsertion() in SortedUnionFindTest to ensure the canonical elements of subsets do not change at times they shouldn't (during union by size) --- .../common/collect/SortedUnionFindTest.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java index df0f0e36d..5e0aae8e3 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java @@ -75,4 +75,29 @@ public void testUnion_MergeExistingSubsets() { assertThat(unionFind.find(i).equals(canon)).isTrue(); } } + + @Test + public void testUnion_ConstantCanonicalElementDuringNonlinearInsertion() { + SortedUnionFind newUnionFind = new SortedTreeSetUnionFind<>(); + + newUnionFind.union(3, 3); + newUnionFind.union(3, 2); + newUnionFind.union(3, 5); + newUnionFind.union(3, 1); + newUnionFind.union(3, 8); + newUnionFind.union(3, 6); + newUnionFind.union(3, 9); + newUnionFind.union(3, 7); + newUnionFind.union(3, 4); + + assertThat(newUnionFind.find(3)).isEqualTo(3); + assertThat(newUnionFind.find(2)).isEqualTo(3); + assertThat(newUnionFind.find(5)).isEqualTo(3); + assertThat(newUnionFind.find(1)).isEqualTo(3); + assertThat(newUnionFind.find(8)).isEqualTo(3); + assertThat(newUnionFind.find(6)).isEqualTo(3); + assertThat(newUnionFind.find(9)).isEqualTo(3); + assertThat(newUnionFind.find(7)).isEqualTo(3); + assertThat(newUnionFind.find(4)).isEqualTo(3); + } } From f765a9c93eafabffde0345fbfb27cc1b4c0c2bcc Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 19 Jun 2026 17:08:40 +0200 Subject: [PATCH 39/55] Switch usage of Set to Map to facilitate tracking each subset's canonical element in UnionFind and SortedUnionFind --- src/org/sosy_lab/common/collect/SortedUnionFind.java | 3 ++- src/org/sosy_lab/common/collect/UnionFind.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFind.java b/src/org/sosy_lab/common/collect/SortedUnionFind.java index f60205bf4..ca201ca5c 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFind.java @@ -8,6 +8,7 @@ package org.sosy_lab.common.collect; +import java.util.Collection; import java.util.Set; public interface SortedUnionFind { @@ -15,7 +16,7 @@ public interface SortedUnionFind { void union(T e1, T e2); - Set> getAllSubsets(); + Collection> getAllSubsets(); boolean contains(T e); } diff --git a/src/org/sosy_lab/common/collect/UnionFind.java b/src/org/sosy_lab/common/collect/UnionFind.java index 61ac7f2bc..21999beb9 100644 --- a/src/org/sosy_lab/common/collect/UnionFind.java +++ b/src/org/sosy_lab/common/collect/UnionFind.java @@ -8,6 +8,7 @@ package org.sosy_lab.common.collect; +import java.util.Collection; import java.util.Set; public interface UnionFind { @@ -15,7 +16,7 @@ public interface UnionFind { void union(T e1, T e2); - Set> getAllSubsets(); + Collection> getAllSubsets(); boolean contains(T e); } From 6221788e09f4300924784b6fde010ee4daba9f8d Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 19 Jun 2026 17:09:24 +0200 Subject: [PATCH 40/55] Implement changes to interface (exchange Set for Map) in SortedTreeSetUnionFind --- .../collect/SortedTreeSetUnionFind.java | 57 ++++++++----------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index 6fa021249..572977913 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -9,26 +9,30 @@ package org.sosy_lab.common.collect; import com.google.errorprone.annotations.Var; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import java.util.NavigableSet; import java.util.Set; import java.util.TreeSet; public class SortedTreeSetUnionFind implements SortedUnionFind { - private final Set> setOfSets; + private final Map> setOfSets; public SortedTreeSetUnionFind() { - setOfSets = new HashSet<>(); + setOfSets = new HashMap<>(); } @Override public T find(T e) { - for (NavigableSet current : setOfSets) { + for (NavigableSet current : setOfSets.values()) { if (current.contains(e)) { - return current.first(); + for (T element : current) { + if (setOfSets.containsKey(element)) { + return element; + } + } } } @@ -46,7 +50,7 @@ public void union(T e1, T e2) { if (e1.equals(e2)) { addElementAsNewSet(e1); } else { - List canonicalElements = getListOfCanonicalElements(); + Set canonicalElements = setOfSets.keySet(); if (canonicalElements.contains(e1)) { if (canonicalElements.contains(e2)) { @@ -67,7 +71,7 @@ private void addElementAsNewSet(T e) { if (!contains(e)) { NavigableSet newSet = new TreeSet<>(); newSet.add(e); - setOfSets.add(newSet); + setOfSets.put(e, newSet); } else { throw new IllegalArgumentException("Element already contained"); } @@ -75,11 +79,10 @@ private void addElementAsNewSet(T e) { private void addElementToExistingSet(T e, T canon) { if (!contains(e)) { - for (NavigableSet treeSet : setOfSets) { - if (treeSet.first().equals(canon)) { - setOfSets.remove(treeSet); - treeSet.add(e); - setOfSets.add(treeSet); + for (NavigableSet currentSet : setOfSets.values()) { + if (currentSet.contains(canon)) { + currentSet.add(e); + setOfSets.replace(canon, currentSet); break; } } @@ -92,10 +95,10 @@ private void mergeExistingSets(T e1, T e2) { @Var NavigableSet set1 = null; @Var NavigableSet set2 = null; - for (NavigableSet current : setOfSets) { - if (current.first().equals(e1)) { + for (NavigableSet current : setOfSets.values()) { + if (current.contains(e1)) { set1 = current; - } else if (current.first().equals(e2)) { + } else if (current.contains(e2)) { set2 = current; } } @@ -110,31 +113,21 @@ private void mergeExistingSets(T e1, T e2) { // it needs to be) if (size1 > size2) { set1.addAll(set2); - setOfSets.remove(set2); + setOfSets.remove(e2); } else { set2.addAll(set1); - setOfSets.remove(set1); // TODO it seems removal doesn't actually take place though it should - } - } - - private List getListOfCanonicalElements() { - List list = new ArrayList<>(); - - for (NavigableSet treeSet : setOfSets) { - list.add(treeSet.first()); + setOfSets.remove(e1); // TODO it seems removal doesn't actually take place though it should } - - return list; } @Override - public Set> getAllSubsets() { - return setOfSets; + public Collection> getAllSubsets() { + return setOfSets.values(); } @Override public boolean contains(T e) { - for (NavigableSet current : setOfSets) { + for (NavigableSet current : setOfSets.values()) { if (current.contains(e)) { return true; } From 7a175587c7e51738ae7a9b9d99a4d27b01f998d8 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 19 Jun 2026 17:11:15 +0200 Subject: [PATCH 41/55] Remove resolved TODO notes in SortedTreeSetUnionFind --- src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index 572977913..074dc7eb0 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -109,14 +109,12 @@ private void mergeExistingSets(T e1, T e2) { int size1 = set1.size(); int size2 = set2.size(); - // TODO potential problem: this could cause canon elem to not be the same as before (even though - // it needs to be) if (size1 > size2) { set1.addAll(set2); setOfSets.remove(e2); } else { set2.addAll(set1); - setOfSets.remove(e1); // TODO it seems removal doesn't actually take place though it should + setOfSets.remove(e1); } } From a3ce5408c95e7bf52ec500dc5f913a7f5b5c6c83 Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 23 Jun 2026 09:35:02 +0200 Subject: [PATCH 42/55] Add documentation to UnionFind --- .../sosy_lab/common/collect/UnionFind.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/org/sosy_lab/common/collect/UnionFind.java b/src/org/sosy_lab/common/collect/UnionFind.java index 21999beb9..73220f5f8 100644 --- a/src/org/sosy_lab/common/collect/UnionFind.java +++ b/src/org/sosy_lab/common/collect/UnionFind.java @@ -11,12 +11,42 @@ import java.util.Collection; import java.util.Set; +/** + * Interface for a sorted Union-Find or Disjoint-Set data structure. Uses a {@link Collection} of + * {@link Set}s. + * + * @param type of elements added to the Union-Find. + */ public interface UnionFind { + /** + * 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 + */ T find(T e); + /** + * Merges the sets represented by the two input values according to standard Union-Find behaviour. + * + * @param e1 first element + * @param e2 second element + */ void union(T e1, T e2); + /** + * Provides a {@link Collection} containing all current subsets. + * + * @return {@link Collection} containing all current subsets + */ Collection> getAllSubsets(); + /** + * 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 + */ boolean contains(T e); } From 493a22053da1701d44205d6da0f29df174668e53 Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 23 Jun 2026 09:35:14 +0200 Subject: [PATCH 43/55] Add documentation to SortedUnionFind --- .../common/collect/SortedUnionFind.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFind.java b/src/org/sosy_lab/common/collect/SortedUnionFind.java index ca201ca5c..c7df2667c 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFind.java @@ -11,12 +11,43 @@ import java.util.Collection; import java.util.Set; +/** + * Interface for a sorted Union-Find or Disjoint-Set data structure. Uses a {@link Collection} of + * {@link Set}s. + * + * @param type of elements added to the Union-Find. Must be {@link Comparable} to ensure correct + * ordering. + */ public interface SortedUnionFind { + /** + * 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 + */ T find(T e); + /** + * Merges the sets represented by the two input values according to standard Union-Find behaviour. + * + * @param e1 first element + * @param e2 second element + */ void union(T e1, T e2); + /** + * Provides a {@link Collection} containing all current subsets. + * + * @return {@link Collection} containing all current subsets + */ Collection> getAllSubsets(); + /** + * 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 + */ boolean contains(T e); } From 7370d471718707bfe05e6e5d17a17b5ae2d833be Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 23 Jun 2026 09:59:14 +0200 Subject: [PATCH 44/55] Add documentation to SortedTreeSetUnionFind --- .../collect/SortedTreeSetUnionFind.java | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index 074dc7eb0..90a5cb6e8 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -16,14 +16,31 @@ 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 type of elements added to the Union-Find. Must be {@link Comparable} to ensure correct + * ordering. + */ public class SortedTreeSetUnionFind implements SortedUnionFind { private final Map> 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) { for (NavigableSet current : setOfSets.values()) { @@ -39,11 +56,15 @@ public T find(T e) { throw new IllegalArgumentException("Element not contained"); } - /* - USE - - add new element to own new set: e1 and e2 both element to be added - - add new element to existing set: one e new element, other e canon. elem. of set to add to - - merge two existing sets: e1 and e2 canon. elem.s of sets to be merged + /** + * Merges the sets represented by the two input values according to standard Union-Find behaviour. + * + *

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) { @@ -118,11 +139,23 @@ private void mergeExistingSets(T e1, T e2) { } } + /** + * Provides a {@link Collection} containing all current subsets. + * + * @return {@link Collection} containing all current subsets + */ @Override public Collection> 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) { for (NavigableSet current : setOfSets.values()) { From 61437f3c0155d8bf01ce3a3bb2aa3c66ed7e3424 Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 23 Jun 2026 10:06:26 +0200 Subject: [PATCH 45/55] Declare type T to be of Comparable in SortedUnionFind and SortedTreeSetUnionFind --- src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java | 2 +- src/org/sosy_lab/common/collect/SortedUnionFind.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index 90a5cb6e8..813853cba 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -25,7 +25,7 @@ * @param type of elements added to the Union-Find. Must be {@link Comparable} to ensure correct * ordering. */ -public class SortedTreeSetUnionFind implements SortedUnionFind { +public class SortedTreeSetUnionFind> implements SortedUnionFind { private final Map> setOfSets; diff --git a/src/org/sosy_lab/common/collect/SortedUnionFind.java b/src/org/sosy_lab/common/collect/SortedUnionFind.java index c7df2667c..818e91cc0 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFind.java @@ -18,7 +18,7 @@ * @param type of elements added to the Union-Find. Must be {@link Comparable} to ensure correct * ordering. */ -public interface SortedUnionFind { +public interface SortedUnionFind> { /** * Returns the canonical element of the set containing the provided element. * From 352907ce83268145df923eb60efcb5ae39a0b2b5 Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 23 Jun 2026 10:31:33 +0200 Subject: [PATCH 46/55] Add AbstractImmutableUnionFind --- .../collect/AbstractImmutableUnionFind.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/AbstractImmutableUnionFind.java diff --git a/src/org/sosy_lab/common/collect/AbstractImmutableUnionFind.java b/src/org/sosy_lab/common/collect/AbstractImmutableUnionFind.java new file mode 100644 index 000000000..d964a951a --- /dev/null +++ b/src/org/sosy_lab/common/collect/AbstractImmutableUnionFind.java @@ -0,0 +1,36 @@ +// 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 +// +// SPDX-License-Identifier: Apache-2.0 + +package org.sosy_lab.common.collect; + +import com.google.errorprone.annotations.DoNotCall; + +public abstract class AbstractImmutableUnionFind implements UnionFind { + + /** + * @throws UnsupportedOperationException Always. + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + @DoNotCall + public final T find(T e) { + throw new UnsupportedOperationException(); + } + + /** + * @throws UnsupportedOperationException Always. + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + @DoNotCall + public final void union(T e1, T e2) { + throw new UnsupportedOperationException(); + } +} From 92088a195cc181a0fe5ed8c11cfce4ceba6072ac Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 23 Jun 2026 10:33:43 +0200 Subject: [PATCH 47/55] Add AbstractImmutableSortedUnionFind --- .../AbstractImmutableSortedUnionFind.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/AbstractImmutableSortedUnionFind.java diff --git a/src/org/sosy_lab/common/collect/AbstractImmutableSortedUnionFind.java b/src/org/sosy_lab/common/collect/AbstractImmutableSortedUnionFind.java new file mode 100644 index 000000000..5dfc53838 --- /dev/null +++ b/src/org/sosy_lab/common/collect/AbstractImmutableSortedUnionFind.java @@ -0,0 +1,36 @@ +// 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 +// +// SPDX-License-Identifier: Apache-2.0 + +package org.sosy_lab.common.collect; + +import com.google.errorprone.annotations.DoNotCall; + +public abstract class AbstractImmutableSortedUnionFind> + implements SortedUnionFind { + /** + * @throws UnsupportedOperationException Always. + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + @DoNotCall + public final T find(T e) { + throw new UnsupportedOperationException(); + } + + /** + * @throws UnsupportedOperationException Always. + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + @DoNotCall + public final void union(T e1, T e2) { + throw new UnsupportedOperationException(); + } +} From d2234c8f93e4a694ebf175d53d714fac3dd35eaa Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 23 Jun 2026 13:25:13 +0200 Subject: [PATCH 48/55] Add fix to stop SortedTreeSetUnionFind from failing "test nulls"; not entirely sure what I did though so someone qualified will need to look it over --- .../common/collect/PackageSanityTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/org/sosy_lab/common/collect/PackageSanityTest.java b/src/org/sosy_lab/common/collect/PackageSanityTest.java index d349efcf9..4d6ad7dfc 100644 --- a/src/org/sosy_lab/common/collect/PackageSanityTest.java +++ b/src/org/sosy_lab/common/collect/PackageSanityTest.java @@ -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 { @@ -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() {} } From fca310224f539544a1457ee459bd071621f86ef6 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 25 Jun 2026 15:35:49 +0200 Subject: [PATCH 49/55] Remove find() from deprecated methods; shouldn't have ended up there in the first place --- .../collect/AbstractImmutableSortedUnionFind.java | 11 ----------- .../common/collect/AbstractImmutableUnionFind.java | 12 ------------ 2 files changed, 23 deletions(-) diff --git a/src/org/sosy_lab/common/collect/AbstractImmutableSortedUnionFind.java b/src/org/sosy_lab/common/collect/AbstractImmutableSortedUnionFind.java index 5dfc53838..4a010aa79 100644 --- a/src/org/sosy_lab/common/collect/AbstractImmutableSortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/AbstractImmutableSortedUnionFind.java @@ -12,17 +12,6 @@ public abstract class AbstractImmutableSortedUnionFind> implements SortedUnionFind { - /** - * @throws UnsupportedOperationException Always. - * @deprecated Unsupported operation. - */ - @Deprecated - @Override - @DoNotCall - public final T find(T e) { - throw new UnsupportedOperationException(); - } - /** * @throws UnsupportedOperationException Always. * @deprecated Unsupported operation. diff --git a/src/org/sosy_lab/common/collect/AbstractImmutableUnionFind.java b/src/org/sosy_lab/common/collect/AbstractImmutableUnionFind.java index d964a951a..d1fd7db87 100644 --- a/src/org/sosy_lab/common/collect/AbstractImmutableUnionFind.java +++ b/src/org/sosy_lab/common/collect/AbstractImmutableUnionFind.java @@ -11,18 +11,6 @@ import com.google.errorprone.annotations.DoNotCall; public abstract class AbstractImmutableUnionFind implements UnionFind { - - /** - * @throws UnsupportedOperationException Always. - * @deprecated Unsupported operation. - */ - @Deprecated - @Override - @DoNotCall - public final T find(T e) { - throw new UnsupportedOperationException(); - } - /** * @throws UnsupportedOperationException Always. * @deprecated Unsupported operation. From 4dd0356a6b19f5e385bc4557c6bb2729dc9e06b8 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 25 Jun 2026 15:41:53 +0200 Subject: [PATCH 50/55] Add interface PersistentSortedUnionFind --- .../collect/PersistentSortedUnionFind.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/PersistentSortedUnionFind.java diff --git a/src/org/sosy_lab/common/collect/PersistentSortedUnionFind.java b/src/org/sosy_lab/common/collect/PersistentSortedUnionFind.java new file mode 100644 index 000000000..0cd8ba08f --- /dev/null +++ b/src/org/sosy_lab/common/collect/PersistentSortedUnionFind.java @@ -0,0 +1,29 @@ +// 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 +// +// 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 java.util.Map; +import java.util.NavigableSet; + +public interface PersistentSortedUnionFind> extends SortedUnionFind { + + @CheckReturnValue + Map> unionAndCopy(T e1, T e2); + + /** + * @throws UnsupportedOperationException Always. + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + @DoNotCall + void union(T e1, T e2); +} From f7ebd96b54d387c19c29e06d0e8c12af01587e72 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 25 Jun 2026 15:45:07 +0200 Subject: [PATCH 51/55] Add interface PersistentUnionFind --- .../common/collect/PersistentUnionFind.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/PersistentUnionFind.java diff --git a/src/org/sosy_lab/common/collect/PersistentUnionFind.java b/src/org/sosy_lab/common/collect/PersistentUnionFind.java new file mode 100644 index 000000000..48e368104 --- /dev/null +++ b/src/org/sosy_lab/common/collect/PersistentUnionFind.java @@ -0,0 +1,28 @@ +// 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 +// +// 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 java.util.Map; +import java.util.NavigableSet; + +public interface PersistentUnionFind extends UnionFind{ + @CheckReturnValue + Map> unionAndCopy(T e1, T e2); + + /** + * @throws UnsupportedOperationException Always. + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + @DoNotCall + void union(T e1, T e2); +} From 89e3a797abfa17c27eca91627e286727ecfc6c32 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 25 Jun 2026 15:55:45 +0200 Subject: [PATCH 52/55] Add documentation to PersistentUnionFind and PersistentSortedUnionFind --- .../collect/PersistentSortedUnionFind.java | 19 ++++++++++++++++ .../common/collect/PersistentUnionFind.java | 22 ++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/PersistentSortedUnionFind.java b/src/org/sosy_lab/common/collect/PersistentSortedUnionFind.java index 0cd8ba08f..738fabd12 100644 --- a/src/org/sosy_lab/common/collect/PersistentSortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/PersistentSortedUnionFind.java @@ -10,11 +10,30 @@ 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. + * + *

All modifying operations inherited from {@link SortedUnionFind} are not supported and will + * always throw {@link UnsupportedOperationException}. + * + * @param The type of values. + */ +@Immutable(containerOf = "T") public interface PersistentSortedUnionFind> extends SortedUnionFind { + /** + * 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> unionAndCopy(T e1, T e2); diff --git a/src/org/sosy_lab/common/collect/PersistentUnionFind.java b/src/org/sosy_lab/common/collect/PersistentUnionFind.java index 48e368104..f6b0fba2b 100644 --- a/src/org/sosy_lab/common/collect/PersistentUnionFind.java +++ b/src/org/sosy_lab/common/collect/PersistentUnionFind.java @@ -10,10 +10,30 @@ 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; -public interface PersistentUnionFind extends UnionFind{ +/** + * 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. + * + *

All modifying operations inherited from {@link UnionFind} are not supported and will always + * throw {@link UnsupportedOperationException}. + * + * @param The type of values. + */ +@Immutable(containerOf = "T") +public interface PersistentUnionFind extends UnionFind { + + /** + * 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> unionAndCopy(T e1, T e2); From f45227466b547a41e676bcaf00bad3c67656ee3e Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 26 Jun 2026 11:29:49 +0200 Subject: [PATCH 53/55] Check input values are not null in SortedTreeSetUnionFind --- .../common/collect/SortedTreeSetUnionFind.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index 813853cba..22bf56c0b 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -8,6 +8,7 @@ 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; @@ -43,6 +44,8 @@ public SortedTreeSetUnionFind() { */ @Override public T find(T e) { + + Preconditions.checkNotNull(e); for (NavigableSet current : setOfSets.values()) { if (current.contains(e)) { for (T element : current) { @@ -56,6 +59,8 @@ public T find(T e) { throw new IllegalArgumentException("Element not contained"); } + // TODO merge instead of throwing exceptions + /** * Merges the sets represented by the two input values according to standard Union-Find behaviour. * @@ -68,6 +73,10 @@ public T find(T e) { */ @Override public void union(T e1, T e2) { + + Preconditions.checkNotNull(e1); + Preconditions.checkNotNull(e2); + if (e1.equals(e2)) { addElementAsNewSet(e1); } else { @@ -89,6 +98,7 @@ public void union(T e1, T e2) { } private void addElementAsNewSet(T e) { + if (!contains(e)) { NavigableSet newSet = new TreeSet<>(); newSet.add(e); @@ -99,6 +109,7 @@ private void addElementAsNewSet(T e) { } private void addElementToExistingSet(T e, T canon) { + if (!contains(e)) { for (NavigableSet currentSet : setOfSets.values()) { if (currentSet.contains(canon)) { @@ -113,6 +124,7 @@ private void addElementToExistingSet(T e, T canon) { } private void mergeExistingSets(T e1, T e2) { + @Var NavigableSet set1 = null; @Var NavigableSet set2 = null; @@ -158,6 +170,9 @@ public Collection> getAllSubsets() { */ @Override public boolean contains(T e) { + + Preconditions.checkNotNull(e); + for (NavigableSet current : setOfSets.values()) { if (current.contains(e)) { return true; From a0dacef36275d54b585045bbd56eaac8af45ef00 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 26 Jun 2026 11:53:44 +0200 Subject: [PATCH 54/55] Refactor union() to not through unnecessary exceptions in SortedTreeSetUnionFind --- src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index 22bf56c0b..a1cfa032f 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -103,8 +103,6 @@ private void addElementAsNewSet(T e) { NavigableSet newSet = new TreeSet<>(); newSet.add(e); setOfSets.put(e, newSet); - } else { - throw new IllegalArgumentException("Element already contained"); } } @@ -119,7 +117,7 @@ private void addElementToExistingSet(T e, T canon) { } } } else { - throw new IllegalArgumentException("Element already contained"); + mergeExistingSets(e, canon); } } From 56133befd3ad0148cd05ca9eede3d9bf15ea69a3 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 26 Jun 2026 11:54:08 +0200 Subject: [PATCH 55/55] Refactor union() to not through unnecessary exceptions in SortedTreeSetUnionFind --- src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index a1cfa032f..13cf4380e 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -59,8 +59,6 @@ public T find(T e) { throw new IllegalArgumentException("Element not contained"); } - // TODO merge instead of throwing exceptions - /** * Merges the sets represented by the two input values according to standard Union-Find behaviour. *