From 63a45e6f437a4fce1260d890ff1de7c9b37a9bb9 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 25 May 2026 13:52:23 +0200 Subject: [PATCH 001/101] 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 002/101] 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 003/101] 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 004/101] 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 005/101] 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 006/101] 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 007/101] 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 008/101] 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 009/101] 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 010/101] 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 011/101] 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 012/101] 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 013/101] 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 014/101] 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 015/101] 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 016/101] 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 017/101] 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 018/101] 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 019/101] 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 020/101] 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 021/101] 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 022/101] 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 023/101] 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 024/101] 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 025/101] 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 026/101] 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 027/101] 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 028/101] 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 029/101] 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 030/101] 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 031/101] 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 032/101] 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 033/101] 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 034/101] 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 035/101] 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 036/101] 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 037/101] 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 038/101] 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 039/101] 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 040/101] 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 041/101] 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 042/101] 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 043/101] 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 044/101] 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 045/101] 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 046/101] 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 047/101] 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 048/101] 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 049/101] 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 050/101] 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 051/101] 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 052/101] 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 053/101] 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 054/101] 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 055/101] 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. * From efc2bcbfd7514b71c39043d86895858f6f23b8ee Mon Sep 17 00:00:00 2001 From: Colleen Date: Sun, 5 Jul 2026 17:53:21 +0200 Subject: [PATCH 056/101] Add beginnings of additional tests in SortedUnionFindTest; not done yet but needing to save progress --- .../common/collect/SortedUnionFindTest.java | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java index 5e0aae8e3..c95117e3e 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java @@ -12,6 +12,12 @@ import com.google.common.collect.Range; import com.google.errorprone.annotations.Var; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Random; +import java.util.Set; import org.junit.BeforeClass; import org.junit.Test; @@ -100,4 +106,150 @@ public void testUnion_ConstantCanonicalElementDuringNonlinearInsertion() { assertThat(newUnionFind.find(7)).isEqualTo(3); assertThat(newUnionFind.find(4)).isEqualTo(3); } + + @Test + public void testUnion_Strings() { + // TODO + + Random random = new Random(1357111317L); + AuxiliarySortedUnionFind expected = new AuxiliarySortedUnionFind<>(); + SortedUnionFind unionFind = new SortedTreeSetUnionFind<>(); + + int noOfSubsets = 5; + int sizeOfSubsets = 10; + + for (int i = 0; i < noOfSubsets; i++) { + String canon = Integer.toString(random.nextInt()); + + unionFind.union(canon, canon); + expected.union(canon, canon); + + for (int j = 1; j < sizeOfSubsets; j++) { + String elem = Integer.toString(random.nextInt()); + unionFind.union(canon, elem); + expected.union(canon, elem); + } + } + + // TODO now check they're the same + } + + protected class AuxiliarySortedUnionFind> { + ArrayList> subsets; + + AuxiliarySortedUnionFind() { + subsets = new ArrayList<>(); + } + + // TODO + /* + String getContentsAsInt() { + int contents; + ArrayList subsetsCopy = (ArrayList) subsets.clone(); + }*/ + + SubsetOfAuxiliarySortedUnionFind find(T e) { + for (SubsetOfAuxiliarySortedUnionFind current : subsets) { + if (current.contains(e)) { + return current; + } + } + + throw new NoSuchElementException(); + } + + void union(T e1, T e2) { + if (contains(e1)) { + if (contains(e2)) { + mergeExistingSubsets(e1, e2); + } else { + addToExistingSubset(e1, e2); + } + } else if (contains(e2)) { + addToExistingSubset(e2, e1); + } else { + addAsNewSubset(e1, e2); + } + } + + boolean contains(T e) { + for (SubsetOfAuxiliarySortedUnionFind current : subsets) { + if (current.contains(e)) { + return true; + } + } + + return false; + } + + private void mergeExistingSubsets(T e1, T e2) { + SubsetOfAuxiliarySortedUnionFind subset1 = find(e1); + SubsetOfAuxiliarySortedUnionFind subset2 = find(e2); + + subsets.remove(subset1); + subsets.remove(subset2); + + if (subset1.size() >= subset2.size()) { + Iterator iterator = subset2.iterator(); + + while (iterator.hasNext()) { + T current = iterator.next(); + subset1.add(current); + } + + subsets.add(subset1); + } else { + Iterator iterator = subset1.iterator(); + + while (iterator.hasNext()) { + T current = iterator.next(); + subset2.add(current); + } + + subsets.add(subset2); + } + } + + private void addToExistingSubset(T alreadyContained, T newElement) { + SubsetOfAuxiliarySortedUnionFind subset = find(alreadyContained); + + subsets.remove(subset); + subset.add(newElement); + subsets.add(subset); + } + + private void addAsNewSubset(T e1, T e2) { + SubsetOfAuxiliarySortedUnionFind newSubset = new SubsetOfAuxiliarySortedUnionFind<>(e1); + newSubset.add(e2); + subsets.add(newSubset); + } + } + + protected class SubsetOfAuxiliarySortedUnionFind> { + final T canon; + Set set = + new HashSet<>(); // TODO potentially change to data structure that is already sorted to + // avoid work later on + + protected SubsetOfAuxiliarySortedUnionFind(T firstElement) { + this.canon = firstElement; + this.set.add(firstElement); + } + + protected void add(T e) { + set.add(e); + } + + protected boolean contains(T e) { + return set.contains(e); + } + + protected int size() { + return set.size(); + } + + protected Iterator iterator() { + return set.iterator(); + } + } } From 9f939bad6bd2a0f8099e8ce8647b670b5758bc23 Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 7 Jul 2026 09:30:40 +0200 Subject: [PATCH 057/101] Remove previously commited nested classes from SortedUnionFindTest as they are not helpful --- .../common/collect/SortedUnionFindTest.java | 127 +----------------- 1 file changed, 2 insertions(+), 125 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java index c95117e3e..9c0f76947 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java @@ -12,12 +12,6 @@ import com.google.common.collect.Range; import com.google.errorprone.annotations.Var; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.Random; -import java.util.Set; import org.junit.BeforeClass; import org.junit.Test; @@ -107,6 +101,7 @@ public void testUnion_ConstantCanonicalElementDuringNonlinearInsertion() { assertThat(newUnionFind.find(4)).isEqualTo(3); } + /* @Test public void testUnion_Strings() { // TODO @@ -133,123 +128,5 @@ public void testUnion_Strings() { // TODO now check they're the same } - - protected class AuxiliarySortedUnionFind> { - ArrayList> subsets; - - AuxiliarySortedUnionFind() { - subsets = new ArrayList<>(); - } - - // TODO - /* - String getContentsAsInt() { - int contents; - ArrayList subsetsCopy = (ArrayList) subsets.clone(); - }*/ - - SubsetOfAuxiliarySortedUnionFind find(T e) { - for (SubsetOfAuxiliarySortedUnionFind current : subsets) { - if (current.contains(e)) { - return current; - } - } - - throw new NoSuchElementException(); - } - - void union(T e1, T e2) { - if (contains(e1)) { - if (contains(e2)) { - mergeExistingSubsets(e1, e2); - } else { - addToExistingSubset(e1, e2); - } - } else if (contains(e2)) { - addToExistingSubset(e2, e1); - } else { - addAsNewSubset(e1, e2); - } - } - - boolean contains(T e) { - for (SubsetOfAuxiliarySortedUnionFind current : subsets) { - if (current.contains(e)) { - return true; - } - } - - return false; - } - - private void mergeExistingSubsets(T e1, T e2) { - SubsetOfAuxiliarySortedUnionFind subset1 = find(e1); - SubsetOfAuxiliarySortedUnionFind subset2 = find(e2); - - subsets.remove(subset1); - subsets.remove(subset2); - - if (subset1.size() >= subset2.size()) { - Iterator iterator = subset2.iterator(); - - while (iterator.hasNext()) { - T current = iterator.next(); - subset1.add(current); - } - - subsets.add(subset1); - } else { - Iterator iterator = subset1.iterator(); - - while (iterator.hasNext()) { - T current = iterator.next(); - subset2.add(current); - } - - subsets.add(subset2); - } - } - - private void addToExistingSubset(T alreadyContained, T newElement) { - SubsetOfAuxiliarySortedUnionFind subset = find(alreadyContained); - - subsets.remove(subset); - subset.add(newElement); - subsets.add(subset); - } - - private void addAsNewSubset(T e1, T e2) { - SubsetOfAuxiliarySortedUnionFind newSubset = new SubsetOfAuxiliarySortedUnionFind<>(e1); - newSubset.add(e2); - subsets.add(newSubset); - } - } - - protected class SubsetOfAuxiliarySortedUnionFind> { - final T canon; - Set set = - new HashSet<>(); // TODO potentially change to data structure that is already sorted to - // avoid work later on - - protected SubsetOfAuxiliarySortedUnionFind(T firstElement) { - this.canon = firstElement; - this.set.add(firstElement); - } - - protected void add(T e) { - set.add(e); - } - - protected boolean contains(T e) { - return set.contains(e); - } - - protected int size() { - return set.size(); - } - - protected Iterator iterator() { - return set.iterator(); - } - } + */ } From 2f4969da6e75b93f34ccc8992720eb6d0aef071f Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 7 Jul 2026 10:39:33 +0200 Subject: [PATCH 058/101] Add testUnion_Strings() to SortedUnionFindTest --- .../common/collect/SortedUnionFindTest.java | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java index 9c0f76947..e8a3a54e4 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java @@ -12,6 +12,7 @@ import com.google.common.collect.Range; import com.google.errorprone.annotations.Var; +import java.util.Collection; import org.junit.BeforeClass; import org.junit.Test; @@ -101,32 +102,42 @@ public void testUnion_ConstantCanonicalElementDuringNonlinearInsertion() { assertThat(newUnionFind.find(4)).isEqualTo(3); } - /* @Test public void testUnion_Strings() { - // TODO + SortedUnionFind unionFindString = new SortedTreeSetUnionFind<>(); + String expected = ".-1..0..1..2..3..4..5..6..7..8..9."; - Random random = new Random(1357111317L); - AuxiliarySortedUnionFind expected = new AuxiliarySortedUnionFind<>(); - SortedUnionFind unionFind = new SortedTreeSetUnionFind<>(); + for (int i = 0; i <= 2; i++) { + unionFindString.union(Integer.toString(0), Integer.toString(i)); + } + for (int i = 3; i <= 5; i++) { + unionFindString.union(Integer.toString(3), Integer.toString(i)); + } + for (int i = 6; i <= 8; i++) { + unionFindString.union(Integer.toString(6), Integer.toString(i)); + } + unionFindString.union(Integer.toString(9), Integer.toString(9)); + assertThat(unionFindString.getAllSubsets().size()).isEqualTo(4); - int noOfSubsets = 5; - int sizeOfSubsets = 10; + unionFindString.union(Integer.toString(0), Integer.toString(6)); + assertThat(unionFindString.getAllSubsets().size()).isEqualTo(3); - for (int i = 0; i < noOfSubsets; i++) { - String canon = Integer.toString(random.nextInt()); + unionFindString.union(Integer.toString(7), Integer.toString(-1)); + assertThat(unionFindString.getAllSubsets().size()).isEqualTo(3); - unionFind.union(canon, canon); - expected.union(canon, canon); + unionFindString.union(Integer.toString(0), Integer.toString(3)); + assertThat(unionFindString.getAllSubsets().size()).isEqualTo(2); - for (int j = 1; j < sizeOfSubsets; j++) { - String elem = Integer.toString(random.nextInt()); - unionFind.union(canon, elem); - expected.union(canon, elem); + unionFindString.union(Integer.toString(0), Integer.toString(9)); + assertThat(unionFindString.getAllSubsets().size()).isEqualTo(1); + + @Var String result = ""; + + for (Collection subset : unionFindString.getAllSubsets()) { + for (String element : subset) { + result = result.concat("." + Integer.valueOf(element) + "."); } } - - // TODO now check they're the same + assertThat(result).isEqualTo(expected); } - */ } From 07825110cb7caf65d55e05b629337ada1e8a71d8 Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 7 Jul 2026 10:48:57 +0200 Subject: [PATCH 059/101] Correct test syntax and add more variety to test cases in testUnion_Strings() in SortedUnionFindTest --- .../common/collect/SortedUnionFindTest.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java index e8a3a54e4..46c62d2ab 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java @@ -116,20 +116,25 @@ public void testUnion_Strings() { for (int i = 6; i <= 8; i++) { unionFindString.union(Integer.toString(6), Integer.toString(i)); } + // case: both elements the same; to be added as new subset unionFindString.union(Integer.toString(9), Integer.toString(9)); - assertThat(unionFindString.getAllSubsets().size()).isEqualTo(4); + assertThat(unionFindString.getAllSubsets()).hasSize(4); + // case: both canonical elements unionFindString.union(Integer.toString(0), Integer.toString(6)); - assertThat(unionFindString.getAllSubsets().size()).isEqualTo(3); + assertThat(unionFindString.getAllSubsets()).hasSize(3); + // case: one contained but not canonical, one not contained unionFindString.union(Integer.toString(7), Integer.toString(-1)); - assertThat(unionFindString.getAllSubsets().size()).isEqualTo(3); + assertThat(unionFindString.getAllSubsets()).hasSize(3); - unionFindString.union(Integer.toString(0), Integer.toString(3)); - assertThat(unionFindString.getAllSubsets().size()).isEqualTo(2); + // case: both contained but neither canonical elements + unionFindString.union(Integer.toString(1), Integer.toString(4)); + assertThat(unionFindString.getAllSubsets()).hasSize(2); + // case: both canonical elements unionFindString.union(Integer.toString(0), Integer.toString(9)); - assertThat(unionFindString.getAllSubsets().size()).isEqualTo(1); + assertThat(unionFindString.getAllSubsets()).hasSize(1); @Var String result = ""; From 37770d5388d50c0d902bd782c1a6efdacb213a84 Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 7 Jul 2026 10:55:49 +0200 Subject: [PATCH 060/101] Correct test syntax in SortedUnionFindTest --- src/org/sosy_lab/common/collect/SortedUnionFindTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java index 46c62d2ab..bc98b62f7 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java @@ -105,7 +105,7 @@ public void testUnion_ConstantCanonicalElementDuringNonlinearInsertion() { @Test public void testUnion_Strings() { SortedUnionFind unionFindString = new SortedTreeSetUnionFind<>(); - String expected = ".-1..0..1..2..3..4..5..6..7..8..9."; + String expected = ".-1.0.1.2.3.4.5.6.7.8.9."; for (int i = 0; i <= 2; i++) { unionFindString.union(Integer.toString(0), Integer.toString(i)); @@ -136,11 +136,11 @@ public void testUnion_Strings() { unionFindString.union(Integer.toString(0), Integer.toString(9)); assertThat(unionFindString.getAllSubsets()).hasSize(1); - @Var String result = ""; + @Var String result = "."; for (Collection subset : unionFindString.getAllSubsets()) { for (String element : subset) { - result = result.concat("." + Integer.valueOf(element) + "."); + result = result + (Integer.valueOf(element) + "."); } } assertThat(result).isEqualTo(expected); From 9334994de6603e2e53c108d13e99f95c3efed6b6 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 9 Jul 2026 19:10:29 +0200 Subject: [PATCH 061/101] Make result comparison more straightforward in SortedUnionFindTest --- .../sosy_lab/common/collect/SortedUnionFindTest.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java index bc98b62f7..e9ed511e4 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java @@ -105,7 +105,7 @@ public void testUnion_ConstantCanonicalElementDuringNonlinearInsertion() { @Test public void testUnion_Strings() { SortedUnionFind unionFindString = new SortedTreeSetUnionFind<>(); - String expected = ".-1.0.1.2.3.4.5.6.7.8.9."; + Integer[] expected = {-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; for (int i = 0; i <= 2; i++) { unionFindString.union(Integer.toString(0), Integer.toString(i)); @@ -136,13 +136,6 @@ public void testUnion_Strings() { unionFindString.union(Integer.toString(0), Integer.toString(9)); assertThat(unionFindString.getAllSubsets()).hasSize(1); - @Var String result = "."; - - for (Collection subset : unionFindString.getAllSubsets()) { - for (String element : subset) { - result = result + (Integer.valueOf(element) + "."); - } - } - assertThat(result).isEqualTo(expected); + assertThat(unionFindString.getAllSubsets().iterator().next()).containsExactlyElementsIn(expected).inOrder(); } } From ea44c05f52d3172fa02aead6cae9faf3a6700926 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 9 Jul 2026 19:14:05 +0200 Subject: [PATCH 062/101] Attempt to fix (but not yet succeed) subset removal bug in SortedTreeSetUnionFind --- .../common/collect/SortedTreeSetUnionFind.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index 13cf4380e..ac37b2d14 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -109,13 +109,18 @@ private void addElementToExistingSet(T e, T canon) { if (!contains(e)) { for (NavigableSet currentSet : setOfSets.values()) { if (currentSet.contains(canon)) { + assert(setOfSets.remove(canon, currentSet)); currentSet.add(e); - setOfSets.replace(canon, currentSet); + setOfSets.put(canon, currentSet); break; } } } else { - mergeExistingSets(e, canon); + for(T key : setOfSets.keySet()) { + for(NavigableSet currentSet : setOfSets.values()) { + if(currentSet.contains(key) && currentSet.contains(e)) mergeExistingSets(key, canon); + } + } } } @@ -138,12 +143,15 @@ private void mergeExistingSets(T e1, T e2) { int size1 = set1.size(); int size2 = set2.size(); + assert(setOfSets.remove(e1, set1)); + assert(setOfSets.remove(e2, set2)); + if (size1 > size2) { set1.addAll(set2); - setOfSets.remove(e2); + setOfSets.put(e1, set1); } else { set2.addAll(set1); - setOfSets.remove(e1); + setOfSets.put(e2, set2); } } From ee6fb37262082bc0eb66e6eacf174af6c0b0631c Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 9 Jul 2026 19:16:12 +0200 Subject: [PATCH 063/101] Mark new bug discovery in SortedUnionFindTest --- src/org/sosy_lab/common/collect/SortedUnionFindTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java index e9ed511e4..e6407834a 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java @@ -136,6 +136,7 @@ public void testUnion_Strings() { unionFindString.union(Integer.toString(0), Integer.toString(9)); assertThat(unionFindString.getAllSubsets()).hasSize(1); + //TODO just realised this is currently comparing String to Integer... :S assertThat(unionFindString.getAllSubsets().iterator().next()).containsExactlyElementsIn(expected).inOrder(); } } From 9c86ef758b19a843ab44367e5222edc84261a2aa Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 10 Jul 2026 08:52:46 +0200 Subject: [PATCH 064/101] Fix bug in SortedUnionFindTest (was comparing Integer to String) --- src/org/sosy_lab/common/collect/SortedUnionFindTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java index e6407834a..a41caa230 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/SortedUnionFindTest.java @@ -12,7 +12,6 @@ import com.google.common.collect.Range; import com.google.errorprone.annotations.Var; -import java.util.Collection; import org.junit.BeforeClass; import org.junit.Test; @@ -105,7 +104,7 @@ public void testUnion_ConstantCanonicalElementDuringNonlinearInsertion() { @Test public void testUnion_Strings() { SortedUnionFind unionFindString = new SortedTreeSetUnionFind<>(); - Integer[] expected = {-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + String[] expected = {"-1", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; for (int i = 0; i <= 2; i++) { unionFindString.union(Integer.toString(0), Integer.toString(i)); @@ -136,7 +135,8 @@ public void testUnion_Strings() { unionFindString.union(Integer.toString(0), Integer.toString(9)); assertThat(unionFindString.getAllSubsets()).hasSize(1); - //TODO just realised this is currently comparing String to Integer... :S - assertThat(unionFindString.getAllSubsets().iterator().next()).containsExactlyElementsIn(expected).inOrder(); + assertThat(unionFindString.getAllSubsets().iterator().next()) + .containsExactlyElementsIn(expected) + .inOrder(); } } From 9b27c6023e2feaab14d4b6df0df19db15ad6b7af Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 10 Jul 2026 09:25:23 +0200 Subject: [PATCH 065/101] Fix union bug in SortedTreeSetUnionFind --- .../collect/SortedTreeSetUnionFind.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java index ac37b2d14..12127d429 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java @@ -89,8 +89,17 @@ public void union(T e1, T e2) { } else if (canonicalElements.contains(e2)) { addElementToExistingSet(e1, e2); } else { - addElementAsNewSet(e1); - addElementToExistingSet(e2, e1); + + if (contains(e1)) { + if (contains(e2)) { + mergeExistingSets(find(e1), find(e2)); + } else { + addElementToExistingSet(e2, find(e1)); + } + } else { + addElementAsNewSet(e1); + addElementToExistingSet(e2, e1); + } } } } @@ -109,18 +118,14 @@ private void addElementToExistingSet(T e, T canon) { if (!contains(e)) { for (NavigableSet currentSet : setOfSets.values()) { if (currentSet.contains(canon)) { - assert(setOfSets.remove(canon, currentSet)); + assert setOfSets.remove(canon, currentSet); currentSet.add(e); setOfSets.put(canon, currentSet); break; } } } else { - for(T key : setOfSets.keySet()) { - for(NavigableSet currentSet : setOfSets.values()) { - if(currentSet.contains(key) && currentSet.contains(e)) mergeExistingSets(key, canon); - } - } + mergeExistingSets(find(e), canon); } } @@ -143,8 +148,8 @@ private void mergeExistingSets(T e1, T e2) { int size1 = set1.size(); int size2 = set2.size(); - assert(setOfSets.remove(e1, set1)); - assert(setOfSets.remove(e2, set2)); + assert setOfSets.remove(e1, set1); + assert setOfSets.remove(e2, set2); if (size1 > size2) { set1.addAll(set2); From 5917c151d18f2ee991f3924df92e9fb687cd2f8e Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 10 Jul 2026 12:59:43 +0200 Subject: [PATCH 066/101] Add separate test class for benchmarking UnionFind called UnionFindSimpleBenchmarkTest; currently causes stack overflow even for small values --- .../collect/UnionFindSimpleBenchmarkTest.java | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java diff --git a/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java b/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java new file mode 100644 index 000000000..b8dcb2f44 --- /dev/null +++ b/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java @@ -0,0 +1,146 @@ +// 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.base.Preconditions; +import com.google.common.collect.Sets; +import java.time.Duration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import org.junit.Test; + +public class UnionFindSimpleBenchmarkTest { + final int lowerBound = 2; + final int factorForComparison = 10; + + @Test + public void unionBigOQuadraticEvaluationTest() { + Set> allUnionFindsOfFirstLoop = new HashSet<>(); + + Duration timeBeforeFirstLoop = Duration.ofNanos(System.nanoTime()); + for (int i = 0; i < lowerBound; i++) { + Set values = new HashSet<>(); + for (int j = 0; j <= i; j++) { + values.add(j); + } + + allUnionFindsOfFirstLoop.addAll(generateUnionFinds(getPermutations(values))); + } + Duration timeAfterFirstLoop = Duration.ofNanos(System.nanoTime()); + Duration timeOfFirstLoop = timeAfterFirstLoop.minus(timeBeforeFirstLoop); + + final int higherBound = lowerBound * factorForComparison; + + Duration timeBeforeSecondLoop = Duration.ofNanos(System.nanoTime()); + for (int i = 0; i < higherBound; i++) { + Set values = new HashSet<>(); + for (int j = 0; j <= i; j++) { + values.add(j); + } + + allUnionFindsOfFirstLoop.addAll(generateUnionFinds(getPermutations(values))); + } + Duration timeAfterSecondLoop = Duration.ofNanos(System.nanoTime()); + Duration timeOfSecondLoop = timeAfterSecondLoop.minus(timeBeforeSecondLoop); + + assertThat(timeOfSecondLoop) + .isLessThan(timeOfFirstLoop.multipliedBy(factorForComparison * factorForComparison)); + } + + private Set> generateUnionFinds(Set>> pInput) { + Preconditions.checkNotNull(pInput); + + Set> unionFindSet = new HashSet<>(); + + for (Set> subsetOfSets : pInput) { + SortedUnionFind unionFind = new SortedTreeSetUnionFind<>(); + + for (Set subSubset : subsetOfSets) { + if (!subSubset.isEmpty()) { + Iterator iterator = subSubset.iterator(); + Integer value = iterator.next(); + unionFind.union(value, value); + if (subSubset.size() > 1) { + while (iterator.hasNext()) { + unionFind.union(value, iterator.next()); + } + } + } + } + + unionFindSet.add(unionFind); + } + + return unionFindSet; + } + + private Set>> getPermutations(Set pInput) { + Preconditions.checkNotNull(pInput); + + int size = pInput.size(); + Set>> allPermutations = new HashSet<>(); + + Set> powerSet = Sets.powerSet(pInput); + + for (Set currentSet : powerSet) { + int freeSlots = size - currentSet.size(); + Set remainingValues = new HashSet<>(pInput); + remainingValues.removeAll(currentSet); + + if (freeSlots > 1) { + for (Set> combinationValues : getPermutations(remainingValues)) { + Set> subPermutation = new HashSet<>(); + subPermutation.add(currentSet); + subPermutation.addAll(combinationValues); + allPermutations.add(subPermutation); + } + } else if (freeSlots == 1) { + Set> subPermutation = new HashSet<>(); + subPermutation.add(currentSet); + subPermutation.add(remainingValues); + allPermutations.add(subPermutation); + } else if (freeSlots == 0) { + Set> subPermutation = new HashSet<>(); + subPermutation.add(currentSet); + allPermutations.add(subPermutation); + } + } + + return removeTooSmallSets(allPermutations, size); + } + + private Set>> removeTooSmallSets(Set>> pInput, int pN) { + Preconditions.checkNotNull(pInput); + + Set>> returnSet = new HashSet<>(pInput); + + for (Set> currentSubset : pInput) { + if (!totalNoOfElemsInSubsetsEquals(currentSubset, pN)) returnSet.remove(currentSubset); + } + + return returnSet; + } + + private boolean totalNoOfElemsInSubsetsEquals(Set> pInput, int pN) { + Preconditions.checkNotNull(pInput); + + int counter = 0; + + for (Set currentSubset : pInput) { + for (Integer e : currentSubset) { + counter++; + } + } + + return counter == pN; + } +} From d67006e69083b559693e452231a248744a696ebc Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 10 Jul 2026 13:12:45 +0200 Subject: [PATCH 067/101] Reduce method calls in UnionFindSimpleBenchmarkTest --- .../collect/UnionFindSimpleBenchmarkTest.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java b/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java index b8dcb2f44..f16ede81a 100644 --- a/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java +++ b/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java @@ -115,7 +115,19 @@ private Set>> getPermutations(Set pInput) { } } - return removeTooSmallSets(allPermutations, size); + Set>> filteredPermutations = new HashSet<>(allPermutations); + for (Set> currentSubset : allPermutations) { + int counter = 0; + + for (Set currentSubSubset : currentSubset) { + for (Integer e : currentSubSubset) { + counter++; + } + } + if (!(counter == size)) filteredPermutations.remove(currentSubset); + } + + return filteredPermutations; } private Set>> removeTooSmallSets(Set>> pInput, int pN) { From 69f5fa6599bbcad3ab14a50fdb6ed20eebb4e61e Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 10 Jul 2026 14:09:05 +0200 Subject: [PATCH 068/101] Fix UnionFindSimpleBenchmarkTest that it finishes within a reasonable amount of time; only for small values though; next step: replace recursion with iterative approach --- .../collect/UnionFindSimpleBenchmarkTest.java | 44 +++++-------------- 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java b/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java index f16ede81a..b52cfa02f 100644 --- a/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java +++ b/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java @@ -20,16 +20,16 @@ public class UnionFindSimpleBenchmarkTest { final int lowerBound = 2; - final int factorForComparison = 10; + final int factorForComparison = 3; @Test public void unionBigOQuadraticEvaluationTest() { Set> allUnionFindsOfFirstLoop = new HashSet<>(); Duration timeBeforeFirstLoop = Duration.ofNanos(System.nanoTime()); - for (int i = 0; i < lowerBound; i++) { + for (int i = 1; i <= lowerBound; i++) { Set values = new HashSet<>(); - for (int j = 0; j <= i; j++) { + for (int j = 1; j <= i; j++) { values.add(j); } @@ -39,15 +39,16 @@ public void unionBigOQuadraticEvaluationTest() { Duration timeOfFirstLoop = timeAfterFirstLoop.minus(timeBeforeFirstLoop); final int higherBound = lowerBound * factorForComparison; + Set> allUnionFindsOfSecondLoop = new HashSet<>(); Duration timeBeforeSecondLoop = Duration.ofNanos(System.nanoTime()); - for (int i = 0; i < higherBound; i++) { + for (int i = 1; i <= higherBound; i++) { Set values = new HashSet<>(); - for (int j = 0; j <= i; j++) { + for (int j = 1; j <= i; j++) { values.add(j); } - allUnionFindsOfFirstLoop.addAll(generateUnionFinds(getPermutations(values))); + allUnionFindsOfSecondLoop.addAll(generateUnionFinds(getPermutations(values))); } Duration timeAfterSecondLoop = Duration.ofNanos(System.nanoTime()); Duration timeOfSecondLoop = timeAfterSecondLoop.minus(timeBeforeSecondLoop); @@ -96,6 +97,10 @@ private Set>> getPermutations(Set pInput) { Set remainingValues = new HashSet<>(pInput); remainingValues.removeAll(currentSet); + if (currentSet.isEmpty()) { + continue; + } + if (freeSlots > 1) { for (Set> combinationValues : getPermutations(remainingValues)) { Set> subPermutation = new HashSet<>(); @@ -115,6 +120,7 @@ private Set>> getPermutations(Set pInput) { } } + // reduce to those that contain all required numbers Set>> filteredPermutations = new HashSet<>(allPermutations); for (Set> currentSubset : allPermutations) { int counter = 0; @@ -129,30 +135,4 @@ private Set>> getPermutations(Set pInput) { return filteredPermutations; } - - private Set>> removeTooSmallSets(Set>> pInput, int pN) { - Preconditions.checkNotNull(pInput); - - Set>> returnSet = new HashSet<>(pInput); - - for (Set> currentSubset : pInput) { - if (!totalNoOfElemsInSubsetsEquals(currentSubset, pN)) returnSet.remove(currentSubset); - } - - return returnSet; - } - - private boolean totalNoOfElemsInSubsetsEquals(Set> pInput, int pN) { - Preconditions.checkNotNull(pInput); - - int counter = 0; - - for (Set currentSubset : pInput) { - for (Integer e : currentSubset) { - counter++; - } - } - - return counter == pN; - } } From 1898af12289a56f163e3c19075b2359a590ba42b Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 10 Jul 2026 16:51:19 +0200 Subject: [PATCH 069/101] Replace recursion with iterative approach in UnionFindSimpleBenchmarkTest; the maths still needs sorting out though --- .../collect/UnionFindSimpleBenchmarkTest.java | 133 ++++++++---------- 1 file changed, 57 insertions(+), 76 deletions(-) diff --git a/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java b/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java index b52cfa02f..cb7b5ed1b 100644 --- a/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java +++ b/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java @@ -11,58 +11,93 @@ import static com.google.common.truth.Truth.assertThat; import com.google.common.base.Preconditions; -import com.google.common.collect.Sets; +import com.google.errorprone.annotations.Var; import java.time.Duration; +import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Set; import org.junit.Test; public class UnionFindSimpleBenchmarkTest { final int lowerBound = 2; - final int factorForComparison = 3; + final int factorForComparison = 5; @Test public void unionBigOQuadraticEvaluationTest() { + // BEGINNING of 1st loop Set> allUnionFindsOfFirstLoop = new HashSet<>(); Duration timeBeforeFirstLoop = Duration.ofNanos(System.nanoTime()); - for (int i = 1; i <= lowerBound; i++) { - Set values = new HashSet<>(); - for (int j = 1; j <= i; j++) { - values.add(j); - } - allUnionFindsOfFirstLoop.addAll(generateUnionFinds(getPermutations(values))); - } + List>> partitions1 = generatePartitions(lowerBound); + transformPartitionsToUnionFind(partitions1, allUnionFindsOfFirstLoop); + Duration timeAfterFirstLoop = Duration.ofNanos(System.nanoTime()); Duration timeOfFirstLoop = timeAfterFirstLoop.minus(timeBeforeFirstLoop); + // END of 1st loop - final int higherBound = lowerBound * factorForComparison; + // BEGINNING of 2nd loop + int upperBound = lowerBound * factorForComparison; Set> allUnionFindsOfSecondLoop = new HashSet<>(); Duration timeBeforeSecondLoop = Duration.ofNanos(System.nanoTime()); - for (int i = 1; i <= higherBound; i++) { - Set values = new HashSet<>(); - for (int j = 1; j <= i; j++) { - values.add(j); - } - allUnionFindsOfSecondLoop.addAll(generateUnionFinds(getPermutations(values))); - } + List>> partitions2 = generatePartitions(upperBound); + transformPartitionsToUnionFind(partitions2, allUnionFindsOfSecondLoop); + Duration timeAfterSecondLoop = Duration.ofNanos(System.nanoTime()); Duration timeOfSecondLoop = timeAfterSecondLoop.minus(timeBeforeSecondLoop); + // END of 2nd loop assertThat(timeOfSecondLoop) .isLessThan(timeOfFirstLoop.multipliedBy(factorForComparison * factorForComparison)); } - private Set> generateUnionFinds(Set>> pInput) { - Preconditions.checkNotNull(pInput); + private static List>> generatePartitions(int pHighestNumber) { + @Var List>> allPermutations = new ArrayList<>(); + + // initialise allPermutations + Set> init = new HashSet<>(); + Set initSubset = new HashSet<>(); + initSubset.add(0); + init.add(initSubset); + allPermutations.add(init); + + for (int i = 1; i <= pHighestNumber; i++) { + + List>> newSets = new ArrayList<>(); - Set> unionFindSet = new HashSet<>(); + for (Set> existingSet : allPermutations) { + for (Set existingSubset : existingSet) { + Set> setWithNumber = new HashSet<>(existingSet); + setWithNumber.remove(existingSubset); + Set subsetWithNumber = new HashSet<>(existingSubset); + subsetWithNumber.add(i); + setWithNumber.add(subsetWithNumber); + newSets.add(setWithNumber); + } + + Set> currentExistingSet = new HashSet<>(existingSet); + Set subsetWithCurrentI = new HashSet<>(); + subsetWithCurrentI.add(i); + currentExistingSet.add(subsetWithCurrentI); + newSets.add(currentExistingSet); + } + + allPermutations = newSets; + } - for (Set> subsetOfSets : pInput) { + return allPermutations; + } + + private static void transformPartitionsToUnionFind( + List>> pPartitions, Set> pSetOfUnionFinds) { + Preconditions.checkNotNull(pPartitions); + Preconditions.checkNotNull(pSetOfUnionFinds); + + for (Set> subsetOfSets : pPartitions) { SortedUnionFind unionFind = new SortedTreeSetUnionFind<>(); for (Set subSubset : subsetOfSets) { @@ -78,61 +113,7 @@ private Set> generateUnionFinds(Set>> } } - unionFindSet.add(unionFind); + pSetOfUnionFinds.add(unionFind); } - - return unionFindSet; - } - - private Set>> getPermutations(Set pInput) { - Preconditions.checkNotNull(pInput); - - int size = pInput.size(); - Set>> allPermutations = new HashSet<>(); - - Set> powerSet = Sets.powerSet(pInput); - - for (Set currentSet : powerSet) { - int freeSlots = size - currentSet.size(); - Set remainingValues = new HashSet<>(pInput); - remainingValues.removeAll(currentSet); - - if (currentSet.isEmpty()) { - continue; - } - - if (freeSlots > 1) { - for (Set> combinationValues : getPermutations(remainingValues)) { - Set> subPermutation = new HashSet<>(); - subPermutation.add(currentSet); - subPermutation.addAll(combinationValues); - allPermutations.add(subPermutation); - } - } else if (freeSlots == 1) { - Set> subPermutation = new HashSet<>(); - subPermutation.add(currentSet); - subPermutation.add(remainingValues); - allPermutations.add(subPermutation); - } else if (freeSlots == 0) { - Set> subPermutation = new HashSet<>(); - subPermutation.add(currentSet); - allPermutations.add(subPermutation); - } - } - - // reduce to those that contain all required numbers - Set>> filteredPermutations = new HashSet<>(allPermutations); - for (Set> currentSubset : allPermutations) { - int counter = 0; - - for (Set currentSubSubset : currentSubset) { - for (Integer e : currentSubSubset) { - counter++; - } - } - if (!(counter == size)) filteredPermutations.remove(currentSubset); - } - - return filteredPermutations; } } From 52571a885887d7c164ab5b342fc5cbbda7f438fe Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 14 Jul 2026 10:16:46 +0200 Subject: [PATCH 070/101] Correct calculations in UnionFindSimpleBenchmarkTest --- .../collect/UnionFindSimpleBenchmarkTest.java | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java b/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java index cb7b5ed1b..803eacab7 100644 --- a/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java +++ b/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java @@ -12,6 +12,7 @@ import com.google.common.base.Preconditions; import com.google.errorprone.annotations.Var; +import java.math.BigInteger; import java.time.Duration; import java.util.ArrayList; import java.util.HashSet; @@ -51,8 +52,14 @@ public void unionBigOQuadraticEvaluationTest() { Duration timeOfSecondLoop = timeAfterSecondLoop.minus(timeBeforeSecondLoop); // END of 2nd loop - assertThat(timeOfSecondLoop) - .isLessThan(timeOfFirstLoop.multipliedBy(factorForComparison * factorForComparison)); + // will throw for larger n's as they won't fit into long + long bellOfLowerBound = getBellNoOfN(lowerBound).longValueExact(); + long bellOfUpperBound = getBellNoOfN(upperBound).longValueExact(); + + assertThat(timeOfSecondLoop.dividedBy(bellOfUpperBound)) + .isLessThan( + timeOfFirstLoop.multipliedBy( + (bellOfUpperBound * bellOfUpperBound) / (bellOfLowerBound * bellOfLowerBound))); } private static List>> generatePartitions(int pHighestNumber) { @@ -116,4 +123,26 @@ private static void transformPartitionsToUnionFind( pSetOfUnionFinds.add(unionFind); } } + + // calculates the Bell Number of a given n>=0 using the Bell Triangle + // uses BigInteger because int/Integer would run out of space at comparatively small n's + private static BigInteger getBellNoOfN(int n) { + @Var List previousRow = new ArrayList<>(); + + // initialise for n=0 + previousRow.add(BigInteger.valueOf(1)); + + for (int i = 1; i < n; i++) { + List currentRow = new ArrayList<>(); + currentRow.add(previousRow.get(previousRow.size() - 1)); + + for (int j = 1; j <= i; j++) { + currentRow.add(previousRow.get(j - 1).add(currentRow.get(j - 1))); + } + + previousRow = currentRow; + } + + return previousRow.get(previousRow.size() - 1); + } } From 5d6cca6bb7ad6e2f9d816f78f23a590d05b594c6 Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 14 Jul 2026 10:37:37 +0200 Subject: [PATCH 071/101] Increase magnitudes slightly in UnionFindSimpleBenchmarkTest --- .../sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java b/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java index 803eacab7..ccd74f797 100644 --- a/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java +++ b/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java @@ -22,8 +22,8 @@ import org.junit.Test; public class UnionFindSimpleBenchmarkTest { - final int lowerBound = 2; - final int factorForComparison = 5; + final int lowerBound = 3; + final int factorForComparison = 4; @Test public void unionBigOQuadraticEvaluationTest() { From 2dab5525b4613a44ca1d0ddd1f46c1accea26753 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 16 Jul 2026 17:19:49 +0200 Subject: [PATCH 072/101] Move all UnionFind classes to new subpackage union_find --- src/org/sosy_lab/common/collect/PackageSanityTest.java | 1 + .../{ => union_find}/AbstractImmutableSortedUnionFind.java | 2 +- .../collect/{ => union_find}/AbstractImmutableUnionFind.java | 2 +- .../collect/{ => union_find}/PersistentSortedUnionFind.java | 2 +- .../common/collect/{ => union_find}/PersistentUnionFind.java | 2 +- .../common/collect/{ => union_find}/SortedTreeSetUnionFind.java | 2 +- .../common/collect/{ => union_find}/SortedUnionFind.java | 2 +- .../common/collect/{ => union_find}/SortedUnionFindTest.java | 2 +- src/org/sosy_lab/common/collect/{ => union_find}/UnionFind.java | 2 +- .../collect/{ => union_find}/UnionFindSimpleBenchmarkTest.java | 2 +- 10 files changed, 10 insertions(+), 9 deletions(-) rename src/org/sosy_lab/common/collect/{ => union_find}/AbstractImmutableSortedUnionFind.java (93%) rename src/org/sosy_lab/common/collect/{ => union_find}/AbstractImmutableUnionFind.java (92%) rename src/org/sosy_lab/common/collect/{ => union_find}/PersistentSortedUnionFind.java (97%) rename src/org/sosy_lab/common/collect/{ => union_find}/PersistentUnionFind.java (96%) rename src/org/sosy_lab/common/collect/{ => union_find}/SortedTreeSetUnionFind.java (99%) rename src/org/sosy_lab/common/collect/{ => union_find}/SortedUnionFind.java (96%) rename src/org/sosy_lab/common/collect/{ => union_find}/SortedUnionFindTest.java (98%) rename src/org/sosy_lab/common/collect/{ => union_find}/UnionFind.java (96%) rename src/org/sosy_lab/common/collect/{ => union_find}/UnionFindSimpleBenchmarkTest.java (99%) diff --git a/src/org/sosy_lab/common/collect/PackageSanityTest.java b/src/org/sosy_lab/common/collect/PackageSanityTest.java index 4d6ad7dfc..eda379bd9 100644 --- a/src/org/sosy_lab/common/collect/PackageSanityTest.java +++ b/src/org/sosy_lab/common/collect/PackageSanityTest.java @@ -12,6 +12,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import org.sosy_lab.common.Classes; +import org.sosy_lab.common.collect.union_find.SortedTreeSetUnionFind; public class PackageSanityTest extends AbstractPackageSanityTests { diff --git a/src/org/sosy_lab/common/collect/AbstractImmutableSortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java similarity index 93% rename from src/org/sosy_lab/common/collect/AbstractImmutableSortedUnionFind.java rename to src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java index 4a010aa79..93f50bc82 100644 --- a/src/org/sosy_lab/common/collect/AbstractImmutableSortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java @@ -6,7 +6,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.sosy_lab.common.collect; +package org.sosy_lab.common.collect.union_find; import com.google.errorprone.annotations.DoNotCall; diff --git a/src/org/sosy_lab/common/collect/AbstractImmutableUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java similarity index 92% rename from src/org/sosy_lab/common/collect/AbstractImmutableUnionFind.java rename to src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java index d1fd7db87..5291681c4 100644 --- a/src/org/sosy_lab/common/collect/AbstractImmutableUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java @@ -6,7 +6,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.sosy_lab.common.collect; +package org.sosy_lab.common.collect.union_find; import com.google.errorprone.annotations.DoNotCall; diff --git a/src/org/sosy_lab/common/collect/PersistentSortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java similarity index 97% rename from src/org/sosy_lab/common/collect/PersistentSortedUnionFind.java rename to src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java index 738fabd12..1c9484ce1 100644 --- a/src/org/sosy_lab/common/collect/PersistentSortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java @@ -6,7 +6,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.sosy_lab.common.collect; +package org.sosy_lab.common.collect.union_find; import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.DoNotCall; diff --git a/src/org/sosy_lab/common/collect/PersistentUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java similarity index 96% rename from src/org/sosy_lab/common/collect/PersistentUnionFind.java rename to src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java index f6b0fba2b..769c73c20 100644 --- a/src/org/sosy_lab/common/collect/PersistentUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java @@ -6,7 +6,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.sosy_lab.common.collect; +package org.sosy_lab.common.collect.union_find; import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.DoNotCall; diff --git a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java similarity index 99% rename from src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java rename to src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java index 12127d429..b0c0e87d0 100644 --- a/src/org/sosy_lab/common/collect/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java @@ -6,7 +6,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.sosy_lab.common.collect; +package org.sosy_lab.common.collect.union_find; import com.google.common.base.Preconditions; import com.google.errorprone.annotations.Var; diff --git a/src/org/sosy_lab/common/collect/SortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedUnionFind.java similarity index 96% rename from src/org/sosy_lab/common/collect/SortedUnionFind.java rename to src/org/sosy_lab/common/collect/union_find/SortedUnionFind.java index 818e91cc0..3a07a74a9 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedUnionFind.java @@ -6,7 +6,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.sosy_lab.common.collect; +package org.sosy_lab.common.collect.union_find; import java.util.Collection; import java.util.Set; diff --git a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/union_find/SortedUnionFindTest.java similarity index 98% rename from src/org/sosy_lab/common/collect/SortedUnionFindTest.java rename to src/org/sosy_lab/common/collect/union_find/SortedUnionFindTest.java index a41caa230..363bde722 100644 --- a/src/org/sosy_lab/common/collect/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedUnionFindTest.java @@ -6,7 +6,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.sosy_lab.common.collect; +package org.sosy_lab.common.collect.union_find; import static com.google.common.truth.Truth.assertThat; diff --git a/src/org/sosy_lab/common/collect/UnionFind.java b/src/org/sosy_lab/common/collect/union_find/UnionFind.java similarity index 96% rename from src/org/sosy_lab/common/collect/UnionFind.java rename to src/org/sosy_lab/common/collect/union_find/UnionFind.java index 73220f5f8..1b87de772 100644 --- a/src/org/sosy_lab/common/collect/UnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/UnionFind.java @@ -6,7 +6,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.sosy_lab.common.collect; +package org.sosy_lab.common.collect.union_find; import java.util.Collection; import java.util.Set; diff --git a/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java b/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java similarity index 99% rename from src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java rename to src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java index ccd74f797..2dbf04428 100644 --- a/src/org/sosy_lab/common/collect/UnionFindSimpleBenchmarkTest.java +++ b/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java @@ -6,7 +6,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.sosy_lab.common.collect; +package org.sosy_lab.common.collect.union_find; import static com.google.common.truth.Truth.assertThat; From 43b4aaa04138c29fb4bbb056f36e981543be8ae5 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 16 Jul 2026 17:38:54 +0200 Subject: [PATCH 073/101] Rename variable as previous name was misleading --- .../union_find/SortedTreeSetUnionFind.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java index b0c0e87d0..93e9faf2b 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java @@ -28,11 +28,11 @@ */ public class SortedTreeSetUnionFind> implements SortedUnionFind { - private final Map> setOfSets; + private final Map> mapOfSets; /** Generates an empty {@link SortedTreeSetUnionFind}. */ public SortedTreeSetUnionFind() { - setOfSets = new HashMap<>(); + mapOfSets = new HashMap<>(); } /** @@ -46,10 +46,10 @@ public SortedTreeSetUnionFind() { public T find(T e) { Preconditions.checkNotNull(e); - for (NavigableSet current : setOfSets.values()) { + for (NavigableSet current : mapOfSets.values()) { if (current.contains(e)) { for (T element : current) { - if (setOfSets.containsKey(element)) { + if (mapOfSets.containsKey(element)) { return element; } } @@ -78,7 +78,7 @@ public void union(T e1, T e2) { if (e1.equals(e2)) { addElementAsNewSet(e1); } else { - Set canonicalElements = setOfSets.keySet(); + Set canonicalElements = mapOfSets.keySet(); if (canonicalElements.contains(e1)) { if (canonicalElements.contains(e2)) { @@ -109,18 +109,18 @@ private void addElementAsNewSet(T e) { if (!contains(e)) { NavigableSet newSet = new TreeSet<>(); newSet.add(e); - setOfSets.put(e, newSet); + mapOfSets.put(e, newSet); } } private void addElementToExistingSet(T e, T canon) { if (!contains(e)) { - for (NavigableSet currentSet : setOfSets.values()) { + for (NavigableSet currentSet : mapOfSets.values()) { if (currentSet.contains(canon)) { - assert setOfSets.remove(canon, currentSet); + assert mapOfSets.remove(canon, currentSet); currentSet.add(e); - setOfSets.put(canon, currentSet); + mapOfSets.put(canon, currentSet); break; } } @@ -134,7 +134,7 @@ private void mergeExistingSets(T e1, T e2) { @Var NavigableSet set1 = null; @Var NavigableSet set2 = null; - for (NavigableSet current : setOfSets.values()) { + for (NavigableSet current : mapOfSets.values()) { if (current.contains(e1)) { set1 = current; } else if (current.contains(e2)) { @@ -148,15 +148,15 @@ private void mergeExistingSets(T e1, T e2) { int size1 = set1.size(); int size2 = set2.size(); - assert setOfSets.remove(e1, set1); - assert setOfSets.remove(e2, set2); + assert mapOfSets.remove(e1, set1); + assert mapOfSets.remove(e2, set2); if (size1 > size2) { set1.addAll(set2); - setOfSets.put(e1, set1); + mapOfSets.put(e1, set1); } else { set2.addAll(set1); - setOfSets.put(e2, set2); + mapOfSets.put(e2, set2); } } @@ -167,7 +167,7 @@ private void mergeExistingSets(T e1, T e2) { */ @Override public Collection> getAllSubsets() { - return setOfSets.values(); + return mapOfSets.values(); } /** @@ -182,7 +182,7 @@ public boolean contains(T e) { Preconditions.checkNotNull(e); - for (NavigableSet current : setOfSets.values()) { + for (NavigableSet current : mapOfSets.values()) { if (current.contains(e)) { return true; } From 61278c749be815a0cb49fdc40e1dfd7d283156fd Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 16 Jul 2026 18:16:34 +0200 Subject: [PATCH 074/101] Ignore benchmark test as it is not intended as part of the regular UnionFind test suite due to time and memory constraints --- .../common/collect/union_find/UnionFindSimpleBenchmarkTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java b/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java index 2dbf04428..269b13791 100644 --- a/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java +++ b/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java @@ -19,12 +19,14 @@ import java.util.Iterator; import java.util.List; import java.util.Set; +import org.junit.Ignore; import org.junit.Test; public class UnionFindSimpleBenchmarkTest { final int lowerBound = 3; final int factorForComparison = 4; + @Ignore @Test public void unionBigOQuadraticEvaluationTest() { // BEGINNING of 1st loop From 919356e381925736e47789b215a30afc34628234 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 16 Jul 2026 18:19:02 +0200 Subject: [PATCH 075/101] Add AbstractGenericUnionFind as basis for further union-find implementations --- .../collect/AbstractGenericUnionFind.java | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/AbstractGenericUnionFind.java diff --git a/src/org/sosy_lab/common/collect/AbstractGenericUnionFind.java b/src/org/sosy_lab/common/collect/AbstractGenericUnionFind.java new file mode 100644 index 000000000..6adf07355 --- /dev/null +++ b/src/org/sosy_lab/common/collect/AbstractGenericUnionFind.java @@ -0,0 +1,198 @@ +// 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.common.base.Preconditions; +import com.google.errorprone.annotations.Var; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import org.sosy_lab.common.collect.union_find.UnionFind; + +/** + * An abstract, generic implementation of {@link UnionFind} using a {@link Map} of {@link Set}s. In + * order to represent subsets by canonical elements, each one is mapped to its representative + * canonical element. This is always the first element added to the subset, unless it has changed + * due to union operations. The union is implemented as union by size. + * + * @param type of elements added to the Union-Find. + */ +public abstract class AbstractGenericUnionFind, M extends Map> + implements UnionFind { + + private final M mapOfSets; + + /** + * Takes an empty map of the desired kind and allocates it to the variable mapOfSets. This enables + * child classes to simply pass an object of the desired kind without having to modify the + * constructor and methods. + * + * @param emptyMapOfSets empty map of desired type + */ + public AbstractGenericUnionFind(M emptyMapOfSets) { + mapOfSets = emptyMapOfSets; + } + + /** + * Returns the canonical element of the set containing the provided element. + * + * @param e element for which set is to be found + * @return canonical element of the found set + * @throws IllegalArgumentException if element is not contained in any subset + */ + @Override + public T find(T e) { + + Preconditions.checkNotNull(e); + for (S current : mapOfSets.values()) { + if (current.contains(e)) { + for (T element : current) { + if (mapOfSets.containsKey(element)) { + return element; + } + } + } + } + + throw new IllegalArgumentException("Element not contained"); + } + + /** + * Merges the sets represented by the two input values according to standard Union-Find behaviour. + * + *

USES: Add new element as new set: pass it as both e1 and e2. Add new element to existing + * set: one input value is the new element, the other the canonical element of the set to be added + * to. Merge two existing sets: e1, e2 canonical elements of sets to be merged. + * + * @param e1 first element + * @param e2 second element + */ + @SuppressWarnings("unchecked cast") + @Override + public void union(T e1, T e2) { + + Preconditions.checkNotNull(e1); + Preconditions.checkNotNull(e2); + + if (e1.equals(e2)) { + addElementAsNewSet(e1); + } else { + S canonicalElements = (S) mapOfSets.keySet(); + + if (canonicalElements.contains(e1)) { + if (canonicalElements.contains(e2)) { + mergeExistingSets(e1, e2); + } else { + addElementToExistingSet(e2, e1); + } + } else if (canonicalElements.contains(e2)) { + addElementToExistingSet(e1, e2); + } else { + + if (contains(e1)) { + if (contains(e2)) { + mergeExistingSets(find(e1), find(e2)); + } else { + addElementToExistingSet(e2, find(e1)); + } + } else { + addElementAsNewSet(e1); + addElementToExistingSet(e2, e1); + } + } + } + } + + @SuppressWarnings("unchecked cast") + private void addElementAsNewSet(T e) { + + if (!contains(e)) { + S newSet = (S) Set.of(); + newSet.add(e); + mapOfSets.put(e, newSet); + } + } + + private void addElementToExistingSet(T e, T canon) { + + if (!contains(e)) { + for (S currentSet : mapOfSets.values()) { + if (currentSet.contains(canon)) { + assert mapOfSets.remove(canon, currentSet); + currentSet.add(e); + mapOfSets.put(canon, currentSet); + break; + } + } + } else { + mergeExistingSets(find(e), canon); + } + } + + private void mergeExistingSets(T e1, T e2) { + + @Var S set1 = null; + @Var S set2 = null; + + for (S current : mapOfSets.values()) { + if (current.contains(e1)) { + set1 = current; + } else if (current.contains(e2)) { + set2 = current; + } + } + + assert set1 != null; + assert set2 != null; + + int size1 = set1.size(); + int size2 = set2.size(); + + assert mapOfSets.remove(e1, set1); + assert mapOfSets.remove(e2, set2); + + if (size1 > size2) { + set1.addAll(set2); + mapOfSets.put(e1, set1); + } else { + set2.addAll(set1); + mapOfSets.put(e2, set2); + } + } + + /** + * Provides a {@link Collection} containing all current subsets. + * + * @return {@link Collection} containing all current subsets + */ + @Override + public Collection getAllSubsets() { + return mapOfSets.values(); + } + + /** + * Checks whether the provided element is contained in any current subset and returns true or + * false accordingly. + * + * @param e element to be searched for + * @return true if contained, false if not + */ + @Override + public boolean contains(T e) { + + Preconditions.checkNotNull(e); + + for (S current : mapOfSets.values()) { + if (current.contains(e)) { + return true; + } + } + return false; + } +} From 6bee232ccd0eba572e73fb27ad01c424455ffa6e Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 16 Jul 2026 18:25:27 +0200 Subject: [PATCH 076/101] Ignore whole benchmark test class instead of just test method --- .../common/collect/union_find/UnionFindSimpleBenchmarkTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java b/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java index 269b13791..e2ee15e2e 100644 --- a/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java +++ b/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java @@ -22,11 +22,11 @@ import org.junit.Ignore; import org.junit.Test; +@Ignore public class UnionFindSimpleBenchmarkTest { final int lowerBound = 3; final int factorForComparison = 4; - @Ignore @Test public void unionBigOQuadraticEvaluationTest() { // BEGINNING of 1st loop From 9f6958085bf68fdc5c762fd0c86b190c59cc8139 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 16 Jul 2026 18:27:23 +0200 Subject: [PATCH 077/101] Move AbstractGenericUnionFind to union_find package --- .../collect/{ => union_find}/AbstractGenericUnionFind.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename src/org/sosy_lab/common/collect/{ => union_find}/AbstractGenericUnionFind.java (98%) diff --git a/src/org/sosy_lab/common/collect/AbstractGenericUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java similarity index 98% rename from src/org/sosy_lab/common/collect/AbstractGenericUnionFind.java rename to src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java index 6adf07355..d309350b6 100644 --- a/src/org/sosy_lab/common/collect/AbstractGenericUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java @@ -6,14 +6,13 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.sosy_lab.common.collect; +package org.sosy_lab.common.collect.union_find; import com.google.common.base.Preconditions; import com.google.errorprone.annotations.Var; import java.util.Collection; import java.util.Map; import java.util.Set; -import org.sosy_lab.common.collect.union_find.UnionFind; /** * An abstract, generic implementation of {@link UnionFind} using a {@link Map} of {@link Set}s. In From af996d258f66d4b24b0bd08ba70d409a8e66d4da Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 16 Jul 2026 18:37:47 +0200 Subject: [PATCH 078/101] Fic bug causing build to fail (incorrect warning suppression) --- .../common/collect/union_find/AbstractGenericUnionFind.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java index d309350b6..3d2caf2e4 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java @@ -72,7 +72,7 @@ public T find(T e) { * @param e1 first element * @param e2 second element */ - @SuppressWarnings("unchecked cast") + @SuppressWarnings("unchecked") @Override public void union(T e1, T e2) { @@ -108,7 +108,7 @@ public void union(T e1, T e2) { } } - @SuppressWarnings("unchecked cast") + @SuppressWarnings("unchecked") private void addElementAsNewSet(T e) { if (!contains(e)) { From 344cd5fc7cab5eff1aeb83aaa16bca46b719cc89 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 16 Jul 2026 19:03:21 +0200 Subject: [PATCH 079/101] Add package-info.java to union_find package --- .../common/collect/union_find/package-info.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/union_find/package-info.java diff --git a/src/org/sosy_lab/common/collect/union_find/package-info.java b/src/org/sosy_lab/common/collect/union_find/package-info.java new file mode 100644 index 000000000..b0d3d10a4 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/package-info.java @@ -0,0 +1,17 @@ +// 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 + +/** + * This package contains additional interfaces and implementations for collections, as well as + * further collection utilities. + */ +@com.google.errorprone.annotations.CheckReturnValue +@javax.annotation.ParametersAreNonnullByDefault +@org.sosy_lab.common.annotations.ReturnValuesAreNonnullByDefault +@org.sosy_lab.common.annotations.FieldsAreNonnullByDefault +package org.sosy_lab.common.collect.union_find; From 761acec2a74ee609072166524d0f90381e2c68b1 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 16 Jul 2026 19:04:08 +0200 Subject: [PATCH 080/101] Make SortedTreeSetUnionFind extend AbstractGenericUnionFind --- .../union_find/AbstractGenericUnionFind.java | 2 +- .../union_find/SortedTreeSetUnionFind.java | 170 +----------------- 2 files changed, 6 insertions(+), 166 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java index 3d2caf2e4..0b5abf9c2 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java @@ -25,7 +25,7 @@ public abstract class AbstractGenericUnionFind, M extends Map> implements UnionFind { - private final M mapOfSets; + protected final M mapOfSets; /** * Takes an empty map of the desired kind and allocates it to the variable mapOfSets. This enables diff --git a/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java index 93e9faf2b..3cd8e1418 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java @@ -8,13 +8,7 @@ package org.sosy_lab.common.collect.union_find; -import com.google.common.base.Preconditions; -import com.google.errorprone.annotations.Var; -import java.util.Collection; import java.util.HashMap; -import java.util.Map; -import java.util.NavigableSet; -import java.util.Set; import java.util.TreeSet; /** @@ -26,167 +20,13 @@ * @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> mapOfSets; +public class SortedTreeSetUnionFind< + T extends Comparable, S extends TreeSet, M extends HashMap> + extends AbstractGenericUnionFind implements SortedUnionFind { /** Generates an empty {@link SortedTreeSetUnionFind}. */ + @SuppressWarnings("unchecked") public SortedTreeSetUnionFind() { - mapOfSets = new HashMap<>(); - } - - /** - * Returns the canonical element of the set containing the provided element. - * - * @param e element for which set is to be found - * @return canonical element of the found set - * @throws IllegalArgumentException if element is not contained in any subset - */ - @Override - public T find(T e) { - - Preconditions.checkNotNull(e); - for (NavigableSet current : mapOfSets.values()) { - if (current.contains(e)) { - for (T element : current) { - if (mapOfSets.containsKey(element)) { - return element; - } - } - } - } - - throw new IllegalArgumentException("Element not contained"); - } - - /** - * Merges the sets represented by the two input values according to standard Union-Find behaviour. - * - *

USES: Add new element as new set: pass it as both e1 and e2. Add new element to existing - * set: one input value is the new element, the other the canonical element of the set to be added - * to. Merge two existing sets: e1, e2 canonical elements of sets to be merged. - * - * @param e1 first element - * @param e2 second element - */ - @Override - public void union(T e1, T e2) { - - Preconditions.checkNotNull(e1); - Preconditions.checkNotNull(e2); - - if (e1.equals(e2)) { - addElementAsNewSet(e1); - } else { - Set canonicalElements = mapOfSets.keySet(); - - if (canonicalElements.contains(e1)) { - if (canonicalElements.contains(e2)) { - mergeExistingSets(e1, e2); - } else { - addElementToExistingSet(e2, e1); - } - } else if (canonicalElements.contains(e2)) { - addElementToExistingSet(e1, e2); - } else { - - if (contains(e1)) { - if (contains(e2)) { - mergeExistingSets(find(e1), find(e2)); - } else { - addElementToExistingSet(e2, find(e1)); - } - } else { - addElementAsNewSet(e1); - addElementToExistingSet(e2, e1); - } - } - } - } - - private void addElementAsNewSet(T e) { - - if (!contains(e)) { - NavigableSet newSet = new TreeSet<>(); - newSet.add(e); - mapOfSets.put(e, newSet); - } - } - - private void addElementToExistingSet(T e, T canon) { - - if (!contains(e)) { - for (NavigableSet currentSet : mapOfSets.values()) { - if (currentSet.contains(canon)) { - assert mapOfSets.remove(canon, currentSet); - currentSet.add(e); - mapOfSets.put(canon, currentSet); - break; - } - } - } else { - mergeExistingSets(find(e), canon); - } - } - - private void mergeExistingSets(T e1, T e2) { - - @Var NavigableSet set1 = null; - @Var NavigableSet set2 = null; - - for (NavigableSet current : mapOfSets.values()) { - if (current.contains(e1)) { - set1 = current; - } else if (current.contains(e2)) { - set2 = current; - } - } - - assert set1 != null; - assert set2 != null; - - int size1 = set1.size(); - int size2 = set2.size(); - - assert mapOfSets.remove(e1, set1); - assert mapOfSets.remove(e2, set2); - - if (size1 > size2) { - set1.addAll(set2); - mapOfSets.put(e1, set1); - } else { - set2.addAll(set1); - mapOfSets.put(e2, set2); - } - } - - /** - * Provides a {@link Collection} containing all current subsets. - * - * @return {@link Collection} containing all current subsets - */ - @Override - public Collection> getAllSubsets() { - return mapOfSets.values(); - } - - /** - * Checks whether the provided element is contained in any current subset and returns true or - * false accordingly. - * - * @param e element to be searched for - * @return true if contained, false if not - */ - @Override - public boolean contains(T e) { - - Preconditions.checkNotNull(e); - - for (NavigableSet current : mapOfSets.values()) { - if (current.contains(e)) { - return true; - } - } - return false; + super((M) new HashMap()); } } From e9e53d7e1fb94945cb46beec3268508ac73c041c Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 17 Jul 2026 10:38:49 +0200 Subject: [PATCH 081/101] Correct package info --- src/org/sosy_lab/common/collect/union_find/package-info.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/package-info.java b/src/org/sosy_lab/common/collect/union_find/package-info.java index b0d3d10a4..5e38b4818 100644 --- a/src/org/sosy_lab/common/collect/union_find/package-info.java +++ b/src/org/sosy_lab/common/collect/union_find/package-info.java @@ -7,8 +7,7 @@ // SPDX-License-Identifier: Apache-2.0 /** - * This package contains additional interfaces and implementations for collections, as well as - * further collection utilities. + * This package contains all interfaces and classes related to union-find. */ @com.google.errorprone.annotations.CheckReturnValue @javax.annotation.ParametersAreNonnullByDefault From eb1c8b8d5f9401f24740ff07db0c36a9509cb6b0 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 17 Jul 2026 14:16:14 +0200 Subject: [PATCH 082/101] Rework how AbstractGenericUnionFind gets correct types from subclasses --- .../union_find/AbstractGenericUnionFind.java | 12 +++++++----- .../union_find/SortedTreeSetUnionFind.java | 19 +++++++++++++++---- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java index 0b5abf9c2..73c53c92e 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java @@ -31,11 +31,10 @@ public abstract class AbstractGenericUnionFind, M extends Ma * Takes an empty map of the desired kind and allocates it to the variable mapOfSets. This enables * child classes to simply pass an object of the desired kind without having to modify the * constructor and methods. - * - * @param emptyMapOfSets empty map of desired type */ - public AbstractGenericUnionFind(M emptyMapOfSets) { - mapOfSets = emptyMapOfSets; + @SuppressWarnings("unchecked") + public AbstractGenericUnionFind() { + mapOfSets = (M) getEmptyMap(); } /** @@ -112,7 +111,7 @@ public void union(T e1, T e2) { private void addElementAsNewSet(T e) { if (!contains(e)) { - S newSet = (S) Set.of(); + S newSet = (S) getEmptySet(); newSet.add(e); mapOfSets.put(e, newSet); } @@ -194,4 +193,7 @@ public boolean contains(T e) { } return false; } + + protected abstract Set getEmptySet(); + protected abstract Map> getEmptyMap(); } diff --git a/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java index 3cd8e1418..c38c8c368 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java @@ -9,6 +9,8 @@ package org.sosy_lab.common.collect.union_find; import java.util.HashMap; +import java.util.Map; +import java.util.Set; import java.util.TreeSet; /** @@ -21,12 +23,21 @@ * ordering. */ public class SortedTreeSetUnionFind< - T extends Comparable, S extends TreeSet, M extends HashMap> - extends AbstractGenericUnionFind implements SortedUnionFind { + T extends Comparable> + extends AbstractGenericUnionFind, Map>> implements SortedUnionFind { /** Generates an empty {@link SortedTreeSetUnionFind}. */ - @SuppressWarnings("unchecked") public SortedTreeSetUnionFind() { - super((M) new HashMap()); + super(); + } + + @Override + protected Set getEmptySet() { + return new TreeSet<>(); + } + + @Override + protected Map> getEmptyMap() { + return new HashMap<>(); } } From 599310be26dc66ffea1c416d58ec63201c881cda Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 17 Jul 2026 15:13:33 +0200 Subject: [PATCH 083/101] Remove superfluous super() call in SortedTreeSetUnionFind constructor --- .../common/collect/union_find/SortedTreeSetUnionFind.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java index c38c8c368..41b5cbe1a 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java @@ -27,9 +27,7 @@ public class SortedTreeSetUnionFind< extends AbstractGenericUnionFind, Map>> implements SortedUnionFind { /** Generates an empty {@link SortedTreeSetUnionFind}. */ - public SortedTreeSetUnionFind() { - super(); - } + public SortedTreeSetUnionFind() {} @Override protected Set getEmptySet() { From 4de804396c632a886fc48be31408c9b999066875 Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 21 Jul 2026 10:39:59 +0200 Subject: [PATCH 084/101] format-source --- .../common/collect/union_find/AbstractGenericUnionFind.java | 1 + .../common/collect/union_find/SortedTreeSetUnionFind.java | 3 +-- src/org/sosy_lab/common/collect/union_find/package-info.java | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java index 73c53c92e..461120d01 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java @@ -195,5 +195,6 @@ public boolean contains(T e) { } protected abstract Set getEmptySet(); + protected abstract Map> getEmptyMap(); } diff --git a/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java index 41b5cbe1a..81a4a9742 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java @@ -22,8 +22,7 @@ * @param type of elements added to the Union-Find. Must be {@link Comparable} to ensure correct * ordering. */ -public class SortedTreeSetUnionFind< - T extends Comparable> +public class SortedTreeSetUnionFind> extends AbstractGenericUnionFind, Map>> implements SortedUnionFind { /** Generates an empty {@link SortedTreeSetUnionFind}. */ diff --git a/src/org/sosy_lab/common/collect/union_find/package-info.java b/src/org/sosy_lab/common/collect/union_find/package-info.java index 5e38b4818..0061c6a88 100644 --- a/src/org/sosy_lab/common/collect/union_find/package-info.java +++ b/src/org/sosy_lab/common/collect/union_find/package-info.java @@ -6,9 +6,7 @@ // // SPDX-License-Identifier: Apache-2.0 -/** - * This package contains all interfaces and classes related to union-find. - */ +/** This package contains all interfaces and classes related to union-find. */ @com.google.errorprone.annotations.CheckReturnValue @javax.annotation.ParametersAreNonnullByDefault @org.sosy_lab.common.annotations.ReturnValuesAreNonnullByDefault From 220ec5cdfa7d6d596958c9878ab1093861295920 Mon Sep 17 00:00:00 2001 From: Colleen Date: Wed, 22 Jul 2026 18:35:46 +0200 Subject: [PATCH 085/101] Refactor AbstractGenericUnionFind to remove unnecessary code --- .../collect/union_find/AbstractGenericUnionFind.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java index 461120d01..76a3e3a6b 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java @@ -122,9 +122,7 @@ private void addElementToExistingSet(T e, T canon) { if (!contains(e)) { for (S currentSet : mapOfSets.values()) { if (currentSet.contains(canon)) { - assert mapOfSets.remove(canon, currentSet); currentSet.add(e); - mapOfSets.put(canon, currentSet); break; } } @@ -152,15 +150,12 @@ private void mergeExistingSets(T e1, T e2) { int size1 = set1.size(); int size2 = set2.size(); - assert mapOfSets.remove(e1, set1); - assert mapOfSets.remove(e2, set2); - if (size1 > size2) { set1.addAll(set2); - mapOfSets.put(e1, set1); + assert mapOfSets.remove(e2, set2); } else { set2.addAll(set1); - mapOfSets.put(e2, set2); + assert mapOfSets.remove(e1, set1); } } From 1834c734d9c2647d6410fe28cf2595d57a8f5f2c Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 23 Jul 2026 09:47:14 +0200 Subject: [PATCH 086/101] Use sorted data structure for mapOfSets (previously only for subsets) in SortedUnionFind --- .../common/collect/union_find/SortedTreeSetUnionFind.java | 8 ++++++-- .../common/collect/union_find/SortedUnionFind.java | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java index 81a4a9742..869422969 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedTreeSetUnionFind.java @@ -10,7 +10,10 @@ import java.util.HashMap; import java.util.Map; +import java.util.NavigableMap; +import java.util.NavigableSet; import java.util.Set; +import java.util.TreeMap; import java.util.TreeSet; /** @@ -23,7 +26,8 @@ * ordering. */ public class SortedTreeSetUnionFind> - extends AbstractGenericUnionFind, Map>> implements SortedUnionFind { + extends AbstractGenericUnionFind, NavigableMap>> + implements SortedUnionFind { /** Generates an empty {@link SortedTreeSetUnionFind}. */ public SortedTreeSetUnionFind() {} @@ -35,6 +39,6 @@ protected Set getEmptySet() { @Override protected Map> getEmptyMap() { - return new HashMap<>(); + return new TreeMap<>(); } } diff --git a/src/org/sosy_lab/common/collect/union_find/SortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedUnionFind.java index 3a07a74a9..a8de46a58 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedUnionFind.java @@ -9,6 +9,7 @@ package org.sosy_lab.common.collect.union_find; import java.util.Collection; +import java.util.NavigableSet; import java.util.Set; /** @@ -40,7 +41,7 @@ public interface SortedUnionFind> { * * @return {@link Collection} containing all current subsets */ - Collection> getAllSubsets(); + Collection> getAllSubsets(); /** * Checks whether the provided element is contained in any current subset and returns true or From 3a54e64208456bb9cf78fac01056f3b92a5ac4c1 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 23 Jul 2026 11:44:04 +0200 Subject: [PATCH 087/101] Refactor AbstractGenericUnionFind to simplify awkward code --- .../union_find/AbstractGenericUnionFind.java | 34 +++++-------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java index 76a3e3a6b..d1eb45de8 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java @@ -9,7 +9,6 @@ package org.sosy_lab.common.collect.union_find; import com.google.common.base.Preconditions; -import com.google.errorprone.annotations.Var; import java.util.Collection; import java.util.Map; import java.util.Set; @@ -48,13 +47,10 @@ public AbstractGenericUnionFind() { public T find(T e) { Preconditions.checkNotNull(e); - for (S current : mapOfSets.values()) { - if (current.contains(e)) { - for (T element : current) { - if (mapOfSets.containsKey(element)) { - return element; - } - } + + for (T key : mapOfSets.keySet()) { + if (mapOfSets.get(key).contains(e)) { + return key; } } @@ -81,7 +77,7 @@ public void union(T e1, T e2) { if (e1.equals(e2)) { addElementAsNewSet(e1); } else { - S canonicalElements = (S) mapOfSets.keySet(); + Set canonicalElements = mapOfSets.keySet(); if (canonicalElements.contains(e1)) { if (canonicalElements.contains(e2)) { @@ -120,29 +116,17 @@ private void addElementAsNewSet(T e) { private void addElementToExistingSet(T e, T canon) { if (!contains(e)) { - for (S currentSet : mapOfSets.values()) { - if (currentSet.contains(canon)) { - currentSet.add(e); - break; - } - } + mapOfSets.get(canon).add(e); } else { mergeExistingSets(find(e), canon); } } + // e1 will be new canonical element only if it's set is actually bigger, otherwise e2 new canon private void mergeExistingSets(T e1, T e2) { - @Var S set1 = null; - @Var S set2 = null; - - for (S current : mapOfSets.values()) { - if (current.contains(e1)) { - set1 = current; - } else if (current.contains(e2)) { - set2 = current; - } - } + S set1 = mapOfSets.get(e1); + S set2 = mapOfSets.get(e2); assert set1 != null; assert set2 != null; From a180bcc6023b9efbae97b400489317f65baeeb0c Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 23 Jul 2026 12:03:32 +0200 Subject: [PATCH 088/101] Refactor AbstractGenericUnionFind to simplify awkward code --- .../collect/union_find/AbstractGenericUnionFind.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java index d1eb45de8..6e8a8ae32 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java @@ -11,6 +11,7 @@ import com.google.common.base.Preconditions; import java.util.Collection; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; /** @@ -48,9 +49,9 @@ public T find(T e) { Preconditions.checkNotNull(e); - for (T key : mapOfSets.keySet()) { - if (mapOfSets.get(key).contains(e)) { - return key; + for (Entry mapping : mapOfSets.entrySet()) { + if (mapping.getValue().contains(e)) { + return mapping.getKey(); } } From b9efedc537c093aeefa26580d6fe90c24616217e Mon Sep 17 00:00:00 2001 From: BaierD Date: Thu, 23 Jul 2026 15:34:33 +0200 Subject: [PATCH 089/101] UnionFindSimpleBenchmarkTest: make test parameterized and add 2 TODOs and commented out debug output (designed together with c-m-elliott in a meeting) --- .../UnionFindSimpleBenchmarkTest.java | 54 +++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java b/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java index e2ee15e2e..e41985f90 100644 --- a/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java +++ b/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java @@ -11,6 +11,7 @@ import static com.google.common.truth.Truth.assertThat; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.Var; import java.math.BigInteger; import java.time.Duration; @@ -21,11 +22,45 @@ import java.util.Set; import org.junit.Ignore; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; @Ignore +@RunWith(Parameterized.class) public class UnionFindSimpleBenchmarkTest { - final int lowerBound = 3; - final int factorForComparison = 4; + + private static final int maximumLower = 8; + private static final int maximumUpper = 10; + + private final int lowerBound; + private final int upperBound; + + /** + * Builds parameters for lowerBound and upperBound (as 2-Tuples). lowerBounds are computed from 1 + * to maximumLower. And maximumUpper, for each lowerBound, from current lowerBound to + * maximumUpper. + */ + @Parameters(name = "{index}: lowerBound {0}, upperBound {1}") + public static List getBounds() { + ImmutableList.Builder outer = ImmutableList.builder(); + for (int lower = 1; lower <= maximumLower; lower++) { + for (int upper = 2; upper <= maximumUpper; upper++) { + if (upper > lower) { + Integer[] inner = new Integer[2]; + inner[0] = lower; + inner[1] = upper; + outer.add(inner); + } + } + } + return outer.build(); + } + + public UnionFindSimpleBenchmarkTest(int lowerBound, int upperBound) { + this.lowerBound = lowerBound; + this.upperBound = upperBound; + } @Test public void unionBigOQuadraticEvaluationTest() { @@ -40,9 +75,9 @@ public void unionBigOQuadraticEvaluationTest() { Duration timeAfterFirstLoop = Duration.ofNanos(System.nanoTime()); Duration timeOfFirstLoop = timeAfterFirstLoop.minus(timeBeforeFirstLoop); // END of 1st loop + // System.out.println("Time for first loop: " + timeOfFirstLoop.getSeconds() + "s" + "\n"); // BEGINNING of 2nd loop - int upperBound = lowerBound * factorForComparison; Set> allUnionFindsOfSecondLoop = new HashSet<>(); Duration timeBeforeSecondLoop = Duration.ofNanos(System.nanoTime()); @@ -53,6 +88,7 @@ public void unionBigOQuadraticEvaluationTest() { Duration timeAfterSecondLoop = Duration.ofNanos(System.nanoTime()); Duration timeOfSecondLoop = timeAfterSecondLoop.minus(timeBeforeSecondLoop); // END of 2nd loop + // System.out.println("Time for second loop: " + timeOfSecondLoop.getSeconds() + "s" + "\n"); // will throw for larger n's as they won't fit into long long bellOfLowerBound = getBellNoOfN(lowerBound).longValueExact(); @@ -64,6 +100,10 @@ public void unionBigOQuadraticEvaluationTest() { (bellOfUpperBound * bellOfUpperBound) / (bellOfLowerBound * bellOfLowerBound))); } + // TODO: add a method that computes only permutations with n elements. + + // TODO: this computes all permutations from 2 to pHighestNumber + 2 -> make it compute them only + // from 1 to pHighestNumber private static List>> generatePartitions(int pHighestNumber) { @Var List>> allPermutations = new ArrayList<>(); @@ -95,6 +135,14 @@ private static List>> generatePartitions(int pHighestNumber) { newSets.add(currentExistingSet); } + /* + // This prints all permutations that are added without duplicates + for (Set> newSet : newSets) { + if (!allPermutations.contains(newSet)) { + System.out.println(newSet); + } + } + */ allPermutations = newSets; } From 056f4e1ea84101b258cabd5e029e235dd0816050 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 23 Jul 2026 15:05:58 +0200 Subject: [PATCH 090/101] Add class TreeNode for parent-pointer tree --- .../common/collect/union_find/TreeNode.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/union_find/TreeNode.java diff --git a/src/org/sosy_lab/common/collect/union_find/TreeNode.java b/src/org/sosy_lab/common/collect/union_find/TreeNode.java new file mode 100644 index 000000000..a96a780de --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/TreeNode.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.union_find; + +public class TreeNode { + + TreeNode parent; + T value; + + public TreeNode(TreeNode parent, T value) { + this.parent = parent; + this.value = value; + } + + public TreeNode getParent() { + return parent; + } + + public T getValue() { + return value; + } +} From a93864c3bee7783dc59c8e42fd13250ec3caa07d Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 23 Jul 2026 15:37:44 +0200 Subject: [PATCH 091/101] Add static creation methods in TreeNode; make parent null in root nodes for now --- .../common/collect/union_find/TreeNode.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/TreeNode.java b/src/org/sosy_lab/common/collect/union_find/TreeNode.java index a96a780de..b861183af 100644 --- a/src/org/sosy_lab/common/collect/union_find/TreeNode.java +++ b/src/org/sosy_lab/common/collect/union_find/TreeNode.java @@ -8,12 +8,19 @@ package org.sosy_lab.common.collect.union_find; +import javax.annotation.Nullable; + public class TreeNode { - TreeNode parent; + @Nullable TreeNode parent; T value; - public TreeNode(TreeNode parent, T value) { + private TreeNode(T value) { + this.parent = null; + this.value = value; + } + + private TreeNode(TreeNode parent, T value) { this.parent = parent; this.value = value; } @@ -25,4 +32,12 @@ public TreeNode getParent() { public T getValue() { return value; } + + public static TreeNode getNewRootNode(V value) { + return new TreeNode(value); + } + + public static TreeNode getNewNode(TreeNode parent, V value) { + return new TreeNode(parent, value); + } } From 47a8e3f7dbcdfd11c4c4cd61b4938c323ac3d16c Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 23 Jul 2026 16:00:32 +0200 Subject: [PATCH 092/101] Add simple ParentPointerTree class that uses TreeNode --- .../collect/union_find/ParentPointerTree.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java diff --git a/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java b/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java new file mode 100644 index 000000000..dc6f939fa --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java @@ -0,0 +1,52 @@ +// 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.union_find; + +import java.util.ArrayList; +import java.util.List; + +public class ParentPointerTree { + private final TreeNode root; + private final List> listOfNodes; + private int nextParentIndex; + private boolean timeToMoveOn; + private int size; + + public ParentPointerTree(T rootValue) { + this.root = TreeNode.getNewRootNode(rootValue); + listOfNodes = new ArrayList<>(); + listOfNodes.add(this.root); + nextParentIndex = 0; + timeToMoveOn = false; + size = 1; + } + + public TreeNode getRoot() { + return root; + } + + public int getSize() { + return size; + } + + public void addAsNewNode(T value) { + TreeNode node = TreeNode.getNewNode(listOfNodes.get(nextParentIndex), value); + updateNextParent(); + size++; + } + + // will currently create a binary tree as it increases counter every 2nd insert + private void updateNextParent() { + if(!timeToMoveOn) { + timeToMoveOn = true; + } else { + nextParentIndex++; + } + } +} From ebb1322892c9a93095ce5adf30d9a59aa73eb638 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 23 Jul 2026 16:09:29 +0200 Subject: [PATCH 093/101] Code format and tiny fixes --- .../common/collect/union_find/ParentPointerTree.java | 3 ++- src/org/sosy_lab/common/collect/union_find/TreeNode.java | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java b/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java index dc6f939fa..4a0ea0f51 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java @@ -37,13 +37,14 @@ public int getSize() { public void addAsNewNode(T value) { TreeNode node = TreeNode.getNewNode(listOfNodes.get(nextParentIndex), value); + listOfNodes.add(node); updateNextParent(); size++; } // will currently create a binary tree as it increases counter every 2nd insert private void updateNextParent() { - if(!timeToMoveOn) { + if (!timeToMoveOn) { timeToMoveOn = true; } else { nextParentIndex++; diff --git a/src/org/sosy_lab/common/collect/union_find/TreeNode.java b/src/org/sosy_lab/common/collect/union_find/TreeNode.java index b861183af..518abf04b 100644 --- a/src/org/sosy_lab/common/collect/union_find/TreeNode.java +++ b/src/org/sosy_lab/common/collect/union_find/TreeNode.java @@ -12,7 +12,7 @@ public class TreeNode { - @Nullable TreeNode parent; + @Nullable TreeNode parent; T value; private TreeNode(T value) { @@ -25,6 +25,7 @@ private TreeNode(TreeNode parent, T value) { this.value = value; } + @Nullable public TreeNode getParent() { return parent; } @@ -34,10 +35,10 @@ public T getValue() { } public static TreeNode getNewRootNode(V value) { - return new TreeNode(value); + return new TreeNode<>(value); } public static TreeNode getNewNode(TreeNode parent, V value) { - return new TreeNode(parent, value); + return new TreeNode<>(parent, value); } } From 9b9be09a002d26aa351351aaa3c8e3a2bebacfd3 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 23 Jul 2026 16:21:54 +0200 Subject: [PATCH 094/101] Add skeleton of ParentPointerTreeUnionFind --- .../ParentPointerTreeUnionFind.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java diff --git a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java new file mode 100644 index 000000000..b70b987ca --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.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.union_find; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class ParentPointerTreeUnionFind implements UnionFind { + + private final Map> forest; + + public ParentPointerTreeUnionFind() { + forest = new HashMap<>(); + } + + @Override + public T find(T e) { + //TODO + return null; + } + + @Override + public void union(T e1, T e2) { + //TODO + } + + @Override + public Collection> getAllSubsets() { + //TODO + return List.of(); + } + + @Override + public boolean contains(T e) { + //TODO + return false; + } +} From 87a1fb13a62ea65aa6d9545fef7b08bd35f2ac3b Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 24 Jul 2026 10:25:50 +0200 Subject: [PATCH 095/101] Implement most methods of ParentPointerTreeUnionFind and add required helper methods to ParentPointerTree --- .../union_find/AbstractGenericUnionFind.java | 2 +- .../collect/union_find/ParentPointerTree.java | 21 +++++ .../ParentPointerTreeUnionFind.java | 93 +++++++++++++++++-- 3 files changed, 106 insertions(+), 10 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java index 6e8a8ae32..d5e078a13 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java @@ -123,7 +123,7 @@ private void addElementToExistingSet(T e, T canon) { } } - // e1 will be new canonical element only if it's set is actually bigger, otherwise e2 new canon + // e1 will be new canonical element only if its set is actually bigger, otherwise e2 new canon private void mergeExistingSets(T e1, T e2) { S set1 = mapOfSets.get(e1); diff --git a/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java b/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java index 4a0ea0f51..610e73745 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java @@ -35,6 +35,16 @@ public int getSize() { return size; } + public boolean contains(T value) { + for (TreeNode current : listOfNodes) { + if (current.value.equals(value)) { + return true; + } + } + + return false; + } + public void addAsNewNode(T value) { TreeNode node = TreeNode.getNewNode(listOfNodes.get(nextParentIndex), value); listOfNodes.add(node); @@ -42,6 +52,17 @@ public void addAsNewNode(T value) { size++; } + public boolean appendTree(ParentPointerTree tree) { + TreeNode rootToBeAdded = tree.getRoot(); + + assert rootToBeAdded.parent == listOfNodes.get(nextParentIndex); + + size += tree.size; + listOfNodes.addAll(tree.listOfNodes); + updateNextParent(); + return true; + } + // will currently create a binary tree as it increases counter every 2nd insert private void updateNextParent() { if (!timeToMoveOn) { diff --git a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java index b70b987ca..c3f306fcf 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -8,10 +8,12 @@ package org.sosy_lab.common.collect.union_find; +import com.google.common.base.Preconditions; +import com.google.errorprone.annotations.Var; import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; public class ParentPointerTreeUnionFind implements UnionFind { @@ -23,25 +25,98 @@ public ParentPointerTreeUnionFind() { } @Override - public T find(T e) { - //TODO - return null; + public T find(T value) { + + Preconditions.checkNotNull(value); + + for (Entry> entry : forest.entrySet()) { + T key = entry.getKey(); + ParentPointerTree tree = entry.getValue(); + if (key.equals(value) || tree.contains(value)) { + return key; + } + } + + throw new IllegalArgumentException("Element not contained."); } @Override - public void union(T e1, T e2) { - //TODO + public void union(T value1, T value2) { + + Preconditions.checkNotNull(value1); + Preconditions.checkNotNull(value2); + + if (value1.equals(value2)) { + addElementAsNewSet(value1); + } else { + if (contains(value1)) { + if (contains(value2)) { + mergeExistingSets(find(value1), find(value2)); + } else { + addElementToExistingSet(value2, find(value1)); + } + } else if (contains(value2)) { + addElementToExistingSet(value1, find(value2)); + } else { + addElementAsNewSet(value1); + addElementToExistingSet(value2, value1); + } + } } @Override public Collection> getAllSubsets() { - //TODO - return List.of(); + // TODO + return null; } @Override public boolean contains(T e) { - //TODO + + for (ParentPointerTree tree : forest.values()) { + if (tree.contains(e)) { + return true; + } + } + return false; } + + private void addElementAsNewSet(T value) { + + if (!contains(value)) { + ParentPointerTree tree = new ParentPointerTree<>(value); + forest.put(value, tree); + } + } + + // canon1 will be new canonical element only if its set is actually bigger, otherwise canon2 new + // canon + private void mergeExistingSets(T canon1, T canon2) { + + @Var ParentPointerTree tree1 = null; + @Var ParentPointerTree tree2 = null; + + while (tree1 == null || tree2 == null) { + for (Entry> entry : forest.entrySet()) { + if (entry.getKey().equals(canon1)) { + tree1 = entry.getValue(); + } else if (entry.getKey().equals(canon2)) { + tree2 = entry.getValue(); + } + } + } + + if (tree1.getSize() > tree2.getSize()) { + assert tree1.appendTree(tree2); + assert forest.remove(canon2, tree2); + } else { + assert tree2.appendTree(tree1); + assert forest.remove(canon1, tree1); + } + } + + private void addElementToExistingSet(T value, T canon) { + forest.get(canon).addAsNewNode(value); + } } From 2a7248f87569a4380a4f1710b4be9cec52027075 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 24 Jul 2026 11:13:19 +0200 Subject: [PATCH 096/101] Implement missing methods in ParentPointerTreeUnionFind and add required helper methods to ParentPointerTree; return type of getAllSubsets() may need to be changed in the future --- .../union_find/AbstractGenericUnionFind.java | 1 - .../collect/union_find/ParentPointerTree.java | 17 +++++++++++++++-- .../union_find/ParentPointerTreeUnionFind.java | 12 ++++++++++-- .../common/collect/union_find/TreeNode.java | 4 ++-- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java index d5e078a13..d048df16e 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java @@ -68,7 +68,6 @@ public T find(T e) { * @param e1 first element * @param e2 second element */ - @SuppressWarnings("unchecked") @Override public void union(T e1, T e2) { diff --git a/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java b/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java index 610e73745..27100c9b6 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java @@ -9,7 +9,9 @@ package org.sosy_lab.common.collect.union_find; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class ParentPointerTree { private final TreeNode root; @@ -37,7 +39,7 @@ public int getSize() { public boolean contains(T value) { for (TreeNode current : listOfNodes) { - if (current.value.equals(value)) { + if (current.getValue().equals(value)) { return true; } } @@ -55,7 +57,7 @@ public void addAsNewNode(T value) { public boolean appendTree(ParentPointerTree tree) { TreeNode rootToBeAdded = tree.getRoot(); - assert rootToBeAdded.parent == listOfNodes.get(nextParentIndex); + assert rootToBeAdded.getParent() == listOfNodes.get(nextParentIndex); size += tree.size; listOfNodes.addAll(tree.listOfNodes); @@ -63,6 +65,17 @@ public boolean appendTree(ParentPointerTree tree) { return true; } + public Set getSetOfNodeValues() { + + Set allNodeValues = new HashSet<>(); + + for (TreeNode node : listOfNodes) { + allNodeValues.add(node.getValue()); + } + + return allNodeValues; + } + // will currently create a binary tree as it increases counter every 2nd insert private void updateNextParent() { if (!timeToMoveOn) { diff --git a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java index c3f306fcf..10d2aaf60 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -10,8 +10,10 @@ import com.google.common.base.Preconditions; import com.google.errorprone.annotations.Var; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -66,8 +68,14 @@ public void union(T value1, T value2) { @Override public Collection> getAllSubsets() { - // TODO - return null; + + List> allSubsets = new ArrayList<>(); + + for (ParentPointerTree tree : forest.values()) { + allSubsets.add(tree.getSetOfNodeValues()); + } + + return allSubsets; } @Override diff --git a/src/org/sosy_lab/common/collect/union_find/TreeNode.java b/src/org/sosy_lab/common/collect/union_find/TreeNode.java index 518abf04b..d253e2e42 100644 --- a/src/org/sosy_lab/common/collect/union_find/TreeNode.java +++ b/src/org/sosy_lab/common/collect/union_find/TreeNode.java @@ -12,8 +12,8 @@ public class TreeNode { - @Nullable TreeNode parent; - T value; + @Nullable private TreeNode parent; + private final T value; private TreeNode(T value) { this.parent = null; From 5dfd68004e4d79662097adfbd692554f3671efc5 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 24 Jul 2026 12:03:49 +0200 Subject: [PATCH 097/101] Avoid null value in TreeNode and bug fix --- .../common/collect/union_find/ParentPointerTree.java | 5 ++++- .../sosy_lab/common/collect/union_find/TreeNode.java | 10 ++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java b/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java index 27100c9b6..abebab072 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java @@ -8,6 +8,7 @@ package org.sosy_lab.common.collect.union_find; +import com.google.common.base.Preconditions; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -57,7 +58,9 @@ public void addAsNewNode(T value) { public boolean appendTree(ParentPointerTree tree) { TreeNode rootToBeAdded = tree.getRoot(); - assert rootToBeAdded.getParent() == listOfNodes.get(nextParentIndex); + TreeNode parent = listOfNodes.get(nextParentIndex); + Preconditions.checkNotNull(parent); + rootToBeAdded.setParent(parent); size += tree.size; listOfNodes.addAll(tree.listOfNodes); diff --git a/src/org/sosy_lab/common/collect/union_find/TreeNode.java b/src/org/sosy_lab/common/collect/union_find/TreeNode.java index d253e2e42..cca160144 100644 --- a/src/org/sosy_lab/common/collect/union_find/TreeNode.java +++ b/src/org/sosy_lab/common/collect/union_find/TreeNode.java @@ -8,15 +8,14 @@ package org.sosy_lab.common.collect.union_find; -import javax.annotation.Nullable; public class TreeNode { - @Nullable private TreeNode parent; + private TreeNode parent; private final T value; private TreeNode(T value) { - this.parent = null; + this.parent = this; this.value = value; } @@ -25,11 +24,14 @@ private TreeNode(TreeNode parent, T value) { this.value = value; } - @Nullable public TreeNode getParent() { return parent; } + public void setParent(TreeNode parent) { + this.parent = parent; + } + public T getValue() { return value; } From aedbd2238f638c83b7ff8ef523f5d023bbc1b66d Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 24 Jul 2026 14:58:55 +0200 Subject: [PATCH 098/101] Make TreeNode a final class --- src/org/sosy_lab/common/collect/union_find/TreeNode.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/TreeNode.java b/src/org/sosy_lab/common/collect/union_find/TreeNode.java index cca160144..8d3edabc8 100644 --- a/src/org/sosy_lab/common/collect/union_find/TreeNode.java +++ b/src/org/sosy_lab/common/collect/union_find/TreeNode.java @@ -8,8 +8,7 @@ package org.sosy_lab.common.collect.union_find; - -public class TreeNode { +public final class TreeNode { private TreeNode parent; private final T value; From 54b57a8390a2bc05d2930f0119dae793b76909b0 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 24 Jul 2026 15:32:44 +0200 Subject: [PATCH 099/101] Add mapping of each value to its node in ParentPointerTree and adapt methods where necessary --- .../collect/union_find/ParentPointerTree.java | 29 +++++++++---------- .../ParentPointerTreeUnionFind.java | 6 ++-- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java b/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java index abebab072..7bf9ec500 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java @@ -10,21 +10,25 @@ import com.google.common.base.Preconditions; import java.util.ArrayList; -import java.util.HashSet; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; public class ParentPointerTree { private final TreeNode root; private final List> listOfNodes; + private final Map> mapOfNodes; private int nextParentIndex; private boolean timeToMoveOn; private int size; public ParentPointerTree(T rootValue) { - this.root = TreeNode.getNewRootNode(rootValue); + root = TreeNode.getNewRootNode(rootValue); listOfNodes = new ArrayList<>(); listOfNodes.add(this.root); + mapOfNodes = new HashMap<>(); + mapOfNodes.put(rootValue, root); nextParentIndex = 0; timeToMoveOn = false; size = 1; @@ -39,23 +43,21 @@ public int getSize() { } public boolean contains(T value) { - for (TreeNode current : listOfNodes) { - if (current.getValue().equals(value)) { - return true; - } - } - return false; + return mapOfNodes.containsKey(value); } public void addAsNewNode(T value) { + TreeNode node = TreeNode.getNewNode(listOfNodes.get(nextParentIndex), value); listOfNodes.add(node); + mapOfNodes.put(value, node); updateNextParent(); size++; } public boolean appendTree(ParentPointerTree tree) { + TreeNode rootToBeAdded = tree.getRoot(); TreeNode parent = listOfNodes.get(nextParentIndex); @@ -64,23 +66,20 @@ public boolean appendTree(ParentPointerTree tree) { size += tree.size; listOfNodes.addAll(tree.listOfNodes); + mapOfNodes.putAll(tree.mapOfNodes); updateNextParent(); + return true; } public Set getSetOfNodeValues() { - Set allNodeValues = new HashSet<>(); - - for (TreeNode node : listOfNodes) { - allNodeValues.add(node.getValue()); - } - - return allNodeValues; + return mapOfNodes.keySet(); } // will currently create a binary tree as it increases counter every 2nd insert private void updateNextParent() { + if (!timeToMoveOn) { timeToMoveOn = true; } else { diff --git a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java index 10d2aaf60..d00d6bd50 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -32,10 +32,8 @@ public T find(T value) { Preconditions.checkNotNull(value); for (Entry> entry : forest.entrySet()) { - T key = entry.getKey(); - ParentPointerTree tree = entry.getValue(); - if (key.equals(value) || tree.contains(value)) { - return key; + if (entry.getValue().contains(value)) { + return entry.getKey(); } } From e3d08a9786669485b481965887707902ba562939 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 24 Jul 2026 15:49:07 +0200 Subject: [PATCH 100/101] Fix build --- .../common/collect/union_find/UnionFindSimpleBenchmarkTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java b/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java index e41985f90..324a6730e 100644 --- a/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java +++ b/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java @@ -42,7 +42,7 @@ public class UnionFindSimpleBenchmarkTest { * maximumUpper. */ @Parameters(name = "{index}: lowerBound {0}, upperBound {1}") - public static List getBounds() { + public static ImmutableList getBounds() { ImmutableList.Builder outer = ImmutableList.builder(); for (int lower = 1; lower <= maximumLower; lower++) { for (int upper = 2; upper <= maximumUpper; upper++) { From a1cd3b9f5a7b49c5ecb4ff403f3a1cac3ad94ef6 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 24 Jul 2026 16:23:25 +0200 Subject: [PATCH 101/101] Start on improving partition generation in benchmark test --- .../UnionFindSimpleBenchmarkTest.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java b/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java index 324a6730e..2040568f0 100644 --- a/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java +++ b/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java @@ -101,6 +101,28 @@ public void unionBigOQuadraticEvaluationTest() { } // TODO: add a method that computes only permutations with n elements. + /* + private static List>> generatePartitionsNEW(int pHighestNumber) { + @Var List>> allPartitions = new ArrayList<>(); + final Set> singletonSet = new HashSet<>(); + + for (int i = 0; i <= pHighestNumber; i++) { + Set singleNumberSet = Set.of(i); + singletonSet.add(singleNumberSet); + } + allPartitions.add(singletonSet); + + for (int i = 2; i <= pHighestNumber; i++) { + // TODO + List>> partitionsSoFar = new ArrayList<>(allPartitions); + + for (Set> current : partitionsSoFar) { + // TODO + } + } + + return allPartitions; + }*/ // TODO: this computes all permutations from 2 to pHighestNumber + 2 -> make it compute them only // from 1 to pHighestNumber