From 63a45e6f437a4fce1260d890ff1de7c9b37a9bb9 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 25 May 2026 13:52:23 +0200 Subject: [PATCH 001/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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/183] 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 From d816adb0eb06b1135127acf48e397b6b96d0dada Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 28 Jul 2026 10:51:08 +0200 Subject: [PATCH 102/183] Implement partition generation in a more scalable way in UnionFindSimpleBenchmarkTest --- .../UnionFindSimpleBenchmarkTest.java | 106 +++++++++--------- 1 file changed, 54 insertions(+), 52 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 2040568f0..94a3cecf5 100644 --- a/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java +++ b/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java @@ -100,75 +100,77 @@ public void unionBigOQuadraticEvaluationTest() { (bellOfUpperBound * bellOfUpperBound) / (bellOfLowerBound * bellOfLowerBound))); } - // 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<>(); + private static List>> generatePartitions(int pHighestNumber) { + List>> allPartitions = new ArrayList<>(); + Set> singletonSet = new HashSet<>(); - for (int i = 0; i <= pHighestNumber; i++) { + for (int i = 1; i <= pHighestNumber; i++) { Set singleNumberSet = Set.of(i); singletonSet.add(singleNumberSet); } allPartitions.add(singletonSet); + // System.out.println("New loop with upper bound " + pHighestNumber + ":"); + // System.out.println(singletonSet); + // biggest subset size currently being created for (int i = 2; i <= pHighestNumber; i++) { - // TODO - List>> partitionsSoFar = new ArrayList<>(allPartitions); + // deep copy of allPartitions to iterate over + List>> partitionsSoFar = new ArrayList<>(); + for (Set> currentSet : allPartitions) { + partitionsSoFar.add(createDeepCopy(currentSet)); + } + // iterate over all partitions that currently exist for (Set> current : partitionsSoFar) { - // TODO + // add no.s from 1 - pHighestNumber to subsets + for (int j = 1; j <= pHighestNumber; j++) { + // add j to each subset that doesn't contain it yet by merging with subset containing j + breaker: + for (Set currentSubset : current) { + if (!currentSubset.contains(j)) { + Set> copyOfCurrent = createDeepCopy(current); + + @Var Set toBeAddedTo = new HashSet<>(); + @Var Set toRemove = new HashSet<>(); + + for (Set subsetOfCopy : copyOfCurrent) { + if (subsetOfCopy.equals(currentSubset)) { + toBeAddedTo = subsetOfCopy; + } else if (subsetOfCopy.contains(j)) { + toRemove = subsetOfCopy; + } + } + + copyOfCurrent.remove(toRemove); + copyOfCurrent.remove(toBeAddedTo); + toBeAddedTo.addAll(toRemove); + copyOfCurrent.add(toBeAddedTo); + + for (Set> set : allPartitions) { + if (copyOfCurrent.equals(set)) { + continue breaker; + } + } + allPartitions.add(copyOfCurrent); + // System.out.println(copyOfCurrent); + } + } + } } } return allPartitions; - }*/ - - // 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<>(); - - // 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<>(); - - 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); - } + private static Set> createDeepCopy(Set> pSets) { + Set> copy = new HashSet<>(); - /* - // This prints all permutations that are added without duplicates - for (Set> newSet : newSets) { - if (!allPermutations.contains(newSet)) { - System.out.println(newSet); - } - } - */ - allPermutations = newSets; + for (Set subset : pSets) { + Set copyOfSubset = new HashSet<>(subset); + copy.add(copyOfSubset); } - return allPermutations; + return copy; } private static void transformPartitionsToUnionFind( From 53c373743ab87eaf87d0631be5cf849ef0e6fb86 Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 28 Jul 2026 11:09:00 +0200 Subject: [PATCH 103/183] Make refaster happy --- .../collect/union_find/UnionFindSimpleBenchmarkTest.java | 3 ++- 1 file changed, 2 insertions(+), 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 94a3cecf5..151cc96e8 100644 --- a/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java +++ b/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java @@ -105,7 +105,8 @@ private static List>> generatePartitions(int pHighestNumber) { Set> singletonSet = new HashSet<>(); for (int i = 1; i <= pHighestNumber; i++) { - Set singleNumberSet = Set.of(i); + Set singleNumberSet = new HashSet<>(); + singleNumberSet.add(i); singletonSet.add(singleNumberSet); } allPartitions.add(singletonSet); From d72cd52a89746f1a30bb62ef86f01c6f6560cbad Mon Sep 17 00:00:00 2001 From: Colleen Date: Wed, 12 Aug 2026 15:04:14 +0200 Subject: [PATCH 104/183] Move union-find tests to subpackage --- .../{ => tests}/SortedUnionFindTest.java | 4 +++- .../{ => tests}/UnionFindSimpleBenchmarkTest.java | 4 +++- .../collect/union_find/tests/package-info.java | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) rename src/org/sosy_lab/common/collect/union_find/{ => tests}/SortedUnionFindTest.java (96%) rename src/org/sosy_lab/common/collect/union_find/{ => tests}/UnionFindSimpleBenchmarkTest.java (97%) create mode 100644 src/org/sosy_lab/common/collect/union_find/tests/package-info.java diff --git a/src/org/sosy_lab/common/collect/union_find/SortedUnionFindTest.java b/src/org/sosy_lab/common/collect/union_find/tests/SortedUnionFindTest.java similarity index 96% rename from src/org/sosy_lab/common/collect/union_find/SortedUnionFindTest.java rename to src/org/sosy_lab/common/collect/union_find/tests/SortedUnionFindTest.java index 363bde722..836c8cbcb 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/union_find/tests/SortedUnionFindTest.java @@ -6,7 +6,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.sosy_lab.common.collect.union_find; +package org.sosy_lab.common.collect.union_find.tests; import static com.google.common.truth.Truth.assertThat; @@ -14,6 +14,8 @@ import com.google.errorprone.annotations.Var; import org.junit.BeforeClass; import org.junit.Test; +import org.sosy_lab.common.collect.union_find.SortedTreeSetUnionFind; +import org.sosy_lab.common.collect.union_find.SortedUnionFind; public class SortedUnionFindTest { diff --git a/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java b/src/org/sosy_lab/common/collect/union_find/tests/UnionFindSimpleBenchmarkTest.java similarity index 97% rename from src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java rename to src/org/sosy_lab/common/collect/union_find/tests/UnionFindSimpleBenchmarkTest.java index 151cc96e8..3c4f3016a 100644 --- a/src/org/sosy_lab/common/collect/union_find/UnionFindSimpleBenchmarkTest.java +++ b/src/org/sosy_lab/common/collect/union_find/tests/UnionFindSimpleBenchmarkTest.java @@ -6,7 +6,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.sosy_lab.common.collect.union_find; +package org.sosy_lab.common.collect.union_find.tests; import static com.google.common.truth.Truth.assertThat; @@ -25,6 +25,8 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import org.sosy_lab.common.collect.union_find.SortedTreeSetUnionFind; +import org.sosy_lab.common.collect.union_find.SortedUnionFind; @Ignore @RunWith(Parameterized.class) diff --git a/src/org/sosy_lab/common/collect/union_find/tests/package-info.java b/src/org/sosy_lab/common/collect/union_find/tests/package-info.java new file mode 100644 index 000000000..12386aa48 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/tests/package-info.java @@ -0,0 +1,14 @@ +// 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 all test classes related to union-find. */ +@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.tests; From 3b1644a16afb687d50fb977ad972150ed601fefe Mon Sep 17 00:00:00 2001 From: Colleen Date: Wed, 12 Aug 2026 19:00:42 +0200 Subject: [PATCH 105/183] Write union-find tests for ParentPointerTreeUnionFind --- .../tests/ParentPointerTreeUnionFindTest.java | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindTest.java diff --git a/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindTest.java b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindTest.java new file mode 100644 index 000000000..c09ff49dc --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindTest.java @@ -0,0 +1,151 @@ +// 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.tests; + +import static com.google.common.truth.Truth.assertThat; + +import java.util.Collection; +import java.util.Set; +import org.junit.Before; +import org.junit.Test; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind; + +public class ParentPointerTreeUnionFindTest { + private ParentPointerTreeUnionFind integerUnionFind; + + @Before + public void setup() { + integerUnionFind = new ParentPointerTreeUnionFind<>(); + } + + @Test + public void testFind_afterSelfUnion_returnsItself() { + + integerUnionFind.union(0, 0); + + assertThat(integerUnionFind.find(0)).isEqualTo(0); + } + + @Test + public void testUnion_twoNewElements_producesSingleSubsetOfSizeTwo() { + + integerUnionFind.union(0, 1); + + assertThat(integerUnionFind.find(0)).isEqualTo(integerUnionFind.find(1)); + assertThat(integerUnionFind.getAllSubsets()).hasSize(1); + } + + @Test + public void testUnion_disjointPairs_produceDistinctSubsets() { + + integerUnionFind.union(0, 1); + integerUnionFind.union(2, 3); + + assertThat(integerUnionFind.find(0)).isNotEqualTo(integerUnionFind.find(2)); + assertThat(integerUnionFind.getAllSubsets()).hasSize(2); + } + + @Test + public void testUnion_severalElementsToSameSubset() { + + integerUnionFind.union(0, 1); + integerUnionFind.union(1, 2); + integerUnionFind.union(2, 3); + + Integer canon = integerUnionFind.find(0); + assertThat(integerUnionFind.find(0)).isEqualTo(canon); + assertThat(integerUnionFind.find(1)).isEqualTo(canon); + assertThat(integerUnionFind.find(2)).isEqualTo(canon); + assertThat(integerUnionFind.find(3)).isEqualTo(canon); + assertThat(integerUnionFind.getAllSubsets()).hasSize(1); + } + + @Test + public void testUnion_duplicateUnionCall_doesNotLeadToDuplicates() { + + integerUnionFind.union(0, 1); + integerUnionFind.union(0, 1); + integerUnionFind.union(1, 0); + + assertThat(integerUnionFind.find(0)).isEqualTo(integerUnionFind.find(1)); + assertThat(integerUnionFind.getAllSubsets()).hasSize(1); + } + + @Test + public void testUnion_mergesTwoExistingMultiElementSubsets() { + + integerUnionFind.union(0, 1); + integerUnionFind.union(0, 2); + integerUnionFind.union(3, 4); + integerUnionFind.union(3, 5); + + assertThat(integerUnionFind.getAllSubsets()).hasSize(2); + + integerUnionFind.union(0, 3); + + Integer canon = integerUnionFind.find(0); + for (int i = 0; i <= 5; i++) { + assertThat(integerUnionFind.find(i)).isEqualTo(canon); + } + } + + @Test + public void testUnion_constantCanonicalElementDuringNonLinearInsertion() { + + integerUnionFind.union(3, 3); + integerUnionFind.union(3, 2); + integerUnionFind.union(3, 5); + integerUnionFind.union(3, 1); + integerUnionFind.union(3, 8); + integerUnionFind.union(3, 6); + integerUnionFind.union(3, 9); + integerUnionFind.union(3, 7); + integerUnionFind.union(3, 4); + integerUnionFind.union(3, 0); + + Integer canon = integerUnionFind.find(3); + for (int i = 0; i <= 9; i++) { + assertThat(integerUnionFind.find(i)).isEqualTo(canon); + } + } + + @Test + public void testGetAllSubsets_reflectsCorrectMembershipAfterMultipleUnions() { + + for (int i = 0; i <= 4; i++) { + integerUnionFind.union(0, i); + } + for (int i = 5; i <= 9; i++) { + integerUnionFind.union(5, i); + } + + Collection> subsets = integerUnionFind.getAllSubsets(); + + assertThat(subsets).hasSize(2); + for (Set subset : subsets) { + assertThat(subset).hasSize(5); + } + } + + @Test + public void testUnion_StringElements() { + + ParentPointerTreeUnionFind stringUnionFind = new ParentPointerTreeUnionFind<>(); + + stringUnionFind.union(Integer.toString(0), Integer.toString(1)); + stringUnionFind.union(Integer.toString(0), Integer.toString(2)); + stringUnionFind.union(Integer.toString(3), Integer.toString(4)); + + assertThat(stringUnionFind.find(Integer.toString(0))) + .isEqualTo(stringUnionFind.find(Integer.toString(2))); + assertThat(stringUnionFind.find(Integer.toString(0))) + .isNotEqualTo(stringUnionFind.find(Integer.toString(3))); + assertThat(stringUnionFind.getAllSubsets()).hasSize(2); + } +} From 4341fb07dc57d557988e9d93d69f567379d5ed15 Mon Sep 17 00:00:00 2001 From: Colleen Date: Wed, 12 Aug 2026 19:01:09 +0200 Subject: [PATCH 106/183] Fix bugs in ParentPointerTreeUnionFind uncovered by tests --- .../ParentPointerTreeUnionFind.java | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) 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 d00d6bd50..e60e5e2d1 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -51,7 +51,12 @@ public void union(T value1, T value2) { } else { if (contains(value1)) { if (contains(value2)) { - mergeExistingSets(find(value1), find(value2)); + T canon1 = find(value1); + T canon2 = find(value2); + + if (!canon1.equals(canon2)) { + mergeExistingSets(find(value1), find(value2)); + } } else { addElementToExistingSet(value2, find(value1)); } @@ -103,16 +108,25 @@ 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(); + for (Entry> entry : forest.entrySet()) { + if (entry.getKey().equals(canon1)) { + tree1 = entry.getValue(); + + if (tree2 != null) { + break; + } + } else if (entry.getKey().equals(canon2)) { + tree2 = entry.getValue(); + + if (tree1 != null) { + break; } } } + Preconditions.checkNotNull(tree1); + Preconditions.checkNotNull(tree2); + if (tree1.getSize() > tree2.getSize()) { assert tree1.appendTree(tree2); assert forest.remove(canon2, tree2); From 166ca8624970b0fcc6167f1530d81274dd453367 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 13 Aug 2026 13:29:37 +0200 Subject: [PATCH 107/183] Add ParentPointerTreeSortedUnionFind --- .../ParentPointerTreeSortedUnionFind.java | 34 +++++++++++++++++++ .../ParentPointerTreeUnionFind.java | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 src/org/sosy_lab/common/collect/union_find/ParentPointerTreeSortedUnionFind.java diff --git a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeSortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeSortedUnionFind.java new file mode 100644 index 000000000..037e53326 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeSortedUnionFind.java @@ -0,0 +1,34 @@ +// 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.Collection; +import java.util.List; +import java.util.NavigableMap; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +public class ParentPointerTreeSortedUnionFind extends ParentPointerTreeUnionFind { + + // subsets are in order of their canonical elements; elements in subsets are sorted as well + @Override + public Collection> getAllSubsets() { + + NavigableMap> forestSortedByKeys = new TreeMap<>(forest); + List> allSubsets = new ArrayList<>(); + + for (ParentPointerTree tree : forestSortedByKeys.values()) { + allSubsets.add(new TreeSet<>(tree.getSetOfNodeValues())); + } + + return allSubsets; + } +} 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 e60e5e2d1..962ff6494 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -20,7 +20,7 @@ public class ParentPointerTreeUnionFind implements UnionFind { - private final Map> forest; + protected final Map> forest; public ParentPointerTreeUnionFind() { forest = new HashMap<>(); From 4f9b6ab6b2922927836471b8dd204d85280348ea Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 13 Aug 2026 14:12:18 +0200 Subject: [PATCH 108/183] Add test class testing only sortedness for ParentPointerTreeSortedUnionFind --- ...entPointerTreeUnionFindSortednessTest.java | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java diff --git a/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java new file mode 100644 index 000000000..4e1b4eec0 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java @@ -0,0 +1,120 @@ +// 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.tests; + +import static com.google.common.truth.Truth.assertThat; + +import java.util.Collection; +import java.util.Set; +import org.junit.Before; +import org.junit.Test; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeSortedUnionFind; + +public class ParentPointerTreeUnionFindSortednessTest { + + private ParentPointerTreeSortedUnionFind sortedUnionFind; + + @Before + public void setup() { + sortedUnionFind = new ParentPointerTreeSortedUnionFind<>(); + } + + @Test + public void testGetAllSubsets_elementsAddedInAscendingOrder_remainSorted() { + + sortedUnionFind.union(0, 1); + sortedUnionFind.union(0, 2); + sortedUnionFind.union(0, 3); + + assertThat(onlySubsetOf(sortedUnionFind)).containsExactly(0, 1, 2, 3).inOrder(); + } + + @Test + public void testGetAllSubsets_elementsAddedInDescendingOrder_areSortedAscending() { + + sortedUnionFind.union(3, 2); + sortedUnionFind.union(3, 1); + sortedUnionFind.union(3, 0); + + assertThat(onlySubsetOf(sortedUnionFind)).containsExactly(0, 1, 2, 3).inOrder(); + } + + @Test + public void testGetAllSubsets_nonlinearInsertionOrder_areReturnedSorted() { + + sortedUnionFind.union(3, 2); + sortedUnionFind.union(3, 4); + sortedUnionFind.union(3, 0); + sortedUnionFind.union(3, 5); + sortedUnionFind.union(3, 1); + + assertThat(onlySubsetOf(sortedUnionFind)).containsExactly(0, 1, 2, 3, 4, 5).inOrder(); + } + + @Test + public void testGetAllSubsets_multipleSubsets_eachSortedIndependently() { + + sortedUnionFind.union(0, 1); + sortedUnionFind.union(0, 2); + sortedUnionFind.union(10, 11); + sortedUnionFind.union(10, 12); + + for (Collection subset : sortedUnionFind.getAllSubsets()) { + assertThat(subset).isInOrder(); + } + } + + @Test + public void testGetAllSubsets_afterMergingTwoSubsets_resultIsSorted() { + + sortedUnionFind.union(0, 1); + sortedUnionFind.union(0, 2); + sortedUnionFind.union(3, 4); + sortedUnionFind.union(3, 5); + + sortedUnionFind.union(0, 3); + + assertThat(onlySubsetOf(sortedUnionFind)).containsExactly(0, 1, 2, 3, 4, 5).inOrder(); + } + + @Test + public void testGetAllSubsets_stringElements_areSortedAlphabetically() { + + ParentPointerTreeSortedUnionFind stringSortedUnionFind = + new ParentPointerTreeSortedUnionFind<>(); + String[] expected = {"-1", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; + + for (int i = 0; i <= 2; i++) { + stringSortedUnionFind.union("0", Integer.toString(i)); + } + for (int i = 3; i <= 5; i++) { + stringSortedUnionFind.union("3", Integer.toString(i)); + } + for (int i = 6; i <= 8; i++) { + stringSortedUnionFind.union("6", Integer.toString(i)); + } + + stringSortedUnionFind.union("9", "9"); + stringSortedUnionFind.union("0", "6"); + stringSortedUnionFind.union("6", "-1"); + stringSortedUnionFind.union("1", "4"); + stringSortedUnionFind.union("0", "9"); + + assertThat(onlySubsetOf(stringSortedUnionFind)).containsExactlyElementsIn(expected).inOrder(); + } + + private static Set onlySubsetOf( + ParentPointerTreeSortedUnionFind sortedUnionFind) { + + Collection> subsets = sortedUnionFind.getAllSubsets(); + + assertThat(subsets).hasSize(1); + return subsets.iterator().next(); + } +} From b9c64731b264950bd2808040ba347aa30f37493a Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 13 Aug 2026 14:15:51 +0200 Subject: [PATCH 109/183] Rename ParentPointerTreeSortedUnionFind to SortedParentPointerTreeUnionFind --- ...nd.java => SortedParentPointerTreeUnionFind.java} | 2 +- .../ParentPointerTreeUnionFindSortednessTest.java | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) rename src/org/sosy_lab/common/collect/union_find/{ParentPointerTreeSortedUnionFind.java => SortedParentPointerTreeUnionFind.java} (93%) diff --git a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeSortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java similarity index 93% rename from src/org/sosy_lab/common/collect/union_find/ParentPointerTreeSortedUnionFind.java rename to src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java index 037e53326..307a2064e 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeSortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java @@ -16,7 +16,7 @@ import java.util.TreeMap; import java.util.TreeSet; -public class ParentPointerTreeSortedUnionFind extends ParentPointerTreeUnionFind { +public class SortedParentPointerTreeUnionFind extends ParentPointerTreeUnionFind { // subsets are in order of their canonical elements; elements in subsets are sorted as well @Override diff --git a/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java index 4e1b4eec0..6240fa45c 100644 --- a/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java +++ b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java @@ -14,15 +14,15 @@ import java.util.Set; import org.junit.Before; import org.junit.Test; -import org.sosy_lab.common.collect.union_find.ParentPointerTreeSortedUnionFind; +import org.sosy_lab.common.collect.union_find.SortedParentPointerTreeUnionFind; public class ParentPointerTreeUnionFindSortednessTest { - private ParentPointerTreeSortedUnionFind sortedUnionFind; + private SortedParentPointerTreeUnionFind sortedUnionFind; @Before public void setup() { - sortedUnionFind = new ParentPointerTreeSortedUnionFind<>(); + sortedUnionFind = new SortedParentPointerTreeUnionFind<>(); } @Test @@ -86,8 +86,8 @@ public void testGetAllSubsets_afterMergingTwoSubsets_resultIsSorted() { @Test public void testGetAllSubsets_stringElements_areSortedAlphabetically() { - ParentPointerTreeSortedUnionFind stringSortedUnionFind = - new ParentPointerTreeSortedUnionFind<>(); + SortedParentPointerTreeUnionFind stringSortedUnionFind = + new SortedParentPointerTreeUnionFind<>(); String[] expected = {"-1", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; for (int i = 0; i <= 2; i++) { @@ -110,7 +110,7 @@ public void testGetAllSubsets_stringElements_areSortedAlphabetically() { } private static Set onlySubsetOf( - ParentPointerTreeSortedUnionFind sortedUnionFind) { + SortedParentPointerTreeUnionFind sortedUnionFind) { Collection> subsets = sortedUnionFind.getAllSubsets(); From c9d73799ff303e4b6fdb31234a35012af063e74d Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 13 Aug 2026 14:32:11 +0200 Subject: [PATCH 110/183] Fix code format --- .../tests/ParentPointerTreeUnionFindSortednessTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java index 6240fa45c..8447bb844 100644 --- a/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java +++ b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java @@ -109,8 +109,7 @@ public void testGetAllSubsets_stringElements_areSortedAlphabetically() { assertThat(onlySubsetOf(stringSortedUnionFind)).containsExactlyElementsIn(expected).inOrder(); } - private static Set onlySubsetOf( - SortedParentPointerTreeUnionFind sortedUnionFind) { + private static Set onlySubsetOf(SortedParentPointerTreeUnionFind sortedUnionFind) { Collection> subsets = sortedUnionFind.getAllSubsets(); From fe3ad12a9487691f6cb04c28e9c92d2403fba9d0 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 13 Aug 2026 14:41:43 +0200 Subject: [PATCH 111/183] Fix bug in ParentPointerTree causing tree to not be binary --- .../sosy_lab/common/collect/union_find/ParentPointerTree.java | 1 + 1 file changed, 1 insertion(+) 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 7bf9ec500..2435f91c9 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java @@ -84,6 +84,7 @@ private void updateNextParent() { timeToMoveOn = true; } else { nextParentIndex++; + timeToMoveOn = false; } } } From f0320e40a2996e21d8e4c280493f19f9cf6bcf13 Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 13 Aug 2026 14:50:53 +0200 Subject: [PATCH 112/183] Add documentation to ParentPointerTreeUnionFind --- .../ParentPointerTreeUnionFind.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) 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 962ff6494..b5055791e 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -18,14 +18,30 @@ import java.util.Map.Entry; import java.util.Set; +/** + * An implementation of {@link UnionFind} using a {@link Map} of {@link ParentPointerTree}s. In + * order to represent subsets (the trees) 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 class ParentPointerTreeUnionFind implements UnionFind { protected final Map> forest; + /** Creates an empty instance. */ public ParentPointerTreeUnionFind() { forest = new HashMap<>(); } + /** + * Returns the canonical element of the set containing the provided element. + * + * @param value 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 value) { @@ -40,6 +56,16 @@ public T find(T value) { 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 value1 and value2. 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: value1, value2 canonical elements of sets to be merged. + * + * @param value1 first element + * @param value2 second element + */ @Override public void union(T value1, T value2) { @@ -69,6 +95,11 @@ public void union(T value1, T value2) { } } + /** + * Provides a {@link Collection} containing all current subsets. + * + * @return {@link Collection} containing all current subsets + */ @Override public Collection> getAllSubsets() { @@ -81,6 +112,13 @@ public Collection> getAllSubsets() { return allSubsets; } + /** + * 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) { From 8a8f25cea189447bea6b11374d0baaac16c52a0b Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 13 Aug 2026 14:53:51 +0200 Subject: [PATCH 113/183] Add documentation to SortedParentPointerTreeUnionFind --- .../union_find/SortedParentPointerTreeUnionFind.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java index 307a2064e..5e800866a 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java @@ -18,6 +18,13 @@ public class SortedParentPointerTreeUnionFind extends ParentPointerTreeUnionFind { + /** + * Provides a {@link Collection} containing all current subsets. It contains the subsets sorted by + * their canonical elements in ascending order. The contents of the subsets are also sorted in + * ascending order. + * + * @return sorted {@link Collection} containing all current subsets + */ // subsets are in order of their canonical elements; elements in subsets are sorted as well @Override public Collection> getAllSubsets() { From 452c919deaae618b475a90c25d91796401772fc3 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 10:53:41 +0200 Subject: [PATCH 114/183] Remove manual size tracking in ParentPointerTree as ArrayList already tracks size actively --- .../common/collect/union_find/ParentPointerTree.java | 6 +----- 1 file changed, 1 insertion(+), 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 2435f91c9..f79afafa3 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java @@ -21,7 +21,6 @@ public class ParentPointerTree { private final Map> mapOfNodes; private int nextParentIndex; private boolean timeToMoveOn; - private int size; public ParentPointerTree(T rootValue) { root = TreeNode.getNewRootNode(rootValue); @@ -31,7 +30,6 @@ public ParentPointerTree(T rootValue) { mapOfNodes.put(rootValue, root); nextParentIndex = 0; timeToMoveOn = false; - size = 1; } public TreeNode getRoot() { @@ -39,7 +37,7 @@ public TreeNode getRoot() { } public int getSize() { - return size; + return listOfNodes.size(); } public boolean contains(T value) { @@ -53,7 +51,6 @@ public void addAsNewNode(T value) { listOfNodes.add(node); mapOfNodes.put(value, node); updateNextParent(); - size++; } public boolean appendTree(ParentPointerTree tree) { @@ -64,7 +61,6 @@ public boolean appendTree(ParentPointerTree tree) { Preconditions.checkNotNull(parent); rootToBeAdded.setParent(parent); - size += tree.size; listOfNodes.addAll(tree.listOfNodes); mapOfNodes.putAll(tree.mapOfNodes); updateNextParent(); From face617098ca6f934a10005a727bae9ce8b55b1b Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 14:52:42 +0200 Subject: [PATCH 115/183] Introduce AbstractTreeNode class --- .../collect/union_find/AbstractTreeNode.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/union_find/AbstractTreeNode.java diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractTreeNode.java b/src/org/sosy_lab/common/collect/union_find/AbstractTreeNode.java new file mode 100644 index 000000000..97226e207 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/AbstractTreeNode.java @@ -0,0 +1,37 @@ +// 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 abstract class AbstractTreeNode { + + protected AbstractTreeNode parent; + protected final T value; + + protected AbstractTreeNode(T value) { + this.parent = this; + this.value = value; + } + + protected AbstractTreeNode(AbstractTreeNode parent, T value) { + this.parent = parent; + this.value = value; + } + + public AbstractTreeNode getParent() { + return parent; + } + + public void setParent(AbstractTreeNode parent) { + this.parent = parent; + } + + public T getValue() { + return value; + } +} From 8f9a3cb5e61a16373de4276845fec4fa8e937448 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 14:54:26 +0200 Subject: [PATCH 116/183] Rename TreeNode to NonRootNode which now extends AbstractTreeNode and functions accordingly --- .../collect/union_find/NonRootNode.java | 16 +++++++ .../common/collect/union_find/TreeNode.java | 45 ------------------- 2 files changed, 16 insertions(+), 45 deletions(-) create mode 100644 src/org/sosy_lab/common/collect/union_find/NonRootNode.java delete mode 100644 src/org/sosy_lab/common/collect/union_find/TreeNode.java diff --git a/src/org/sosy_lab/common/collect/union_find/NonRootNode.java b/src/org/sosy_lab/common/collect/union_find/NonRootNode.java new file mode 100644 index 000000000..e5fef5861 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/NonRootNode.java @@ -0,0 +1,16 @@ +// 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 final class NonRootNode extends AbstractTreeNode { + + public NonRootNode(AbstractTreeNode parent, T value) { + super(parent, value); + } +} diff --git a/src/org/sosy_lab/common/collect/union_find/TreeNode.java b/src/org/sosy_lab/common/collect/union_find/TreeNode.java deleted file mode 100644 index 8d3edabc8..000000000 --- a/src/org/sosy_lab/common/collect/union_find/TreeNode.java +++ /dev/null @@ -1,45 +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.union_find; - -public final class TreeNode { - - private TreeNode parent; - private final T value; - - private TreeNode(T value) { - this.parent = this; - this.value = value; - } - - private TreeNode(TreeNode parent, T value) { - this.parent = parent; - this.value = value; - } - - public TreeNode getParent() { - return parent; - } - - public void setParent(TreeNode parent) { - this.parent = parent; - } - - 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 f2ef0f06b8769de66aee323ca39fe57d8d1633ee Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 14:55:05 +0200 Subject: [PATCH 117/183] Add RootNode which extends AbstractTreeNode and functions accordingly --- .../common/collect/union_find/RootNode.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/union_find/RootNode.java diff --git a/src/org/sosy_lab/common/collect/union_find/RootNode.java b/src/org/sosy_lab/common/collect/union_find/RootNode.java new file mode 100644 index 000000000..94a16947c --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/RootNode.java @@ -0,0 +1,39 @@ +// 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 final class RootNode extends AbstractTreeNode { + + private int rank; + private int size; + + public RootNode(T value) { + + super(value); + + this.rank = 0; + this.size = 1; + } + + public int getRank() { + return rank; + } + + public int getSize() { + return size; + } + + public void incrementSizeByOne() { + size++; + } + + public void incrementSizeBy(int n) { + size += n; + } +} From 9293ac08b24b1450c2ef864e2febaa7970ab5408 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 15:09:55 +0200 Subject: [PATCH 118/183] Privatise variables in AbstractTreeNode --- .../sosy_lab/common/collect/union_find/AbstractTreeNode.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractTreeNode.java b/src/org/sosy_lab/common/collect/union_find/AbstractTreeNode.java index 97226e207..89830409c 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractTreeNode.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractTreeNode.java @@ -10,8 +10,8 @@ public abstract class AbstractTreeNode { - protected AbstractTreeNode parent; - protected final T value; + private AbstractTreeNode parent; + private final T value; protected AbstractTreeNode(T value) { this.parent = this; From 3fdb15dec84140de8f95681594f225009282424f Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 15:51:24 +0200 Subject: [PATCH 119/183] Add method for increasing rank in RootNode --- src/org/sosy_lab/common/collect/union_find/RootNode.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/org/sosy_lab/common/collect/union_find/RootNode.java b/src/org/sosy_lab/common/collect/union_find/RootNode.java index 94a16947c..dc678d092 100644 --- a/src/org/sosy_lab/common/collect/union_find/RootNode.java +++ b/src/org/sosy_lab/common/collect/union_find/RootNode.java @@ -29,6 +29,10 @@ public int getSize() { return size; } + public void incrementRankByOne() { + rank++; + } + public void incrementSizeByOne() { size++; } From 82c9e3c7a7e2ffce6f1974733825856f2a2dc798 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 16:06:54 +0200 Subject: [PATCH 120/183] Adapt ParentPointerTreeUnionFind to use new node types and not need the class ParentPointerTree anymore --- .../ParentPointerTreeUnionFind.java | 99 ++++++++++--------- 1 file changed, 50 insertions(+), 49 deletions(-) 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 b5055791e..725664fcd 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -9,13 +9,10 @@ package org.sosy_lab.common.collect.union_find; 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.HashSet; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; /** @@ -28,11 +25,11 @@ */ public class ParentPointerTreeUnionFind implements UnionFind { - protected final Map> forest; + protected final Map> allNodes; /** Creates an empty instance. */ public ParentPointerTreeUnionFind() { - forest = new HashMap<>(); + allNodes = new HashMap<>(); } /** @@ -47,10 +44,17 @@ public T find(T value) { Preconditions.checkNotNull(value); - for (Entry> entry : forest.entrySet()) { - if (entry.getValue().contains(value)) { - return entry.getKey(); + AbstractTreeNode node = allNodes.get(value); + + if (node != null) { + AbstractTreeNode parent = node.getParent(); + + while (!node.equals(parent)) { + node = parent; + parent = node.getParent(); } + + return parent.getValue(); } throw new IllegalArgumentException("Element not contained."); @@ -81,7 +85,7 @@ public void union(T value1, T value2) { T canon2 = find(value2); if (!canon1.equals(canon2)) { - mergeExistingSets(find(value1), find(value2)); + mergeExistingSets(canon1, canon2); } } else { addElementToExistingSet(value2, find(value1)); @@ -90,7 +94,7 @@ public void union(T value1, T value2) { addElementToExistingSet(value1, find(value2)); } else { addElementAsNewSet(value1); - addElementToExistingSet(value2, value1); + addElementToExistingSet(value2, find(value1)); } } } @@ -103,13 +107,22 @@ public void union(T value1, T value2) { @Override public Collection> getAllSubsets() { - List> allSubsets = new ArrayList<>(); + Map> allSubsets = new HashMap<>(); + + for (AbstractTreeNode node : allNodes.values()) { + + T canon = find(node.getValue()); - for (ParentPointerTree tree : forest.values()) { - allSubsets.add(tree.getSetOfNodeValues()); + if (allSubsets.containsKey(canon)) { + allSubsets.get(canon).add(node.getValue()); + } else { + HashSet set = new HashSet<>(); + set.add(node.getValue()); + allSubsets.put(canon, set); + } } - return allSubsets; + return allSubsets.values(); } /** @@ -122,59 +135,47 @@ public Collection> getAllSubsets() { @Override public boolean contains(T e) { - for (ParentPointerTree tree : forest.values()) { - if (tree.contains(e)) { - return true; - } - } - - return false; + return allNodes.containsKey(e); } private void addElementAsNewSet(T value) { if (!contains(value)) { - ParentPointerTree tree = new ParentPointerTree<>(value); - forest.put(value, tree); + RootNode root = new RootNode<>(value); + allNodes.put(value, root); } } + // union by size! // canon1 will be new canonical element only if its set is actually bigger, otherwise canon2 new // canon + // only call with elements that are definitely canonical! private void mergeExistingSets(T canon1, T canon2) { - @Var ParentPointerTree tree1 = null; - @Var ParentPointerTree tree2 = null; + Preconditions.checkNotNull(canon1); + Preconditions.checkNotNull(canon2); - for (Entry> entry : forest.entrySet()) { - if (entry.getKey().equals(canon1)) { - tree1 = entry.getValue(); + RootNode rootNode1 = (RootNode) allNodes.get(canon1); + RootNode rootNode2 = (RootNode) allNodes.get(canon2); - if (tree2 != null) { - break; - } - } else if (entry.getKey().equals(canon2)) { - tree2 = entry.getValue(); + int size1 = rootNode1.getSize(); + int size2 = rootNode2.getSize(); - if (tree1 != null) { - break; - } - } - } - - Preconditions.checkNotNull(tree1); - Preconditions.checkNotNull(tree2); - - if (tree1.getSize() > tree2.getSize()) { - assert tree1.appendTree(tree2); - assert forest.remove(canon2, tree2); + if (size1 > size2) { + rootNode2.setParent(rootNode1); + rootNode1.incrementSizeBy(rootNode2.getSize()); } else { - assert tree2.appendTree(tree1); - assert forest.remove(canon1, tree1); + rootNode1.setParent(rootNode2); + rootNode2.incrementSizeBy(rootNode1.getSize()); } } private void addElementToExistingSet(T value, T canon) { - forest.get(canon).addAsNewNode(value); + + RootNode root = (RootNode) allNodes.get(canon); + NonRootNode newNode = new NonRootNode<>(root, value); + root.incrementSizeByOne(); + + allNodes.put(value, newNode); } } From 23793c0a73e6572aa8ac1eb2d0b48e309e1be69c Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 16:12:18 +0200 Subject: [PATCH 121/183] Adapt SortedParentPointerTreeUnionFind to use new node types and not need the class ParentPointerTree anymore --- .../SortedParentPointerTreeUnionFind.java | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java index 5e800866a..e12852452 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java @@ -8,15 +8,15 @@ package org.sosy_lab.common.collect.union_find; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; import java.util.NavigableMap; +import java.util.NavigableSet; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; -public class SortedParentPointerTreeUnionFind extends ParentPointerTreeUnionFind { +public class SortedParentPointerTreeUnionFind> + extends ParentPointerTreeUnionFind { /** * Provides a {@link Collection} containing all current subsets. It contains the subsets sorted by @@ -29,13 +29,21 @@ public class SortedParentPointerTreeUnionFind extends ParentPointerTreeUnionF @Override public Collection> getAllSubsets() { - NavigableMap> forestSortedByKeys = new TreeMap<>(forest); - List> allSubsets = new ArrayList<>(); + NavigableMap> allSubsets = new TreeMap<>(); - for (ParentPointerTree tree : forestSortedByKeys.values()) { - allSubsets.add(new TreeSet<>(tree.getSetOfNodeValues())); + for (AbstractTreeNode node : allNodes.values()) { + + T canon = find(node.getValue()); + + if (allSubsets.containsKey(canon)) { + allSubsets.get(canon).add(node.getValue()); + } else { + NavigableSet set = new TreeSet<>(); + set.add(node.getValue()); + allSubsets.put(canon, set); + } } - return allSubsets; + return allSubsets.values(); } } From be9878232d06c27835f5ead7ddfee2f7f115e4db Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 16:23:19 +0200 Subject: [PATCH 122/183] Fix type parameter in ParentPointerTreeUnionFindSortednessTest --- .../tests/ParentPointerTreeUnionFindSortednessTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java index 8447bb844..beb839623 100644 --- a/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java +++ b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java @@ -109,7 +109,8 @@ public void testGetAllSubsets_stringElements_areSortedAlphabetically() { assertThat(onlySubsetOf(stringSortedUnionFind)).containsExactlyElementsIn(expected).inOrder(); } - private static Set onlySubsetOf(SortedParentPointerTreeUnionFind sortedUnionFind) { + private static > Set onlySubsetOf( + SortedParentPointerTreeUnionFind sortedUnionFind) { Collection> subsets = sortedUnionFind.getAllSubsets(); From 4aebe0616801c03332584e072e886b5f1f9ae766 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 16:32:18 +0200 Subject: [PATCH 123/183] Delete ParentPointerTree.java --- .../collect/union_find/ParentPointerTree.java | 86 ------------------- 1 file changed, 86 deletions(-) delete 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 deleted file mode 100644 index f79afafa3..000000000 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTree.java +++ /dev/null @@ -1,86 +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.union_find; - -import com.google.common.base.Preconditions; -import java.util.ArrayList; -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; - - public ParentPointerTree(T rootValue) { - root = TreeNode.getNewRootNode(rootValue); - listOfNodes = new ArrayList<>(); - listOfNodes.add(this.root); - mapOfNodes = new HashMap<>(); - mapOfNodes.put(rootValue, root); - nextParentIndex = 0; - timeToMoveOn = false; - } - - public TreeNode getRoot() { - return root; - } - - public int getSize() { - return listOfNodes.size(); - } - - public boolean contains(T value) { - - 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(); - } - - public boolean appendTree(ParentPointerTree tree) { - - TreeNode rootToBeAdded = tree.getRoot(); - - TreeNode parent = listOfNodes.get(nextParentIndex); - Preconditions.checkNotNull(parent); - rootToBeAdded.setParent(parent); - - listOfNodes.addAll(tree.listOfNodes); - mapOfNodes.putAll(tree.mapOfNodes); - updateNextParent(); - - return true; - } - - public Set getSetOfNodeValues() { - - return mapOfNodes.keySet(); - } - - // will currently create a binary tree as it increases counter every 2nd insert - private void updateNextParent() { - - if (!timeToMoveOn) { - timeToMoveOn = true; - } else { - nextParentIndex++; - timeToMoveOn = false; - } - } -} From c9da6083d9caf14c967b62fe1e1799db33db8087 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 16:32:41 +0200 Subject: [PATCH 124/183] Update existing documentation --- .../union_find/ParentPointerTreeUnionFind.java | 9 +++++---- .../union_find/SortedParentPointerTreeUnionFind.java | 11 +++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) 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 725664fcd..bc8983371 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -16,10 +16,11 @@ import java.util.Set; /** - * An implementation of {@link UnionFind} using a {@link Map} of {@link ParentPointerTree}s. In - * order to represent subsets (the trees) 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. + * An implementation of {@link UnionFind} using a {@link Map} of {@link AbstractTreeNode}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. Each subset is stored as a parent pointer tree comprised of {@link NonRootNode}s with + * exactly one {@link RootNode} as the root. The union is implemented as union by size. * * @param type of elements added to the Union-Find. */ diff --git a/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java index e12852452..22fed4136 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java @@ -9,12 +9,23 @@ package org.sosy_lab.common.collect.union_find; import java.util.Collection; +import java.util.Map; import java.util.NavigableMap; import java.util.NavigableSet; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; +/** + * A sorted implementation of {@link UnionFind} using a {@link Map} of {@link AbstractTreeNode}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. Each subset is stored as a parent pointer tree comprised of {@link + * NonRootNode}s with exactly one {@link RootNode} as the root. The union is implemented as union by + * size. + * + * @param type of elements added to the Union-Find. Must be comparable. + */ public class SortedParentPointerTreeUnionFind> extends ParentPointerTreeUnionFind { From 352a2b6ca314781113b71f7c35824b532721b295 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 16:39:27 +0200 Subject: [PATCH 125/183] Code format fixes --- .../collect/union_find/ParentPointerTreeUnionFind.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 bc8983371..80ff02941 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -9,6 +9,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.HashSet; @@ -45,10 +46,10 @@ public T find(T value) { Preconditions.checkNotNull(value); - AbstractTreeNode node = allNodes.get(value); + @Var AbstractTreeNode node = allNodes.get(value); if (node != null) { - AbstractTreeNode parent = node.getParent(); + @Var AbstractTreeNode parent = node.getParent(); while (!node.equals(parent)) { node = parent; From f3f3764ce4f4f2e9a3a759b19e7af271b66f0fca Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 17:01:55 +0200 Subject: [PATCH 126/183] Add documentation to new classes --- .../collect/union_find/AbstractTreeNode.java | 18 +++++++++++++++ .../collect/union_find/NonRootNode.java | 12 ++++++++++ .../common/collect/union_find/RootNode.java | 23 +++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractTreeNode.java b/src/org/sosy_lab/common/collect/union_find/AbstractTreeNode.java index 89830409c..244c63e02 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractTreeNode.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractTreeNode.java @@ -8,16 +8,34 @@ package org.sosy_lab.common.collect.union_find; +/** + * An abstract class of nodes from which a simple parent pointer tree can be built. + * + * @param type of elements each node holds as value + */ public abstract class AbstractTreeNode { private AbstractTreeNode parent; private final T value; + /** + * Constructor for a root node. The parent variable points to itself, thus indicating this is a + * root node. If appended to another tree, parent can be reallocated to the new parent node, while + * the current node simply functions as a non-root node from then on. + * + * @param value element to be stored in the node + */ protected AbstractTreeNode(T value) { this.parent = this; this.value = value; } + /** + * Constructor for a non-root node. + * + * @param parent parent node (can be root or non-root) + * @param value element to be stored in the node + */ protected AbstractTreeNode(AbstractTreeNode parent, T value) { this.parent = parent; this.value = value; diff --git a/src/org/sosy_lab/common/collect/union_find/NonRootNode.java b/src/org/sosy_lab/common/collect/union_find/NonRootNode.java index e5fef5861..3c1dee908 100644 --- a/src/org/sosy_lab/common/collect/union_find/NonRootNode.java +++ b/src/org/sosy_lab/common/collect/union_find/NonRootNode.java @@ -8,8 +8,20 @@ package org.sosy_lab.common.collect.union_find; +/** + * An implementation of {@link AbstractTreeNode} resulting in nodes that can only be used as + * non-root nodes but not as root nodes. + * + * @param type of elements each node holds as value + */ public final class NonRootNode extends AbstractTreeNode { + /** + * Constructor for a non-root node. + * + * @param parent parent node (can be root or non-root) + * @param value element to be stored in the node + */ public NonRootNode(AbstractTreeNode parent, T value) { super(parent, value); } diff --git a/src/org/sosy_lab/common/collect/union_find/RootNode.java b/src/org/sosy_lab/common/collect/union_find/RootNode.java index dc678d092..638fe753d 100644 --- a/src/org/sosy_lab/common/collect/union_find/RootNode.java +++ b/src/org/sosy_lab/common/collect/union_find/RootNode.java @@ -8,11 +8,27 @@ package org.sosy_lab.common.collect.union_find; +/** + * An implementation of {@link AbstractTreeNode} resulting in nodes that can be used as non-root + * nodes or root nodes. Their primary intended use is as root nodes. Rank describes the maximum + * height of the tree of this node and its child nodes (i.e. without path compression). Size + * describes the total number of elements in the tree represented by this root node. + * + * @param type of elements each node holds as value + */ public final class RootNode extends AbstractTreeNode { private int rank; private int size; + /** + * Constructor for a root node. The parent variable points to itself, thus indicating this is a + * root node. If appended to another tree, parent can be reallocated to the new parent node, while + * the current node simply functions as a non-root node from then on. In the beginning, rank is 0 + * and size is 1. + * + * @param value element to be stored in the node + */ public RootNode(T value) { super(value); @@ -29,14 +45,21 @@ public int getSize() { return size; } + /** Increments rank by one. */ public void incrementRankByOne() { rank++; } + /** Increments size by one. */ public void incrementSizeByOne() { size++; } + /** + * Increments size by n. + * + * @param n number by which size is to be increased. + */ public void incrementSizeBy(int n) { size += n; } From 842e5c0b6f910a543cb24bdc89ca30d4bb0c8fb2 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 17:06:39 +0200 Subject: [PATCH 127/183] Code format fix --- .../common/collect/union_find/ParentPointerTreeUnionFind.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 80ff02941..5a385befe 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -118,7 +118,7 @@ public Collection> getAllSubsets() { if (allSubsets.containsKey(canon)) { allSubsets.get(canon).add(node.getValue()); } else { - HashSet set = new HashSet<>(); + Set set = new HashSet<>(); set.add(node.getValue()); allSubsets.put(canon, set); } From cba998f6f5146f95a7f8ce25c0c9c8f57effe6fb Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 17:33:05 +0200 Subject: [PATCH 128/183] Add union by rank to ParentPointerTreeUnionFind and option to select which union is to be performed via a constructor parameter --- .../ParentPointerTreeUnionFind.java | 55 ++++++++++++++++--- .../SortedParentPointerTreeUnionFind.java | 5 ++ ...entPointerTreeUnionFindSortednessTest.java | 5 +- .../tests/ParentPointerTreeUnionFindTest.java | 6 +- 4 files changed, 58 insertions(+), 13 deletions(-) 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 5a385befe..9e83467c9 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -27,11 +27,18 @@ */ public class ParentPointerTreeUnionFind implements UnionFind { + public enum UnionType { + UNION_BY_RANK, + UNION_BY_SIZE + } + protected final Map> allNodes; + private final UnionType unionType; /** Creates an empty instance. */ - public ParentPointerTreeUnionFind() { + public ParentPointerTreeUnionFind(UnionType unionType) { allNodes = new HashMap<>(); + this.unionType = unionType; } /** @@ -148,15 +155,32 @@ private void addElementAsNewSet(T value) { } } - // union by size! - // canon1 will be new canonical element only if its set is actually bigger, otherwise canon2 new - // canon // only call with elements that are definitely canonical! private void mergeExistingSets(T canon1, T canon2) { Preconditions.checkNotNull(canon1); Preconditions.checkNotNull(canon2); + if (unionType == UnionType.UNION_BY_SIZE) { + unionBySize(canon1, canon2); + } else { + unionByRank(canon1, canon2); + } + } + + private void addElementToExistingSet(T value, T canon) { + + RootNode root = (RootNode) allNodes.get(canon); + NonRootNode newNode = new NonRootNode<>(root, value); + root.incrementSizeByOne(); + + allNodes.put(value, newNode); + } + + // canon1 will be new canonical element only if its set is actually bigger, otherwise canon2 new + // canon + private void unionBySize(T canon1, T canon2) { + RootNode rootNode1 = (RootNode) allNodes.get(canon1); RootNode rootNode2 = (RootNode) allNodes.get(canon2); @@ -172,12 +196,25 @@ private void mergeExistingSets(T canon1, T canon2) { } } - private void addElementToExistingSet(T value, T canon) { + // canon1 will be new canonical element only if its rank is actually greater, otherwise canon2 new + // canon + private void unionByRank(T canon1, T canon2) { - RootNode root = (RootNode) allNodes.get(canon); - NonRootNode newNode = new NonRootNode<>(root, value); - root.incrementSizeByOne(); + RootNode rootNode1 = (RootNode) allNodes.get(canon1); + RootNode rootNode2 = (RootNode) allNodes.get(canon2); - allNodes.put(value, newNode); + int rank1 = rootNode1.getRank(); + int rank2 = rootNode2.getRank(); + + if (rank1 > rank2) { + rootNode2.setParent(rootNode1); + } else { + rootNode1.setParent(rootNode2); + + // as rank only changes if both ranks are the same + if (rank1 == rank2) { + rootNode2.incrementRankByOne(); + } + } } } diff --git a/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java index 22fed4136..09c7350ea 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java @@ -29,6 +29,11 @@ public class SortedParentPointerTreeUnionFind> extends ParentPointerTreeUnionFind { + /** Creates an empty instance. */ + public SortedParentPointerTreeUnionFind(UnionType unionType) { + super(unionType); + } + /** * Provides a {@link Collection} containing all current subsets. It contains the subsets sorted by * their canonical elements in ascending order. The contents of the subsets are also sorted in diff --git a/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java index beb839623..7fd8cedec 100644 --- a/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java +++ b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindSortednessTest.java @@ -14,6 +14,7 @@ import java.util.Set; import org.junit.Before; import org.junit.Test; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; import org.sosy_lab.common.collect.union_find.SortedParentPointerTreeUnionFind; public class ParentPointerTreeUnionFindSortednessTest { @@ -22,7 +23,7 @@ public class ParentPointerTreeUnionFindSortednessTest { @Before public void setup() { - sortedUnionFind = new SortedParentPointerTreeUnionFind<>(); + sortedUnionFind = new SortedParentPointerTreeUnionFind<>(UnionType.UNION_BY_SIZE); } @Test @@ -87,7 +88,7 @@ public void testGetAllSubsets_afterMergingTwoSubsets_resultIsSorted() { public void testGetAllSubsets_stringElements_areSortedAlphabetically() { SortedParentPointerTreeUnionFind stringSortedUnionFind = - new SortedParentPointerTreeUnionFind<>(); + new SortedParentPointerTreeUnionFind<>(UnionType.UNION_BY_SIZE); String[] expected = {"-1", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; for (int i = 0; i <= 2; i++) { diff --git a/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindTest.java b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindTest.java index c09ff49dc..93a158b9b 100644 --- a/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindTest.java @@ -15,13 +15,14 @@ import org.junit.Before; import org.junit.Test; import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; public class ParentPointerTreeUnionFindTest { private ParentPointerTreeUnionFind integerUnionFind; @Before public void setup() { - integerUnionFind = new ParentPointerTreeUnionFind<>(); + integerUnionFind = new ParentPointerTreeUnionFind<>(UnionType.UNION_BY_SIZE); } @Test @@ -136,7 +137,8 @@ public void testGetAllSubsets_reflectsCorrectMembershipAfterMultipleUnions() { @Test public void testUnion_StringElements() { - ParentPointerTreeUnionFind stringUnionFind = new ParentPointerTreeUnionFind<>(); + ParentPointerTreeUnionFind stringUnionFind = + new ParentPointerTreeUnionFind<>(UnionType.UNION_BY_SIZE); stringUnionFind.union(Integer.toString(0), Integer.toString(1)); stringUnionFind.union(Integer.toString(0), Integer.toString(2)); From 84f91e9a0e871c974cda3d9624ba2afccbbdcf2c Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 17:38:00 +0200 Subject: [PATCH 129/183] Update documentation to include union by rank --- .../collect/union_find/ParentPointerTreeUnionFind.java | 9 +++++++-- .../union_find/SortedParentPointerTreeUnionFind.java | 10 +++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) 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 9e83467c9..a49a4c867 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -21,7 +21,8 @@ * 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. Each subset is stored as a parent pointer tree comprised of {@link NonRootNode}s with - * exactly one {@link RootNode} as the root. The union is implemented as union by size. + * exactly one {@link RootNode} as the root. The union can be performed either by size or by rank, + * determined by a constructor parameter. * * @param type of elements added to the Union-Find. */ @@ -35,7 +36,11 @@ public enum UnionType { protected final Map> allNodes; private final UnionType unionType; - /** Creates an empty instance. */ + /** + * Creates an empty instance. + * + * @param unionType type of union to be performed for all unions on this instance + */ public ParentPointerTreeUnionFind(UnionType unionType) { allNodes = new HashMap<>(); this.unionType = unionType; diff --git a/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java index 09c7350ea..f0fb23dce 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java @@ -21,15 +21,19 @@ * 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. Each subset is stored as a parent pointer tree comprised of {@link - * NonRootNode}s with exactly one {@link RootNode} as the root. The union is implemented as union by - * size. + * NonRootNode}s with exactly one {@link RootNode} as the root. The union can be performed either by + * size or by rank, determined by a constructor parameter. * * @param type of elements added to the Union-Find. Must be comparable. */ public class SortedParentPointerTreeUnionFind> extends ParentPointerTreeUnionFind { - /** Creates an empty instance. */ + /** + * Creates an empty instance. + * + * @param unionType type of union to be performed for all unions on this instance + */ public SortedParentPointerTreeUnionFind(UnionType unionType) { super(unionType); } From 06c16485890e3e92e8305f05668b3ff941f83ea3 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 14 Aug 2026 17:52:31 +0200 Subject: [PATCH 130/183] Update tests to cover both union by size and union by rank --- .../ParentPointerTreeUnionFindByRankTest.java | 153 ++++++++++++++++++ ...ParentPointerTreeUnionFindBySizeTest.java} | 2 +- 2 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindByRankTest.java rename src/org/sosy_lab/common/collect/union_find/tests/{ParentPointerTreeUnionFindTest.java => ParentPointerTreeUnionFindBySizeTest.java} (98%) diff --git a/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindByRankTest.java b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindByRankTest.java new file mode 100644 index 000000000..840f86211 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindByRankTest.java @@ -0,0 +1,153 @@ +// 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.tests; + +import static com.google.common.truth.Truth.assertThat; + +import java.util.Collection; +import java.util.Set; +import org.junit.Before; +import org.junit.Test; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; + +public class ParentPointerTreeUnionFindByRankTest { + private ParentPointerTreeUnionFind integerUnionFind; + + @Before + public void setup() { + integerUnionFind = new ParentPointerTreeUnionFind<>(UnionType.UNION_BY_RANK); + } + + @Test + public void testFind_afterSelfUnion_returnsItself() { + + integerUnionFind.union(0, 0); + + assertThat(integerUnionFind.find(0)).isEqualTo(0); + } + + @Test + public void testUnion_twoNewElements_producesSingleSubsetOfSizeTwo() { + + integerUnionFind.union(0, 1); + + assertThat(integerUnionFind.find(0)).isEqualTo(integerUnionFind.find(1)); + assertThat(integerUnionFind.getAllSubsets()).hasSize(1); + } + + @Test + public void testUnion_disjointPairs_produceDistinctSubsets() { + + integerUnionFind.union(0, 1); + integerUnionFind.union(2, 3); + + assertThat(integerUnionFind.find(0)).isNotEqualTo(integerUnionFind.find(2)); + assertThat(integerUnionFind.getAllSubsets()).hasSize(2); + } + + @Test + public void testUnion_severalElementsToSameSubset() { + + integerUnionFind.union(0, 1); + integerUnionFind.union(1, 2); + integerUnionFind.union(2, 3); + + Integer canon = integerUnionFind.find(0); + assertThat(integerUnionFind.find(0)).isEqualTo(canon); + assertThat(integerUnionFind.find(1)).isEqualTo(canon); + assertThat(integerUnionFind.find(2)).isEqualTo(canon); + assertThat(integerUnionFind.find(3)).isEqualTo(canon); + assertThat(integerUnionFind.getAllSubsets()).hasSize(1); + } + + @Test + public void testUnion_duplicateUnionCall_doesNotLeadToDuplicates() { + + integerUnionFind.union(0, 1); + integerUnionFind.union(0, 1); + integerUnionFind.union(1, 0); + + assertThat(integerUnionFind.find(0)).isEqualTo(integerUnionFind.find(1)); + assertThat(integerUnionFind.getAllSubsets()).hasSize(1); + } + + @Test + public void testUnion_mergesTwoExistingMultiElementSubsets() { + + integerUnionFind.union(0, 1); + integerUnionFind.union(0, 2); + integerUnionFind.union(3, 4); + integerUnionFind.union(3, 5); + + assertThat(integerUnionFind.getAllSubsets()).hasSize(2); + + integerUnionFind.union(0, 3); + + Integer canon = integerUnionFind.find(0); + for (int i = 0; i <= 5; i++) { + assertThat(integerUnionFind.find(i)).isEqualTo(canon); + } + } + + @Test + public void testUnion_constantCanonicalElementDuringNonLinearInsertion() { + + integerUnionFind.union(3, 3); + integerUnionFind.union(3, 2); + integerUnionFind.union(3, 5); + integerUnionFind.union(3, 1); + integerUnionFind.union(3, 8); + integerUnionFind.union(3, 6); + integerUnionFind.union(3, 9); + integerUnionFind.union(3, 7); + integerUnionFind.union(3, 4); + integerUnionFind.union(3, 0); + + Integer canon = integerUnionFind.find(3); + for (int i = 0; i <= 9; i++) { + assertThat(integerUnionFind.find(i)).isEqualTo(canon); + } + } + + @Test + public void testGetAllSubsets_reflectsCorrectMembershipAfterMultipleUnions() { + + for (int i = 0; i <= 4; i++) { + integerUnionFind.union(0, i); + } + for (int i = 5; i <= 9; i++) { + integerUnionFind.union(5, i); + } + + Collection> subsets = integerUnionFind.getAllSubsets(); + + assertThat(subsets).hasSize(2); + for (Set subset : subsets) { + assertThat(subset).hasSize(5); + } + } + + @Test + public void testUnion_StringElements() { + + ParentPointerTreeUnionFind stringUnionFind = + new ParentPointerTreeUnionFind<>(UnionType.UNION_BY_RANK); + + stringUnionFind.union(Integer.toString(0), Integer.toString(1)); + stringUnionFind.union(Integer.toString(0), Integer.toString(2)); + stringUnionFind.union(Integer.toString(3), Integer.toString(4)); + + assertThat(stringUnionFind.find(Integer.toString(0))) + .isEqualTo(stringUnionFind.find(Integer.toString(2))); + assertThat(stringUnionFind.find(Integer.toString(0))) + .isNotEqualTo(stringUnionFind.find(Integer.toString(3))); + assertThat(stringUnionFind.getAllSubsets()).hasSize(2); + } +} diff --git a/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindTest.java b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindBySizeTest.java similarity index 98% rename from src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindTest.java rename to src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindBySizeTest.java index 93a158b9b..09eb3c0bc 100644 --- a/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/union_find/tests/ParentPointerTreeUnionFindBySizeTest.java @@ -17,7 +17,7 @@ import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind; import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; -public class ParentPointerTreeUnionFindTest { +public class ParentPointerTreeUnionFindBySizeTest { private ParentPointerTreeUnionFind integerUnionFind; @Before From 27e702e34bb9e249fde98f66c7e3757cfcc8902d Mon Sep 17 00:00:00 2001 From: Colleen Date: Sat, 15 Aug 2026 09:49:47 +0200 Subject: [PATCH 131/183] Add path compression to ParentPointerTreeUnionFind --- .../union_find/ParentPointerTreeUnionFind.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) 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 a49a4c867..47a523a45 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -10,9 +10,11 @@ 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.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -47,7 +49,8 @@ public ParentPointerTreeUnionFind(UnionType unionType) { } /** - * Returns the canonical element of the set containing the provided element. + * Returns the canonical element of the set containing the provided element. Applies path + * compression where possible. * * @param value element for which set is to be found * @return canonical element of the found set @@ -58,16 +61,23 @@ public T find(T value) { Preconditions.checkNotNull(value); + List> toBeCompressed = new ArrayList<>(); @Var AbstractTreeNode node = allNodes.get(value); if (node != null) { @Var AbstractTreeNode parent = node.getParent(); while (!node.equals(parent)) { + toBeCompressed.add(node); node = parent; parent = node.getParent(); } + for (AbstractTreeNode current : toBeCompressed) { + + current.setParent(parent); + } + return parent.getValue(); } From a58eaee2f91e430b93b6db9aa705aa77fdf3a70a Mon Sep 17 00:00:00 2001 From: Colleen Date: Sat, 15 Aug 2026 16:44:14 +0200 Subject: [PATCH 132/183] Add implementation of ImmutableParentPointerTreeUnionFind --- .../ImmutableParentPointerTreeUnionFind.java | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java new file mode 100644 index 000000000..fcbd4d113 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java @@ -0,0 +1,101 @@ +// 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 com.google.common.base.Preconditions; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.Var; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; + +public class ImmutableParentPointerTreeUnionFind extends AbstractImmutableUnionFind { + + private final Map> allNodes; + + private ImmutableParentPointerTreeUnionFind(Map> allNodes) { + this.allNodes = allNodes; + } + + @Override + public T find(T e) { + + Preconditions.checkNotNull(e); + + @Var AbstractTreeNode node = allNodes.get(e); + + if (node != null) { + @Var AbstractTreeNode parent = node.getParent(); + + while (!node.equals(parent)) { + node = parent; + parent = node.getParent(); + } + + return parent.getValue(); + } + + throw new IllegalArgumentException("Element not contained."); + } + + @Override + public Collection> getAllSubsets() { + + Map> allSubsets = new HashMap<>(); + + for (AbstractTreeNode node : allNodes.values()) { + + T canon = find(node.getValue()); + + if (allSubsets.containsKey(canon)) { + allSubsets.get(canon).add(node.getValue()); + } else { + Set set = new HashSet<>(); + set.add(node.getValue()); + allSubsets.put(canon, set); + } + } + + return allSubsets.values(); + } + + @Override + public boolean contains(T e) { + + return allNodes.containsKey(e); + } + + public static final class Builder { + + ParentPointerTreeUnionFind unionFind; + + private Builder(UnionType unionType) { + unionFind = new ParentPointerTreeUnionFind<>(unionType); + } + + public static Builder getBuilder(UnionType unionType) { + return new Builder<>(unionType); + } + + @CanIgnoreReturnValue + public Builder union(T value1, T value2) { + + unionFind.union(value1, value2); + + return this; + } + + public ImmutableParentPointerTreeUnionFind build() { + return new ImmutableParentPointerTreeUnionFind<>(unionFind.allNodes); + } + } +} From 1f6f4ccbaef22da80cbafece408232e8ae50bf17 Mon Sep 17 00:00:00 2001 From: Colleen Date: Sat, 15 Aug 2026 17:17:00 +0200 Subject: [PATCH 133/183] Increase compatibility between sorted and unsorted union-find versions --- .../collect/union_find/SortedUnionFind.java | 27 ++----------------- 1 file changed, 2 insertions(+), 25 deletions(-) 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 a8de46a58..66f2f9da0 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedUnionFind.java @@ -19,36 +19,13 @@ * @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); +public interface SortedUnionFind> extends UnionFind { /** * Provides a {@link Collection} containing all current subsets. * * @return {@link Collection} containing all current subsets */ + @Override 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 4206ed76d2ac5cce7f886e96e551c9c6f7dd4fb8 Mon Sep 17 00:00:00 2001 From: Colleen Date: Sat, 15 Aug 2026 17:17:39 +0200 Subject: [PATCH 134/183] Add SortedUnionFind interface to SortedParentPointerTreeUnionFind --- .../union_find/SortedParentPointerTreeUnionFind.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java index f0fb23dce..d24d8437b 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java @@ -12,7 +12,6 @@ import java.util.Map; import java.util.NavigableMap; import java.util.NavigableSet; -import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; @@ -27,7 +26,7 @@ * @param type of elements added to the Union-Find. Must be comparable. */ public class SortedParentPointerTreeUnionFind> - extends ParentPointerTreeUnionFind { + extends ParentPointerTreeUnionFind implements SortedUnionFind { /** * Creates an empty instance. @@ -47,9 +46,9 @@ public SortedParentPointerTreeUnionFind(UnionType unionType) { */ // subsets are in order of their canonical elements; elements in subsets are sorted as well @Override - public Collection> getAllSubsets() { + public Collection> getAllSubsets() { - NavigableMap> allSubsets = new TreeMap<>(); + NavigableMap> allSubsets = new TreeMap<>(); for (AbstractTreeNode node : allNodes.values()) { From 0d464c4048e7f3ca5b070994af31faa5a808b739 Mon Sep 17 00:00:00 2001 From: Colleen Date: Sat, 15 Aug 2026 17:36:55 +0200 Subject: [PATCH 135/183] Change abstract immutable classes to interfaces due to compatibility issues --- .../union_find/ImmutableParentPointerTreeUnionFind.java | 2 +- ...bleSortedUnionFind.java => ImmutableSortedUnionFind.java} | 5 ++--- ...stractImmutableUnionFind.java => ImmutableUnionFind.java} | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) rename src/org/sosy_lab/common/collect/union_find/{AbstractImmutableSortedUnionFind.java => ImmutableSortedUnionFind.java} (77%) rename src/org/sosy_lab/common/collect/union_find/{AbstractImmutableUnionFind.java => ImmutableUnionFind.java} (81%) diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java index fcbd4d113..1b85a1fa2 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java @@ -18,7 +18,7 @@ import java.util.Set; import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; -public class ImmutableParentPointerTreeUnionFind extends AbstractImmutableUnionFind { +public class ImmutableParentPointerTreeUnionFind implements ImmutableUnionFind { private final Map> allNodes; diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedUnionFind.java similarity index 77% rename from src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java rename to src/org/sosy_lab/common/collect/union_find/ImmutableSortedUnionFind.java index 93f50bc82..6c1470c57 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedUnionFind.java @@ -10,8 +10,7 @@ import com.google.errorprone.annotations.DoNotCall; -public abstract class AbstractImmutableSortedUnionFind> - implements SortedUnionFind { +public interface ImmutableSortedUnionFind> extends SortedUnionFind { /** * @throws UnsupportedOperationException Always. * @deprecated Unsupported operation. @@ -19,7 +18,7 @@ public abstract class AbstractImmutableSortedUnionFind> @Deprecated @Override @DoNotCall - public final void union(T e1, T e2) { + default void union(T e1, T e2) { throw new UnsupportedOperationException(); } } diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableUnionFind.java similarity index 81% rename from src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java rename to src/org/sosy_lab/common/collect/union_find/ImmutableUnionFind.java index 5291681c4..2d15b898a 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableUnionFind.java @@ -10,7 +10,7 @@ import com.google.errorprone.annotations.DoNotCall; -public abstract class AbstractImmutableUnionFind implements UnionFind { +public interface ImmutableUnionFind extends UnionFind { /** * @throws UnsupportedOperationException Always. * @deprecated Unsupported operation. @@ -18,7 +18,7 @@ public abstract class AbstractImmutableUnionFind implements UnionFind { @Deprecated @Override @DoNotCall - public final void union(T e1, T e2) { + default void union(T e1, T e2) { throw new UnsupportedOperationException(); } } From aa3e1f4f0b5b4bd835947d3f65328ce05d219fa4 Mon Sep 17 00:00:00 2001 From: Colleen Date: Sat, 15 Aug 2026 17:40:06 +0200 Subject: [PATCH 136/183] Change abstract immutable classes to interfaces due to compatibility issues --- .../common/collect/union_find/PersistentUnionFind.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java index 769c73c20..67f27264f 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java @@ -11,8 +11,6 @@ import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.DoNotCall; import com.google.errorprone.annotations.Immutable; -import java.util.Map; -import java.util.NavigableSet; /** * Interface for a persistent union-find. A persistent data structure is immutable, but provides @@ -35,7 +33,7 @@ public interface PersistentUnionFind extends UnionFind { * @return new instance that the desired changes have been applied to */ @CheckReturnValue - Map> unionAndCopy(T e1, T e2); + PersistentUnionFind unionAndCopy(T e1, T e2); /** * @throws UnsupportedOperationException Always. From 57a0fe7f0ac57bb0408f47ea8ad3c9bfd8bb194e Mon Sep 17 00:00:00 2001 From: Colleen Date: Sat, 15 Aug 2026 18:01:16 +0200 Subject: [PATCH 137/183] Change abstract immutable classes to interfaces due to compatibility issues --- .../union_find/ImmutableSortedUnionFind.java | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedUnionFind.java index 6c1470c57..6303ba8a0 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedUnionFind.java @@ -8,17 +8,6 @@ package org.sosy_lab.common.collect.union_find; -import com.google.errorprone.annotations.DoNotCall; -public interface ImmutableSortedUnionFind> extends SortedUnionFind { - /** - * @throws UnsupportedOperationException Always. - * @deprecated Unsupported operation. - */ - @Deprecated - @Override - @DoNotCall - default void union(T e1, T e2) { - throw new UnsupportedOperationException(); - } -} +public interface ImmutableSortedUnionFind> + extends ImmutableUnionFind, SortedUnionFind {} From 0c9ba0445ac8944c7b7db02a22d1a74f00b83829 Mon Sep 17 00:00:00 2001 From: Colleen Date: Sat, 15 Aug 2026 18:01:54 +0200 Subject: [PATCH 138/183] Add ImmutableSortedParentPointerTreeUnionFind --- .../ImmutableParentPointerTreeUnionFind.java | 4 +- ...tableSortedParentPointerTreeUnionFind.java | 45 +++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java index 1b85a1fa2..c82034e87 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java @@ -20,9 +20,9 @@ public class ImmutableParentPointerTreeUnionFind implements ImmutableUnionFind { - private final Map> allNodes; + protected final Map> allNodes; - private ImmutableParentPointerTreeUnionFind(Map> allNodes) { + protected ImmutableParentPointerTreeUnionFind(Map> allNodes) { this.allNodes = allNodes; } diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java new file mode 100644 index 000000000..2b5b8f46f --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.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.union_find; + +import java.util.Collection; +import java.util.Map; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.TreeMap; +import java.util.TreeSet; + +public class ImmutableSortedParentPointerTreeUnionFind> + extends ImmutableParentPointerTreeUnionFind implements ImmutableSortedUnionFind { + + protected ImmutableSortedParentPointerTreeUnionFind(Map> allNodes) { + super(allNodes); + } + + @Override + public Collection> getAllSubsets() { + + NavigableMap> allSubsets = new TreeMap<>(); + + for (AbstractTreeNode node : allNodes.values()) { + + T canon = find(node.getValue()); + + if (allSubsets.containsKey(canon)) { + allSubsets.get(canon).add(node.getValue()); + } else { + NavigableSet set = new TreeSet<>(); + set.add(node.getValue()); + allSubsets.put(canon, set); + } + } + + return allSubsets.values(); + } +} From fee7bf6becffd13ff83c0859e8115b6cbdca7680 Mon Sep 17 00:00:00 2001 From: Colleen Date: Sat, 15 Aug 2026 18:08:49 +0200 Subject: [PATCH 139/183] Code format fix --- .../common/collect/union_find/ImmutableSortedUnionFind.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedUnionFind.java index 6303ba8a0..618cdf741 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedUnionFind.java @@ -8,6 +8,5 @@ package org.sosy_lab.common.collect.union_find; - public interface ImmutableSortedUnionFind> extends ImmutableUnionFind, SortedUnionFind {} From f97d033b3c3bf714ee3f347b207204039b4a12ed Mon Sep 17 00:00:00 2001 From: Colleen Date: Sat, 15 Aug 2026 18:42:34 +0200 Subject: [PATCH 140/183] Revert immutable interfaces back to abstract classes due to other compatibility issues --- ... => AbstractImmutableSortedUnionFind.java} | 4 +- ...d.java => AbstractImmutableUnionFind.java} | 4 +- .../ImmutableParentPointerTreeUnionFind.java | 4 +- ...tableSortedParentPointerTreeUnionFind.java | 62 ++++++++++++++++++- 4 files changed, 66 insertions(+), 8 deletions(-) rename src/org/sosy_lab/common/collect/union_find/{ImmutableSortedUnionFind.java => AbstractImmutableSortedUnionFind.java} (64%) rename src/org/sosy_lab/common/collect/union_find/{ImmutableUnionFind.java => AbstractImmutableUnionFind.java} (81%) diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java similarity index 64% rename from src/org/sosy_lab/common/collect/union_find/ImmutableSortedUnionFind.java rename to src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java index 618cdf741..ed7cc3950 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java @@ -8,5 +8,5 @@ package org.sosy_lab.common.collect.union_find; -public interface ImmutableSortedUnionFind> - extends ImmutableUnionFind, SortedUnionFind {} +public abstract class AbstractImmutableSortedUnionFind> + extends AbstractImmutableUnionFind implements SortedUnionFind {} diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java similarity index 81% rename from src/org/sosy_lab/common/collect/union_find/ImmutableUnionFind.java rename to src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java index 2d15b898a..5291681c4 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java @@ -10,7 +10,7 @@ import com.google.errorprone.annotations.DoNotCall; -public interface ImmutableUnionFind extends UnionFind { +public abstract class AbstractImmutableUnionFind implements UnionFind { /** * @throws UnsupportedOperationException Always. * @deprecated Unsupported operation. @@ -18,7 +18,7 @@ public interface ImmutableUnionFind extends UnionFind { @Deprecated @Override @DoNotCall - default void union(T e1, T e2) { + public final void union(T e1, T e2) { throw new UnsupportedOperationException(); } } diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java index c82034e87..9f36f4e47 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java @@ -18,9 +18,9 @@ import java.util.Set; import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; -public class ImmutableParentPointerTreeUnionFind implements ImmutableUnionFind { +public class ImmutableParentPointerTreeUnionFind extends AbstractImmutableUnionFind { - protected final Map> allNodes; + private final Map> allNodes; protected ImmutableParentPointerTreeUnionFind(Map> allNodes) { this.allNodes = allNodes; diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java index 2b5b8f46f..66aaf6d4b 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java @@ -8,18 +8,45 @@ package org.sosy_lab.common.collect.union_find; +import com.google.common.base.Preconditions; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.Var; import java.util.Collection; import java.util.Map; import java.util.NavigableMap; import java.util.NavigableSet; import java.util.TreeMap; import java.util.TreeSet; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; public class ImmutableSortedParentPointerTreeUnionFind> - extends ImmutableParentPointerTreeUnionFind implements ImmutableSortedUnionFind { + extends AbstractImmutableSortedUnionFind { + + private final Map> allNodes; protected ImmutableSortedParentPointerTreeUnionFind(Map> allNodes) { - super(allNodes); + this.allNodes = allNodes; + } + + @Override + public T find(T e) { + + Preconditions.checkNotNull(e); + + @Var AbstractTreeNode node = allNodes.get(e); + + if (node != null) { + @Var AbstractTreeNode parent = node.getParent(); + + while (!node.equals(parent)) { + node = parent; + parent = node.getParent(); + } + + return parent.getValue(); + } + + throw new IllegalArgumentException("Element not contained."); } @Override @@ -42,4 +69,35 @@ public Collection> getAllSubsets() { return allSubsets.values(); } + + @Override + public boolean contains(T e) { + + return allNodes.containsKey(e); + } + + public static final class Builder> { + + SortedParentPointerTreeUnionFind unionFind; + + private Builder(UnionType unionType) { + unionFind = new SortedParentPointerTreeUnionFind(unionType); + } + + public static > Builder getBuilder(UnionType unionType) { + return new Builder<>(unionType); + } + + @CanIgnoreReturnValue + public ImmutableSortedParentPointerTreeUnionFind.Builder union(T value1, T value2) { + + unionFind.union(value1, value2); + + return this; + } + + public ImmutableParentPointerTreeUnionFind build() { + return new ImmutableParentPointerTreeUnionFind<>(unionFind.allNodes); + } + } } From 0c0d85051fc478f20179156d4519dd13e4042e9a Mon Sep 17 00:00:00 2001 From: Colleen Date: Sat, 15 Aug 2026 20:47:20 +0200 Subject: [PATCH 141/183] Make maps in ImmutableParentPointerTreeUnionFind and ImmutableSortedParentPointerTreeUnionFind immutable --- .../union_find/ImmutableParentPointerTreeUnionFind.java | 7 ++++--- .../ImmutableSortedParentPointerTreeUnionFind.java | 9 +++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java index 9f36f4e47..09b06d872 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java @@ -9,6 +9,7 @@ package org.sosy_lab.common.collect.union_find; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.Var; import java.util.Collection; @@ -20,9 +21,9 @@ public class ImmutableParentPointerTreeUnionFind extends AbstractImmutableUnionFind { - private final Map> allNodes; + private final ImmutableMap> allNodes; - protected ImmutableParentPointerTreeUnionFind(Map> allNodes) { + protected ImmutableParentPointerTreeUnionFind(ImmutableMap> allNodes) { this.allNodes = allNodes; } @@ -95,7 +96,7 @@ public Builder union(T value1, T value2) { } public ImmutableParentPointerTreeUnionFind build() { - return new ImmutableParentPointerTreeUnionFind<>(unionFind.allNodes); + return new ImmutableParentPointerTreeUnionFind<>(ImmutableMap.copyOf(unionFind.allNodes)); } } } diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java index 66aaf6d4b..5a099ff5f 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java @@ -9,10 +9,10 @@ package org.sosy_lab.common.collect.union_find; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.Var; import java.util.Collection; -import java.util.Map; import java.util.NavigableMap; import java.util.NavigableSet; import java.util.TreeMap; @@ -22,9 +22,10 @@ public class ImmutableSortedParentPointerTreeUnionFind> extends AbstractImmutableSortedUnionFind { - private final Map> allNodes; + private final ImmutableMap> allNodes; - protected ImmutableSortedParentPointerTreeUnionFind(Map> allNodes) { + protected ImmutableSortedParentPointerTreeUnionFind( + ImmutableMap> allNodes) { this.allNodes = allNodes; } @@ -97,7 +98,7 @@ public ImmutableSortedParentPointerTreeUnionFind.Builder union(T value1, T va } public ImmutableParentPointerTreeUnionFind build() { - return new ImmutableParentPointerTreeUnionFind<>(unionFind.allNodes); + return new ImmutableParentPointerTreeUnionFind<>(ImmutableMap.copyOf(unionFind.allNodes)); } } } From 90d34791bf1aa8d851cf76c5538931be9b0cb4eb Mon Sep 17 00:00:00 2001 From: Colleen Date: Sat, 15 Aug 2026 20:56:39 +0200 Subject: [PATCH 142/183] Code format fix --- .../union_find/ImmutableSortedParentPointerTreeUnionFind.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java index 5a099ff5f..07f53616a 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java @@ -82,7 +82,7 @@ public static final class Builder> { SortedParentPointerTreeUnionFind unionFind; private Builder(UnionType unionType) { - unionFind = new SortedParentPointerTreeUnionFind(unionType); + unionFind = new SortedParentPointerTreeUnionFind<>(unionType); } public static > Builder getBuilder(UnionType unionType) { From 830062892debee63921e00e418eacfe174e78475 Mon Sep 17 00:00:00 2001 From: Colleen Date: Sun, 16 Aug 2026 10:38:49 +0200 Subject: [PATCH 143/183] Logic fix in ParentPointerTreeUnionFind --- .../common/collect/union_find/ParentPointerTreeUnionFind.java | 4 ++++ 1 file changed, 4 insertions(+) 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 47a523a45..9c1fac531 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -189,6 +189,10 @@ private void addElementToExistingSet(T value, T canon) { NonRootNode newNode = new NonRootNode<>(root, value); root.incrementSizeByOne(); + if(root.getRank() == 0) { + root.incrementRankByOne(); + } + allNodes.put(value, newNode); } From afec034a685e78b4854d2ac0775d63b18bfb8a7e Mon Sep 17 00:00:00 2001 From: Colleen Date: Sun, 16 Aug 2026 10:56:53 +0200 Subject: [PATCH 144/183] Logic fixes in ParentPointerTreeUnionFind and SortedParentPointerTreeUnionFind --- .../common/collect/union_find/ParentPointerTreeUnionFind.java | 1 + .../collect/union_find/SortedParentPointerTreeUnionFind.java | 1 + 2 files changed, 2 insertions(+) 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 9c1fac531..77176fca4 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -141,6 +141,7 @@ public Collection> getAllSubsets() { allSubsets.get(canon).add(node.getValue()); } else { Set set = new HashSet<>(); + set.add(canon); set.add(node.getValue()); allSubsets.put(canon, set); } diff --git a/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java index d24d8437b..d5264fec3 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java @@ -58,6 +58,7 @@ public Collection> getAllSubsets() { allSubsets.get(canon).add(node.getValue()); } else { NavigableSet set = new TreeSet<>(); + set.add(canon); set.add(node.getValue()); allSubsets.put(canon, set); } From 307b5b07e1202ea4cf97eb4f816f46168fa3c07f Mon Sep 17 00:00:00 2001 From: Colleen Date: Sun, 16 Aug 2026 10:59:26 +0200 Subject: [PATCH 145/183] Add PersistentSortedParentPointerTreeUnionFind --- .../ParentPointerTreeUnionFind.java | 2 +- ...stentSortedParentPointerTreeUnionFind.java | 249 ++++++++++++++++++ .../union_find/PersistentSortedUnionFind.java | 4 +- 3 files changed, 251 insertions(+), 4 deletions(-) create mode 100644 src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.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 index 77176fca4..fe2a9867d 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -190,7 +190,7 @@ private void addElementToExistingSet(T value, T canon) { NonRootNode newNode = new NonRootNode<>(root, value); root.incrementSizeByOne(); - if(root.getRank() == 0) { + if (root.getRank() == 0) { root.incrementRankByOne(); } diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java new file mode 100644 index 000000000..b192306bf --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java @@ -0,0 +1,249 @@ +// 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 com.google.common.base.Preconditions; +import com.google.errorprone.annotations.Var; +import java.util.Collection; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.TreeMap; +import java.util.TreeSet; +import org.sosy_lab.common.collect.PathCopyingPersistentTreeMap; +import org.sosy_lab.common.collect.PersistentSortedMap; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; + +public class PersistentSortedParentPointerTreeUnionFind> + extends AbstractImmutableSortedUnionFind implements PersistentSortedUnionFind { + + private final PersistentSortedMap mapOfNodesToParents; + private final PersistentSortedMap mapOfRootsToRanks; + private final PersistentSortedMap mapOfRootsToSizes; + private final UnionType unionType; + + private PersistentSortedParentPointerTreeUnionFind(UnionType pUnionType) { + mapOfNodesToParents = PathCopyingPersistentTreeMap.of(); + mapOfRootsToRanks = PathCopyingPersistentTreeMap.of(); + mapOfRootsToSizes = PathCopyingPersistentTreeMap.of(); + unionType = pUnionType; + } + + private PersistentSortedParentPointerTreeUnionFind( + PersistentSortedMap mapOfNodesToParents, + PersistentSortedMap mapOfRootsToRanks, + PersistentSortedMap mapOfRootsToSizes, + UnionType unionType) { + this.mapOfNodesToParents = mapOfNodesToParents; + this.mapOfRootsToRanks = mapOfRootsToRanks; + this.mapOfRootsToSizes = mapOfRootsToSizes; + this.unionType = unionType; + } + + public static > AbstractImmutableSortedUnionFind of( + UnionType unionType) { + return new PersistentSortedParentPointerTreeUnionFind<>(unionType); + } + + @Override + public Collection> getAllSubsets() { + + NavigableMap> allSubsets = new TreeMap<>(); + + for (T current : mapOfNodesToParents.keySet()) { + + T root = mapOfNodesToParents.get(current); + + if (allSubsets.containsKey(root)) { + allSubsets.get(root).add(current); + } else { + NavigableSet set = new TreeSet<>(); + set.add(root); + set.add(current); + allSubsets.put(root, set); + } + } + + return allSubsets.values(); + } + + @Override + public boolean contains(T e) { + + Preconditions.checkNotNull(e); + + return mapOfNodesToParents.containsKey(e); + } + + @Override + public T find(T e) { + + Preconditions.checkNotNull(e); + + T currentNode = e; + T parent = mapOfNodesToParents.get(e); + + if (parent != null) { + while (!currentNode.equals(parent)) { + currentNode = parent; + parent = mapOfNodesToParents.get(currentNode); + } + + return parent; + } + + throw new IllegalArgumentException("Element not contained."); + } + + @Override + public PersistentSortedUnionFind unionAndCopy(T e1, T e2) { + + Preconditions.checkNotNull(e1); + Preconditions.checkNotNull(e2); + + if (e1.equals(e2)) { + return addElementAsNewSetAndCopy(e1); + } else { + if (contains(e1)) { + if (contains(e2)) { + T canon1 = find(e1); + T canon2 = find(e2); + + if (!canon1.equals(canon2)) { + return mergeExistingSetsAndCopy(canon1, canon2); + } + } else { + return addElementToExistingSetAndCopy(e2, find(e1)); + } + } else if (contains(e2)) { + return addElementToExistingSetAndCopy(e1, find(e2)); + } else { + return addTwoElementsAsSetAndCopy(e1, e2); + } + } + + return this; + } + + private PersistentSortedUnionFind addElementAsNewSetAndCopy(T e) { + + if (!contains(e)) { + PersistentSortedMap updatedNodesToParents = mapOfNodesToParents.putAndCopy(e, e); + PersistentSortedMap updatedRootsToRanks = mapOfRootsToRanks.putAndCopy(e, 0); + PersistentSortedMap updatedRootsToSizes = mapOfRootsToSizes.putAndCopy(e, 1); + + return new PersistentSortedParentPointerTreeUnionFind<>( + updatedNodesToParents, updatedRootsToRanks, updatedRootsToSizes, unionType); + } + + return this; + } + + // only call with elements that are definitely canonical! + private PersistentSortedUnionFind mergeExistingSetsAndCopy(T canon1, T canon2) { + + Preconditions.checkNotNull(canon1); + Preconditions.checkNotNull(canon2); + + if (unionType == UnionType.UNION_BY_SIZE) { + return unionBySize(canon1, canon2); + } else { + return unionByRank(canon1, canon2); + } + } + + private PersistentSortedUnionFind addElementToExistingSetAndCopy(T e, T canon) { + + Preconditions.checkNotNull(canon); + + PersistentSortedMap updatedNodesToParents = mapOfNodesToParents.putAndCopy(e, canon); + + int rank = mapOfRootsToRanks.get(canon); + @Var PersistentSortedMap updatedRootsToRanks = mapOfRootsToRanks; + if (rank == 0) { + updatedRootsToRanks = mapOfRootsToRanks.removeAndCopy(canon); + updatedRootsToRanks = updatedRootsToRanks.putAndCopy(canon, ++rank); + } + + int size = mapOfRootsToSizes.get(canon); + @Var + PersistentSortedMap updatedRootsToSizes = mapOfRootsToSizes.removeAndCopy(canon); + updatedRootsToSizes = updatedRootsToSizes.putAndCopy(canon, ++size); + + return new PersistentSortedParentPointerTreeUnionFind<>( + updatedNodesToParents, updatedRootsToRanks, updatedRootsToSizes, unionType); + } + + private PersistentSortedUnionFind addTwoElementsAsSetAndCopy(T e1, T e2) { + + @Var PersistentSortedMap updatedNodesToParents; + updatedNodesToParents = mapOfNodesToParents.putAndCopy(e1, e1); + updatedNodesToParents = updatedNodesToParents.putAndCopy(e2, e1); + + PersistentSortedMap updatedRootsToRanks = mapOfRootsToRanks.putAndCopy(e1, 1); + PersistentSortedMap updatedRootsToSizes = mapOfRootsToSizes.putAndCopy(e1, 2); + + return new PersistentSortedParentPointerTreeUnionFind<>( + updatedNodesToParents, updatedRootsToRanks, updatedRootsToSizes, unionType); + } + + // canon1 will be new canonical element only if its set is actually bigger, otherwise canon2 new + // canon + private PersistentSortedUnionFind unionBySize(T canon1, T canon2) { + + int size1 = mapOfRootsToSizes.get(canon1); + int size2 = mapOfRootsToSizes.get(canon2); + + @Var PersistentSortedMap updatedNodesToParents; + @Var PersistentSortedMap updatedRootsToSizes; + + if (size1 > size2) { + updatedNodesToParents = mapOfNodesToParents.removeAndCopy(canon2); + updatedNodesToParents = updatedNodesToParents.putAndCopy(canon2, canon1); + + updatedRootsToSizes = mapOfRootsToSizes.removeAndCopy(canon1); + updatedRootsToSizes = updatedRootsToSizes.putAndCopy(canon1, size1 + size2); + } else { + updatedNodesToParents = mapOfNodesToParents.removeAndCopy(canon1); + updatedNodesToParents = updatedNodesToParents.putAndCopy(canon1, canon2); + + updatedRootsToSizes = mapOfRootsToSizes.removeAndCopy(canon2); + updatedRootsToSizes = updatedRootsToSizes.putAndCopy(canon2, size2 + size1); + } + + return new PersistentSortedParentPointerTreeUnionFind<>( + updatedNodesToParents, mapOfRootsToRanks, updatedRootsToSizes, unionType); + } + + // canon1 will be new canonical element only if its rank is actually greater, otherwise canon2 new + // canon + private PersistentSortedUnionFind unionByRank(T canon1, T canon2) { + + int rank1 = mapOfRootsToRanks.get(canon1); + int rank2 = mapOfRootsToRanks.get(canon2); + + @Var PersistentSortedMap updatedNodesToParents; + @Var PersistentSortedMap updatedRootsToRanks = mapOfRootsToRanks; + + if (rank1 > rank2) { + updatedNodesToParents = mapOfNodesToParents.removeAndCopy(canon2); + updatedNodesToParents = updatedNodesToParents.putAndCopy(canon2, canon1); + } else { + updatedNodesToParents = mapOfNodesToParents.removeAndCopy(canon1); + updatedNodesToParents = updatedNodesToParents.putAndCopy(canon1, canon2); + + if (rank1 == rank2) { + updatedRootsToRanks = mapOfRootsToRanks.removeAndCopy(canon2); + updatedRootsToRanks = updatedRootsToRanks.putAndCopy(canon2, ++rank2); + } + } + + return new PersistentSortedParentPointerTreeUnionFind<>( + updatedNodesToParents, updatedRootsToRanks, mapOfRootsToSizes, unionType); + } +} diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java index 1c9484ce1..f243fa115 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java @@ -11,8 +11,6 @@ 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 @@ -35,7 +33,7 @@ public interface PersistentSortedUnionFind> extends Sort * @return new instance that the desired changes have been applied to */ @CheckReturnValue - Map> unionAndCopy(T e1, T e2); + PersistentSortedUnionFind unionAndCopy(T e1, T e2); /** * @throws UnsupportedOperationException Always. From f9aa19ba6e1e28ddc566ffe4c54162af186c8dd5 Mon Sep 17 00:00:00 2001 From: Colleen Date: Sun, 16 Aug 2026 12:19:31 +0200 Subject: [PATCH 146/183] Code format fixes --- ...rsistentSortedParentPointerTreeUnionFind.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java index b192306bf..bcdac0d76 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java @@ -11,6 +11,7 @@ import com.google.common.base.Preconditions; import com.google.errorprone.annotations.Var; import java.util.Collection; +import java.util.Map.Entry; import java.util.NavigableMap; import java.util.NavigableSet; import java.util.TreeMap; @@ -19,7 +20,7 @@ import org.sosy_lab.common.collect.PersistentSortedMap; import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; -public class PersistentSortedParentPointerTreeUnionFind> +public final class PersistentSortedParentPointerTreeUnionFind> extends AbstractImmutableSortedUnionFind implements PersistentSortedUnionFind { private final PersistentSortedMap mapOfNodesToParents; @@ -55,8 +56,9 @@ public Collection> getAllSubsets() { NavigableMap> allSubsets = new TreeMap<>(); - for (T current : mapOfNodesToParents.keySet()) { + for (Entry currentEntry : mapOfNodesToParents.entrySet()) { + T current = currentEntry.getKey(); T root = mapOfNodesToParents.get(current); if (allSubsets.containsKey(root)) { @@ -85,8 +87,8 @@ public T find(T e) { Preconditions.checkNotNull(e); - T currentNode = e; - T parent = mapOfNodesToParents.get(e); + @Var T currentNode = e; + @Var T parent = mapOfNodesToParents.get(e); if (parent != null) { while (!currentNode.equals(parent)) { @@ -163,14 +165,14 @@ private PersistentSortedUnionFind addElementToExistingSetAndCopy(T e, T canon PersistentSortedMap updatedNodesToParents = mapOfNodesToParents.putAndCopy(e, canon); - int rank = mapOfRootsToRanks.get(canon); + @Var int rank = mapOfRootsToRanks.get(canon); @Var PersistentSortedMap updatedRootsToRanks = mapOfRootsToRanks; if (rank == 0) { updatedRootsToRanks = mapOfRootsToRanks.removeAndCopy(canon); updatedRootsToRanks = updatedRootsToRanks.putAndCopy(canon, ++rank); } - int size = mapOfRootsToSizes.get(canon); + @Var int size = mapOfRootsToSizes.get(canon); @Var PersistentSortedMap updatedRootsToSizes = mapOfRootsToSizes.removeAndCopy(canon); updatedRootsToSizes = updatedRootsToSizes.putAndCopy(canon, ++size); @@ -225,7 +227,7 @@ private PersistentSortedUnionFind unionBySize(T canon1, T canon2) { private PersistentSortedUnionFind unionByRank(T canon1, T canon2) { int rank1 = mapOfRootsToRanks.get(canon1); - int rank2 = mapOfRootsToRanks.get(canon2); + @Var int rank2 = mapOfRootsToRanks.get(canon2); @Var PersistentSortedMap updatedNodesToParents; @Var PersistentSortedMap updatedRootsToRanks = mapOfRootsToRanks; From 3bd864812b183beb4e14b2779bb1dec9d0795773 Mon Sep 17 00:00:00 2001 From: Colleen Date: Sun, 16 Aug 2026 13:01:42 +0200 Subject: [PATCH 147/183] Code format fixes --- ...stentSortedParentPointerTreeUnionFind.java | 104 +++++++++--------- 1 file changed, 54 insertions(+), 50 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java index bcdac0d76..b7fa9ab6f 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java @@ -24,25 +24,21 @@ public final class PersistentSortedParentPointerTreeUnionFind implements PersistentSortedUnionFind { private final PersistentSortedMap mapOfNodesToParents; - private final PersistentSortedMap mapOfRootsToRanks; - private final PersistentSortedMap mapOfRootsToSizes; + private final PersistentSortedMap mapOfRootsToWeights; private final UnionType unionType; private PersistentSortedParentPointerTreeUnionFind(UnionType pUnionType) { mapOfNodesToParents = PathCopyingPersistentTreeMap.of(); - mapOfRootsToRanks = PathCopyingPersistentTreeMap.of(); - mapOfRootsToSizes = PathCopyingPersistentTreeMap.of(); + mapOfRootsToWeights = PathCopyingPersistentTreeMap.of(); unionType = pUnionType; } private PersistentSortedParentPointerTreeUnionFind( PersistentSortedMap mapOfNodesToParents, - PersistentSortedMap mapOfRootsToRanks, - PersistentSortedMap mapOfRootsToSizes, + PersistentSortedMap mapOfRootsToWeights, UnionType unionType) { this.mapOfNodesToParents = mapOfNodesToParents; - this.mapOfRootsToRanks = mapOfRootsToRanks; - this.mapOfRootsToSizes = mapOfRootsToSizes; + this.mapOfRootsToWeights = mapOfRootsToWeights; this.unionType = unionType; } @@ -59,7 +55,7 @@ public Collection> getAllSubsets() { for (Entry currentEntry : mapOfNodesToParents.entrySet()) { T current = currentEntry.getKey(); - T root = mapOfNodesToParents.get(current); + T root = find(current); if (allSubsets.containsKey(root)) { allSubsets.get(root).add(current); @@ -136,11 +132,16 @@ private PersistentSortedUnionFind addElementAsNewSetAndCopy(T e) { if (!contains(e)) { PersistentSortedMap updatedNodesToParents = mapOfNodesToParents.putAndCopy(e, e); - PersistentSortedMap updatedRootsToRanks = mapOfRootsToRanks.putAndCopy(e, 0); - PersistentSortedMap updatedRootsToSizes = mapOfRootsToSizes.putAndCopy(e, 1); + + PersistentSortedMap updatedRootsToWeights; + if (unionType == UnionType.UNION_BY_RANK) { + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(e, 0); // rank + } else { + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(e, 1); // size + } return new PersistentSortedParentPointerTreeUnionFind<>( - updatedNodesToParents, updatedRootsToRanks, updatedRootsToSizes, unionType); + updatedNodesToParents, updatedRootsToWeights, unionType); } return this; @@ -165,20 +166,22 @@ private PersistentSortedUnionFind addElementToExistingSetAndCopy(T e, T canon PersistentSortedMap updatedNodesToParents = mapOfNodesToParents.putAndCopy(e, canon); - @Var int rank = mapOfRootsToRanks.get(canon); - @Var PersistentSortedMap updatedRootsToRanks = mapOfRootsToRanks; - if (rank == 0) { - updatedRootsToRanks = mapOfRootsToRanks.removeAndCopy(canon); - updatedRootsToRanks = updatedRootsToRanks.putAndCopy(canon, ++rank); - } + PersistentSortedMap updatedRootsToWeights; + if (unionType == UnionType.UNION_BY_RANK) { + @Var int rank = mapOfRootsToWeights.get(canon); - @Var int size = mapOfRootsToSizes.get(canon); - @Var - PersistentSortedMap updatedRootsToSizes = mapOfRootsToSizes.removeAndCopy(canon); - updatedRootsToSizes = updatedRootsToSizes.putAndCopy(canon, ++size); + if (rank == 0) { + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(canon, ++rank); + } else { + updatedRootsToWeights = mapOfRootsToWeights; + } + } else { + @Var int size = mapOfRootsToWeights.get(canon); + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(canon, ++size); + } return new PersistentSortedParentPointerTreeUnionFind<>( - updatedNodesToParents, updatedRootsToRanks, updatedRootsToSizes, unionType); + updatedNodesToParents, updatedRootsToWeights, unionType); } private PersistentSortedUnionFind addTwoElementsAsSetAndCopy(T e1, T e2) { @@ -187,65 +190,66 @@ private PersistentSortedUnionFind addTwoElementsAsSetAndCopy(T e1, T e2) { updatedNodesToParents = mapOfNodesToParents.putAndCopy(e1, e1); updatedNodesToParents = updatedNodesToParents.putAndCopy(e2, e1); - PersistentSortedMap updatedRootsToRanks = mapOfRootsToRanks.putAndCopy(e1, 1); - PersistentSortedMap updatedRootsToSizes = mapOfRootsToSizes.putAndCopy(e1, 2); + PersistentSortedMap updatedRootsToWeights; + if (unionType == UnionType.UNION_BY_RANK) { + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(e1, 1); // rank + } else { + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(e1, 2); // size + } return new PersistentSortedParentPointerTreeUnionFind<>( - updatedNodesToParents, updatedRootsToRanks, updatedRootsToSizes, unionType); + updatedNodesToParents, updatedRootsToWeights, unionType); } // canon1 will be new canonical element only if its set is actually bigger, otherwise canon2 new // canon private PersistentSortedUnionFind unionBySize(T canon1, T canon2) { - int size1 = mapOfRootsToSizes.get(canon1); - int size2 = mapOfRootsToSizes.get(canon2); + int size1 = mapOfRootsToWeights.get(canon1); + int size2 = mapOfRootsToWeights.get(canon2); - @Var PersistentSortedMap updatedNodesToParents; - @Var PersistentSortedMap updatedRootsToSizes; + PersistentSortedMap updatedNodesToParents; + PersistentSortedMap updatedRootsToWeights; if (size1 > size2) { - updatedNodesToParents = mapOfNodesToParents.removeAndCopy(canon2); - updatedNodesToParents = updatedNodesToParents.putAndCopy(canon2, canon1); + updatedNodesToParents = mapOfNodesToParents.putAndCopy(canon2, canon1); - updatedRootsToSizes = mapOfRootsToSizes.removeAndCopy(canon1); - updatedRootsToSizes = updatedRootsToSizes.putAndCopy(canon1, size1 + size2); + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(canon1, size1 + size2); } else { - updatedNodesToParents = mapOfNodesToParents.removeAndCopy(canon1); - updatedNodesToParents = updatedNodesToParents.putAndCopy(canon1, canon2); + updatedNodesToParents = mapOfNodesToParents.putAndCopy(canon1, canon2); - updatedRootsToSizes = mapOfRootsToSizes.removeAndCopy(canon2); - updatedRootsToSizes = updatedRootsToSizes.putAndCopy(canon2, size2 + size1); + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(canon2, size2 + size1); } return new PersistentSortedParentPointerTreeUnionFind<>( - updatedNodesToParents, mapOfRootsToRanks, updatedRootsToSizes, unionType); + updatedNodesToParents, updatedRootsToWeights, unionType); } // canon1 will be new canonical element only if its rank is actually greater, otherwise canon2 new // canon private PersistentSortedUnionFind unionByRank(T canon1, T canon2) { - int rank1 = mapOfRootsToRanks.get(canon1); - @Var int rank2 = mapOfRootsToRanks.get(canon2); + int rank1 = mapOfRootsToWeights.get(canon1); + @Var int rank2 = mapOfRootsToWeights.get(canon2); - @Var PersistentSortedMap updatedNodesToParents; - @Var PersistentSortedMap updatedRootsToRanks = mapOfRootsToRanks; + PersistentSortedMap updatedNodesToParents; + PersistentSortedMap updatedRootsToWeights; if (rank1 > rank2) { - updatedNodesToParents = mapOfNodesToParents.removeAndCopy(canon2); - updatedNodesToParents = updatedNodesToParents.putAndCopy(canon2, canon1); + updatedNodesToParents = mapOfNodesToParents.putAndCopy(canon2, canon1); + + updatedRootsToWeights = mapOfRootsToWeights; } else { - updatedNodesToParents = mapOfNodesToParents.removeAndCopy(canon1); - updatedNodesToParents = updatedNodesToParents.putAndCopy(canon1, canon2); + updatedNodesToParents = mapOfNodesToParents.putAndCopy(canon1, canon2); if (rank1 == rank2) { - updatedRootsToRanks = mapOfRootsToRanks.removeAndCopy(canon2); - updatedRootsToRanks = updatedRootsToRanks.putAndCopy(canon2, ++rank2); + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(canon2, ++rank2); + } else { + updatedRootsToWeights = mapOfRootsToWeights; } } return new PersistentSortedParentPointerTreeUnionFind<>( - updatedNodesToParents, updatedRootsToRanks, mapOfRootsToSizes, unionType); + updatedNodesToParents, updatedRootsToWeights, unionType); } } From c403afda04a4ddc62fd8bf756c0de6531880c5c2 Mon Sep 17 00:00:00 2001 From: Colleen Date: Sun, 16 Aug 2026 15:41:35 +0200 Subject: [PATCH 148/183] Code format fixes --- .../PersistentSortedParentPointerTreeUnionFind.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java index b7fa9ab6f..ab4b86d65 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java @@ -11,7 +11,6 @@ import com.google.common.base.Preconditions; import com.google.errorprone.annotations.Var; import java.util.Collection; -import java.util.Map.Entry; import java.util.NavigableMap; import java.util.NavigableSet; import java.util.TreeMap; @@ -52,9 +51,8 @@ public Collection> getAllSubsets() { NavigableMap> allSubsets = new TreeMap<>(); - for (Entry currentEntry : mapOfNodesToParents.entrySet()) { + for (T current : mapOfNodesToParents.keySet()) { - T current = currentEntry.getKey(); T root = find(current); if (allSubsets.containsKey(root)) { From c835acaa4d3cd38ee3d321544ec3068c61573671 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 17 Aug 2026 10:27:21 +0200 Subject: [PATCH 149/183] Refactor method parameter names --- .../union_find/AbstractGenericUnionFind.java | 86 ++++++------ .../AbstractImmutableUnionFind.java | 2 +- .../collect/union_find/AbstractTreeNode.java | 22 +-- .../ImmutableParentPointerTreeUnionFind.java | 26 ++-- ...tableSortedParentPointerTreeUnionFind.java | 26 ++-- .../collect/union_find/NonRootNode.java | 8 +- .../ParentPointerTreeUnionFind.java | 101 +++++++------- ...stentSortedParentPointerTreeUnionFind.java | 131 +++++++++--------- .../union_find/PersistentSortedUnionFind.java | 8 +- .../union_find/PersistentUnionFind.java | 8 +- .../common/collect/union_find/RootNode.java | 14 +- .../SortedParentPointerTreeUnionFind.java | 6 +- .../common/collect/union_find/UnionFind.java | 14 +- 13 files changed, 227 insertions(+), 225 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 d048df16e..3408c1f5e 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractGenericUnionFind.java @@ -40,17 +40,17 @@ public AbstractGenericUnionFind() { /** * Returns the canonical element of the set containing the provided element. * - * @param e element for which set is to be found + * @param pE 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) { + public T find(T pE) { - Preconditions.checkNotNull(e); + Preconditions.checkNotNull(pE); for (Entry mapping : mapOfSets.entrySet()) { - if (mapping.getValue().contains(e)) { + if (mapping.getValue().contains(pE)) { return mapping.getKey(); } } @@ -61,72 +61,72 @@ public T find(T e) { /** * 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 + *

USES: Add new element as new set: pass it as both pE1 and pE2. 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. + * to. Merge two existing sets: pE1, pE2 canonical elements of sets to be merged. * - * @param e1 first element - * @param e2 second element + * @param pE1 first element + * @param pE2 second element */ @Override - public void union(T e1, T e2) { + public void union(T pE1, T pE2) { - Preconditions.checkNotNull(e1); - Preconditions.checkNotNull(e2); + Preconditions.checkNotNull(pE1); + Preconditions.checkNotNull(pE2); - if (e1.equals(e2)) { - addElementAsNewSet(e1); + if (pE1.equals(pE2)) { + addElementAsNewSet(pE1); } else { Set canonicalElements = mapOfSets.keySet(); - if (canonicalElements.contains(e1)) { - if (canonicalElements.contains(e2)) { - mergeExistingSets(e1, e2); + if (canonicalElements.contains(pE1)) { + if (canonicalElements.contains(pE2)) { + mergeExistingSets(pE1, pE2); } else { - addElementToExistingSet(e2, e1); + addElementToExistingSet(pE2, pE1); } - } else if (canonicalElements.contains(e2)) { - addElementToExistingSet(e1, e2); + } else if (canonicalElements.contains(pE2)) { + addElementToExistingSet(pE1, pE2); } else { - if (contains(e1)) { - if (contains(e2)) { - mergeExistingSets(find(e1), find(e2)); + if (contains(pE1)) { + if (contains(pE2)) { + mergeExistingSets(find(pE1), find(pE2)); } else { - addElementToExistingSet(e2, find(e1)); + addElementToExistingSet(pE2, find(pE1)); } } else { - addElementAsNewSet(e1); - addElementToExistingSet(e2, e1); + addElementAsNewSet(pE1); + addElementToExistingSet(pE2, pE1); } } } } @SuppressWarnings("unchecked") - private void addElementAsNewSet(T e) { + private void addElementAsNewSet(T pE) { - if (!contains(e)) { + if (!contains(pE)) { S newSet = (S) getEmptySet(); - newSet.add(e); - mapOfSets.put(e, newSet); + newSet.add(pE); + mapOfSets.put(pE, newSet); } } - private void addElementToExistingSet(T e, T canon) { + private void addElementToExistingSet(T pE, T pCanon) { - if (!contains(e)) { - mapOfSets.get(canon).add(e); + if (!contains(pE)) { + mapOfSets.get(pCanon).add(pE); } else { - mergeExistingSets(find(e), canon); + mergeExistingSets(find(pE), pCanon); } } - // e1 will be new canonical element only if its set is actually bigger, otherwise e2 new canon - private void mergeExistingSets(T e1, T e2) { + // pE1 will be new canonical element only if its set is actually bigger, otherwise pE2 new canon + private void mergeExistingSets(T pE1, T pE2) { - S set1 = mapOfSets.get(e1); - S set2 = mapOfSets.get(e2); + S set1 = mapOfSets.get(pE1); + S set2 = mapOfSets.get(pE2); assert set1 != null; assert set2 != null; @@ -136,10 +136,10 @@ private void mergeExistingSets(T e1, T e2) { if (size1 > size2) { set1.addAll(set2); - assert mapOfSets.remove(e2, set2); + assert mapOfSets.remove(pE2, set2); } else { set2.addAll(set1); - assert mapOfSets.remove(e1, set1); + assert mapOfSets.remove(pE1, set1); } } @@ -157,16 +157,16 @@ public 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 + * @param pE element to be searched for * @return true if contained, false if not */ @Override - public boolean contains(T e) { + public boolean contains(T pE) { - Preconditions.checkNotNull(e); + Preconditions.checkNotNull(pE); for (S current : mapOfSets.values()) { - if (current.contains(e)) { + if (current.contains(pE)) { return true; } } diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java index 5291681c4..eead23b01 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java @@ -18,7 +18,7 @@ public abstract class AbstractImmutableUnionFind implements UnionFind { @Deprecated @Override @DoNotCall - public final void union(T e1, T e2) { + public final void union(T pE1, T pE2) { throw new UnsupportedOperationException(); } } diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractTreeNode.java b/src/org/sosy_lab/common/collect/union_find/AbstractTreeNode.java index 244c63e02..b8351c0dc 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractTreeNode.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractTreeNode.java @@ -23,30 +23,30 @@ public abstract class AbstractTreeNode { * root node. If appended to another tree, parent can be reallocated to the new parent node, while * the current node simply functions as a non-root node from then on. * - * @param value element to be stored in the node + * @param pValue element to be stored in the node */ - protected AbstractTreeNode(T value) { - this.parent = this; - this.value = value; + protected AbstractTreeNode(T pValue) { + parent = this; + value = pValue; } /** * Constructor for a non-root node. * - * @param parent parent node (can be root or non-root) - * @param value element to be stored in the node + * @param pParent pParent node (can be root or non-root) + * @param pValue element to be stored in the node */ - protected AbstractTreeNode(AbstractTreeNode parent, T value) { - this.parent = parent; - this.value = value; + protected AbstractTreeNode(AbstractTreeNode pParent, T pValue) { + parent = pParent; + value = pValue; } public AbstractTreeNode getParent() { return parent; } - public void setParent(AbstractTreeNode parent) { - this.parent = parent; + public void setParent(AbstractTreeNode pParent) { + parent = pParent; } public T getValue() { diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java index 09b06d872..6c7badc61 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java @@ -23,16 +23,16 @@ public class ImmutableParentPointerTreeUnionFind extends AbstractImmutableUni private final ImmutableMap> allNodes; - protected ImmutableParentPointerTreeUnionFind(ImmutableMap> allNodes) { - this.allNodes = allNodes; + protected ImmutableParentPointerTreeUnionFind(ImmutableMap> pAllNodes) { + allNodes = pAllNodes; } @Override - public T find(T e) { + public T find(T pE) { - Preconditions.checkNotNull(e); + Preconditions.checkNotNull(pE); - @Var AbstractTreeNode node = allNodes.get(e); + @Var AbstractTreeNode node = allNodes.get(pE); if (node != null) { @Var AbstractTreeNode parent = node.getParent(); @@ -70,27 +70,27 @@ public Collection> getAllSubsets() { } @Override - public boolean contains(T e) { + public boolean contains(T pE) { - return allNodes.containsKey(e); + return allNodes.containsKey(pE); } public static final class Builder { ParentPointerTreeUnionFind unionFind; - private Builder(UnionType unionType) { - unionFind = new ParentPointerTreeUnionFind<>(unionType); + private Builder(UnionType pUnionType) { + unionFind = new ParentPointerTreeUnionFind<>(pUnionType); } - public static Builder getBuilder(UnionType unionType) { - return new Builder<>(unionType); + public static Builder getBuilder(UnionType pUnionType) { + return new Builder<>(pUnionType); } @CanIgnoreReturnValue - public Builder union(T value1, T value2) { + public Builder union(T pE1, T pE2) { - unionFind.union(value1, value2); + unionFind.union(pE1, pE2); return this; } diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java index 07f53616a..1001787dd 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java @@ -25,16 +25,16 @@ public class ImmutableSortedParentPointerTreeUnionFind> private final ImmutableMap> allNodes; protected ImmutableSortedParentPointerTreeUnionFind( - ImmutableMap> allNodes) { - this.allNodes = allNodes; + ImmutableMap> pAllNodes) { + allNodes = pAllNodes; } @Override - public T find(T e) { + public T find(T pE) { - Preconditions.checkNotNull(e); + Preconditions.checkNotNull(pE); - @Var AbstractTreeNode node = allNodes.get(e); + @Var AbstractTreeNode node = allNodes.get(pE); if (node != null) { @Var AbstractTreeNode parent = node.getParent(); @@ -72,27 +72,27 @@ public Collection> getAllSubsets() { } @Override - public boolean contains(T e) { + public boolean contains(T pE) { - return allNodes.containsKey(e); + return allNodes.containsKey(pE); } public static final class Builder> { SortedParentPointerTreeUnionFind unionFind; - private Builder(UnionType unionType) { - unionFind = new SortedParentPointerTreeUnionFind<>(unionType); + private Builder(UnionType pUnionType) { + unionFind = new SortedParentPointerTreeUnionFind<>(pUnionType); } - public static > Builder getBuilder(UnionType unionType) { - return new Builder<>(unionType); + public static > Builder getBuilder(UnionType pUnionType) { + return new Builder<>(pUnionType); } @CanIgnoreReturnValue - public ImmutableSortedParentPointerTreeUnionFind.Builder union(T value1, T value2) { + public ImmutableSortedParentPointerTreeUnionFind.Builder union(T pE1, T pE2) { - unionFind.union(value1, value2); + unionFind.union(pE1, pE2); return this; } diff --git a/src/org/sosy_lab/common/collect/union_find/NonRootNode.java b/src/org/sosy_lab/common/collect/union_find/NonRootNode.java index 3c1dee908..e318015b3 100644 --- a/src/org/sosy_lab/common/collect/union_find/NonRootNode.java +++ b/src/org/sosy_lab/common/collect/union_find/NonRootNode.java @@ -19,10 +19,10 @@ public final class NonRootNode extends AbstractTreeNode { /** * Constructor for a non-root node. * - * @param parent parent node (can be root or non-root) - * @param value element to be stored in the node + * @param pParent parent node (can be root or non-root) + * @param pValue element to be stored in the node */ - public NonRootNode(AbstractTreeNode parent, T value) { - super(parent, value); + public NonRootNode(AbstractTreeNode pParent, T pValue) { + super(pParent, pValue); } } 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 fe2a9867d..f61b5f602 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -41,28 +41,28 @@ public enum UnionType { /** * Creates an empty instance. * - * @param unionType type of union to be performed for all unions on this instance + * @param pUnionType type of union to be performed for all unions on this instance */ - public ParentPointerTreeUnionFind(UnionType unionType) { + public ParentPointerTreeUnionFind(UnionType pUnionType) { allNodes = new HashMap<>(); - this.unionType = unionType; + unionType = pUnionType; } /** * Returns the canonical element of the set containing the provided element. Applies path * compression where possible. * - * @param value element for which set is to be found + * @param pE 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 value) { + public T find(T pE) { - Preconditions.checkNotNull(value); + Preconditions.checkNotNull(pE); List> toBeCompressed = new ArrayList<>(); - @Var AbstractTreeNode node = allNodes.get(value); + @Var AbstractTreeNode node = allNodes.get(pE); if (node != null) { @Var AbstractTreeNode parent = node.getParent(); @@ -87,38 +87,38 @@ public T find(T value) { /** * 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 value1 and value2. 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: value1, value2 canonical elements of sets to be merged. + *

USES: Add new element as new set: pass it as both pE1 and pE2. 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: pE1, pE2 canonical elements of sets to be merged. * - * @param value1 first element - * @param value2 second element + * @param pE1 first element + * @param pE2 second element */ @Override - public void union(T value1, T value2) { + public void union(T pE1, T pE2) { - Preconditions.checkNotNull(value1); - Preconditions.checkNotNull(value2); + Preconditions.checkNotNull(pE1); + Preconditions.checkNotNull(pE2); - if (value1.equals(value2)) { - addElementAsNewSet(value1); + if (pE1.equals(pE2)) { + addElementAsNewSet(pE1); } else { - if (contains(value1)) { - if (contains(value2)) { - T canon1 = find(value1); - T canon2 = find(value2); + if (contains(pE1)) { + if (contains(pE2)) { + T canon1 = find(pE1); + T canon2 = find(pE2); if (!canon1.equals(canon2)) { mergeExistingSets(canon1, canon2); } } else { - addElementToExistingSet(value2, find(value1)); + addElementToExistingSet(pE2, find(pE1)); } - } else if (contains(value2)) { - addElementToExistingSet(value1, find(value2)); + } else if (contains(pE2)) { + addElementToExistingSet(pE1, find(pE2)); } else { - addElementAsNewSet(value1); - addElementToExistingSet(value2, find(value1)); + addElementAsNewSet(pE1); + addElementToExistingSet(pE2, find(pE1)); } } } @@ -154,55 +154,55 @@ public 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 + * @param pE element to be searched for * @return true if contained, false if not */ @Override - public boolean contains(T e) { + public boolean contains(T pE) { - return allNodes.containsKey(e); + return allNodes.containsKey(pE); } - private void addElementAsNewSet(T value) { + private void addElementAsNewSet(T pE) { - if (!contains(value)) { - RootNode root = new RootNode<>(value); - allNodes.put(value, root); + if (!contains(pE)) { + RootNode root = new RootNode<>(pE); + allNodes.put(pE, root); } } // only call with elements that are definitely canonical! - private void mergeExistingSets(T canon1, T canon2) { + private void mergeExistingSets(T pCanon1, T pCanon2) { - Preconditions.checkNotNull(canon1); - Preconditions.checkNotNull(canon2); + Preconditions.checkNotNull(pCanon1); + Preconditions.checkNotNull(pCanon2); if (unionType == UnionType.UNION_BY_SIZE) { - unionBySize(canon1, canon2); + unionBySize(pCanon1, pCanon2); } else { - unionByRank(canon1, canon2); + unionByRank(pCanon1, pCanon2); } } - private void addElementToExistingSet(T value, T canon) { + private void addElementToExistingSet(T pE, T pCanon) { - RootNode root = (RootNode) allNodes.get(canon); - NonRootNode newNode = new NonRootNode<>(root, value); + RootNode root = (RootNode) allNodes.get(pCanon); + NonRootNode newNode = new NonRootNode<>(root, pE); root.incrementSizeByOne(); if (root.getRank() == 0) { root.incrementRankByOne(); } - allNodes.put(value, newNode); + allNodes.put(pE, newNode); } - // canon1 will be new canonical element only if its set is actually bigger, otherwise canon2 new + // pCanon1 will be new canonical element only if its set is actually bigger, otherwise pCanon2 new // canon - private void unionBySize(T canon1, T canon2) { + private void unionBySize(T pCanon1, T pCanon2) { - RootNode rootNode1 = (RootNode) allNodes.get(canon1); - RootNode rootNode2 = (RootNode) allNodes.get(canon2); + RootNode rootNode1 = (RootNode) allNodes.get(pCanon1); + RootNode rootNode2 = (RootNode) allNodes.get(pCanon2); int size1 = rootNode1.getSize(); int size2 = rootNode2.getSize(); @@ -216,12 +216,13 @@ private void unionBySize(T canon1, T canon2) { } } - // canon1 will be new canonical element only if its rank is actually greater, otherwise canon2 new + // pCanon1 will be new canonical element only if its rank is actually greater, otherwise pCanon2 + // new // canon - private void unionByRank(T canon1, T canon2) { + private void unionByRank(T pCanon1, T pCanon2) { - RootNode rootNode1 = (RootNode) allNodes.get(canon1); - RootNode rootNode2 = (RootNode) allNodes.get(canon2); + RootNode rootNode1 = (RootNode) allNodes.get(pCanon1); + RootNode rootNode2 = (RootNode) allNodes.get(pCanon2); int rank1 = rootNode1.getRank(); int rank2 = rootNode2.getRank(); diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java index ab4b86d65..b64d44aa3 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java @@ -33,17 +33,17 @@ private PersistentSortedParentPointerTreeUnionFind(UnionType pUnionType) { } private PersistentSortedParentPointerTreeUnionFind( - PersistentSortedMap mapOfNodesToParents, - PersistentSortedMap mapOfRootsToWeights, - UnionType unionType) { - this.mapOfNodesToParents = mapOfNodesToParents; - this.mapOfRootsToWeights = mapOfRootsToWeights; - this.unionType = unionType; + PersistentSortedMap pMapOfNodesToParents, + PersistentSortedMap pMapOfRootsToWeights, + UnionType pUnionType) { + mapOfNodesToParents = pMapOfNodesToParents; + mapOfRootsToWeights = pMapOfRootsToWeights; + unionType = pUnionType; } public static > AbstractImmutableSortedUnionFind of( - UnionType unionType) { - return new PersistentSortedParentPointerTreeUnionFind<>(unionType); + UnionType pUnionType) { + return new PersistentSortedParentPointerTreeUnionFind<>(pUnionType); } @Override @@ -69,20 +69,20 @@ public Collection> getAllSubsets() { } @Override - public boolean contains(T e) { + public boolean contains(T pE) { - Preconditions.checkNotNull(e); + Preconditions.checkNotNull(pE); - return mapOfNodesToParents.containsKey(e); + return mapOfNodesToParents.containsKey(pE); } @Override - public T find(T e) { + public T find(T pE) { - Preconditions.checkNotNull(e); + Preconditions.checkNotNull(pE); - @Var T currentNode = e; - @Var T parent = mapOfNodesToParents.get(e); + @Var T currentNode = pE; + @Var T parent = mapOfNodesToParents.get(pE); if (parent != null) { while (!currentNode.equals(parent)) { @@ -97,45 +97,45 @@ public T find(T e) { } @Override - public PersistentSortedUnionFind unionAndCopy(T e1, T e2) { + public PersistentSortedUnionFind unionAndCopy(T pE1, T pE2) { - Preconditions.checkNotNull(e1); - Preconditions.checkNotNull(e2); + Preconditions.checkNotNull(pE1); + Preconditions.checkNotNull(pE2); - if (e1.equals(e2)) { - return addElementAsNewSetAndCopy(e1); + if (pE1.equals(pE2)) { + return addElementAsNewSetAndCopy(pE1); } else { - if (contains(e1)) { - if (contains(e2)) { - T canon1 = find(e1); - T canon2 = find(e2); + if (contains(pE1)) { + if (contains(pE2)) { + T canon1 = find(pE1); + T canon2 = find(pE2); if (!canon1.equals(canon2)) { return mergeExistingSetsAndCopy(canon1, canon2); } } else { - return addElementToExistingSetAndCopy(e2, find(e1)); + return addElementToExistingSetAndCopy(pE2, find(pE1)); } - } else if (contains(e2)) { - return addElementToExistingSetAndCopy(e1, find(e2)); + } else if (contains(pE2)) { + return addElementToExistingSetAndCopy(pE1, find(pE2)); } else { - return addTwoElementsAsSetAndCopy(e1, e2); + return addTwoElementsAsSetAndCopy(pE1, pE2); } } return this; } - private PersistentSortedUnionFind addElementAsNewSetAndCopy(T e) { + private PersistentSortedUnionFind addElementAsNewSetAndCopy(T pE) { - if (!contains(e)) { - PersistentSortedMap updatedNodesToParents = mapOfNodesToParents.putAndCopy(e, e); + if (!contains(pE)) { + PersistentSortedMap updatedNodesToParents = mapOfNodesToParents.putAndCopy(pE, pE); PersistentSortedMap updatedRootsToWeights; if (unionType == UnionType.UNION_BY_RANK) { - updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(e, 0); // rank + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(pE, 0); // rank } else { - updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(e, 1); // size + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(pE, 1); // size } return new PersistentSortedParentPointerTreeUnionFind<>( @@ -146,102 +146,103 @@ private PersistentSortedUnionFind addElementAsNewSetAndCopy(T e) { } // only call with elements that are definitely canonical! - private PersistentSortedUnionFind mergeExistingSetsAndCopy(T canon1, T canon2) { + private PersistentSortedUnionFind mergeExistingSetsAndCopy(T pCanon1, T pCanon2) { - Preconditions.checkNotNull(canon1); - Preconditions.checkNotNull(canon2); + Preconditions.checkNotNull(pCanon1); + Preconditions.checkNotNull(pCanon2); if (unionType == UnionType.UNION_BY_SIZE) { - return unionBySize(canon1, canon2); + return unionBySize(pCanon1, pCanon2); } else { - return unionByRank(canon1, canon2); + return unionByRank(pCanon1, pCanon2); } } - private PersistentSortedUnionFind addElementToExistingSetAndCopy(T e, T canon) { + private PersistentSortedUnionFind addElementToExistingSetAndCopy(T pE, T pCanon) { - Preconditions.checkNotNull(canon); + Preconditions.checkNotNull(pCanon); - PersistentSortedMap updatedNodesToParents = mapOfNodesToParents.putAndCopy(e, canon); + PersistentSortedMap updatedNodesToParents = mapOfNodesToParents.putAndCopy(pE, pCanon); PersistentSortedMap updatedRootsToWeights; if (unionType == UnionType.UNION_BY_RANK) { - @Var int rank = mapOfRootsToWeights.get(canon); + @Var int rank = mapOfRootsToWeights.get(pCanon); if (rank == 0) { - updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(canon, ++rank); + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(pCanon, ++rank); } else { updatedRootsToWeights = mapOfRootsToWeights; } } else { - @Var int size = mapOfRootsToWeights.get(canon); - updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(canon, ++size); + @Var int size = mapOfRootsToWeights.get(pCanon); + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(pCanon, ++size); } return new PersistentSortedParentPointerTreeUnionFind<>( updatedNodesToParents, updatedRootsToWeights, unionType); } - private PersistentSortedUnionFind addTwoElementsAsSetAndCopy(T e1, T e2) { + private PersistentSortedUnionFind addTwoElementsAsSetAndCopy(T pE1, T pE2) { @Var PersistentSortedMap updatedNodesToParents; - updatedNodesToParents = mapOfNodesToParents.putAndCopy(e1, e1); - updatedNodesToParents = updatedNodesToParents.putAndCopy(e2, e1); + updatedNodesToParents = mapOfNodesToParents.putAndCopy(pE1, pE1); + updatedNodesToParents = updatedNodesToParents.putAndCopy(pE2, pE1); PersistentSortedMap updatedRootsToWeights; if (unionType == UnionType.UNION_BY_RANK) { - updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(e1, 1); // rank + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(pE1, 1); // rank } else { - updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(e1, 2); // size + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(pE1, 2); // size } return new PersistentSortedParentPointerTreeUnionFind<>( updatedNodesToParents, updatedRootsToWeights, unionType); } - // canon1 will be new canonical element only if its set is actually bigger, otherwise canon2 new + // pCanon1 will be new canonical element only if its set is actually bigger, otherwise pCanon2 new // canon - private PersistentSortedUnionFind unionBySize(T canon1, T canon2) { + private PersistentSortedUnionFind unionBySize(T pCanon1, T pCanon2) { - int size1 = mapOfRootsToWeights.get(canon1); - int size2 = mapOfRootsToWeights.get(canon2); + int size1 = mapOfRootsToWeights.get(pCanon1); + int size2 = mapOfRootsToWeights.get(pCanon2); PersistentSortedMap updatedNodesToParents; PersistentSortedMap updatedRootsToWeights; if (size1 > size2) { - updatedNodesToParents = mapOfNodesToParents.putAndCopy(canon2, canon1); + updatedNodesToParents = mapOfNodesToParents.putAndCopy(pCanon2, pCanon1); - updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(canon1, size1 + size2); + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(pCanon1, size1 + size2); } else { - updatedNodesToParents = mapOfNodesToParents.putAndCopy(canon1, canon2); + updatedNodesToParents = mapOfNodesToParents.putAndCopy(pCanon1, pCanon2); - updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(canon2, size2 + size1); + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(pCanon2, size2 + size1); } return new PersistentSortedParentPointerTreeUnionFind<>( updatedNodesToParents, updatedRootsToWeights, unionType); } - // canon1 will be new canonical element only if its rank is actually greater, otherwise canon2 new + // pCanon1 will be new canonical element only if its rank is actually greater, otherwise pCanon2 + // new // canon - private PersistentSortedUnionFind unionByRank(T canon1, T canon2) { + private PersistentSortedUnionFind unionByRank(T pCanon1, T pCanon2) { - int rank1 = mapOfRootsToWeights.get(canon1); - @Var int rank2 = mapOfRootsToWeights.get(canon2); + int rank1 = mapOfRootsToWeights.get(pCanon1); + @Var int rank2 = mapOfRootsToWeights.get(pCanon2); PersistentSortedMap updatedNodesToParents; PersistentSortedMap updatedRootsToWeights; if (rank1 > rank2) { - updatedNodesToParents = mapOfNodesToParents.putAndCopy(canon2, canon1); + updatedNodesToParents = mapOfNodesToParents.putAndCopy(pCanon2, pCanon1); updatedRootsToWeights = mapOfRootsToWeights; } else { - updatedNodesToParents = mapOfNodesToParents.putAndCopy(canon1, canon2); + updatedNodesToParents = mapOfNodesToParents.putAndCopy(pCanon1, pCanon2); if (rank1 == rank2) { - updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(canon2, ++rank2); + updatedRootsToWeights = mapOfRootsToWeights.putAndCopy(pCanon2, ++rank2); } else { updatedRootsToWeights = mapOfRootsToWeights; } diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java index f243fa115..03dd442f9 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java @@ -28,12 +28,12 @@ public interface PersistentSortedUnionFind> extends Sort /** * Replacement for {@link #union(Comparable, Comparable)} that returns a fresh new instance. * - * @param e1 first element - * @param e2 second element + * @param pE1 first element + * @param pE2 second element * @return new instance that the desired changes have been applied to */ @CheckReturnValue - PersistentSortedUnionFind unionAndCopy(T e1, T e2); + PersistentSortedUnionFind unionAndCopy(T pE1, T pE2); /** * @throws UnsupportedOperationException Always. @@ -42,5 +42,5 @@ public interface PersistentSortedUnionFind> extends Sort @Deprecated @Override @DoNotCall - void union(T e1, T e2); + void union(T pE1, T pE2); } diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java index 67f27264f..4d3d03abb 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java @@ -28,12 +28,12 @@ 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 + * @param pE1 first element + * @param pE2 second element * @return new instance that the desired changes have been applied to */ @CheckReturnValue - PersistentUnionFind unionAndCopy(T e1, T e2); + PersistentUnionFind unionAndCopy(T pE1, T pE2); /** * @throws UnsupportedOperationException Always. @@ -42,5 +42,5 @@ public interface PersistentUnionFind extends UnionFind { @Deprecated @Override @DoNotCall - void union(T e1, T e2); + void union(T pE1, T pE2); } diff --git a/src/org/sosy_lab/common/collect/union_find/RootNode.java b/src/org/sosy_lab/common/collect/union_find/RootNode.java index 638fe753d..0860d3944 100644 --- a/src/org/sosy_lab/common/collect/union_find/RootNode.java +++ b/src/org/sosy_lab/common/collect/union_find/RootNode.java @@ -27,11 +27,11 @@ public final class RootNode extends AbstractTreeNode { * the current node simply functions as a non-root node from then on. In the beginning, rank is 0 * and size is 1. * - * @param value element to be stored in the node + * @param pValue element to be stored in the node */ - public RootNode(T value) { + public RootNode(T pValue) { - super(value); + super(pValue); this.rank = 0; this.size = 1; @@ -56,11 +56,11 @@ public void incrementSizeByOne() { } /** - * Increments size by n. + * Increments size by pN. * - * @param n number by which size is to be increased. + * @param pN number by which size is to be increased. */ - public void incrementSizeBy(int n) { - size += n; + public void incrementSizeBy(int pN) { + size += pN; } } diff --git a/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java index d5264fec3..d2097dd93 100644 --- a/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/SortedParentPointerTreeUnionFind.java @@ -31,10 +31,10 @@ public class SortedParentPointerTreeUnionFind> /** * Creates an empty instance. * - * @param unionType type of union to be performed for all unions on this instance + * @param pUnionType type of union to be performed for all unions on this instance */ - public SortedParentPointerTreeUnionFind(UnionType unionType) { - super(unionType); + public SortedParentPointerTreeUnionFind(UnionType pUnionType) { + super(pUnionType); } /** diff --git a/src/org/sosy_lab/common/collect/union_find/UnionFind.java b/src/org/sosy_lab/common/collect/union_find/UnionFind.java index 1b87de772..03cfb99a7 100644 --- a/src/org/sosy_lab/common/collect/union_find/UnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/UnionFind.java @@ -21,18 +21,18 @@ public interface UnionFind { /** * Returns the canonical element of the set containing the provided element. * - * @param e element for which set is to be found + * @param pE element for which set is to be found * @return canonical element of the found set */ - T find(T e); + T find(T pE); /** * Merges the sets represented by the two input values according to standard Union-Find behaviour. * - * @param e1 first element - * @param e2 second element + * @param pE1 first element + * @param pE2 second element */ - void union(T e1, T e2); + void union(T pE1, T pE2); /** * Provides a {@link Collection} containing all current subsets. @@ -45,8 +45,8 @@ public interface UnionFind { * Checks whether the provided element is contained in any current subset and returns true or * false accordingly. * - * @param e element to be searched for + * @param pE element to be searched for * @return true if contained, false if not */ - boolean contains(T e); + boolean contains(T pE); } From 2f50963c3c4b97a4912a2929a08b467a398ba8c8 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 17 Aug 2026 11:13:21 +0200 Subject: [PATCH 150/183] Add new documentation and refactor existing documentation --- .../AbstractImmutableSortedUnionFind.java | 5 ++ .../AbstractImmutableUnionFind.java | 5 ++ .../ImmutableParentPointerTreeUnionFind.java | 40 ++++++++++++++ ...tableSortedParentPointerTreeUnionFind.java | 43 +++++++++++++++ ...stentSortedParentPointerTreeUnionFind.java | 54 +++++++++++++++++++ .../union_find/PersistentSortedUnionFind.java | 2 - .../union_find/PersistentUnionFind.java | 2 - 7 files changed, 147 insertions(+), 4 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java index ed7cc3950..8a15075c5 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java @@ -8,5 +8,10 @@ package org.sosy_lab.common.collect.union_find; +/** + * An abstract class for sorted immutable Union-Find implementations. + * + * @param type of elements added to the Union-Find. Must be comparable. + */ public abstract class AbstractImmutableSortedUnionFind> extends AbstractImmutableUnionFind implements SortedUnionFind {} diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java index eead23b01..d1c1f48e9 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java @@ -10,6 +10,11 @@ import com.google.errorprone.annotations.DoNotCall; +/** + * An abstract class for immutable Union-Find implementations. + * + * @param type of elements added to the Union-Find + */ public abstract class AbstractImmutableUnionFind implements UnionFind { /** * @throws UnsupportedOperationException Always. diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java index 6c7badc61..750ae153f 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java @@ -19,14 +19,36 @@ import java.util.Set; import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; +/** + * An implementation of {@link UnionFind} using a {@link ImmutableMap} of each element {@link T} to + * its node {@link AbstractTreeNode}. Each node contains a reference to its respective parent node, + * thus resulting in a parent pointer tree structure for each subset. These are each represented by + * canonical elements which are the root of each tree. This is always the first element added to the + * subset, unless it has changed due to union operations. The union can be performed either by size + * or by rank, * determined by a constructor parameter. + * + * @param type of elements added to the Union-Find. + */ public class ImmutableParentPointerTreeUnionFind extends AbstractImmutableUnionFind { private final ImmutableMap> allNodes; + /** + * Only for internal use by the builder. + * + * @param pAllNodes finished immutable map storing all nodes contained in this Union-Find + */ protected ImmutableParentPointerTreeUnionFind(ImmutableMap> pAllNodes) { allNodes = pAllNodes; } + /** + * Returns the canonical element of the set containing the provided element. + * + * @param pE 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 pE) { @@ -48,6 +70,11 @@ public T find(T pE) { throw new IllegalArgumentException("Element not contained."); } + /** + * Provides a {@link Collection} containing all current subsets. + * + * @return {@link Collection} containing all current subsets + */ @Override public Collection> getAllSubsets() { @@ -69,12 +96,25 @@ public Collection> getAllSubsets() { return allSubsets.values(); } + /** + * Checks whether the provided element is contained in any current subset and returns true or + * false accordingly. + * + * @param pE element to be searched for + * @return true if contained, false if not + */ @Override public boolean contains(T pE) { return allNodes.containsKey(pE); } + /** + * Builder class which first collects the data in a mutable Union-Find and converts it to an + * immutable Union-Find when {@code build()} is called. + * + * @param type of elements added to the Union-Find + */ public static final class Builder { ParentPointerTreeUnionFind unionFind; diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java index 1001787dd..55b391a9d 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java @@ -19,16 +19,39 @@ import java.util.TreeSet; import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; +/** + * A sorted implementation of {@link UnionFind} using a {@link ImmutableMap} of each element {@link + * T} to its node {@link AbstractTreeNode}. Each node contains a reference to its respective parent + * node, thus resulting in a parent pointer tree structure for each subset. These are each + * represented by canonical elements which are the root of each tree. This is always the first + * element added to the subset, unless it has changed due to union operations. The union can be + * performed either by size or by rank, * determined by a constructor parameter. The elements are + * stored in unsorted structures, but {@code getAllSubsets()} returns a sorted view. + * + * @param type of elements added to the Union-Find. Must be comparable. + */ public class ImmutableSortedParentPointerTreeUnionFind> extends AbstractImmutableSortedUnionFind { private final ImmutableMap> allNodes; + /** + * Only for internal use by the builder. + * + * @param pAllNodes finished immutable map storing all nodes contained in this Union-Find + */ protected ImmutableSortedParentPointerTreeUnionFind( ImmutableMap> pAllNodes) { allNodes = pAllNodes; } + /** + * Returns the canonical element of the set containing the provided element. + * + * @param pE 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 pE) { @@ -50,6 +73,13 @@ public T find(T pE) { throw new IllegalArgumentException("Element not contained."); } + /** + * Provides a {@link Collection} containing all current subsets. The subsets are sorted by their + * canonical elements in ascending order. The contents of each subset are equally sorted in + * ascending order. + * + * @return {@link Collection} containing all current subsets + */ @Override public Collection> getAllSubsets() { @@ -71,12 +101,25 @@ public Collection> getAllSubsets() { return allSubsets.values(); } + /** + * Checks whether the provided element is contained in any current subset and returns true or + * false accordingly. + * + * @param pE element to be searched for + * @return true if contained, false if not + */ @Override public boolean contains(T pE) { return allNodes.containsKey(pE); } + /** + * Builder class which first collects the data in a mutable sorted Union-Find and converts it to + * an immutable Union-Find when {@code build()} is called. + * + * @param type of elements added to the Union-Find + */ public static final class Builder> { SortedParentPointerTreeUnionFind unionFind; diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java index b64d44aa3..89ed8f497 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java @@ -9,6 +9,7 @@ package org.sosy_lab.common.collect.union_find; import com.google.common.base.Preconditions; +import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.Var; import java.util.Collection; import java.util.NavigableMap; @@ -19,6 +20,18 @@ import org.sosy_lab.common.collect.PersistentSortedMap; import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; +/** + * Implementation of 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. The union can be performed either by size or by rank, determined by a constructor + * parameter. + * + *

All modifying operations inherited from {@link SortedUnionFind} are not supported and will + * always throw {@link UnsupportedOperationException}. + * + * @param The type of elements added to the Union-Find. Must be comparable. + */ public final class PersistentSortedParentPointerTreeUnionFind> extends AbstractImmutableSortedUnionFind implements PersistentSortedUnionFind { @@ -41,11 +54,25 @@ private PersistentSortedParentPointerTreeUnionFind( unionType = pUnionType; } + /** + * Returns a fresh, empty Union-Find instance of the given union type. + * + * @param pUnionType specifies whether the union is performed by rank or by size + * @return empty instance + * @param type of elements added to the Union-Find + */ public static > AbstractImmutableSortedUnionFind of( UnionType pUnionType) { return new PersistentSortedParentPointerTreeUnionFind<>(pUnionType); } + /** + * Provides a {@link Collection} containing all current subsets. The subsets are sorted by their + * canonical elements in ascending order. The contents of each subset are equally sorted in + * ascending order. + * + * @return {@link Collection} containing all current subsets + */ @Override public Collection> getAllSubsets() { @@ -68,6 +95,13 @@ public Collection> getAllSubsets() { return allSubsets.values(); } + /** + * Checks whether the provided element is contained in any current subset and returns true or + * false accordingly. + * + * @param pE element to be searched for + * @return true if contained, false if not + */ @Override public boolean contains(T pE) { @@ -76,6 +110,13 @@ public boolean contains(T pE) { return mapOfNodesToParents.containsKey(pE); } + /** + * Returns the canonical element of the set containing the provided element. + * + * @param pE 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 pE) { @@ -96,6 +137,19 @@ public T find(T pE) { throw new IllegalArgumentException("Element not contained."); } + /** + * Merges the sets represented by the two input values according to standard Union-Find behaviour. + * This operation does not mutate the existing object, but returns a fresh instance to which the + * changes in question have been applied. + * + *

USES: Add new element as new set: pass it as both pE1 and pE2. 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: pE1, pE2 canonical elements of sets to be merged. + * + * @param pE1 first element + * @param pE2 second element + */ + @CheckReturnValue @Override public PersistentSortedUnionFind unionAndCopy(T pE1, T pE2) { diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java index 03dd442f9..e6639fe09 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java @@ -10,7 +10,6 @@ import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.DoNotCall; -import com.google.errorprone.annotations.Immutable; /** * Interface for a persistent and sorted union-find. A persistent data structure is immutable, but @@ -22,7 +21,6 @@ * * @param The type of values. */ -@Immutable(containerOf = "T") public interface PersistentSortedUnionFind> extends SortedUnionFind { /** diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java index 4d3d03abb..4c94271da 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java @@ -10,7 +10,6 @@ import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.DoNotCall; -import com.google.errorprone.annotations.Immutable; /** * Interface for a persistent union-find. A persistent data structure is immutable, but provides @@ -22,7 +21,6 @@ * * @param The type of values. */ -@Immutable(containerOf = "T") public interface PersistentUnionFind extends UnionFind { /** From cc17a1109c95f1c3709547776a0b6e33725fef7b Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 17 Aug 2026 11:27:06 +0200 Subject: [PATCH 151/183] Code style fixes --- .../ImmutableParentPointerTreeUnionFind.java | 12 ++++++------ .../ImmutableSortedParentPointerTreeUnionFind.java | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java index 750ae153f..f8070aa90 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java @@ -20,12 +20,12 @@ import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; /** - * An implementation of {@link UnionFind} using a {@link ImmutableMap} of each element {@link T} to - * its node {@link AbstractTreeNode}. Each node contains a reference to its respective parent node, - * thus resulting in a parent pointer tree structure for each subset. These are each represented by - * canonical elements which are the root of each tree. This is always the first element added to the - * subset, unless it has changed due to union operations. The union can be performed either by size - * or by rank, * determined by a constructor parameter. + * An implementation of {@link UnionFind} using a {@link ImmutableMap} of each element to its {@link + * AbstractTreeNode}. Each node contains a reference to its respective parent node, thus resulting + * in a parent pointer tree structure for each subset. These are each represented by canonical + * elements which are the root of each tree. This is always the first element added to the subset, + * unless it has changed due to union operations. The union can be performed either by size or by + * rank, * determined by a constructor parameter. * * @param type of elements added to the Union-Find. */ diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java index 55b391a9d..f45ac9ea1 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java @@ -20,13 +20,13 @@ import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; /** - * A sorted implementation of {@link UnionFind} using a {@link ImmutableMap} of each element {@link - * T} to its node {@link AbstractTreeNode}. Each node contains a reference to its respective parent - * node, thus resulting in a parent pointer tree structure for each subset. These are each - * represented by canonical elements which are the root of each tree. This is always the first - * element added to the subset, unless it has changed due to union operations. The union can be - * performed either by size or by rank, * determined by a constructor parameter. The elements are - * stored in unsorted structures, but {@code getAllSubsets()} returns a sorted view. + * A sorted implementation of {@link UnionFind} using a {@link ImmutableMap} of each element to its + * {@link AbstractTreeNode}. Each node contains a reference to its respective parent node, thus + * resulting in a parent pointer tree structure for each subset. These are each represented by + * canonical elements which are the root of each tree. This is always the first element added to the + * subset, unless it has changed due to union operations. The union can be performed either by size + * or by rank, * determined by a constructor parameter. The elements are stored in unsorted + * structures, but {@code getAllSubsets()} returns a sorted view. * * @param type of elements added to the Union-Find. Must be comparable. */ From 4800d6c6215d72f0c8a46e0bc6cf34dd4fbf44f2 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 17 Aug 2026 11:41:42 +0200 Subject: [PATCH 152/183] Bug fix --- .../ImmutableSortedParentPointerTreeUnionFind.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java index f45ac9ea1..76737bd20 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java @@ -140,8 +140,9 @@ public ImmutableSortedParentPointerTreeUnionFind.Builder union(T pE1, T pE2) return this; } - public ImmutableParentPointerTreeUnionFind build() { - return new ImmutableParentPointerTreeUnionFind<>(ImmutableMap.copyOf(unionFind.allNodes)); + public ImmutableSortedParentPointerTreeUnionFind build() { + return new ImmutableSortedParentPointerTreeUnionFind<>( + ImmutableMap.copyOf(unionFind.allNodes)); } } } From d640822fcc03d91f32b76ca157b15e627fe008a0 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 17 Aug 2026 14:24:43 +0200 Subject: [PATCH 153/183] Add test class for immutable (unsorted and sorted) parent pointer tree union-finds (does not test sortedness; that will be in a separate test class) --- ...mutableParentPointerTreeUnionFindTest.java | 397 ++++++++++++++++++ 1 file changed, 397 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindTest.java diff --git a/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindTest.java b/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindTest.java new file mode 100644 index 000000000..fce60af97 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindTest.java @@ -0,0 +1,397 @@ +// 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.tests; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; + +import com.google.common.collect.ImmutableList; +import java.util.Collection; +import java.util.Set; +import org.junit.Test; +import org.sosy_lab.common.collect.union_find.ImmutableParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ImmutableSortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; +import org.sosy_lab.common.collect.union_find.UnionFind; + +public class ImmutableParentPointerTreeUnionFindTest { + + private final int[] simpleUnionArgs = new int[] {0, 1}; + + private static ImmutableParentPointerTreeUnionFind buildImmutable( + UnionType pUnionType, int[]... pUnions) { + + ImmutableParentPointerTreeUnionFind.Builder builder = + ImmutableParentPointerTreeUnionFind.Builder.getBuilder(pUnionType); + + for (int[] pair : pUnions) { + builder.union(pair[0], pair[1]); + } + + return builder.build(); + } + + private static ImmutableSortedParentPointerTreeUnionFind buildImmutableSorted( + UnionType pUnionType, int[]... pUnions) { + + ImmutableSortedParentPointerTreeUnionFind.Builder builder = + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(pUnionType); + + for (int[] pair : pUnions) { + builder.union(pair[0], pair[1]); + } + + return builder.build(); + } + + private static ImmutableList> buildUnsortedAndSorted( + UnionType pUnionType, int[]... pUnions) { + + return ImmutableList.of( + buildImmutable(pUnionType, pUnions), buildImmutableSorted(pUnionType, pUnions)); + } + + @Test + public void testFind_afterSelfUnion_returnsItself() { + + int[] unionArgs = new int[] {0, 0}; + + for (UnionFind unionFind : + buildUnsortedAndSorted(UnionType.UNION_BY_SIZE, unionArgs)) { + + assertThat(unionFind.find(0)).isEqualTo(0); + } + } + + @Test + public void testFind_null_throwsNullPointerException() { + + for (UnionFind unionFind : + buildUnsortedAndSorted(UnionType.UNION_BY_SIZE, simpleUnionArgs)) { + + assertThrows(NullPointerException.class, () -> unionFind.find(null)); + } + } + + @Test + public void testFind_elementNotContained_throwsIllegalArgumentException() { + + for (UnionFind unionFind : + buildUnsortedAndSorted(UnionType.UNION_BY_SIZE, simpleUnionArgs)) { + + assertThrows(IllegalArgumentException.class, () -> unionFind.find(10)); + } + } + + @Test + public void testUnion_twoNewElements_producesSingleSubsetOfSizeTwo() { + + for (UnionFind unionFind : + buildUnsortedAndSorted(UnionType.UNION_BY_SIZE, simpleUnionArgs)) { + + assertThat(unionFind.find(0)).isEqualTo(unionFind.find(1)); + assertThat(unionFind.getAllSubsets()).hasSize(1); + } + } + + @Test + public void testUnion_disjointPairs_produceDistinctSubsets() { + + for (UnionFind unionFind : + buildUnsortedAndSorted(UnionType.UNION_BY_SIZE, new int[] {0, 1}, new int[] {2, 3})) { + + assertThat(unionFind.find(0)).isNotEqualTo(unionFind.find(2)); + assertThat(unionFind.getAllSubsets()).hasSize(2); + } + } + + @Test + public void testUnion_severalElementsToSameSubset() { + + for (UnionFind unionFind : + buildUnsortedAndSorted( + UnionType.UNION_BY_SIZE, new int[] {0, 1}, new int[] {1, 2}, new int[] {2, 3})) { + + int canon = unionFind.find(0); + + assertThat(unionFind.find(1)).isEqualTo(canon); + assertThat(unionFind.find(2)).isEqualTo(canon); + assertThat(unionFind.find(3)).isEqualTo(canon); + assertThat(unionFind.getAllSubsets()).hasSize(1); + } + } + + @Test + public void testUnion_duplicateUnionCall_doesNotLeadToDuplicates() { + + for (UnionFind unionFind : + buildUnsortedAndSorted( + UnionType.UNION_BY_SIZE, new int[] {0, 1}, new int[] {0, 1}, new int[] {1, 0})) { + + assertThat(unionFind.find(0)).isEqualTo(unionFind.find(1)); + assertThat(unionFind.getAllSubsets()).hasSize(1); + assertThat(unionFind.getAllSubsets().iterator().next()).hasSize(2); + } + } + + @Test + public void testUnion_mergesTwoExistingMultiElementSubsets() { + + for (UnionFind unionFind : + buildUnsortedAndSorted( + UnionType.UNION_BY_SIZE, + new int[] {0, 1}, + new int[] {0, 2}, + new int[] {3, 4}, + new int[] {3, 5}, + new int[] {0, 3})) { + + assertThat(unionFind.getAllSubsets()).hasSize(1); + + int canon = unionFind.find(0); + + for (int i = 0; i <= 5; i++) { + assertThat(unionFind.find(i)).isEqualTo(canon); + } + } + } + + @Test + public void testUnion_constantCanonicalElementDuringNonLinearInsertion() { + + for (UnionFind unionFind : + buildUnsortedAndSorted( + UnionType.UNION_BY_SIZE, + new int[] {3, 3}, + new int[] {3, 2}, + new int[] {3, 5}, + new int[] {3, 1}, + new int[] {3, 8}, + new int[] {3, 6}, + new int[] {3, 9}, + new int[] {3, 7}, + new int[] {3, 4}, + new int[] {3, 0})) { + + assertThat(unionFind.getAllSubsets()).hasSize(1); + + int canon = unionFind.find(3); + + for (int i = 0; i <= 9; i++) { + assertThat(unionFind.find(i)).isEqualTo(canon); + } + } + } + + @Test + public void testUnion_bothUnionTypes_produceSameGrouping() { + + int[][] unions = {{0, 1}, {0, 2}, {3, 4}, {3, 5}, {0, 3}}; + + for (UnionType unionType : UnionType.values()) { + for (UnionFind unionFind : buildUnsortedAndSorted(unionType, unions)) { + + assertThat(unionFind.getAllSubsets()).hasSize(1); + + int canon = unionFind.find(0); + + for (int i = 0; i <= 5; i++) { + assertThat(unionFind.find(i)).isEqualTo(canon); + } + } + } + } + + @Test + public void testUnion_stringElements() { + + ImmutableParentPointerTreeUnionFind.Builder unsortedBuilder = + ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); + ImmutableSortedParentPointerTreeUnionFind.Builder sortedBuilder = + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); + + ImmutableParentPointerTreeUnionFind unsortedStringUnionFind = + unsortedBuilder.union("0", "1").union("0", "2").union("3", "4").build(); + ImmutableSortedParentPointerTreeUnionFind sortedStringUnionFind = + sortedBuilder.union("0", "1").union("0", "2").union("3", "4").build(); + + assertThat(unsortedStringUnionFind.find("0")).isEqualTo(unsortedStringUnionFind.find("2")); + assertThat(unsortedStringUnionFind.find("0")).isNotEqualTo(unsortedStringUnionFind.find("3")); + assertThat(unsortedStringUnionFind.getAllSubsets()).hasSize(2); + + assertThat(sortedStringUnionFind.find("0")).isEqualTo(sortedStringUnionFind.find("2")); + assertThat(sortedStringUnionFind.find("0")).isNotEqualTo(sortedStringUnionFind.find("3")); + assertThat(sortedStringUnionFind.getAllSubsets()).hasSize(2); + } + + @Test + public void testGetAllSubsets_reflectsCorrectMembershipAfterMultipleUnions() { + + int[][] unions = { + {0, 0}, {0, 1}, {0, 2}, {0, 3}, {0, 4}, {5, 5}, {5, 6}, {5, 7}, {5, 8}, {5, 9} + }; + + for (UnionFind unionFind : buildUnsortedAndSorted(UnionType.UNION_BY_SIZE, unions)) { + + Collection> subsets = unionFind.getAllSubsets(); + + assertThat(subsets).hasSize(2); + + for (Set subset : subsets) { + assertThat(subset).hasSize(5); + } + } + } + + @Test + public void testGetAllSubsets_emptyUnionFind_isEmpty() { + + for (UnionFind unionFind : buildUnsortedAndSorted(UnionType.UNION_BY_SIZE)) { + + assertThat(unionFind.getAllSubsets()).isEmpty(); + } + } + + @Test + public void testContains_elementInSubset_returnsTrue() { + + for (UnionFind unionFind : + buildUnsortedAndSorted(UnionType.UNION_BY_SIZE, simpleUnionArgs)) { + + assertThat(unionFind.contains(0)).isTrue(); + assertThat(unionFind.contains(1)).isTrue(); + } + } + + @Test + public void testContains_elementNotContained_returnsFalse() { + + for (UnionFind unionFind : + buildUnsortedAndSorted(UnionType.UNION_BY_SIZE, simpleUnionArgs)) { + + assertThat(unionFind.contains(10)).isFalse(); + } + } + + @Test + public void testContains_null_returnsFalse() { + + for (UnionFind unionFind : + buildUnsortedAndSorted(UnionType.UNION_BY_SIZE, simpleUnionArgs)) { + + assertThat(unionFind.contains(null)).isFalse(); + } + } + + @Test + public void testBuilder_union_nullElement_throwsNullPointerException() { + + ImmutableParentPointerTreeUnionFind.Builder unsortedBuilder = + ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); + ImmutableSortedParentPointerTreeUnionFind.Builder sortedBuilder = + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); + + assertThrows(NullPointerException.class, () -> unsortedBuilder.union(null, 1)); + assertThrows(NullPointerException.class, () -> sortedBuilder.union(null, 1)); + } + + @Test + public void testBuilder_union_returnsSameBuilderInstance() { + + ImmutableParentPointerTreeUnionFind.Builder unsortedBuilder = + ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); + ImmutableSortedParentPointerTreeUnionFind.Builder sortedBuilder = + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); + + assertThat(unsortedBuilder.union(0, 1)).isSameInstanceAs(unsortedBuilder); + assertThat(sortedBuilder.union(0, 1)).isSameInstanceAs(sortedBuilder); + } + + @Test + public void testBuilder_getBuilder_returnsIndependentBuilders() { + + ImmutableParentPointerTreeUnionFind.Builder unsortedBuilder1 = + ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); + ImmutableParentPointerTreeUnionFind.Builder unsortedBuilder2 = + ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); + + ImmutableSortedParentPointerTreeUnionFind.Builder sortedBuilder1 = + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); + ImmutableSortedParentPointerTreeUnionFind.Builder sortedBuilder2 = + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); + + unsortedBuilder1.union(0, 1); + sortedBuilder1.union(0, 1); + + assertThat(unsortedBuilder1.build().contains(0)).isTrue(); + assertThat(unsortedBuilder2.build().contains(0)).isFalse(); + + assertThat(sortedBuilder1.build().contains(0)).isTrue(); + assertThat(sortedBuilder2.build().contains(0)).isFalse(); + } + + @Test + public void testBuilder_build_laterMutationsDoNotAffectPreviousResult() { + + ImmutableParentPointerTreeUnionFind.Builder unsortedBuilder = + ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); + ImmutableSortedParentPointerTreeUnionFind.Builder sortedBuilder = + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); + + unsortedBuilder.union(0, 1); + sortedBuilder.union(0, 1); + + ImmutableParentPointerTreeUnionFind firstUnsortedResult = unsortedBuilder.build(); + ImmutableSortedParentPointerTreeUnionFind firstSortedResult = sortedBuilder.build(); + + unsortedBuilder.union(2, 3); + sortedBuilder.union(2, 3); + + ImmutableParentPointerTreeUnionFind secondUnsortedResult = unsortedBuilder.build(); + ImmutableSortedParentPointerTreeUnionFind secondSortedResult = sortedBuilder.build(); + + assertThat(firstUnsortedResult.contains(2)).isFalse(); + assertThat(firstUnsortedResult.getAllSubsets()).hasSize(1); + + assertThat(firstSortedResult.contains(2)).isFalse(); + assertThat(firstSortedResult.getAllSubsets()).hasSize(1); + + assertThat(secondUnsortedResult.contains(2)).isTrue(); + assertThat(secondUnsortedResult.getAllSubsets()).hasSize(2); + + assertThat(secondSortedResult.contains(2)).isTrue(); + assertThat(secondSortedResult.getAllSubsets()).hasSize(2); + } + + @Test + public void testMutableUnion_isUnsupported() { + + for (UnionFind unionFind : + buildUnsortedAndSorted(UnionType.UNION_BY_SIZE, simpleUnionArgs)) { + assertThrows(UnsupportedOperationException.class, () -> unionFind.union(0, 1)); + } + } + + @Test + public void testGetAllSubsets_mutatingReturnedCollection_doesNotAffectSubsequentCalls() { + + for (UnionFind unionFind : + buildUnsortedAndSorted(UnionType.UNION_BY_SIZE, simpleUnionArgs)) { + + Collection> firstCall = unionFind.getAllSubsets(); + + firstCall.iterator().next().clear(); + + Collection> secondCall = unionFind.getAllSubsets(); + + assertThat(secondCall).hasSize(1); + assertThat(secondCall.iterator().next()).containsExactly(0, 1); + } + } +} From 9a45b3e15211fbde6deee05bdba5ff44848d66d5 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 17 Aug 2026 19:07:46 +0200 Subject: [PATCH 154/183] Add test class for testing sortedness of ImmutableSortedParentPointerTreeUnionFind --- ...utableParentPointerTreeSortednessTest.java | 193 ++++++++++++++++++ ...mutableParentPointerTreeUnionFindTest.java | 2 +- 2 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeSortednessTest.java diff --git a/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeSortednessTest.java b/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeSortednessTest.java new file mode 100644 index 000000000..d04af8d7a --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeSortednessTest.java @@ -0,0 +1,193 @@ +// 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.tests; + +import static com.google.common.truth.Truth.assertThat; + +import java.util.Collection; +import java.util.Iterator; +import java.util.NavigableSet; +import org.junit.Test; +import org.sosy_lab.common.collect.union_find.ImmutableSortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ImmutableSortedParentPointerTreeUnionFind.Builder; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; + +public class ImmutableParentPointerTreeSortednessTest { + + private static ImmutableSortedParentPointerTreeUnionFind buildImmutableSorted( + UnionType pUnionType, int[]... pUnions) { + + ImmutableSortedParentPointerTreeUnionFind.Builder builder = + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(pUnionType); + + for (int[] pair : pUnions) { + builder.union(pair[0], pair[1]); + } + + return builder.build(); + } + + private static > NavigableSet onlySubsetOf( + ImmutableSortedParentPointerTreeUnionFind pSortedUnionFind) { + + Collection> subsets = pSortedUnionFind.getAllSubsets(); + + assertThat(subsets).hasSize(1); + + return subsets.iterator().next(); + } + + @Test + public void testGetAllSubsets_elementsAddedInAscendingOrder_remainSorted() { + + ImmutableSortedParentPointerTreeUnionFind sortedUnionFind = + buildImmutableSorted( + UnionType.UNION_BY_SIZE, new int[] {0, 1}, new int[] {0, 2}, new int[] {0, 3}); + + assertThat(onlySubsetOf(sortedUnionFind)).containsExactly(0, 1, 2, 3).inOrder(); + } + + @Test + public void testGetAllSubsets_elementsAddedInDescendingOrder_areSortedAscending() { + + ImmutableSortedParentPointerTreeUnionFind sortedUnionFind = + buildImmutableSorted( + UnionType.UNION_BY_SIZE, new int[] {3, 2}, new int[] {3, 1}, new int[] {3, 0}); + + assertThat(onlySubsetOf(sortedUnionFind)).containsExactly(0, 1, 2, 3).inOrder(); + } + + @Test + public void testGetAllSubsets_nonLinearInsertionOrder_areReturnedSorted() { + + ImmutableSortedParentPointerTreeUnionFind sortedUnionFind = + buildImmutableSorted( + UnionType.UNION_BY_SIZE, + new int[] {3, 2}, + new int[] {3, 4}, + new int[] {3, 0}, + new int[] {3, 5}, + new int[] {3, 1}); + + assertThat(onlySubsetOf(sortedUnionFind)).containsExactly(0, 1, 2, 3, 4, 5).inOrder(); + } + + @Test + public void testGetAllSubsets_multipleSubsets_eachSortedIndependently() { + + ImmutableSortedParentPointerTreeUnionFind sortedUnionFind = + buildImmutableSorted( + UnionType.UNION_BY_SIZE, + new int[] {0, 1}, + new int[] {0, 2}, + new int[] {10, 11}, + new int[] {10, 12}); + + for (NavigableSet subset : sortedUnionFind.getAllSubsets()) { + + assertThat(subset).isInOrder(); + } + } + + @Test + public void testGetAllSubsets_afterMergingTwoSubsets_resultIsSorted() { + + ImmutableSortedParentPointerTreeUnionFind sortedUnionFind = + buildImmutableSorted( + UnionType.UNION_BY_SIZE, + new int[] {0, 1}, + new int[] {0, 2}, + new int[] {3, 4}, + new int[] {3, 5}, + new int[] {0, 3}); + + assertThat(onlySubsetOf(sortedUnionFind)).containsExactly(0, 1, 2, 3, 4, 5).inOrder(); + } + + @Test + public void testGetAllSubsets_stringElements_areSortedAlphabetically() { + + Builder builder = Builder.getBuilder(UnionType.UNION_BY_SIZE); + String[] expected = {"-1", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; + + for (int i = 0; i <= 2; i++) { + builder.union("0", Integer.toString(i)); + } + for (int i = 3; i <= 5; i++) { + builder.union("3", Integer.toString(i)); + } + for (int i = 6; i <= 8; i++) { + builder.union("6", Integer.toString(i)); + } + + builder.union("9", "9"); + builder.union("0", "6"); + builder.union("6", "-1"); + builder.union("1", "4"); + builder.union("0", "9"); + + ImmutableSortedParentPointerTreeUnionFind stringSortedUnionFind = builder.build(); + + assertThat(onlySubsetOf(stringSortedUnionFind)).containsExactlyElementsIn(expected).inOrder(); + } + + @Test + public void testGetAllSubsets_isSortedRegardlessOfUnionType() { + + int[][] unions = {{3, 2}, {3, 4}, {3, 0}, {3, 5}, {3, 1}}; + + for (UnionType unionType : UnionType.values()) { + + ImmutableSortedParentPointerTreeUnionFind sortedUnionFind = + buildImmutableSorted(unionType, unions); + + assertThat(onlySubsetOf(sortedUnionFind)).containsExactly(0, 1, 2, 3, 4, 5).inOrder(); + } + } + + @Test + public void testGetAllSubsets_subsetsThemselvesAreOrderedByCanonicalElement() { + + ImmutableSortedParentPointerTreeUnionFind sortedUnionFind = + buildImmutableSorted( + UnionType.UNION_BY_SIZE, + new int[] {10, 11}, + new int[] {10, 12}, + new int[] {0, 1}, + new int[] {0, 2}); + + Collection> subsets = sortedUnionFind.getAllSubsets(); + assertThat(subsets).hasSize(2); + + Iterator> iterator = subsets.iterator(); + NavigableSet firstSubset = iterator.next(); + NavigableSet secondSubset = iterator.next(); + + assertThat(firstSubset.last()).isLessThan(secondSubset.first()); + } + + @Test + public void testGetAllSubsets_returnedSetSupportsNavigableSetOperations() { + + ImmutableSortedParentPointerTreeUnionFind sortedUnionFind = + buildImmutableSorted( + UnionType.UNION_BY_SIZE, + new int[] {0, 1}, + new int[] {0, 2}, + new int[] {0, 3}, + new int[] {0, 4}); + + NavigableSet subset = onlySubsetOf(sortedUnionFind); + + assertThat(subset.first()).isEqualTo(0); + assertThat(subset.last()).isEqualTo(4); + assertThat(subset.higher(1)).isEqualTo(2); + assertThat(subset.lower(3)).isEqualTo(2); + } +} diff --git a/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindTest.java b/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindTest.java index fce60af97..e3ca0084a 100644 --- a/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindTest.java @@ -234,7 +234,7 @@ public void testUnion_stringElements() { public void testGetAllSubsets_reflectsCorrectMembershipAfterMultipleUnions() { int[][] unions = { - {0, 0}, {0, 1}, {0, 2}, {0, 3}, {0, 4}, {5, 5}, {5, 6}, {5, 7}, {5, 8}, {5, 9} + {0, 0}, {0, 1}, {0, 2}, {0, 3}, {0, 4}, {5, 5}, {5, 6}, {5, 7}, {5, 8}, {5, 9}, }; for (UnionFind unionFind : buildUnsortedAndSorted(UnionType.UNION_BY_SIZE, unions)) { From 7949ab9249c42be52b9733e906b1c92c0fe4e7a3 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 17 Aug 2026 19:27:59 +0200 Subject: [PATCH 155/183] Code format fix --- .../tests/ImmutableParentPointerTreeSortednessTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeSortednessTest.java b/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeSortednessTest.java index d04af8d7a..d1b349d77 100644 --- a/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeSortednessTest.java +++ b/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeSortednessTest.java @@ -15,7 +15,6 @@ import java.util.NavigableSet; import org.junit.Test; import org.sosy_lab.common.collect.union_find.ImmutableSortedParentPointerTreeUnionFind; -import org.sosy_lab.common.collect.union_find.ImmutableSortedParentPointerTreeUnionFind.Builder; import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; public class ImmutableParentPointerTreeSortednessTest { @@ -113,7 +112,8 @@ public void testGetAllSubsets_afterMergingTwoSubsets_resultIsSorted() { @Test public void testGetAllSubsets_stringElements_areSortedAlphabetically() { - Builder builder = Builder.getBuilder(UnionType.UNION_BY_SIZE); + ImmutableSortedParentPointerTreeUnionFind.Builder builder = + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); String[] expected = {"-1", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; for (int i = 0; i <= 2; i++) { From 8b627796e14dca0e8e8a04c9090678a3743934ab Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 17 Aug 2026 20:21:57 +0200 Subject: [PATCH 156/183] Add PersistentParentPointerTreeUnionFind --- .../PersistentParentPointerTreeUnionFind.java | 247 ++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java new file mode 100644 index 000000000..3cf4a0034 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java @@ -0,0 +1,247 @@ +// 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 com.google.common.base.Preconditions; +import com.google.errorprone.annotations.CheckReturnValue; +import com.google.errorprone.annotations.Var; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; + +public class PersistentParentPointerTreeUnionFind extends AbstractImmutableUnionFind + implements PersistentUnionFind { + + private final Map mapOfNodesToParents; + private final Map mapOfRootsToWeights; + private final UnionType unionType; + + private PersistentParentPointerTreeUnionFind(UnionType pUnionType) { + mapOfNodesToParents = new HashMap<>(); + mapOfRootsToWeights = new HashMap<>(); + unionType = pUnionType; + } + + private PersistentParentPointerTreeUnionFind( + Map pMapOfNodesToParents, Map pMapOfRootsToWeights, UnionType pUnionType) { + mapOfNodesToParents = pMapOfNodesToParents; + mapOfRootsToWeights = pMapOfRootsToWeights; + unionType = pUnionType; + } + + public static AbstractImmutableUnionFind of(UnionType pUnionType) { + return new PersistentParentPointerTreeUnionFind<>(pUnionType); + } + + @Override + public Collection> getAllSubsets() { + + Map> allSubsets = new HashMap<>(); + + for (T current : mapOfNodesToParents.keySet()) { + + T root = find(current); + + if (allSubsets.containsKey(root)) { + allSubsets.get(root).add(current); + } else { + Set set = new HashSet<>(); + set.add(root); + set.add(current); + allSubsets.put(root, set); + } + } + + return allSubsets.values(); + } + + @Override + public boolean contains(T pE) { + + Preconditions.checkNotNull(pE); + + return mapOfNodesToParents.containsKey(pE); + } + + @Override + public T find(T pE) { + + Preconditions.checkNotNull(pE); + + @Var T currentNode = pE; + @Var T parent = mapOfNodesToParents.get(pE); + + if (parent != null) { + while (!currentNode.equals(parent)) { + currentNode = parent; + parent = mapOfNodesToParents.get(currentNode); + } + + return parent; + } + + throw new IllegalArgumentException("Element not contained."); + } + + @CheckReturnValue + @Override + public PersistentUnionFind unionAndCopy(T pE1, T pE2) { + + Preconditions.checkNotNull(pE1); + Preconditions.checkNotNull(pE2); + + if (pE1.equals(pE2)) { + return addElementAsNewSetAndCopy(pE1); + } else { + if (contains(pE1)) { + if (contains(pE2)) { + T canon1 = find(pE1); + T canon2 = find(pE2); + + if (!canon1.equals(canon2)) { + return mergeExistingSetsAndCopy(canon1, canon2); + } + } else { + return addElementToExistingSetAndCopy(pE2, find(pE1)); + } + } else if (contains(pE2)) { + return addElementToExistingSetAndCopy(pE1, find(pE2)); + } else { + return addTwoElementsAsSetAndCopy(pE1, pE2); + } + } + + return this; + } + + private PersistentUnionFind addElementAsNewSetAndCopy(T pE) { + + if (!contains(pE)) { + Map updatedNodesToParents = new HashMap<>(mapOfNodesToParents); + updatedNodesToParents.put(pE, pE); + + Map updatedRootsToWeights = new HashMap<>(mapOfRootsToWeights); + if (unionType == UnionType.UNION_BY_RANK) { + updatedRootsToWeights.put(pE, 0); // rank + } else { + updatedRootsToWeights.put(pE, 1); // size + } + + return new PersistentParentPointerTreeUnionFind<>( + updatedNodesToParents, updatedRootsToWeights, unionType); + } + + return this; + } + + // only call with elements that are definitely canonical! + private PersistentUnionFind mergeExistingSetsAndCopy(T pCanon1, T pCanon2) { + + Preconditions.checkNotNull(pCanon1); + Preconditions.checkNotNull(pCanon2); + + if (unionType == UnionType.UNION_BY_SIZE) { + return unionBySize(pCanon1, pCanon2); + } else { + return unionByRank(pCanon1, pCanon2); + } + } + + private PersistentUnionFind addElementToExistingSetAndCopy(T pE, T pCanon) { + + Preconditions.checkNotNull(pCanon); + + Map updatedNodesToParents = new HashMap<>(mapOfNodesToParents); + updatedNodesToParents.put(pE, pCanon); + + Map updatedRootsToWeights = new HashMap<>(mapOfRootsToWeights); + if (unionType == UnionType.UNION_BY_RANK) { + @Var int rank = mapOfRootsToWeights.get(pCanon); + + if (rank == 0) { + updatedRootsToWeights.put(pCanon, ++rank); + } + } else { + @Var int size = mapOfRootsToWeights.get(pCanon); + updatedRootsToWeights.put(pCanon, ++size); + } + + return new PersistentParentPointerTreeUnionFind<>( + updatedNodesToParents, updatedRootsToWeights, unionType); + } + + private PersistentUnionFind addTwoElementsAsSetAndCopy(T pE1, T pE2) { + + Map updatedNodesToParents = new HashMap<>(mapOfNodesToParents); + updatedNodesToParents.put(pE1, pE1); + updatedNodesToParents.put(pE2, pE1); + + Map updatedRootsToWeights = new HashMap<>(mapOfRootsToWeights); + if (unionType == UnionType.UNION_BY_RANK) { + updatedRootsToWeights.put(pE1, 1); // rank + } else { + updatedRootsToWeights.put(pE1, 2); // size + } + + return new PersistentParentPointerTreeUnionFind<>( + updatedNodesToParents, updatedRootsToWeights, unionType); + } + + // pCanon1 will be new canonical element only if its set is actually bigger, otherwise pCanon2 new + // canon + private PersistentUnionFind unionBySize(T pCanon1, T pCanon2) { + + int size1 = mapOfRootsToWeights.get(pCanon1); + int size2 = mapOfRootsToWeights.get(pCanon2); + + Map updatedNodesToParents = new HashMap<>(mapOfNodesToParents); + Map updatedRootsToWeights = new HashMap<>(mapOfRootsToWeights); + + if (size1 > size2) { + updatedNodesToParents.put(pCanon2, pCanon1); + + updatedRootsToWeights.put(pCanon1, size1 + size2); + } else { + updatedNodesToParents.put(pCanon1, pCanon2); + + updatedRootsToWeights.put(pCanon2, size2 + size1); + } + + return new PersistentParentPointerTreeUnionFind<>( + updatedNodesToParents, updatedRootsToWeights, unionType); + } + + // pCanon1 will be new canonical element only if its rank is actually greater, otherwise pCanon2 + // new + // canon + private PersistentUnionFind unionByRank(T pCanon1, T pCanon2) { + + int rank1 = mapOfRootsToWeights.get(pCanon1); + @Var int rank2 = mapOfRootsToWeights.get(pCanon2); + + Map updatedNodesToParents = new HashMap<>(mapOfNodesToParents); + Map updatedRootsToWeights = new HashMap<>(mapOfRootsToWeights); + + if (rank1 > rank2) { + updatedNodesToParents.put(pCanon2, pCanon1); + } else { + updatedNodesToParents.put(pCanon1, pCanon2); + + if (rank1 == rank2) { + updatedRootsToWeights.put(pCanon2, ++rank2); + } + } + + return new PersistentParentPointerTreeUnionFind<>( + updatedNodesToParents, updatedRootsToWeights, unionType); + } +} From 4af6da240478e63194c164e23a422f4f95b9403a Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 17 Aug 2026 20:50:24 +0200 Subject: [PATCH 157/183] Add documentation to PersistentParentPointerTreeUnionFind --- .../PersistentParentPointerTreeUnionFind.java | 49 +++++++++++++++++++ ...stentSortedParentPointerTreeUnionFind.java | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java index 3cf4a0034..993a978a9 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java @@ -18,6 +18,17 @@ import java.util.Set; import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; +/** + * Implementation of 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. The union can be performed + * either by size or by rank, determined by a constructor parameter. + * + *

All modifying operations inherited from {@link UnionFind} are not supported and will always + * throw {@link UnsupportedOperationException}. + * + * @param The type of elements added to the Union-Find. + */ public class PersistentParentPointerTreeUnionFind extends AbstractImmutableUnionFind implements PersistentUnionFind { @@ -38,10 +49,22 @@ private PersistentParentPointerTreeUnionFind( unionType = pUnionType; } + /** + * Returns a fresh, empty Union-Find instance of the given union type. + * + * @param pUnionType specifies whether the union is performed by rank or by size + * @return empty instance + * @param type of elements added to the Union-Find. + */ public static AbstractImmutableUnionFind of(UnionType pUnionType) { return new PersistentParentPointerTreeUnionFind<>(pUnionType); } + /** + * Provides a {@link Collection} containing all current subsets. + * + * @return {@link Collection} containing all current subsets + */ @Override public Collection> getAllSubsets() { @@ -64,6 +87,13 @@ public Collection> getAllSubsets() { return allSubsets.values(); } + /** + * Checks whether the provided element is contained in any current subset and returns true or + * false accordingly. + * + * @param pE element to be searched for + * @return true if contained, false if not + */ @Override public boolean contains(T pE) { @@ -72,6 +102,13 @@ public boolean contains(T pE) { return mapOfNodesToParents.containsKey(pE); } + /** + * Returns the canonical element of the set containing the provided element. + * + * @param pE 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 pE) { @@ -92,6 +129,18 @@ public T find(T pE) { throw new IllegalArgumentException("Element not contained."); } + /** + * Merges the sets represented by the two input values according to standard Union-Find behaviour. + * This operation does not mutate the existing object, but returns a fresh instance to which the + * changes in question have been applied. + * + *

USES: Add new element as new set: pass it as both pE1 and pE2. 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: pE1, pE2 canonical elements of sets to be merged. + * + * @param pE1 first element + * @param pE2 second element + */ @CheckReturnValue @Override public PersistentUnionFind unionAndCopy(T pE1, T pE2) { diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java index 89ed8f497..440366ff0 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java @@ -59,7 +59,7 @@ private PersistentSortedParentPointerTreeUnionFind( * * @param pUnionType specifies whether the union is performed by rank or by size * @return empty instance - * @param type of elements added to the Union-Find + * @param type of elements added to the Union-Find. Must be comparable. */ public static > AbstractImmutableSortedUnionFind of( UnionType pUnionType) { From 716a90ae51156957b945d30bb89f9704ae596a69 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 17 Aug 2026 22:08:16 +0200 Subject: [PATCH 158/183] Add tests for unsorted and sorted parent pointer tree union-finds (sortedness will be tested separately) --- .../PersistentParentPointerTreeUnionFind.java | 2 +- ...stentSortedParentPointerTreeUnionFind.java | 3 +- ...sistentParentPointerTreeUnionFindTest.java | 420 ++++++++++++++++++ 3 files changed, 422 insertions(+), 3 deletions(-) create mode 100644 src/org/sosy_lab/common/collect/union_find/tests/PersistentParentPointerTreeUnionFindTest.java diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java index 993a978a9..9f3cd016c 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java @@ -56,7 +56,7 @@ private PersistentParentPointerTreeUnionFind( * @return empty instance * @param type of elements added to the Union-Find. */ - public static AbstractImmutableUnionFind of(UnionType pUnionType) { + public static PersistentUnionFind of(UnionType pUnionType) { return new PersistentParentPointerTreeUnionFind<>(pUnionType); } diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java index 440366ff0..2348c0d32 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java @@ -61,8 +61,7 @@ private PersistentSortedParentPointerTreeUnionFind( * @return empty instance * @param type of elements added to the Union-Find. Must be comparable. */ - public static > AbstractImmutableSortedUnionFind of( - UnionType pUnionType) { + public static > PersistentSortedUnionFind of(UnionType pUnionType) { return new PersistentSortedParentPointerTreeUnionFind<>(pUnionType); } diff --git a/src/org/sosy_lab/common/collect/union_find/tests/PersistentParentPointerTreeUnionFindTest.java b/src/org/sosy_lab/common/collect/union_find/tests/PersistentParentPointerTreeUnionFindTest.java new file mode 100644 index 000000000..2d1e4c944 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/tests/PersistentParentPointerTreeUnionFindTest.java @@ -0,0 +1,420 @@ +// 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.tests; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; + +import com.google.common.collect.ImmutableList; +import java.util.Collection; +import java.util.Set; +import org.junit.Test; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; +import org.sosy_lab.common.collect.union_find.PersistentParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.PersistentSortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.PersistentSortedUnionFind; +import org.sosy_lab.common.collect.union_find.PersistentUnionFind; +import org.sosy_lab.common.collect.union_find.UnionFind; + +public class PersistentParentPointerTreeUnionFindTest { + + private final int[] simpleUnionArgs = new int[] {0, 1}; + + private static PersistentUnionFind emptyPersistentUnionFind(UnionType pUnionType) { + return PersistentParentPointerTreeUnionFind.of(pUnionType); + } + + private static PersistentSortedUnionFind emptyPersistentSortedUnionFind( + UnionType pUnionType) { + return PersistentSortedParentPointerTreeUnionFind.of(pUnionType); + } + + private static PersistentUnionFind applyUnions(UnionType pUnionType, int[]... pUnions) { + + PersistentUnionFind unionFind = emptyPersistentUnionFind(pUnionType); + + for (int[] pair : pUnions) { + unionFind = unionFind.unionAndCopy(pair[0], pair[1]); + } + + return unionFind; + } + + private static PersistentSortedUnionFind applySortedUnions( + UnionType pUnionType, int[]... pUnions) { + + PersistentSortedUnionFind unionFind = emptyPersistentSortedUnionFind(pUnionType); + + for (int[] pair : pUnions) { + unionFind = unionFind.unionAndCopy(pair[0], pair[1]); + } + + return unionFind; + } + + private static ImmutableList> bothVariants( + UnionType pUnionType, int[]... pUnions) { + + return ImmutableList.of( + applyUnions(pUnionType, pUnions), applySortedUnions(pUnionType, pUnions)); + } + + @Test + public void testFind_afterSelfUnion_returnsItself() { + + int[] unionArgs = new int[] {0, 0}; + + for (UnionFind unionFind : bothVariants(UnionType.UNION_BY_SIZE, unionArgs)) { + + assertThat(unionFind.find(0)).isEqualTo(0); + } + } + + @Test + public void testFind_null_throwsNullPointerException() { + + for (UnionFind unionFind : bothVariants(UnionType.UNION_BY_SIZE, simpleUnionArgs)) { + + assertThrows(NullPointerException.class, () -> unionFind.find(null)); + } + } + + @Test + public void testFind_elementNotContained_throwsIllegalArgumentException() { + + for (UnionFind unionFind : bothVariants(UnionType.UNION_BY_SIZE, simpleUnionArgs)) { + + assertThrows(IllegalArgumentException.class, () -> unionFind.find(10)); + } + } + + @Test + public void testUnion_twoNewElements_producesSingleSubsetOfSizeTwo() { + + for (UnionFind unionFind : bothVariants(UnionType.UNION_BY_SIZE, simpleUnionArgs)) { + + assertThat(unionFind.find(0)).isEqualTo(unionFind.find(1)); + assertThat(unionFind.getAllSubsets()).hasSize(1); + } + } + + @Test + public void testUnion_disjointPairs_produceDistinctSubsets() { + + for (UnionFind unionFind : + bothVariants(UnionType.UNION_BY_SIZE, new int[] {0, 1}, new int[] {2, 3})) { + + assertThat(unionFind.find(0)).isNotEqualTo(unionFind.find(2)); + assertThat(unionFind.getAllSubsets()).hasSize(2); + } + } + + @Test + public void testUnion_severalElementsToSameSubset() { + + for (UnionFind unionFind : + bothVariants( + UnionType.UNION_BY_SIZE, new int[] {0, 1}, new int[] {1, 2}, new int[] {2, 3})) { + + int canon = unionFind.find(0); + + assertThat(unionFind.find(1)).isEqualTo(canon); + assertThat(unionFind.find(2)).isEqualTo(canon); + assertThat(unionFind.find(3)).isEqualTo(canon); + assertThat(unionFind.getAllSubsets()).hasSize(1); + } + } + + @Test + public void testUnion_duplicateUnionCall_doesNotLeadToDuplicates() { + + for (UnionFind unionFind : + bothVariants( + UnionType.UNION_BY_SIZE, new int[] {0, 1}, new int[] {0, 1}, new int[] {1, 0})) { + + assertThat(unionFind.find(0)).isEqualTo(unionFind.find(1)); + assertThat(unionFind.getAllSubsets()).hasSize(1); + assertThat(unionFind.getAllSubsets().iterator().next()).hasSize(2); + } + } + + @Test + public void testUnion_mergesTwoExistingMultiElementSubsets() { + + for (UnionFind unionFind : + bothVariants( + UnionType.UNION_BY_SIZE, + new int[] {0, 1}, + new int[] {0, 2}, + new int[] {3, 4}, + new int[] {3, 5}, + new int[] {0, 3})) { + + assertThat(unionFind.getAllSubsets()).hasSize(1); + + int canon = unionFind.find(0); + + for (int i = 0; i <= 5; i++) { + assertThat(unionFind.find(i)).isEqualTo(canon); + } + } + } + + @Test + public void testUnion_constantCanonicalElementDuringNonLinearInsertion() { + + for (UnionFind unionFind : + bothVariants( + UnionType.UNION_BY_SIZE, + new int[] {3, 3}, + new int[] {3, 2}, + new int[] {3, 5}, + new int[] {3, 1}, + new int[] {3, 8}, + new int[] {3, 6}, + new int[] {3, 9}, + new int[] {3, 7}, + new int[] {3, 4}, + new int[] {3, 0})) { + + assertThat(unionFind.getAllSubsets()).hasSize(1); + + int canon = unionFind.find(3); + + for (int i = 0; i <= 9; i++) { + assertThat(unionFind.find(i)).isEqualTo(canon); + } + } + } + + @Test + public void testUnion_bothUnionTypes_produceSameGrouping() { + + int[][] unions = {{0, 1}, {0, 2}, {3, 4}, {3, 5}, {0, 3}}; + + for (UnionType unionType : UnionType.values()) { + for (UnionFind unionFind : bothVariants(unionType, unions)) { + + assertThat(unionFind.getAllSubsets()).hasSize(1); + + int canon = unionFind.find(0); + + for (int i = 0; i <= 5; i++) { + assertThat(unionFind.find(i)).isEqualTo(canon); + } + } + } + } + + @Test + public void testUnion_stringElements() { + + PersistentUnionFind unsortedStringUnionFind = + PersistentParentPointerTreeUnionFind.of(UnionType.UNION_BY_SIZE); + unsortedStringUnionFind = + unsortedStringUnionFind + .unionAndCopy("0", "1") + .unionAndCopy("0", "2") + .unionAndCopy("3", "4"); + PersistentSortedUnionFind sortedStringUnionFind = + PersistentSortedParentPointerTreeUnionFind.of(UnionType.UNION_BY_SIZE); + sortedStringUnionFind = + sortedStringUnionFind.unionAndCopy("0", "1").unionAndCopy("0", "2").unionAndCopy("3", "4"); + + assertThat(unsortedStringUnionFind.find("0")).isEqualTo(unsortedStringUnionFind.find("2")); + assertThat(unsortedStringUnionFind.find("0")).isNotEqualTo(unsortedStringUnionFind.find("3")); + assertThat(unsortedStringUnionFind.getAllSubsets()).hasSize(2); + + assertThat(sortedStringUnionFind.find("0")).isEqualTo(sortedStringUnionFind.find("2")); + assertThat(sortedStringUnionFind.find("0")).isNotEqualTo(sortedStringUnionFind.find("3")); + assertThat(sortedStringUnionFind.getAllSubsets()).hasSize(2); + } + + @Test + public void testGetAllSubsets_reflectsCorrectMembershipAfterMultipleUnions() { + + int[][] unions = { + {0, 0}, {0, 1}, {0, 2}, {0, 3}, {0, 4}, {5, 5}, {5, 6}, {5, 7}, {5, 8}, {5, 9}, + }; + + for (UnionFind unionFind : bothVariants(UnionType.UNION_BY_SIZE, unions)) { + + Collection> subsets = unionFind.getAllSubsets(); + + assertThat(subsets).hasSize(2); + + for (Set subset : subsets) { + assertThat(subset).hasSize(5); + } + } + } + + @Test + public void testGetAllSubsets_emptyUnionFind_isEmpty() { + + for (UnionFind unionFind : bothVariants(UnionType.UNION_BY_SIZE)) { + + assertThat(unionFind.getAllSubsets()).isEmpty(); + } + } + + @Test + public void testContains_elementInSubset_returnsTrue() { + + for (UnionFind unionFind : bothVariants(UnionType.UNION_BY_SIZE, simpleUnionArgs)) { + + assertThat(unionFind.contains(0)).isTrue(); + assertThat(unionFind.contains(1)).isTrue(); + } + } + + @Test + public void testContains_elementNotContained_returnsFalse() { + + for (UnionFind unionFind : bothVariants(UnionType.UNION_BY_SIZE, simpleUnionArgs)) { + + assertThat(unionFind.contains(10)).isFalse(); + } + } + + @Test + public void testContains_null_throwsNullPointerException() { + + for (UnionFind unionFind : bothVariants(UnionType.UNION_BY_SIZE, simpleUnionArgs)) { + + assertThrows(NullPointerException.class, () -> unionFind.contains(null)); + } + } + + @Test + public void testUnion_nullElement_throwsNullPointerException() { + + PersistentUnionFind unsortedUnionFind = + emptyPersistentUnionFind(UnionType.UNION_BY_SIZE); + PersistentUnionFind sortedUnionFind = + emptyPersistentUnionFind(UnionType.UNION_BY_SIZE); + + assertThrows(NullPointerException.class, () -> unsortedUnionFind.unionAndCopy(null, 1)); + assertThrows(NullPointerException.class, () -> sortedUnionFind.unionAndCopy(null, 1)); + } + + @Test + public void testUnion_doesNotMutateOriginalInstance() { + + PersistentUnionFind originalUnsorted = + emptyPersistentUnionFind(UnionType.UNION_BY_SIZE).unionAndCopy(0, 1); + PersistentUnionFind updatedUnsorted = originalUnsorted.unionAndCopy(2, 3); + + assertThat(originalUnsorted.contains(2)).isFalse(); + assertThat(originalUnsorted.getAllSubsets()).hasSize(1); + assertThat(updatedUnsorted.contains(2)).isTrue(); + assertThat(updatedUnsorted.getAllSubsets()).hasSize(2); + + PersistentSortedUnionFind originalSorted = + emptyPersistentSortedUnionFind(UnionType.UNION_BY_SIZE).unionAndCopy(0, 1); + PersistentSortedUnionFind updatedSorted = originalSorted.unionAndCopy(2, 3); + + assertThat(originalSorted.contains(2)).isFalse(); + assertThat(originalSorted.getAllSubsets()).hasSize(1); + assertThat(updatedSorted.contains(2)).isTrue(); + assertThat(updatedSorted.getAllSubsets()).hasSize(2); + } + + @Test + public void testUnion_eachVersionKeepsItsOwnSnapshot() { + + PersistentUnionFind version0 = emptyPersistentUnionFind(UnionType.UNION_BY_SIZE); + PersistentUnionFind version1 = version0.unionAndCopy(0, 1); + PersistentUnionFind version2 = version1.unionAndCopy(0, 2); + PersistentUnionFind version3 = version2.unionAndCopy(3, 4); + + assertThat(version0.getAllSubsets()).isEmpty(); + + assertThat(version1.getAllSubsets()).hasSize(1); + assertThat(version1.getAllSubsets().iterator().next()).containsExactly(0, 1); + + assertThat(version2.getAllSubsets()).hasSize(1); + assertThat(version2.getAllSubsets().iterator().next()).containsExactly(0, 1, 2); + + assertThat(version3.getAllSubsets()).hasSize(2); + assertThat(version3.contains(3)).isTrue(); + assertThat(version2.contains(3)).isFalse(); + } + + @Test + public void testUnion_selfUnionOnExistingElement_returnsSameInstance() { + + PersistentUnionFind unsortedUnionFind = + emptyPersistentUnionFind(UnionType.UNION_BY_SIZE).unionAndCopy(0, 1); + PersistentUnionFind sortedUnionFind = + emptyPersistentUnionFind(UnionType.UNION_BY_SIZE).unionAndCopy(0, 1); + + assertThat(unsortedUnionFind.unionAndCopy(0, 0)).isSameInstanceAs(unsortedUnionFind); + assertThat(sortedUnionFind.unionAndCopy(0, 0)).isSameInstanceAs(sortedUnionFind); + } + + @Test + public void testUnion_bothElementsAlreadyInSameSubset_returnsSameInstance() { + + PersistentUnionFind unsortedUnionFind = + emptyPersistentUnionFind(UnionType.UNION_BY_SIZE).unionAndCopy(0, 1).unionAndCopy(0, 2); + PersistentUnionFind sortedUnionFind = + emptyPersistentUnionFind(UnionType.UNION_BY_SIZE).unionAndCopy(0, 1).unionAndCopy(0, 2); + + assertThat(unsortedUnionFind.unionAndCopy(1, 2)).isSameInstanceAs(unsortedUnionFind); + assertThat(sortedUnionFind.unionAndCopy(1, 2)).isSameInstanceAs(sortedUnionFind); + } + + @Test + public void testOf_returnsIndependentEmptyInstance() { + + PersistentUnionFind firstUnsortedUnionFind = + emptyPersistentUnionFind(UnionType.UNION_BY_SIZE); + PersistentUnionFind secondUnsortedUnionFind = + emptyPersistentUnionFind(UnionType.UNION_BY_SIZE); + + firstUnsortedUnionFind = firstUnsortedUnionFind.unionAndCopy(0, 1); + + assertThat(firstUnsortedUnionFind.contains(0)).isTrue(); + assertThat(secondUnsortedUnionFind.contains(0)).isFalse(); + + PersistentUnionFind firstSortedUnionFind = + emptyPersistentUnionFind(UnionType.UNION_BY_SIZE); + PersistentUnionFind secondSortedUnionFind = + emptyPersistentUnionFind(UnionType.UNION_BY_SIZE); + + firstSortedUnionFind = firstSortedUnionFind.unionAndCopy(0, 1); + + assertThat(firstSortedUnionFind.contains(0)).isTrue(); + assertThat(secondSortedUnionFind.contains(0)).isFalse(); + } + + @Test + public void testMutableUnion_isUnsupported() { + + for (UnionFind unionFind : bothVariants(UnionType.UNION_BY_SIZE, simpleUnionArgs)) { + assertThrows(UnsupportedOperationException.class, () -> unionFind.union(0, 1)); + } + } + + @Test + public void testGetAllSubsets_mutatingReturnedCollections_doesNotAffectSubsequentCalls() { + + for (UnionFind unionFind : bothVariants(UnionType.UNION_BY_SIZE, simpleUnionArgs)) { + + Collection> firstCall = unionFind.getAllSubsets(); + firstCall.iterator().next().clear(); + + Collection> secondCall = unionFind.getAllSubsets(); + + assertThat(secondCall).hasSize(1); + assertThat(secondCall.iterator().next()).containsExactly(0, 1); + } + } +} From 06a31f3d1ca8beded360192bc696345542af73b0 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 17 Aug 2026 22:16:43 +0200 Subject: [PATCH 159/183] Code format fixes --- .../tests/PersistentParentPointerTreeUnionFindTest.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/tests/PersistentParentPointerTreeUnionFindTest.java b/src/org/sosy_lab/common/collect/union_find/tests/PersistentParentPointerTreeUnionFindTest.java index 2d1e4c944..7f1698122 100644 --- a/src/org/sosy_lab/common/collect/union_find/tests/PersistentParentPointerTreeUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/union_find/tests/PersistentParentPointerTreeUnionFindTest.java @@ -12,6 +12,7 @@ import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; +import com.google.errorprone.annotations.Var; import java.util.Collection; import java.util.Set; import org.junit.Test; @@ -37,7 +38,7 @@ private static PersistentSortedUnionFind emptyPersistentSortedUnionFind private static PersistentUnionFind applyUnions(UnionType pUnionType, int[]... pUnions) { - PersistentUnionFind unionFind = emptyPersistentUnionFind(pUnionType); + @Var PersistentUnionFind unionFind = emptyPersistentUnionFind(pUnionType); for (int[] pair : pUnions) { unionFind = unionFind.unionAndCopy(pair[0], pair[1]); @@ -49,7 +50,7 @@ private static PersistentUnionFind applyUnions(UnionType pUnionType, in private static PersistentSortedUnionFind applySortedUnions( UnionType pUnionType, int[]... pUnions) { - PersistentSortedUnionFind unionFind = emptyPersistentSortedUnionFind(pUnionType); + @Var PersistentSortedUnionFind unionFind = emptyPersistentSortedUnionFind(pUnionType); for (int[] pair : pUnions) { unionFind = unionFind.unionAndCopy(pair[0], pair[1]); @@ -215,6 +216,7 @@ public void testUnion_bothUnionTypes_produceSameGrouping() { @Test public void testUnion_stringElements() { + @Var PersistentUnionFind unsortedStringUnionFind = PersistentParentPointerTreeUnionFind.of(UnionType.UNION_BY_SIZE); unsortedStringUnionFind = @@ -222,6 +224,7 @@ public void testUnion_stringElements() { .unionAndCopy("0", "1") .unionAndCopy("0", "2") .unionAndCopy("3", "4"); + @Var PersistentSortedUnionFind sortedStringUnionFind = PersistentSortedParentPointerTreeUnionFind.of(UnionType.UNION_BY_SIZE); sortedStringUnionFind = @@ -374,6 +377,7 @@ public void testUnion_bothElementsAlreadyInSameSubset_returnsSameInstance() { @Test public void testOf_returnsIndependentEmptyInstance() { + @Var PersistentUnionFind firstUnsortedUnionFind = emptyPersistentUnionFind(UnionType.UNION_BY_SIZE); PersistentUnionFind secondUnsortedUnionFind = @@ -384,6 +388,7 @@ public void testOf_returnsIndependentEmptyInstance() { assertThat(firstUnsortedUnionFind.contains(0)).isTrue(); assertThat(secondUnsortedUnionFind.contains(0)).isFalse(); + @Var PersistentUnionFind firstSortedUnionFind = emptyPersistentUnionFind(UnionType.UNION_BY_SIZE); PersistentUnionFind secondSortedUnionFind = From 81b99c8d6b12c537931ec9dc2b15d0614e873a39 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 17 Aug 2026 22:39:39 +0200 Subject: [PATCH 160/183] Add sortedness tests for PersistentSortedParentPointerTreeUnionFind --- ...ntPointerTreeUnionFindSortednessTest.java} | 2 +- ...entPointerTreeUnionFindSortednessTest.java | 206 ++++++++++++++++++ 2 files changed, 207 insertions(+), 1 deletion(-) rename src/org/sosy_lab/common/collect/union_find/tests/{ImmutableParentPointerTreeSortednessTest.java => ImmutableParentPointerTreeUnionFindSortednessTest.java} (98%) create mode 100644 src/org/sosy_lab/common/collect/union_find/tests/PersistentParentPointerTreeUnionFindSortednessTest.java diff --git a/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeSortednessTest.java b/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindSortednessTest.java similarity index 98% rename from src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeSortednessTest.java rename to src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindSortednessTest.java index d1b349d77..3eefe2a2a 100644 --- a/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeSortednessTest.java +++ b/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindSortednessTest.java @@ -17,7 +17,7 @@ import org.sosy_lab.common.collect.union_find.ImmutableSortedParentPointerTreeUnionFind; import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; -public class ImmutableParentPointerTreeSortednessTest { +public class ImmutableParentPointerTreeUnionFindSortednessTest { private static ImmutableSortedParentPointerTreeUnionFind buildImmutableSorted( UnionType pUnionType, int[]... pUnions) { diff --git a/src/org/sosy_lab/common/collect/union_find/tests/PersistentParentPointerTreeUnionFindSortednessTest.java b/src/org/sosy_lab/common/collect/union_find/tests/PersistentParentPointerTreeUnionFindSortednessTest.java new file mode 100644 index 000000000..ca636d068 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/tests/PersistentParentPointerTreeUnionFindSortednessTest.java @@ -0,0 +1,206 @@ +// 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.tests; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.errorprone.annotations.Var; +import java.util.Collection; +import java.util.Iterator; +import java.util.NavigableSet; +import org.junit.Test; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; +import org.sosy_lab.common.collect.union_find.PersistentSortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.PersistentSortedUnionFind; + +public class PersistentParentPointerTreeUnionFindSortednessTest { + + private static PersistentSortedUnionFind emptyPersistentSortedUnionFind( + UnionType pUnionType) { + return PersistentSortedParentPointerTreeUnionFind.of(pUnionType); + } + + private static PersistentSortedUnionFind applySortedUnions( + UnionType pUnionType, int[]... pUnions) { + + @Var PersistentSortedUnionFind unionFind = emptyPersistentSortedUnionFind(pUnionType); + + for (int[] pair : pUnions) { + unionFind = unionFind.unionAndCopy(pair[0], pair[1]); + } + + return unionFind; + } + + private static > NavigableSet onlySubsetOf( + PersistentSortedUnionFind pSortedUnionFind) { + + Collection> subsets = pSortedUnionFind.getAllSubsets(); + + assertThat(subsets).hasSize(1); + + return subsets.iterator().next(); + } + + public void testGetAllSubsets_elementsAddedInAscendingOrder_remainSorted() { + + PersistentSortedUnionFind sortedUnionFind = + applySortedUnions( + UnionType.UNION_BY_SIZE, new int[] {0, 1}, new int[] {0, 2}, new int[] {0, 3}); + + assertThat(onlySubsetOf(sortedUnionFind)).containsExactly(0, 1, 2, 3).inOrder(); + } + + @Test + public void testGetAllSubsets_elementsAddedInDescendingOrder_areSortedAscending() { + + PersistentSortedUnionFind sortedUnionFind = + applySortedUnions( + UnionType.UNION_BY_SIZE, new int[] {3, 2}, new int[] {3, 1}, new int[] {3, 0}); + + assertThat(onlySubsetOf(sortedUnionFind)).containsExactly(0, 1, 2, 3).inOrder(); + } + + @Test + public void testGetAllSubsets_nonLinearInsertionOrder_areReturnedSorted() { + + PersistentSortedUnionFind sortedUnionFind = + applySortedUnions( + UnionType.UNION_BY_SIZE, + new int[] {3, 2}, + new int[] {3, 4}, + new int[] {3, 0}, + new int[] {3, 5}, + new int[] {3, 1}); + + assertThat(onlySubsetOf(sortedUnionFind)).containsExactly(0, 1, 2, 3, 4, 5).inOrder(); + } + + @Test + public void testGetAllSubsets_multipleSubsets_eachSortedIndependently() { + + PersistentSortedUnionFind sortedUnionFind = + applySortedUnions( + UnionType.UNION_BY_SIZE, + new int[] {0, 1}, + new int[] {0, 2}, + new int[] {10, 11}, + new int[] {10, 12}); + + for (NavigableSet subset : sortedUnionFind.getAllSubsets()) { + + assertThat(subset).isInOrder(); + } + } + + @Test + public void testGetAllSubsets_afterMergingTwoSubsets_resultIsSorted() { + + PersistentSortedUnionFind sortedUnionFind = + applySortedUnions( + UnionType.UNION_BY_SIZE, + new int[] {0, 1}, + new int[] {0, 2}, + new int[] {3, 4}, + new int[] {3, 5}, + new int[] {0, 3}); + + assertThat(onlySubsetOf(sortedUnionFind)).containsExactly(0, 1, 2, 3, 4, 5).inOrder(); + } + + @Test + public void testGetAllSubsets_stringElements_areSortedAlphabetically() { + + PersistentSortedUnionFind stringSortedUnionFind = + PersistentSortedParentPointerTreeUnionFind.of(UnionType.UNION_BY_SIZE); + String[] expected = {"-1", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; + + for (int i = 0; i <= 2; i++) { + stringSortedUnionFind = stringSortedUnionFind.unionAndCopy("0", Integer.toString(i)); + } + for (int i = 3; i <= 5; i++) { + stringSortedUnionFind = stringSortedUnionFind.unionAndCopy("3", Integer.toString(i)); + } + for (int i = 6; i <= 8; i++) { + stringSortedUnionFind = stringSortedUnionFind.unionAndCopy("6", Integer.toString(i)); + } + + stringSortedUnionFind = stringSortedUnionFind.unionAndCopy("9", "9"); + stringSortedUnionFind = stringSortedUnionFind.unionAndCopy("0", "6"); + stringSortedUnionFind = stringSortedUnionFind.unionAndCopy("6", "-1"); + stringSortedUnionFind = stringSortedUnionFind.unionAndCopy("1", "4"); + stringSortedUnionFind = stringSortedUnionFind.unionAndCopy("0", "9"); + + assertThat(onlySubsetOf(stringSortedUnionFind)).containsExactlyElementsIn(expected).inOrder(); + } + + @Test + public void testGetAllSubsets_isSortedRegardlessOfUnionType() { + + int[][] unions = {{3, 2}, {3, 4}, {3, 0}, {3, 5}, {3, 1}}; + + for (UnionType unionType : UnionType.values()) { + + PersistentSortedUnionFind sortedUnionFind = applySortedUnions(unionType, unions); + + assertThat(onlySubsetOf(sortedUnionFind)).containsExactly(0, 1, 2, 3, 4, 5).inOrder(); + } + } + + @Test + public void testGetAllSubsets_subsetsThemselvesAreOrderedByCanonicalElement() { + + PersistentSortedUnionFind sortedUnionFind = + applySortedUnions( + UnionType.UNION_BY_SIZE, + new int[] {10, 11}, + new int[] {10, 12}, + new int[] {0, 1}, + new int[] {0, 2}); + + Collection> subsets = sortedUnionFind.getAllSubsets(); + assertThat(subsets).hasSize(2); + + Iterator> iterator = subsets.iterator(); + NavigableSet firstSubset = iterator.next(); + NavigableSet secondSubset = iterator.next(); + + assertThat(firstSubset.last()).isLessThan(secondSubset.first()); + } + + @Test + public void testGetAllSubsets_returnedSetSupportsNavigableSetOperations() { + + PersistentSortedUnionFind sortedUnionFind = + applySortedUnions( + UnionType.UNION_BY_SIZE, + new int[] {0, 1}, + new int[] {0, 2}, + new int[] {0, 3}, + new int[] {0, 4}); + + NavigableSet subset = onlySubsetOf(sortedUnionFind); + + assertThat(subset.first()).isEqualTo(0); + assertThat(subset.last()).isEqualTo(4); + assertThat(subset.higher(1)).isEqualTo(2); + assertThat(subset.lower(3)).isEqualTo(2); + } + + @Test + public void testGetAllSubsets_earlierVersionRemainsSortedAfterLaterUnions() { + + PersistentSortedUnionFind version0 = + applySortedUnions(UnionType.UNION_BY_SIZE, new int[] {3, 1}); + PersistentSortedUnionFind version1 = version0.unionAndCopy(3, 2); + + assertThat(onlySubsetOf(version0)).containsExactly(1, 3).inOrder(); + assertThat(onlySubsetOf(version1)).containsExactly(1, 2, 3).inOrder(); + } +} From 33fb7984f87ae4a182cec286e54cbf35db2da99d Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 17 Aug 2026 22:42:24 +0200 Subject: [PATCH 161/183] Make PersistentParentPointerTreeUnionFind a final class --- .../union_find/PersistentParentPointerTreeUnionFind.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java index 9f3cd016c..93bdb750c 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java @@ -29,7 +29,7 @@ * * @param The type of elements added to the Union-Find. */ -public class PersistentParentPointerTreeUnionFind extends AbstractImmutableUnionFind +public final class PersistentParentPointerTreeUnionFind extends AbstractImmutableUnionFind implements PersistentUnionFind { private final Map mapOfNodesToParents; From 225cc24a0e09b23460429abc008f1a7c3ef8528f Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 17 Aug 2026 22:50:02 +0200 Subject: [PATCH 162/183] Small bug fixes --- ...PersistentParentPointerTreeUnionFindSortednessTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/tests/PersistentParentPointerTreeUnionFindSortednessTest.java b/src/org/sosy_lab/common/collect/union_find/tests/PersistentParentPointerTreeUnionFindSortednessTest.java index ca636d068..18688b3ed 100644 --- a/src/org/sosy_lab/common/collect/union_find/tests/PersistentParentPointerTreeUnionFindSortednessTest.java +++ b/src/org/sosy_lab/common/collect/union_find/tests/PersistentParentPointerTreeUnionFindSortednessTest.java @@ -48,6 +48,7 @@ private static > NavigableSet onlySubsetOf( return subsets.iterator().next(); } + @Test public void testGetAllSubsets_elementsAddedInAscendingOrder_remainSorted() { PersistentSortedUnionFind sortedUnionFind = @@ -117,6 +118,7 @@ public void testGetAllSubsets_afterMergingTwoSubsets_resultIsSorted() { @Test public void testGetAllSubsets_stringElements_areSortedAlphabetically() { + @Var PersistentSortedUnionFind stringSortedUnionFind = PersistentSortedParentPointerTreeUnionFind.of(UnionType.UNION_BY_SIZE); String[] expected = {"-1", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; @@ -196,8 +198,9 @@ public void testGetAllSubsets_returnedSetSupportsNavigableSetOperations() { @Test public void testGetAllSubsets_earlierVersionRemainsSortedAfterLaterUnions() { - PersistentSortedUnionFind version0 = - applySortedUnions(UnionType.UNION_BY_SIZE, new int[] {3, 1}); + int[] union = new int[] {3, 1}; + + PersistentSortedUnionFind version0 = applySortedUnions(UnionType.UNION_BY_SIZE, union); PersistentSortedUnionFind version1 = version0.unionAndCopy(3, 2); assertThat(onlySubsetOf(version0)).containsExactly(1, 3).inOrder(); From fc834a667de411b3e0ab188fc76ef36fff65ea4f Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 18 Aug 2026 16:01:32 +0200 Subject: [PATCH 163/183] Add add() and addAll() for adding either one or more sets at a time to ParentPointerTreeUnionFind --- .../ParentPointerTreeUnionFind.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) 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 f61b5f602..add039978 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -163,6 +163,46 @@ public boolean contains(T pE) { return allNodes.containsKey(pE); } + /** + * Adds the contents of a set of elements to the Union-Find. A random element in the set (the + * first one accessed) will be used as the canonical element. If the set contains elements already + * found elsewhere in the Union-Find, these sets will be merged accordingly. + * + * @param pSet set to be added to the Union-Find + */ + public void add(Set pSet) { + + Preconditions.checkNotNull(pSet); + + @Var T canon = null; + + for (T current : pSet) { + + if (canon == null) { + canon = current; + addElementAsNewSet(canon); + } + + addElementToExistingSet(current, canon); + } + } + + /** + * Adds multiple sets of elements to the Union-Find. For each set, a random element in the set + * (the first one accessed) will be used as the canonical element. If a set contains elements + * already found elsewhere in the Union-Find, these sets will be merged accordingly. + * + * @param pSets sets to be added to the Union-Find + */ + public void addAll(Collection> pSets) { + + Preconditions.checkNotNull(pSets); + + for (Set set : pSets) { + add(set); + } + } + private void addElementAsNewSet(T pE) { if (!contains(pE)) { From 8795910aed73e1382711f7ac3dbaebd12784aee7 Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 18 Aug 2026 16:28:51 +0200 Subject: [PATCH 164/183] Add add() and addAll() to builder of ImmutableParentPointerTreeUnionFind --- .../ImmutableParentPointerTreeUnionFind.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java index f8070aa90..d8dcd1d9e 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java @@ -111,7 +111,9 @@ public boolean contains(T pE) { /** * Builder class which first collects the data in a mutable Union-Find and converts it to an - * immutable Union-Find when {@code build()} is called. + * immutable Union-Find when {@code build()} is called. See documentation in {@link + * ParentPointerTreeUnionFind} for explanations on {@code union()}, {@code add()} and {@code + * addAll()} as the methods in this builder simply pass to their aforementioned namesakes. * * @param type of elements added to the Union-Find */ @@ -135,6 +137,22 @@ public Builder union(T pE1, T pE2) { return this; } + @CanIgnoreReturnValue + public Builder add(Set pSet) { + + unionFind.add(pSet); + + return this; + } + + @CanIgnoreReturnValue + public Builder addAll(Collection> pSets) { + + unionFind.addAll(pSets); + + return this; + } + public ImmutableParentPointerTreeUnionFind build() { return new ImmutableParentPointerTreeUnionFind<>(ImmutableMap.copyOf(unionFind.allNodes)); } From a3f499afd03242e875ca16b0be299244e655a957 Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 18 Aug 2026 16:41:00 +0200 Subject: [PATCH 165/183] Add add() and addAll() to builder of ImmutableSortedParentPointerTreeUnionFind --- ...tableSortedParentPointerTreeUnionFind.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java index 76737bd20..fa551b882 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java @@ -15,6 +15,7 @@ import java.util.Collection; import java.util.NavigableMap; import java.util.NavigableSet; +import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; @@ -116,7 +117,9 @@ public boolean contains(T pE) { /** * Builder class which first collects the data in a mutable sorted Union-Find and converts it to - * an immutable Union-Find when {@code build()} is called. + * an immutable Union-Find when {@code build()} is called. See documentation in {@link + * ParentPointerTreeUnionFind} for explanations on {@code union()}, {@code add()} and {@code + * addAll()} as the methods in this builder simply pass to their aforementioned namesakes. * * @param type of elements added to the Union-Find */ @@ -133,13 +136,29 @@ public static > Builder getBuilder(UnionType pUnionTy } @CanIgnoreReturnValue - public ImmutableSortedParentPointerTreeUnionFind.Builder union(T pE1, T pE2) { + public Builder union(T pE1, T pE2) { unionFind.union(pE1, pE2); return this; } + @CanIgnoreReturnValue + public Builder add(Set pSet) { + + unionFind.add(pSet); + + return this; + } + + @CanIgnoreReturnValue + public Builder addAll(Collection> pSets) { + + unionFind.addAll(pSets); + + return this; + } + public ImmutableSortedParentPointerTreeUnionFind build() { return new ImmutableSortedParentPointerTreeUnionFind<>( ImmutableMap.copyOf(unionFind.allNodes)); From 85fdfbbf844db09f2569fa4ee574ff59ee2af1d3 Mon Sep 17 00:00:00 2001 From: Colleen Date: Tue, 18 Aug 2026 17:21:38 +0200 Subject: [PATCH 166/183] Add AbstractImmutableParentPointerTreeBuilder to avoid code duplication --- ...ractImmutableParentPointerTreeBuilder.java | 58 +++++++++++++++++++ .../ImmutableParentPointerTreeUnionFind.java | 35 ++--------- ...tableSortedParentPointerTreeUnionFind.java | 34 ++--------- ...mutableParentPointerTreeUnionFindTest.java | 42 +++++++------- 4 files changed, 89 insertions(+), 80 deletions(-) create mode 100644 src/org/sosy_lab/common/collect/union_find/AbstractImmutableParentPointerTreeBuilder.java diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableParentPointerTreeBuilder.java b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableParentPointerTreeBuilder.java new file mode 100644 index 000000000..f5f9e598c --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableParentPointerTreeBuilder.java @@ -0,0 +1,58 @@ +// 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 com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.util.Collection; +import java.util.Set; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; + +/** + * Abstract builder class which first collects the data in a mutable Union-Find and converts it to + * an immutable Union-Find when {@code build()} is called. See documentation in {@link + * ParentPointerTreeUnionFind} for explanations on {@code union()}, {@code add()} and {@code + * addAll()} as the methods in this builder simply pass to their aforementioned namesakes. + * + * @param type of elements added to the Union-Find + */ +public abstract class AbstractImmutableParentPointerTreeBuilder { + + ParentPointerTreeUnionFind unionFind; + + protected AbstractImmutableParentPointerTreeBuilder(UnionType pUnionType) { + unionFind = new ParentPointerTreeUnionFind<>(pUnionType); + } + + @CanIgnoreReturnValue + public AbstractImmutableParentPointerTreeBuilder union(T pE1, T pE2) { + + unionFind.union(pE1, pE2); + + return this; + } + + @CanIgnoreReturnValue + public AbstractImmutableParentPointerTreeBuilder add(Set pSet) { + + unionFind.add(pSet); + + return this; + } + + @CanIgnoreReturnValue + public AbstractImmutableParentPointerTreeBuilder addAll(Collection> pSets) { + + unionFind.addAll(pSets); + + return this; + } + + // get map from mutable Union-Find instance and convert to immutable map, then pass to constructor + public abstract AbstractImmutableUnionFind build(); +} diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java index d8dcd1d9e..8e54b0fe7 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java @@ -10,7 +10,6 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; -import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.Var; import java.util.Collection; import java.util.HashMap; @@ -117,42 +116,18 @@ public boolean contains(T pE) { * * @param type of elements added to the Union-Find */ - public static final class Builder { - - ParentPointerTreeUnionFind unionFind; + public static final class Builder extends AbstractImmutableParentPointerTreeBuilder { private Builder(UnionType pUnionType) { - unionFind = new ParentPointerTreeUnionFind<>(pUnionType); + super(pUnionType); } - public static Builder getBuilder(UnionType pUnionType) { + public static AbstractImmutableParentPointerTreeBuilder getBuilder( + UnionType pUnionType) { return new Builder<>(pUnionType); } - @CanIgnoreReturnValue - public Builder union(T pE1, T pE2) { - - unionFind.union(pE1, pE2); - - return this; - } - - @CanIgnoreReturnValue - public Builder add(Set pSet) { - - unionFind.add(pSet); - - return this; - } - - @CanIgnoreReturnValue - public Builder addAll(Collection> pSets) { - - unionFind.addAll(pSets); - - return this; - } - + @Override public ImmutableParentPointerTreeUnionFind build() { return new ImmutableParentPointerTreeUnionFind<>(ImmutableMap.copyOf(unionFind.allNodes)); } diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java index fa551b882..f344691e4 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java @@ -10,12 +10,10 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; -import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.Var; import java.util.Collection; import java.util.NavigableMap; import java.util.NavigableSet; -import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; @@ -123,42 +121,18 @@ public boolean contains(T pE) { * * @param type of elements added to the Union-Find */ - public static final class Builder> { - - SortedParentPointerTreeUnionFind unionFind; + public static final class Builder> + extends AbstractImmutableParentPointerTreeBuilder { private Builder(UnionType pUnionType) { - unionFind = new SortedParentPointerTreeUnionFind<>(pUnionType); + super(pUnionType); } public static > Builder getBuilder(UnionType pUnionType) { return new Builder<>(pUnionType); } - @CanIgnoreReturnValue - public Builder union(T pE1, T pE2) { - - unionFind.union(pE1, pE2); - - return this; - } - - @CanIgnoreReturnValue - public Builder add(Set pSet) { - - unionFind.add(pSet); - - return this; - } - - @CanIgnoreReturnValue - public Builder addAll(Collection> pSets) { - - unionFind.addAll(pSets); - - return this; - } - + @Override public ImmutableSortedParentPointerTreeUnionFind build() { return new ImmutableSortedParentPointerTreeUnionFind<>( ImmutableMap.copyOf(unionFind.allNodes)); diff --git a/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindTest.java b/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindTest.java index e3ca0084a..d3873a0e7 100644 --- a/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindTest.java @@ -15,6 +15,8 @@ import java.util.Collection; import java.util.Set; import org.junit.Test; +import org.sosy_lab.common.collect.union_find.AbstractImmutableParentPointerTreeBuilder; +import org.sosy_lab.common.collect.union_find.AbstractImmutableUnionFind; import org.sosy_lab.common.collect.union_find.ImmutableParentPointerTreeUnionFind; import org.sosy_lab.common.collect.union_find.ImmutableSortedParentPointerTreeUnionFind; import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; @@ -24,10 +26,10 @@ public class ImmutableParentPointerTreeUnionFindTest { private final int[] simpleUnionArgs = new int[] {0, 1}; - private static ImmutableParentPointerTreeUnionFind buildImmutable( + private static AbstractImmutableUnionFind buildImmutable( UnionType pUnionType, int[]... pUnions) { - ImmutableParentPointerTreeUnionFind.Builder builder = + AbstractImmutableParentPointerTreeBuilder builder = ImmutableParentPointerTreeUnionFind.Builder.getBuilder(pUnionType); for (int[] pair : pUnions) { @@ -211,14 +213,14 @@ public void testUnion_bothUnionTypes_produceSameGrouping() { @Test public void testUnion_stringElements() { - ImmutableParentPointerTreeUnionFind.Builder unsortedBuilder = + AbstractImmutableParentPointerTreeBuilder unsortedBuilder = ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); - ImmutableSortedParentPointerTreeUnionFind.Builder sortedBuilder = + AbstractImmutableParentPointerTreeBuilder sortedBuilder = ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); - ImmutableParentPointerTreeUnionFind unsortedStringUnionFind = + AbstractImmutableUnionFind unsortedStringUnionFind = unsortedBuilder.union("0", "1").union("0", "2").union("3", "4").build(); - ImmutableSortedParentPointerTreeUnionFind sortedStringUnionFind = + AbstractImmutableUnionFind sortedStringUnionFind = sortedBuilder.union("0", "1").union("0", "2").union("3", "4").build(); assertThat(unsortedStringUnionFind.find("0")).isEqualTo(unsortedStringUnionFind.find("2")); @@ -292,9 +294,9 @@ public void testContains_null_returnsFalse() { @Test public void testBuilder_union_nullElement_throwsNullPointerException() { - ImmutableParentPointerTreeUnionFind.Builder unsortedBuilder = + AbstractImmutableParentPointerTreeBuilder unsortedBuilder = ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); - ImmutableSortedParentPointerTreeUnionFind.Builder sortedBuilder = + AbstractImmutableParentPointerTreeBuilder sortedBuilder = ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); assertThrows(NullPointerException.class, () -> unsortedBuilder.union(null, 1)); @@ -304,9 +306,9 @@ public void testBuilder_union_nullElement_throwsNullPointerException() { @Test public void testBuilder_union_returnsSameBuilderInstance() { - ImmutableParentPointerTreeUnionFind.Builder unsortedBuilder = + AbstractImmutableParentPointerTreeBuilder unsortedBuilder = ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); - ImmutableSortedParentPointerTreeUnionFind.Builder sortedBuilder = + AbstractImmutableParentPointerTreeBuilder sortedBuilder = ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); assertThat(unsortedBuilder.union(0, 1)).isSameInstanceAs(unsortedBuilder); @@ -316,14 +318,14 @@ public void testBuilder_union_returnsSameBuilderInstance() { @Test public void testBuilder_getBuilder_returnsIndependentBuilders() { - ImmutableParentPointerTreeUnionFind.Builder unsortedBuilder1 = + AbstractImmutableParentPointerTreeBuilder unsortedBuilder1 = ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); - ImmutableParentPointerTreeUnionFind.Builder unsortedBuilder2 = + AbstractImmutableParentPointerTreeBuilder unsortedBuilder2 = ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); - ImmutableSortedParentPointerTreeUnionFind.Builder sortedBuilder1 = + AbstractImmutableParentPointerTreeBuilder sortedBuilder1 = ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); - ImmutableSortedParentPointerTreeUnionFind.Builder sortedBuilder2 = + AbstractImmutableParentPointerTreeBuilder sortedBuilder2 = ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); unsortedBuilder1.union(0, 1); @@ -339,22 +341,22 @@ public void testBuilder_getBuilder_returnsIndependentBuilders() { @Test public void testBuilder_build_laterMutationsDoNotAffectPreviousResult() { - ImmutableParentPointerTreeUnionFind.Builder unsortedBuilder = + AbstractImmutableParentPointerTreeBuilder unsortedBuilder = ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); - ImmutableSortedParentPointerTreeUnionFind.Builder sortedBuilder = + AbstractImmutableParentPointerTreeBuilder sortedBuilder = ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE); unsortedBuilder.union(0, 1); sortedBuilder.union(0, 1); - ImmutableParentPointerTreeUnionFind firstUnsortedResult = unsortedBuilder.build(); - ImmutableSortedParentPointerTreeUnionFind firstSortedResult = sortedBuilder.build(); + AbstractImmutableUnionFind firstUnsortedResult = unsortedBuilder.build(); + AbstractImmutableUnionFind firstSortedResult = sortedBuilder.build(); unsortedBuilder.union(2, 3); sortedBuilder.union(2, 3); - ImmutableParentPointerTreeUnionFind secondUnsortedResult = unsortedBuilder.build(); - ImmutableSortedParentPointerTreeUnionFind secondSortedResult = sortedBuilder.build(); + AbstractImmutableUnionFind secondUnsortedResult = unsortedBuilder.build(); + AbstractImmutableUnionFind secondSortedResult = sortedBuilder.build(); assertThat(firstUnsortedResult.contains(2)).isFalse(); assertThat(firstUnsortedResult.getAllSubsets()).hasSize(1); From 4ee5fcf2c91ce37ef63c1b7c28863b675ec8d05d Mon Sep 17 00:00:00 2001 From: Colleen Date: Wed, 19 Aug 2026 16:03:26 +0200 Subject: [PATCH 167/183] Add immutable annotations where necessary --- .../AbstractImmutableParentPointerTreeBuilder.java | 4 +++- .../union_find/AbstractImmutableSortedUnionFind.java | 3 +++ .../collect/union_find/AbstractImmutableUnionFind.java | 7 +++---- .../union_find/ImmutableParentPointerTreeUnionFind.java | 2 ++ .../ImmutableSortedParentPointerTreeUnionFind.java | 2 ++ .../union_find/PersistentParentPointerTreeUnionFind.java | 2 ++ .../PersistentSortedParentPointerTreeUnionFind.java | 2 ++ .../collect/union_find/PersistentSortedUnionFind.java | 2 ++ .../common/collect/union_find/PersistentUnionFind.java | 2 ++ 9 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableParentPointerTreeBuilder.java b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableParentPointerTreeBuilder.java index f5f9e598c..9d2969a56 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableParentPointerTreeBuilder.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableParentPointerTreeBuilder.java @@ -9,6 +9,7 @@ package org.sosy_lab.common.collect.union_find; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.Immutable; import java.util.Collection; import java.util.Set; import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; @@ -21,9 +22,10 @@ * * @param type of elements added to the Union-Find */ +@Immutable(containerOf = "T") public abstract class AbstractImmutableParentPointerTreeBuilder { - ParentPointerTreeUnionFind unionFind; + final ParentPointerTreeUnionFind unionFind; protected AbstractImmutableParentPointerTreeBuilder(UnionType pUnionType) { unionFind = new ParentPointerTreeUnionFind<>(pUnionType); diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java index 8a15075c5..dab872d08 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableSortedUnionFind.java @@ -8,10 +8,13 @@ package org.sosy_lab.common.collect.union_find; +import com.google.errorprone.annotations.Immutable; + /** * An abstract class for sorted immutable Union-Find implementations. * * @param type of elements added to the Union-Find. Must be comparable. */ +@Immutable(containerOf = "T") public abstract class AbstractImmutableSortedUnionFind> extends AbstractImmutableUnionFind implements SortedUnionFind {} diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java index d1c1f48e9..940b1903c 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableUnionFind.java @@ -9,17 +9,16 @@ package org.sosy_lab.common.collect.union_find; import com.google.errorprone.annotations.DoNotCall; +import com.google.errorprone.annotations.Immutable; /** * An abstract class for immutable Union-Find implementations. * * @param type of elements added to the Union-Find */ +@Immutable(containerOf = "T") public abstract class AbstractImmutableUnionFind implements UnionFind { - /** - * @throws UnsupportedOperationException Always. - * @deprecated Unsupported operation. - */ + @Deprecated @Override @DoNotCall diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java index 8e54b0fe7..b6bae9b49 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java @@ -10,6 +10,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; +import com.google.errorprone.annotations.Immutable; import com.google.errorprone.annotations.Var; import java.util.Collection; import java.util.HashMap; @@ -28,6 +29,7 @@ * * @param type of elements added to the Union-Find. */ +@Immutable(containerOf = "T") public class ImmutableParentPointerTreeUnionFind extends AbstractImmutableUnionFind { private final ImmutableMap> allNodes; diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java index f344691e4..121d6a95b 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java @@ -10,6 +10,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; +import com.google.errorprone.annotations.Immutable; import com.google.errorprone.annotations.Var; import java.util.Collection; import java.util.NavigableMap; @@ -29,6 +30,7 @@ * * @param type of elements added to the Union-Find. Must be comparable. */ +@Immutable(containerOf = "T") public class ImmutableSortedParentPointerTreeUnionFind> extends AbstractImmutableSortedUnionFind { diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java index 93bdb750c..c99c5972c 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java @@ -10,6 +10,7 @@ import com.google.common.base.Preconditions; import com.google.errorprone.annotations.CheckReturnValue; +import com.google.errorprone.annotations.Immutable; import com.google.errorprone.annotations.Var; import java.util.Collection; import java.util.HashMap; @@ -29,6 +30,7 @@ * * @param The type of elements added to the Union-Find. */ +@Immutable(containerOf = "T") public final class PersistentParentPointerTreeUnionFind extends AbstractImmutableUnionFind implements PersistentUnionFind { diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java index 2348c0d32..e22051447 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentSortedParentPointerTreeUnionFind.java @@ -10,6 +10,7 @@ import com.google.common.base.Preconditions; import com.google.errorprone.annotations.CheckReturnValue; +import com.google.errorprone.annotations.Immutable; import com.google.errorprone.annotations.Var; import java.util.Collection; import java.util.NavigableMap; @@ -32,6 +33,7 @@ * * @param The type of elements added to the Union-Find. Must be comparable. */ +@Immutable(containerOf = "T") public final class PersistentSortedParentPointerTreeUnionFind> extends AbstractImmutableSortedUnionFind implements PersistentSortedUnionFind { diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java index e6639fe09..03dd442f9 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java @@ -10,6 +10,7 @@ import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.DoNotCall; +import com.google.errorprone.annotations.Immutable; /** * Interface for a persistent and sorted union-find. A persistent data structure is immutable, but @@ -21,6 +22,7 @@ * * @param The type of values. */ +@Immutable(containerOf = "T") public interface PersistentSortedUnionFind> extends SortedUnionFind { /** diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java index 4c94271da..4d3d03abb 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentUnionFind.java @@ -10,6 +10,7 @@ import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.DoNotCall; +import com.google.errorprone.annotations.Immutable; /** * Interface for a persistent union-find. A persistent data structure is immutable, but provides @@ -21,6 +22,7 @@ * * @param The type of values. */ +@Immutable(containerOf = "T") public interface PersistentUnionFind extends UnionFind { /** From 36d5aaab47133d7a4ca1b89b10685a5149ab3500 Mon Sep 17 00:00:00 2001 From: Colleen Date: Wed, 19 Aug 2026 16:13:46 +0200 Subject: [PATCH 168/183] Add immutable tree nodes --- .../union_find/AbstractImmutableTreeNode.java | 41 ++++++++++++++++++ .../union_find/ImmutableNonRootNode.java | 25 +++++++++++ .../collect/union_find/ImmutableRootNode.java | 42 +++++++++++++++++++ .../common/collect/union_find/RootNode.java | 4 +- 4 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 src/org/sosy_lab/common/collect/union_find/AbstractImmutableTreeNode.java create mode 100644 src/org/sosy_lab/common/collect/union_find/ImmutableNonRootNode.java create mode 100644 src/org/sosy_lab/common/collect/union_find/ImmutableRootNode.java diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableTreeNode.java b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableTreeNode.java new file mode 100644 index 000000000..7a75d11b6 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableTreeNode.java @@ -0,0 +1,41 @@ +// 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 com.google.errorprone.annotations.DoNotCall; +import com.google.errorprone.annotations.Immutable; + +/** + * An abstract class of immutable nodes from which a simple immutable parent pointer tree can be + * built. + * + * @param type of elements each node holds as value + */ +@Immutable(containerOf = "T") +public class AbstractImmutableTreeNode extends AbstractTreeNode { + + protected AbstractImmutableTreeNode(T pValue) { + super(pValue); + } + + protected AbstractImmutableTreeNode(AbstractTreeNode pParent, T pValue) { + super(pParent, pValue); + } + + /** + * @throws UnsupportedOperationException Always. + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + @DoNotCall + public void setParent(AbstractTreeNode pParent) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableNonRootNode.java b/src/org/sosy_lab/common/collect/union_find/ImmutableNonRootNode.java new file mode 100644 index 000000000..f57f4f053 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableNonRootNode.java @@ -0,0 +1,25 @@ +// This file is part of SoSy-Lab Common, +// a library of useful utilities: +// https://github.com/sosy-lab/java-common-lib +// +// SPDX-FileCopyrightText: 2026 Dirk Beyer +// +// SPDX-License-Identifier: Apache-2.0 + +package org.sosy_lab.common.collect.union_find; + +import com.google.errorprone.annotations.Immutable; + +/** + * An implementation of {@link AbstractImmutableTreeNode} resulting in immutable nodes that can only + * be used as non-root nodes but not as root nodes. + * + * @param type of elements each node holds as value + */ +@Immutable(containerOf = "T") +public final class ImmutableNonRootNode extends AbstractImmutableTreeNode { + + public ImmutableNonRootNode(AbstractImmutableTreeNode pParent, T pValue) { + super(pParent, pValue); + } +} diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableRootNode.java b/src/org/sosy_lab/common/collect/union_find/ImmutableRootNode.java new file mode 100644 index 000000000..ae28ea599 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableRootNode.java @@ -0,0 +1,42 @@ +// 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 com.google.errorprone.annotations.Immutable; + +/** + * An implementation of {@link AbstractImmutableTreeNode} resulting in immutable nodes that can be + * used as non-root nodes or root nodes. Their primary intended use is as root nodes. Rank describes + * the maximum height of the tree of this node and its child nodes (i.e. without path compression). + * Size describes the total number of elements in the tree represented by this root node. + * + * @param type of elements each node holds as value + */ +@Immutable(containerOf = "T") +public class ImmutableRootNode extends AbstractImmutableTreeNode { + + private final int rank; + private final int size; + + protected ImmutableRootNode(T pValue, Integer pRank, Integer pSize) { + + super(pValue); + + rank = pRank; + size = pSize; + } + + public int getRank() { + return rank; + } + + public int getSize() { + return size; + } +} diff --git a/src/org/sosy_lab/common/collect/union_find/RootNode.java b/src/org/sosy_lab/common/collect/union_find/RootNode.java index 0860d3944..989177118 100644 --- a/src/org/sosy_lab/common/collect/union_find/RootNode.java +++ b/src/org/sosy_lab/common/collect/union_find/RootNode.java @@ -33,8 +33,8 @@ public RootNode(T pValue) { super(pValue); - this.rank = 0; - this.size = 1; + rank = 0; + size = 1; } public int getRank() { From aa10c26d847cf0900064b6a7cfdd140ae145de5b Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 20 Aug 2026 15:57:50 +0200 Subject: [PATCH 169/183] Revert "Add immutable tree nodes" This reverts commit 36d5aaab47133d7a4ca1b89b10685a5149ab3500. --- .../union_find/AbstractImmutableTreeNode.java | 41 ------------------ .../union_find/ImmutableNonRootNode.java | 25 ----------- .../collect/union_find/ImmutableRootNode.java | 42 ------------------- .../common/collect/union_find/RootNode.java | 4 +- 4 files changed, 2 insertions(+), 110 deletions(-) delete mode 100644 src/org/sosy_lab/common/collect/union_find/AbstractImmutableTreeNode.java delete mode 100644 src/org/sosy_lab/common/collect/union_find/ImmutableNonRootNode.java delete mode 100644 src/org/sosy_lab/common/collect/union_find/ImmutableRootNode.java diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableTreeNode.java b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableTreeNode.java deleted file mode 100644 index 7a75d11b6..000000000 --- a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableTreeNode.java +++ /dev/null @@ -1,41 +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.union_find; - -import com.google.errorprone.annotations.DoNotCall; -import com.google.errorprone.annotations.Immutable; - -/** - * An abstract class of immutable nodes from which a simple immutable parent pointer tree can be - * built. - * - * @param type of elements each node holds as value - */ -@Immutable(containerOf = "T") -public class AbstractImmutableTreeNode extends AbstractTreeNode { - - protected AbstractImmutableTreeNode(T pValue) { - super(pValue); - } - - protected AbstractImmutableTreeNode(AbstractTreeNode pParent, T pValue) { - super(pParent, pValue); - } - - /** - * @throws UnsupportedOperationException Always. - * @deprecated Unsupported operation. - */ - @Deprecated - @Override - @DoNotCall - public void setParent(AbstractTreeNode pParent) { - throw new UnsupportedOperationException(); - } -} diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableNonRootNode.java b/src/org/sosy_lab/common/collect/union_find/ImmutableNonRootNode.java deleted file mode 100644 index f57f4f053..000000000 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableNonRootNode.java +++ /dev/null @@ -1,25 +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.union_find; - -import com.google.errorprone.annotations.Immutable; - -/** - * An implementation of {@link AbstractImmutableTreeNode} resulting in immutable nodes that can only - * be used as non-root nodes but not as root nodes. - * - * @param type of elements each node holds as value - */ -@Immutable(containerOf = "T") -public final class ImmutableNonRootNode extends AbstractImmutableTreeNode { - - public ImmutableNonRootNode(AbstractImmutableTreeNode pParent, T pValue) { - super(pParent, pValue); - } -} diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableRootNode.java b/src/org/sosy_lab/common/collect/union_find/ImmutableRootNode.java deleted file mode 100644 index ae28ea599..000000000 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableRootNode.java +++ /dev/null @@ -1,42 +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.union_find; - -import com.google.errorprone.annotations.Immutable; - -/** - * An implementation of {@link AbstractImmutableTreeNode} resulting in immutable nodes that can be - * used as non-root nodes or root nodes. Their primary intended use is as root nodes. Rank describes - * the maximum height of the tree of this node and its child nodes (i.e. without path compression). - * Size describes the total number of elements in the tree represented by this root node. - * - * @param type of elements each node holds as value - */ -@Immutable(containerOf = "T") -public class ImmutableRootNode extends AbstractImmutableTreeNode { - - private final int rank; - private final int size; - - protected ImmutableRootNode(T pValue, Integer pRank, Integer pSize) { - - super(pValue); - - rank = pRank; - size = pSize; - } - - public int getRank() { - return rank; - } - - public int getSize() { - return size; - } -} diff --git a/src/org/sosy_lab/common/collect/union_find/RootNode.java b/src/org/sosy_lab/common/collect/union_find/RootNode.java index 989177118..0860d3944 100644 --- a/src/org/sosy_lab/common/collect/union_find/RootNode.java +++ b/src/org/sosy_lab/common/collect/union_find/RootNode.java @@ -33,8 +33,8 @@ public RootNode(T pValue) { super(pValue); - rank = 0; - size = 1; + this.rank = 0; + this.size = 1; } public int getRank() { From 21deda264ee3a5981016b0e0c93027be4a05f3ae Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 20 Aug 2026 16:33:14 +0200 Subject: [PATCH 170/183] Suppress immutability warnings where mutable data structures used strictly internally --- .../AbstractImmutableParentPointerTreeBuilder.java | 3 +++ .../union_find/ImmutableParentPointerTreeUnionFind.java | 3 +++ .../ImmutableSortedParentPointerTreeUnionFind.java | 3 +++ .../union_find/PersistentParentPointerTreeUnionFind.java | 8 ++++++++ 4 files changed, 17 insertions(+) diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableParentPointerTreeBuilder.java b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableParentPointerTreeBuilder.java index 9d2969a56..83286a305 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableParentPointerTreeBuilder.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableParentPointerTreeBuilder.java @@ -25,6 +25,9 @@ @Immutable(containerOf = "T") public abstract class AbstractImmutableParentPointerTreeBuilder { + // union-find not immutable but only used internally and never mutated passed outward + // build() returns an immutable union-find that contains a copy of this union-find's map + @SuppressWarnings("Immutable") final ParentPointerTreeUnionFind unionFind; protected AbstractImmutableParentPointerTreeBuilder(UnionType pUnionType) { diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java index b6bae9b49..99b6a9223 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java @@ -32,6 +32,9 @@ @Immutable(containerOf = "T") public class ImmutableParentPointerTreeUnionFind extends AbstractImmutableUnionFind { + // tree nodes are not immutable but only used internally and never mutated after creation + // immutable tree nodes would make conversion during build() difficult and time-consuming + @SuppressWarnings("Immutable") private final ImmutableMap> allNodes; /** diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java index 121d6a95b..60f90a3b6 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java @@ -34,6 +34,9 @@ public class ImmutableSortedParentPointerTreeUnionFind> extends AbstractImmutableSortedUnionFind { + // tree nodes are not immutable but only used internally and never mutated after creation + // immutable tree nodes would make conversion during build() difficult and time-consuming + @SuppressWarnings("Immutable") private final ImmutableMap> allNodes; /** diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java index c99c5972c..d645e31ee 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentParentPointerTreeUnionFind.java @@ -34,8 +34,16 @@ public final class PersistentParentPointerTreeUnionFind extends AbstractImmutableUnionFind implements PersistentUnionFind { + // map only used internally and never mutated after creation + // TODO convert to ImmutableMap once Guava upgraded to 31.1 and buildKeepingLast() available + @SuppressWarnings("Immutable") private final Map mapOfNodesToParents; + + // map only used internally and never mutated after creation + // TODO convert to ImmutableMap once Guava upgraded to 31.1 and buildKeepingLast() available + @SuppressWarnings("Immutable") private final Map mapOfRootsToWeights; + private final UnionType unionType; private PersistentParentPointerTreeUnionFind(UnionType pUnionType) { From 3ec0c88f3d5e8fd8f7b26319a60835dc8ae5b8da Mon Sep 17 00:00:00 2001 From: Colleen Date: Thu, 20 Aug 2026 16:53:14 +0200 Subject: [PATCH 171/183] Add private method findNode to ParentPointerTreeUnionFind to simplify union methods slightly --- .../ParentPointerTreeUnionFind.java | 83 ++++++++++++------- 1 file changed, 52 insertions(+), 31 deletions(-) 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 add039978..74ef081d7 100644 --- a/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ParentPointerTreeUnionFind.java @@ -109,16 +109,16 @@ public void union(T pE1, T pE2) { T canon2 = find(pE2); if (!canon1.equals(canon2)) { - mergeExistingSets(canon1, canon2); + mergeExistingSets(findNode(canon1), findNode(canon2)); } } else { - addElementToExistingSet(pE2, find(pE1)); + addElementToExistingSet(pE2, findNode(pE1)); } } else if (contains(pE2)) { - addElementToExistingSet(pE1, find(pE2)); + addElementToExistingSet(pE1, findNode(pE2)); } else { addElementAsNewSet(pE1); - addElementToExistingSet(pE2, find(pE1)); + addElementToExistingSet(pE2, findNode(pE1)); } } } @@ -183,7 +183,7 @@ public void add(Set pSet) { addElementAsNewSet(canon); } - addElementToExistingSet(current, canon); + addElementToExistingSet(current, findNode(canon)); } } @@ -212,7 +212,7 @@ private void addElementAsNewSet(T pE) { } // only call with elements that are definitely canonical! - private void mergeExistingSets(T pCanon1, T pCanon2) { + private void mergeExistingSets(RootNode pCanon1, RootNode pCanon2) { Preconditions.checkNotNull(pCanon1); Preconditions.checkNotNull(pCanon2); @@ -224,14 +224,13 @@ private void mergeExistingSets(T pCanon1, T pCanon2) { } } - private void addElementToExistingSet(T pE, T pCanon) { + private void addElementToExistingSet(T pE, RootNode pCanon) { - RootNode root = (RootNode) allNodes.get(pCanon); - NonRootNode newNode = new NonRootNode<>(root, pE); - root.incrementSizeByOne(); + NonRootNode newNode = new NonRootNode<>(pCanon, pE); + pCanon.incrementSizeByOne(); - if (root.getRank() == 0) { - root.incrementRankByOne(); + if (pCanon.getRank() == 0) { + pCanon.incrementRankByOne(); } allNodes.put(pE, newNode); @@ -239,43 +238,65 @@ private void addElementToExistingSet(T pE, T pCanon) { // pCanon1 will be new canonical element only if its set is actually bigger, otherwise pCanon2 new // canon - private void unionBySize(T pCanon1, T pCanon2) { + private void unionBySize(RootNode pCanon1, RootNode pCanon2) { - RootNode rootNode1 = (RootNode) allNodes.get(pCanon1); - RootNode rootNode2 = (RootNode) allNodes.get(pCanon2); - - int size1 = rootNode1.getSize(); - int size2 = rootNode2.getSize(); + int size1 = pCanon1.getSize(); + int size2 = pCanon2.getSize(); if (size1 > size2) { - rootNode2.setParent(rootNode1); - rootNode1.incrementSizeBy(rootNode2.getSize()); + pCanon2.setParent(pCanon1); + pCanon1.incrementSizeBy(pCanon2.getSize()); } else { - rootNode1.setParent(rootNode2); - rootNode2.incrementSizeBy(rootNode1.getSize()); + pCanon1.setParent(pCanon2); + pCanon2.incrementSizeBy(pCanon1.getSize()); } } // pCanon1 will be new canonical element only if its rank is actually greater, otherwise pCanon2 // new // canon - private void unionByRank(T pCanon1, T pCanon2) { - - RootNode rootNode1 = (RootNode) allNodes.get(pCanon1); - RootNode rootNode2 = (RootNode) allNodes.get(pCanon2); + private void unionByRank(RootNode pCanon1, RootNode pCanon2) { - int rank1 = rootNode1.getRank(); - int rank2 = rootNode2.getRank(); + int rank1 = pCanon1.getRank(); + int rank2 = pCanon2.getRank(); if (rank1 > rank2) { - rootNode2.setParent(rootNode1); + pCanon2.setParent(pCanon1); } else { - rootNode1.setParent(rootNode2); + pCanon1.setParent(pCanon2); // as rank only changes if both ranks are the same if (rank1 == rank2) { - rootNode2.incrementRankByOne(); + pCanon2.incrementRankByOne(); } } } + + // like find; returns node of canonical element of the set pE belongs to, not value + private RootNode findNode(T pE) { + + Preconditions.checkNotNull(pE); + + List> toBeCompressed = new ArrayList<>(); + @Var AbstractTreeNode node = allNodes.get(pE); + + if (node != null) { + @Var AbstractTreeNode parent = node.getParent(); + + while (!node.equals(parent)) { + toBeCompressed.add(node); + node = parent; + parent = node.getParent(); + } + + for (AbstractTreeNode current : toBeCompressed) { + + current.setParent(parent); + } + + return (RootNode) parent; + } + + throw new IllegalArgumentException("Element not contained."); + } } From cd7c1cd8bbccd806e3955b7b2b6b1ed1cc5379bf Mon Sep 17 00:00:00 2001 From: Colleen Date: Wed, 2 Sep 2026 22:21:14 +0200 Subject: [PATCH 172/183] Add benchmarking package and a first benchmarking test --- .../union_find/PersistentSortedUnionFind.java | 4 +- ...ingleElementsIntoExistingSetBenchmark.java | 135 ++++++++++++++++++ .../union_find/benchmarking/package-info.java | 14 ++ 3 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java create mode 100644 src/org/sosy_lab/common/collect/union_find/benchmarking/package-info.java diff --git a/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java b/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java index 03dd442f9..004075b3d 100644 --- a/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/PersistentSortedUnionFind.java @@ -23,7 +23,8 @@ * @param The type of values. */ @Immutable(containerOf = "T") -public interface PersistentSortedUnionFind> extends SortedUnionFind { +public interface PersistentSortedUnionFind> + extends SortedUnionFind, PersistentUnionFind { /** * Replacement for {@link #union(Comparable, Comparable)} that returns a fresh new instance. @@ -32,6 +33,7 @@ public interface PersistentSortedUnionFind> extends Sort * @param pE2 second element * @return new instance that the desired changes have been applied to */ + @Override @CheckReturnValue PersistentSortedUnionFind unionAndCopy(T pE1, T pE2); diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java new file mode 100644 index 000000000..7966f65b9 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java @@ -0,0 +1,135 @@ +// 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.benchmarking; + +import com.google.errorprone.annotations.Var; +import org.sosy_lab.common.collect.union_find.AbstractImmutableParentPointerTreeBuilder; +import org.sosy_lab.common.collect.union_find.ImmutableParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ImmutableSortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; +import org.sosy_lab.common.collect.union_find.PersistentParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.PersistentSortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.PersistentUnionFind; +import org.sosy_lab.common.collect.union_find.SortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.UnionFind; + +public final class UnionSingleElementsIntoExistingSetBenchmark { + + public static void main(String[] args) { + + @Var boolean immutable = false; + @Var boolean persistent = false; + @Var boolean sorted = false; + @Var boolean unionByRank = false; + @Var int n = 0; + + if (args.length == 0) { + System.exit(1); + } + + for (String string : args) { + + switch (string) { + case "-immutable" -> { + immutable = true; + } + + case "-persistent" -> { + persistent = true; + } + + case "-sorted" -> sorted = true; + + case "-rank" -> unionByRank = true; + + default -> { + try { + n = Integer.parseInt(string); + } catch (NumberFormatException pE) { + throw new IllegalArgumentException("Incompatible args", pE); + } + } + } + } + + if (!immutable && !persistent && !sorted) { + if (unionByRank) { + mutable(new ParentPointerTreeUnionFind<>(UnionType.UNION_BY_RANK), n); + } else { + mutable(new ParentPointerTreeUnionFind<>(UnionType.UNION_BY_SIZE), n); + } + } else if (!immutable && !persistent && sorted) { + if (unionByRank) { + mutable(new SortedParentPointerTreeUnionFind<>(UnionType.UNION_BY_RANK), n); + } else { + mutable(new SortedParentPointerTreeUnionFind<>(UnionType.UNION_BY_SIZE), n); + } + } else if (immutable && !sorted) { + if (unionByRank) { + immutable( + ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_RANK), n); + } else { + immutable( + ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE), n); + } + } else if (immutable && sorted) { + if (unionByRank) { + immutable( + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_RANK), + n); + } else { + immutable( + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE), + n); + } + } else if (persistent && !sorted) { + if (unionByRank) { + persistent(PersistentParentPointerTreeUnionFind.of(UnionType.UNION_BY_RANK), n); + } else { + persistent(PersistentParentPointerTreeUnionFind.of(UnionType.UNION_BY_SIZE), n); + } + } else if (persistent && sorted) { + if (unionByRank) { + persistent(PersistentSortedParentPointerTreeUnionFind.of(UnionType.UNION_BY_RANK), n); + } else { + persistent(PersistentSortedParentPointerTreeUnionFind.of(UnionType.UNION_BY_SIZE), n); + } + } else { + System.exit(1); + } + System.exit(0); + } + + private static void mutable(UnionFind pUnionFind, int pN) { + + for (int i = 0; i < pN; i++) { + pUnionFind.union(0, i); + } + } + + private static void immutable( + AbstractImmutableParentPointerTreeBuilder pBuilder, int pN) { + + for (int i = 0; i < pN; i++) { + pBuilder.union(0, i); + } + } + + private static void persistent(PersistentUnionFind pUnionFind, int pN) { + + @Var PersistentUnionFind unionFind = pUnionFind; + + for (int i = 0; i < pN; i++) { + unionFind = unionFind.unionAndCopy(0, i); + } + } + + private UnionSingleElementsIntoExistingSetBenchmark() {} +} diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/package-info.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/package-info.java new file mode 100644 index 000000000..acce67737 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/package-info.java @@ -0,0 +1,14 @@ +// 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 all benchmarking classes related to union-find. */ +@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.benchmarking; From 6ce45fbf789522cf3d1735fa4fb3994ea5723f83 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 4 Sep 2026 17:54:02 +0200 Subject: [PATCH 173/183] Add benchmarking test to run on algs4 input --- .../benchmarking/Algs4DatasetBenchmark.java | 188 ++++++++++++++++++ ...ingleElementsIntoExistingSetBenchmark.java | 7 +- 2 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java new file mode 100644 index 000000000..6aca09570 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java @@ -0,0 +1,188 @@ +// 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.benchmarking; + +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.Var; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Pattern; +import javax.annotation.Nullable; +import org.sosy_lab.common.collect.union_find.AbstractImmutableParentPointerTreeBuilder; +import org.sosy_lab.common.collect.union_find.AbstractImmutableUnionFind; +import org.sosy_lab.common.collect.union_find.ImmutableParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ImmutableSortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; +import org.sosy_lab.common.collect.union_find.PersistentParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.PersistentSortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.PersistentUnionFind; +import org.sosy_lab.common.collect.union_find.SortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.UnionFind; + +public final class Algs4DatasetBenchmark { + + public static void main(String[] args) { + + @Var boolean immutable = false; + @Var boolean persistent = false; + @Var boolean sorted = false; + @Var boolean unionByRank = false; + @Var + @Nullable + Path filePath = null; + List unionInput = new ArrayList<>(); + + if (args.length == 0) { + System.exit(1); + } + + for (String string : args) { + + switch (string) { + case "-immutable" -> { + immutable = true; + } + + case "-persistent" -> { + persistent = true; + } + + case "-sorted" -> sorted = true; + + case "-rank" -> unionByRank = true; + + default -> { + filePath = Path.of(string); + } + } + + try { + Preconditions.checkNotNull(filePath); + + for (@Var String line : Files.readAllLines(filePath)) { + + line = line.trim(); + + if (line.isEmpty()) { + continue; + } + + List tokens = Splitter.on(Pattern.compile("\\s+")).splitToList(line); + + unionInput.add(Integer.parseInt(tokens.get(0))); + unionInput.add(Integer.parseInt(tokens.get(1))); + } + } catch (IOException e) { + System.exit(1); + } + } + + Iterator iterator = unionInput.iterator(); + + if (!immutable && !persistent && !sorted) { + if (unionByRank) { + mutable(new ParentPointerTreeUnionFind<>(UnionType.UNION_BY_RANK), iterator); + } else { + mutable(new ParentPointerTreeUnionFind<>(UnionType.UNION_BY_SIZE), iterator); + } + } else if (!immutable && !persistent && sorted) { + if (unionByRank) { + mutable(new SortedParentPointerTreeUnionFind<>(UnionType.UNION_BY_RANK), iterator); + } else { + mutable(new SortedParentPointerTreeUnionFind<>(UnionType.UNION_BY_SIZE), iterator); + } + } else if (immutable && !sorted) { + if (unionByRank) { + immutable( + ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_RANK), + iterator); + } else { + immutable( + ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE), + iterator); + } + } else if (immutable && sorted) { + if (unionByRank) { + immutable( + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_RANK), + iterator); + } else { + immutable( + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE), + iterator); + } + } else if (persistent && !sorted) { + if (unionByRank) { + persistent(PersistentParentPointerTreeUnionFind.of(UnionType.UNION_BY_RANK), iterator); + } else { + persistent(PersistentParentPointerTreeUnionFind.of(UnionType.UNION_BY_SIZE), iterator); + } + } else if (persistent && sorted) { + if (unionByRank) { + persistent( + PersistentSortedParentPointerTreeUnionFind.of(UnionType.UNION_BY_RANK), iterator); + } else { + persistent( + PersistentSortedParentPointerTreeUnionFind.of(UnionType.UNION_BY_SIZE), iterator); + } + } else { + System.exit(1); + } + System.exit(0); + } + + private static void mutable(UnionFind pUnionFind, Iterator pIterator) { + + while (pIterator.hasNext()) { + + int a = pIterator.next(); + int b = pIterator.next(); + + pUnionFind.union(a, b); + } + } + + @CanIgnoreReturnValue + private static AbstractImmutableUnionFind immutable( + AbstractImmutableParentPointerTreeBuilder pBuilder, Iterator pIterator) { + + while (pIterator.hasNext()) { + + int a = pIterator.next(); + int b = pIterator.next(); + + pBuilder.union(a, b); + } + + return pBuilder.build(); + } + + private static void persistent( + PersistentUnionFind pUnionFind, Iterator pIterator) { + + @Var PersistentUnionFind unionFind = pUnionFind; + + while (pIterator.hasNext()) { + + int a = pIterator.next(); + int b = pIterator.next(); + + unionFind = unionFind.unionAndCopy(a, b); + } + } + + private Algs4DatasetBenchmark() {} +} diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java index 7966f65b9..bcda6f83c 100644 --- a/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java @@ -8,8 +8,10 @@ package org.sosy_lab.common.collect.union_find.benchmarking; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.Var; import org.sosy_lab.common.collect.union_find.AbstractImmutableParentPointerTreeBuilder; +import org.sosy_lab.common.collect.union_find.AbstractImmutableUnionFind; import org.sosy_lab.common.collect.union_find.ImmutableParentPointerTreeUnionFind; import org.sosy_lab.common.collect.union_find.ImmutableSortedParentPointerTreeUnionFind; import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind; @@ -114,12 +116,15 @@ private static void mutable(UnionFind pUnionFind, int pN) { } } - private static void immutable( + @CanIgnoreReturnValue + private static AbstractImmutableUnionFind immutable( AbstractImmutableParentPointerTreeBuilder pBuilder, int pN) { for (int i = 0; i < pN; i++) { pBuilder.union(0, i); } + + return pBuilder.build(); } private static void persistent(PersistentUnionFind pUnionFind, int pN) { From d888c6bcf88eb4b024a4953454c35badd94400e9 Mon Sep 17 00:00:00 2001 From: Colleen Date: Fri, 4 Sep 2026 18:08:20 +0200 Subject: [PATCH 174/183] Add jars for benchmark tests --- .idea/artifacts/Algs4Benchmark_jar.xml | 42 +++++++++++++++++++ .../UnionSingleElementsBenchmark_jar.xml | 42 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 .idea/artifacts/Algs4Benchmark_jar.xml create mode 100644 .idea/artifacts/UnionSingleElementsBenchmark_jar.xml diff --git a/.idea/artifacts/Algs4Benchmark_jar.xml b/.idea/artifacts/Algs4Benchmark_jar.xml new file mode 100644 index 000000000..b57240028 --- /dev/null +++ b/.idea/artifacts/Algs4Benchmark_jar.xml @@ -0,0 +1,42 @@ + + + $PROJECT_DIR$/bin/artifacts/Algs4Benchmark_jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/artifacts/UnionSingleElementsBenchmark_jar.xml b/.idea/artifacts/UnionSingleElementsBenchmark_jar.xml new file mode 100644 index 000000000..747cdd3d4 --- /dev/null +++ b/.idea/artifacts/UnionSingleElementsBenchmark_jar.xml @@ -0,0 +1,42 @@ + + + $PROJECT_DIR$/bin/artifacts/UnionSingleElementsBenchmark_jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 2fb0dfa3a94a5c29a0542a6d26f93b8ef3b39760 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 7 Sep 2026 11:20:13 +0200 Subject: [PATCH 175/183] Fix bug in Algs4 test --- .../collect/union_find/benchmarking/Algs4DatasetBenchmark.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java index 6aca09570..cd3378541 100644 --- a/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java @@ -68,6 +68,7 @@ public static void main(String[] args) { filePath = Path.of(string); } } + } try { Preconditions.checkNotNull(filePath); @@ -88,7 +89,7 @@ public static void main(String[] args) { } catch (IOException e) { System.exit(1); } - } + Iterator iterator = unionInput.iterator(); From 9933544696524ae3ff93f73db4db413e72b1589b Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 7 Sep 2026 12:17:44 +0200 Subject: [PATCH 176/183] Changes from creating jar --- src/META-INF/MANIFEST.MF | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/META-INF/MANIFEST.MF diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF new file mode 100644 index 000000000..dd47f1059 --- /dev/null +++ b/src/META-INF/MANIFEST.MF @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Main-Class: org.sosy_lab.common.collect.union_find.benchmarking.Algs4Dat + asetBenchmark + From d3455a840f3dc5971ac7b158e332de85629e9ba2 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 7 Sep 2026 12:17:59 +0200 Subject: [PATCH 177/183] Changes from creating jar --- ...chmark_jar.xml => SoSy_Lab_Common_jar.xml} | 7 +--- .../UnionSingleElementsBenchmark_jar.xml | 42 ------------------- 2 files changed, 2 insertions(+), 47 deletions(-) rename .idea/artifacts/{Algs4Benchmark_jar.xml => SoSy_Lab_Common_jar.xml} (91%) delete mode 100644 .idea/artifacts/UnionSingleElementsBenchmark_jar.xml diff --git a/.idea/artifacts/Algs4Benchmark_jar.xml b/.idea/artifacts/SoSy_Lab_Common_jar.xml similarity index 91% rename from .idea/artifacts/Algs4Benchmark_jar.xml rename to .idea/artifacts/SoSy_Lab_Common_jar.xml index b57240028..882e6ba6f 100644 --- a/.idea/artifacts/Algs4Benchmark_jar.xml +++ b/.idea/artifacts/SoSy_Lab_Common_jar.xml @@ -1,10 +1,7 @@ - - $PROJECT_DIR$/bin/artifacts/Algs4Benchmark_jar + + $PROJECT_DIR$/bin/artifacts/SoSy_Lab_Common_jar - - - diff --git a/.idea/artifacts/UnionSingleElementsBenchmark_jar.xml b/.idea/artifacts/UnionSingleElementsBenchmark_jar.xml deleted file mode 100644 index 747cdd3d4..000000000 --- a/.idea/artifacts/UnionSingleElementsBenchmark_jar.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - $PROJECT_DIR$/bin/artifacts/UnionSingleElementsBenchmark_jar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 07e9a8bf1ac4ddcc7a57f03c5428c9cb613d6f96 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 7 Sep 2026 14:59:09 +0200 Subject: [PATCH 178/183] Modify jar creation to produce clear names and separate manifests --- ..._Common_jar.xml => algs4benchmark_jar.xml} | 15 ++++--- .../union_single_elements_benchmark_jar.xml | 42 +++++++++++++++++++ .../algs4benchmark}/META-INF/MANIFEST.MF | 8 ++-- .../META-INF/MANIFEST.MF | 4 ++ .../benchmarking/Algs4DatasetBenchmark.java | 9 ++-- ...ingleElementsIntoExistingSetBenchmark.java | 13 ++++-- 6 files changed, 73 insertions(+), 18 deletions(-) rename .idea/artifacts/{SoSy_Lab_Common_jar.xml => algs4benchmark_jar.xml} (90%) create mode 100644 .idea/artifacts/union_single_elements_benchmark_jar.xml rename {src => manifests/algs4benchmark}/META-INF/MANIFEST.MF (96%) create mode 100644 manifests/union_single_elements_benchmark/META-INF/MANIFEST.MF diff --git a/.idea/artifacts/SoSy_Lab_Common_jar.xml b/.idea/artifacts/algs4benchmark_jar.xml similarity index 90% rename from .idea/artifacts/SoSy_Lab_Common_jar.xml rename to .idea/artifacts/algs4benchmark_jar.xml index 882e6ba6f..c56fbf56a 100644 --- a/.idea/artifacts/SoSy_Lab_Common_jar.xml +++ b/.idea/artifacts/algs4benchmark_jar.xml @@ -1,8 +1,12 @@ - - $PROJECT_DIR$/bin/artifacts/SoSy_Lab_Common_jar - + + $PROJECT_DIR$/bin/artifacts/algs4benchmark_jar + + + + + @@ -22,9 +26,7 @@ - - @@ -33,7 +35,8 @@ - + + \ No newline at end of file diff --git a/.idea/artifacts/union_single_elements_benchmark_jar.xml b/.idea/artifacts/union_single_elements_benchmark_jar.xml new file mode 100644 index 000000000..93fc475c6 --- /dev/null +++ b/.idea/artifacts/union_single_elements_benchmark_jar.xml @@ -0,0 +1,42 @@ + + + $PROJECT_DIR$/bin/artifacts/union_single_elements_benchmark_jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/META-INF/MANIFEST.MF b/manifests/algs4benchmark/META-INF/MANIFEST.MF similarity index 96% rename from src/META-INF/MANIFEST.MF rename to manifests/algs4benchmark/META-INF/MANIFEST.MF index dd47f1059..691b0a36e 100644 --- a/src/META-INF/MANIFEST.MF +++ b/manifests/algs4benchmark/META-INF/MANIFEST.MF @@ -1,4 +1,4 @@ -Manifest-Version: 1.0 -Main-Class: org.sosy_lab.common.collect.union_find.benchmarking.Algs4Dat - asetBenchmark - +Manifest-Version: 1.0 +Main-Class: org.sosy_lab.common.collect.union_find.benchmarking.Algs4Dat + asetBenchmark + diff --git a/manifests/union_single_elements_benchmark/META-INF/MANIFEST.MF b/manifests/union_single_elements_benchmark/META-INF/MANIFEST.MF new file mode 100644 index 000000000..66ac2bbd2 --- /dev/null +++ b/manifests/union_single_elements_benchmark/META-INF/MANIFEST.MF @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Main-Class: org.sosy_lab.common.collect.union_find.benchmarking.UnionSin + gleElementsIntoExistingSetBenchmark + diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java index cd3378541..2cd40c9f1 100644 --- a/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java @@ -19,7 +19,7 @@ import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; -import javax.annotation.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import org.sosy_lab.common.collect.union_find.AbstractImmutableParentPointerTreeBuilder; import org.sosy_lab.common.collect.union_find.AbstractImmutableUnionFind; import org.sosy_lab.common.collect.union_find.ImmutableParentPointerTreeUnionFind; @@ -44,6 +44,7 @@ public static void main(String[] args) { @Nullable Path filePath = null; List unionInput = new ArrayList<>(); + Pattern pattern = Pattern.compile("\\s+"); if (args.length == 0) { System.exit(1); @@ -75,13 +76,13 @@ public static void main(String[] args) { for (@Var String line : Files.readAllLines(filePath)) { - line = line.trim(); + String trimmedLine = line.trim(); - if (line.isEmpty()) { + if (trimmedLine.isEmpty()) { continue; } - List tokens = Splitter.on(Pattern.compile("\\s+")).splitToList(line); + List tokens = Splitter.on(pattern).splitToList(trimmedLine); unionInput.add(Integer.parseInt(tokens.get(0))); unionInput.add(Integer.parseInt(tokens.get(1))); diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java index bcda6f83c..343439fed 100644 --- a/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java @@ -10,6 +10,8 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.Var; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.sosy_lab.common.collect.union_find.AbstractImmutableParentPointerTreeBuilder; import org.sosy_lab.common.collect.union_find.AbstractImmutableUnionFind; import org.sosy_lab.common.collect.union_find.ImmutableParentPointerTreeUnionFind; @@ -52,10 +54,13 @@ public static void main(String[] args) { case "-rank" -> unionByRank = true; default -> { - try { - n = Integer.parseInt(string); - } catch (NumberFormatException pE) { - throw new IllegalArgumentException("Incompatible args", pE); + + Matcher matcher = Pattern.compile("n_(\\d+)\\.txt$").matcher(string); + + if(matcher.find()) { + n = Integer.parseInt(matcher.group(1)); + } else { + throw new IllegalArgumentException("Incompatible args"); } } } From 687fa8efe5142c38b923dd20d4752d230b90757b Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 7 Sep 2026 15:38:18 +0200 Subject: [PATCH 179/183] Add further benchmark test --- .../find_old_to_new_benchmark_jar.xml | 42 +++++ .../META-INF/MANIFEST.MF | 4 + .../benchmarking/Algs4DatasetBenchmark.java | 33 ++-- .../FindOldToNewSingleSetBenchmark.java | 162 ++++++++++++++++++ ...ingleElementsIntoExistingSetBenchmark.java | 7 +- 5 files changed, 228 insertions(+), 20 deletions(-) create mode 100644 .idea/artifacts/find_old_to_new_benchmark_jar.xml create mode 100644 manifests/find_old_to_new_benchmark/META-INF/MANIFEST.MF create mode 100644 src/org/sosy_lab/common/collect/union_find/benchmarking/FindOldToNewSingleSetBenchmark.java diff --git a/.idea/artifacts/find_old_to_new_benchmark_jar.xml b/.idea/artifacts/find_old_to_new_benchmark_jar.xml new file mode 100644 index 000000000..5b80e9790 --- /dev/null +++ b/.idea/artifacts/find_old_to_new_benchmark_jar.xml @@ -0,0 +1,42 @@ + + + $PROJECT_DIR$/bin/artifacts/find_old_to_new_benchmark_jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/manifests/find_old_to_new_benchmark/META-INF/MANIFEST.MF b/manifests/find_old_to_new_benchmark/META-INF/MANIFEST.MF new file mode 100644 index 000000000..c65b8dbfe --- /dev/null +++ b/manifests/find_old_to_new_benchmark/META-INF/MANIFEST.MF @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Main-Class: org.sosy_lab.common.collect.union_find.benchmarking.FindOldT + oNewSingleSetBenchmark + diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java index 2cd40c9f1..d8be3d3fe 100644 --- a/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java @@ -34,6 +34,8 @@ public final class Algs4DatasetBenchmark { + static final Pattern PATTERN = Pattern.compile("\\s+"); + public static void main(String[] args) { @Var boolean immutable = false; @@ -41,10 +43,8 @@ public static void main(String[] args) { @Var boolean sorted = false; @Var boolean unionByRank = false; @Var - @Nullable - Path filePath = null; + @Nullable Path filePath = null; List unionInput = new ArrayList<>(); - Pattern pattern = Pattern.compile("\\s+"); if (args.length == 0) { System.exit(1); @@ -71,26 +71,25 @@ public static void main(String[] args) { } } - try { - Preconditions.checkNotNull(filePath); + try { + Preconditions.checkNotNull(filePath); - for (@Var String line : Files.readAllLines(filePath)) { + for (String line : Files.readAllLines(filePath)) { - String trimmedLine = line.trim(); + String trimmedLine = line.trim(); - if (trimmedLine.isEmpty()) { - continue; - } + if (trimmedLine.isEmpty()) { + continue; + } - List tokens = Splitter.on(pattern).splitToList(trimmedLine); + List tokens = Splitter.on(PATTERN).splitToList(trimmedLine); - unionInput.add(Integer.parseInt(tokens.get(0))); - unionInput.add(Integer.parseInt(tokens.get(1))); - } - } catch (IOException e) { - System.exit(1); + unionInput.add(Integer.parseInt(tokens.get(0))); + unionInput.add(Integer.parseInt(tokens.get(1))); } - + } catch (IOException e) { + System.exit(1); + } Iterator iterator = unionInput.iterator(); diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/FindOldToNewSingleSetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/FindOldToNewSingleSetBenchmark.java new file mode 100644 index 000000000..85ecafd96 --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/FindOldToNewSingleSetBenchmark.java @@ -0,0 +1,162 @@ +// 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.benchmarking; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.Var; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.sosy_lab.common.collect.union_find.AbstractImmutableParentPointerTreeBuilder; +import org.sosy_lab.common.collect.union_find.ImmutableParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ImmutableSortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; +import org.sosy_lab.common.collect.union_find.PersistentParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.PersistentSortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.PersistentUnionFind; +import org.sosy_lab.common.collect.union_find.SortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.UnionFind; + +public class FindOldToNewSingleSetBenchmark { + + static final Pattern PATTERN = Pattern.compile("\\s+"); + + public static void main(String[] args) { + + @Var boolean immutable = false; + @Var boolean persistent = false; + @Var boolean sorted = false; + @Var boolean unionByRank = false; + @Var int n = 0; + + if (args.length == 0) { + System.exit(1); + } + + for (String string : args) { + + switch (string) { + case "-immutable" -> { + immutable = true; + } + + case "-persistent" -> { + persistent = true; + } + + case "-sorted" -> sorted = true; + + case "-rank" -> unionByRank = true; + + default -> { + Matcher matcher = PATTERN.matcher(string); + + if (matcher.find()) { + n = Integer.parseInt(matcher.group(1)); + } else { + throw new IllegalArgumentException("Incompatible args"); + } + } + } + } + + if (!immutable && !persistent && !sorted) { + if (unionByRank) { + mutable(new ParentPointerTreeUnionFind<>(UnionType.UNION_BY_RANK), n); + } else { + mutable(new ParentPointerTreeUnionFind<>(UnionType.UNION_BY_SIZE), n); + } + } else if (!immutable && !persistent && sorted) { + if (unionByRank) { + mutable(new SortedParentPointerTreeUnionFind<>(UnionType.UNION_BY_RANK), n); + } else { + mutable(new SortedParentPointerTreeUnionFind<>(UnionType.UNION_BY_SIZE), n); + } + } else if (immutable && !sorted) { + if (unionByRank) { + immutable( + ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_RANK), n); + } else { + immutable( + ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE), n); + } + } else if (immutable && sorted) { + if (unionByRank) { + immutable( + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_RANK), + n); + } else { + immutable( + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE), + n); + } + } else if (persistent && !sorted) { + if (unionByRank) { + persistent(PersistentParentPointerTreeUnionFind.of(UnionType.UNION_BY_RANK), n); + } else { + persistent(PersistentParentPointerTreeUnionFind.of(UnionType.UNION_BY_SIZE), n); + } + } else if (persistent && sorted) { + if (unionByRank) { + persistent(PersistentSortedParentPointerTreeUnionFind.of(UnionType.UNION_BY_RANK), n); + } else { + persistent(PersistentSortedParentPointerTreeUnionFind.of(UnionType.UNION_BY_SIZE), n); + } + } else { + System.exit(1); + } + + System.exit(0); + } + + private static void mutable(UnionFind pUnionFind, int pN) { + + for (int i = 0; i < pN; i++) { + pUnionFind.union(0, i); + } + + performFinds(pUnionFind, pN); + } + + private static void immutable( + AbstractImmutableParentPointerTreeBuilder pBuilder, int pN) { + + for (int i = 0; i < pN; i++) { + pBuilder.union(0, i); + } + + performFinds(pBuilder.build(), pN); + } + + private static void persistent(PersistentUnionFind pUnionFind, int pN) { + + @Var PersistentUnionFind unionFind = pUnionFind; + + for (int i = 0; i < pN; i++) { + unionFind = unionFind.unionAndCopy(0, i); + } + + performFinds(unionFind, pN); + } + + @CanIgnoreReturnValue + private static int performFinds(UnionFind pUnionFind, int pN) { + + @Var int root = 0; + + for (int i = 0; i < pN; i++) { + + root = pUnionFind.find(i); + } + + return root; + } + + private FindOldToNewSingleSetBenchmark() {} +} diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java index 343439fed..817934908 100644 --- a/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java @@ -26,6 +26,8 @@ public final class UnionSingleElementsIntoExistingSetBenchmark { + static final Pattern PATTERN = Pattern.compile("\\s+"); + public static void main(String[] args) { @Var boolean immutable = false; @@ -54,10 +56,9 @@ public static void main(String[] args) { case "-rank" -> unionByRank = true; default -> { + Matcher matcher = PATTERN.matcher(string); - Matcher matcher = Pattern.compile("n_(\\d+)\\.txt$").matcher(string); - - if(matcher.find()) { + if (matcher.find()) { n = Integer.parseInt(matcher.group(1)); } else { throw new IllegalArgumentException("Incompatible args"); From 988ee798e6c2eace6939b58f314330eb632db8d4 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 7 Sep 2026 15:47:33 +0200 Subject: [PATCH 180/183] Add further benchmark test --- .../find_new_to_old_benchmark_jar.xml | 42 +++++ .../META-INF/MANIFEST.MF | 4 + .../FindNewToOldSingleSetBenchmark.java | 162 ++++++++++++++++++ .../FindOldToNewSingleSetBenchmark.java | 2 +- 4 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 .idea/artifacts/find_new_to_old_benchmark_jar.xml create mode 100644 manifests/find_new_to_old_benchmark_jar/META-INF/MANIFEST.MF create mode 100644 src/org/sosy_lab/common/collect/union_find/benchmarking/FindNewToOldSingleSetBenchmark.java diff --git a/.idea/artifacts/find_new_to_old_benchmark_jar.xml b/.idea/artifacts/find_new_to_old_benchmark_jar.xml new file mode 100644 index 000000000..caa3efd97 --- /dev/null +++ b/.idea/artifacts/find_new_to_old_benchmark_jar.xml @@ -0,0 +1,42 @@ + + + $PROJECT_DIR$/bin/artifacts/find_new_to_old_benchmark_jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/manifests/find_new_to_old_benchmark_jar/META-INF/MANIFEST.MF b/manifests/find_new_to_old_benchmark_jar/META-INF/MANIFEST.MF new file mode 100644 index 000000000..57e627205 --- /dev/null +++ b/manifests/find_new_to_old_benchmark_jar/META-INF/MANIFEST.MF @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Main-Class: org.sosy_lab.common.collect.union_find.benchmarking.FindNewT + oOldSingleSetBenchmark + diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/FindNewToOldSingleSetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/FindNewToOldSingleSetBenchmark.java new file mode 100644 index 000000000..196410f4c --- /dev/null +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/FindNewToOldSingleSetBenchmark.java @@ -0,0 +1,162 @@ +// 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.benchmarking; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.Var; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.sosy_lab.common.collect.union_find.AbstractImmutableParentPointerTreeBuilder; +import org.sosy_lab.common.collect.union_find.ImmutableParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ImmutableSortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; +import org.sosy_lab.common.collect.union_find.PersistentParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.PersistentSortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.PersistentUnionFind; +import org.sosy_lab.common.collect.union_find.SortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.UnionFind; + +public final class FindNewToOldSingleSetBenchmark { + + static final Pattern PATTERN = Pattern.compile("\\s+"); + + public static void main(String[] args) { + + @Var boolean immutable = false; + @Var boolean persistent = false; + @Var boolean sorted = false; + @Var boolean unionByRank = false; + @Var int n = 0; + + if (args.length == 0) { + System.exit(1); + } + + for (String string : args) { + + switch (string) { + case "-immutable" -> { + immutable = true; + } + + case "-persistent" -> { + persistent = true; + } + + case "-sorted" -> sorted = true; + + case "-rank" -> unionByRank = true; + + default -> { + Matcher matcher = PATTERN.matcher(string); + + if (matcher.find()) { + n = Integer.parseInt(matcher.group(1)); + } else { + throw new IllegalArgumentException("Incompatible args"); + } + } + } + } + + if (!immutable && !persistent && !sorted) { + if (unionByRank) { + mutable(new ParentPointerTreeUnionFind<>(UnionType.UNION_BY_RANK), n); + } else { + mutable(new ParentPointerTreeUnionFind<>(UnionType.UNION_BY_SIZE), n); + } + } else if (!immutable && !persistent && sorted) { + if (unionByRank) { + mutable(new SortedParentPointerTreeUnionFind<>(UnionType.UNION_BY_RANK), n); + } else { + mutable(new SortedParentPointerTreeUnionFind<>(UnionType.UNION_BY_SIZE), n); + } + } else if (immutable && !sorted) { + if (unionByRank) { + immutable( + ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_RANK), n); + } else { + immutable( + ImmutableParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE), n); + } + } else if (immutable && sorted) { + if (unionByRank) { + immutable( + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_RANK), + n); + } else { + immutable( + ImmutableSortedParentPointerTreeUnionFind.Builder.getBuilder(UnionType.UNION_BY_SIZE), + n); + } + } else if (persistent && !sorted) { + if (unionByRank) { + persistent(PersistentParentPointerTreeUnionFind.of(UnionType.UNION_BY_RANK), n); + } else { + persistent(PersistentParentPointerTreeUnionFind.of(UnionType.UNION_BY_SIZE), n); + } + } else if (persistent && sorted) { + if (unionByRank) { + persistent(PersistentSortedParentPointerTreeUnionFind.of(UnionType.UNION_BY_RANK), n); + } else { + persistent(PersistentSortedParentPointerTreeUnionFind.of(UnionType.UNION_BY_SIZE), n); + } + } else { + System.exit(1); + } + + System.exit(0); + } + + private static void mutable(UnionFind pUnionFind, int pN) { + + for (int i = 0; i < pN; i++) { + pUnionFind.union(0, i); + } + + performFinds(pUnionFind, pN); + } + + private static void immutable( + AbstractImmutableParentPointerTreeBuilder pBuilder, int pN) { + + for (int i = 0; i < pN; i++) { + pBuilder.union(0, i); + } + + performFinds(pBuilder.build(), pN); + } + + private static void persistent(PersistentUnionFind pUnionFind, int pN) { + + @Var PersistentUnionFind unionFind = pUnionFind; + + for (int i = 0; i < pN; i++) { + unionFind = unionFind.unionAndCopy(0, i); + } + + performFinds(unionFind, pN); + } + + @CanIgnoreReturnValue + private static int performFinds(UnionFind pUnionFind, int pN) { + + @Var int root = 0; + + for (int i = pN; i >= 0; --i) { + + root = pUnionFind.find(i); + } + + return root; + } + + private FindNewToOldSingleSetBenchmark() {} +} diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/FindOldToNewSingleSetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/FindOldToNewSingleSetBenchmark.java index 85ecafd96..dd107b0f7 100644 --- a/src/org/sosy_lab/common/collect/union_find/benchmarking/FindOldToNewSingleSetBenchmark.java +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/FindOldToNewSingleSetBenchmark.java @@ -23,7 +23,7 @@ import org.sosy_lab.common.collect.union_find.SortedParentPointerTreeUnionFind; import org.sosy_lab.common.collect.union_find.UnionFind; -public class FindOldToNewSingleSetBenchmark { +public final class FindOldToNewSingleSetBenchmark { static final Pattern PATTERN = Pattern.compile("\\s+"); From a01f37a2930ee18c328a2d7902356ff4ab86b960 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 7 Sep 2026 16:13:58 +0200 Subject: [PATCH 181/183] Typo fix --- .../union_find/benchmarking/FindNewToOldSingleSetBenchmark.java | 2 +- .../union_find/benchmarking/FindOldToNewSingleSetBenchmark.java | 2 +- .../UnionSingleElementsIntoExistingSetBenchmark.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/FindNewToOldSingleSetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/FindNewToOldSingleSetBenchmark.java index 196410f4c..bc08fa9b7 100644 --- a/src/org/sosy_lab/common/collect/union_find/benchmarking/FindNewToOldSingleSetBenchmark.java +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/FindNewToOldSingleSetBenchmark.java @@ -25,7 +25,7 @@ public final class FindNewToOldSingleSetBenchmark { - static final Pattern PATTERN = Pattern.compile("\\s+"); + static final Pattern PATTERN = Pattern.compile("n_(\\d+)\\.txt$"); public static void main(String[] args) { diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/FindOldToNewSingleSetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/FindOldToNewSingleSetBenchmark.java index dd107b0f7..69a089de0 100644 --- a/src/org/sosy_lab/common/collect/union_find/benchmarking/FindOldToNewSingleSetBenchmark.java +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/FindOldToNewSingleSetBenchmark.java @@ -25,7 +25,7 @@ public final class FindOldToNewSingleSetBenchmark { - static final Pattern PATTERN = Pattern.compile("\\s+"); + static final Pattern PATTERN = Pattern.compile("n_(\\d+)\\.txt$"); public static void main(String[] args) { diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java index 817934908..2f70a6734 100644 --- a/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java @@ -26,7 +26,7 @@ public final class UnionSingleElementsIntoExistingSetBenchmark { - static final Pattern PATTERN = Pattern.compile("\\s+"); + static final Pattern PATTERN = Pattern.compile("n_(\\d+)\\.txt$"); public static void main(String[] args) { From 12c03eb9f062f34ff8318bf9620d16af6dcc63bc Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 7 Sep 2026 18:43:40 +0200 Subject: [PATCH 182/183] Prevent modifications to immutable Union-Finds after build() has been called --- ...ractImmutableParentPointerTreeBuilder.java | 29 +++++++++++++++---- .../ImmutableParentPointerTreeUnionFind.java | 3 ++ ...tableSortedParentPointerTreeUnionFind.java | 3 ++ ...mutableParentPointerTreeUnionFindTest.java | 8 ++--- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableParentPointerTreeBuilder.java b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableParentPointerTreeBuilder.java index 83286a305..c9ba77f8e 100644 --- a/src/org/sosy_lab/common/collect/union_find/AbstractImmutableParentPointerTreeBuilder.java +++ b/src/org/sosy_lab/common/collect/union_find/AbstractImmutableParentPointerTreeBuilder.java @@ -16,9 +16,10 @@ /** * Abstract builder class which first collects the data in a mutable Union-Find and converts it to - * an immutable Union-Find when {@code build()} is called. See documentation in {@link - * ParentPointerTreeUnionFind} for explanations on {@code union()}, {@code add()} and {@code - * addAll()} as the methods in this builder simply pass to their aforementioned namesakes. + * an immutable Union-Find when {@code build()} is called. From then onward, previously modifying + * methods will not cause further modifications to the Union-Find instance inside. See documentation + * in {@link ParentPointerTreeUnionFind} for explanations on {@code union()}, {@code add()} and + * {@code addAll()} as the methods in this builder simply pass to their aforementioned namesakes. * * @param type of elements added to the Union-Find */ @@ -30,14 +31,23 @@ public abstract class AbstractImmutableParentPointerTreeBuilder { @SuppressWarnings("Immutable") final ParentPointerTreeUnionFind unionFind; + // prevents further modifications after build() + // is never modified once having been switched to false + @SuppressWarnings("Immutable") + boolean modificationsAllowed; + protected AbstractImmutableParentPointerTreeBuilder(UnionType pUnionType) { unionFind = new ParentPointerTreeUnionFind<>(pUnionType); + modificationsAllowed = true; } @CanIgnoreReturnValue public AbstractImmutableParentPointerTreeBuilder union(T pE1, T pE2) { - unionFind.union(pE1, pE2); + if (modificationsAllowed) { + + unionFind.union(pE1, pE2); + } return this; } @@ -45,7 +55,10 @@ public AbstractImmutableParentPointerTreeBuilder union(T pE1, T pE2) { @CanIgnoreReturnValue public AbstractImmutableParentPointerTreeBuilder add(Set pSet) { - unionFind.add(pSet); + if (modificationsAllowed) { + + unionFind.add(pSet); + } return this; } @@ -53,11 +66,15 @@ public AbstractImmutableParentPointerTreeBuilder add(Set pSet) { @CanIgnoreReturnValue public AbstractImmutableParentPointerTreeBuilder addAll(Collection> pSets) { - unionFind.addAll(pSets); + if (modificationsAllowed) { + + unionFind.addAll(pSets); + } return this; } // get map from mutable Union-Find instance and convert to immutable map, then pass to constructor + // set modificationsAllowed to false!! public abstract AbstractImmutableUnionFind build(); } diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java index 99b6a9223..32c367466 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableParentPointerTreeUnionFind.java @@ -134,6 +134,9 @@ public static AbstractImmutableParentPointerTreeBuilder getBuilder( @Override public ImmutableParentPointerTreeUnionFind build() { + + modificationsAllowed = false; + return new ImmutableParentPointerTreeUnionFind<>(ImmutableMap.copyOf(unionFind.allNodes)); } } diff --git a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java index 60f90a3b6..06e3050fa 100644 --- a/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java +++ b/src/org/sosy_lab/common/collect/union_find/ImmutableSortedParentPointerTreeUnionFind.java @@ -139,6 +139,9 @@ public static > Builder getBuilder(UnionType pUnionTy @Override public ImmutableSortedParentPointerTreeUnionFind build() { + + modificationsAllowed = false; + return new ImmutableSortedParentPointerTreeUnionFind<>( ImmutableMap.copyOf(unionFind.allNodes)); } diff --git a/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindTest.java b/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindTest.java index d3873a0e7..0f78d975f 100644 --- a/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindTest.java +++ b/src/org/sosy_lab/common/collect/union_find/tests/ImmutableParentPointerTreeUnionFindTest.java @@ -364,11 +364,11 @@ public void testBuilder_build_laterMutationsDoNotAffectPreviousResult() { assertThat(firstSortedResult.contains(2)).isFalse(); assertThat(firstSortedResult.getAllSubsets()).hasSize(1); - assertThat(secondUnsortedResult.contains(2)).isTrue(); - assertThat(secondUnsortedResult.getAllSubsets()).hasSize(2); + assertThat(secondUnsortedResult.contains(2)).isFalse(); + assertThat(secondUnsortedResult.getAllSubsets()).hasSize(1); - assertThat(secondSortedResult.contains(2)).isTrue(); - assertThat(secondSortedResult.getAllSubsets()).hasSize(2); + assertThat(secondSortedResult.contains(2)).isFalse(); + assertThat(secondSortedResult.getAllSubsets()).hasSize(1); } @Test From 1803638055cca4009216f96cba409f3d7c6da0b0 Mon Sep 17 00:00:00 2001 From: Colleen Date: Mon, 7 Sep 2026 18:57:10 +0200 Subject: [PATCH 183/183] Extend benchmarks to include naive Union-Find implementation --- .../benchmarking/Algs4DatasetBenchmark.java | 16 +++++++++------- .../FindNewToOldSingleSetBenchmark.java | 16 +++++++++------- .../FindOldToNewSingleSetBenchmark.java | 16 +++++++++------- ...onSingleElementsIntoExistingSetBenchmark.java | 16 +++++++++------- 4 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java index d8be3d3fe..b13b28ece 100644 --- a/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/Algs4DatasetBenchmark.java @@ -30,6 +30,7 @@ import org.sosy_lab.common.collect.union_find.PersistentSortedParentPointerTreeUnionFind; import org.sosy_lab.common.collect.union_find.PersistentUnionFind; import org.sosy_lab.common.collect.union_find.SortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.SortedTreeSetUnionFind; import org.sosy_lab.common.collect.union_find.UnionFind; public final class Algs4DatasetBenchmark { @@ -38,6 +39,7 @@ public final class Algs4DatasetBenchmark { public static void main(String[] args) { + @Var boolean naive = false; @Var boolean immutable = false; @Var boolean persistent = false; @Var boolean sorted = false; @@ -53,13 +55,11 @@ public static void main(String[] args) { for (String string : args) { switch (string) { - case "-immutable" -> { - immutable = true; - } + case "-naive" -> naive = true; - case "-persistent" -> { - persistent = true; - } + case "-immutable" -> immutable = true; + + case "-persistent" -> persistent = true; case "-sorted" -> sorted = true; @@ -93,7 +93,9 @@ public static void main(String[] args) { Iterator iterator = unionInput.iterator(); - if (!immutable && !persistent && !sorted) { + if (naive) { + mutable(new SortedTreeSetUnionFind<>(), iterator); + } else if (!immutable && !persistent && !sorted) { if (unionByRank) { mutable(new ParentPointerTreeUnionFind<>(UnionType.UNION_BY_RANK), iterator); } else { diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/FindNewToOldSingleSetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/FindNewToOldSingleSetBenchmark.java index bc08fa9b7..40ca67586 100644 --- a/src/org/sosy_lab/common/collect/union_find/benchmarking/FindNewToOldSingleSetBenchmark.java +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/FindNewToOldSingleSetBenchmark.java @@ -21,6 +21,7 @@ import org.sosy_lab.common.collect.union_find.PersistentSortedParentPointerTreeUnionFind; import org.sosy_lab.common.collect.union_find.PersistentUnionFind; import org.sosy_lab.common.collect.union_find.SortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.SortedTreeSetUnionFind; import org.sosy_lab.common.collect.union_find.UnionFind; public final class FindNewToOldSingleSetBenchmark { @@ -29,6 +30,7 @@ public final class FindNewToOldSingleSetBenchmark { public static void main(String[] args) { + @Var boolean naive = false; @Var boolean immutable = false; @Var boolean persistent = false; @Var boolean sorted = false; @@ -42,13 +44,11 @@ public static void main(String[] args) { for (String string : args) { switch (string) { - case "-immutable" -> { - immutable = true; - } + case "-naive" -> naive = true; - case "-persistent" -> { - persistent = true; - } + case "-immutable" -> immutable = true; + + case "-persistent" -> persistent = true; case "-sorted" -> sorted = true; @@ -66,7 +66,9 @@ public static void main(String[] args) { } } - if (!immutable && !persistent && !sorted) { + if (naive) { + mutable(new SortedTreeSetUnionFind<>(), n); + } else if (!immutable && !persistent && !sorted) { if (unionByRank) { mutable(new ParentPointerTreeUnionFind<>(UnionType.UNION_BY_RANK), n); } else { diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/FindOldToNewSingleSetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/FindOldToNewSingleSetBenchmark.java index 69a089de0..6a6ce3949 100644 --- a/src/org/sosy_lab/common/collect/union_find/benchmarking/FindOldToNewSingleSetBenchmark.java +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/FindOldToNewSingleSetBenchmark.java @@ -21,6 +21,7 @@ import org.sosy_lab.common.collect.union_find.PersistentSortedParentPointerTreeUnionFind; import org.sosy_lab.common.collect.union_find.PersistentUnionFind; import org.sosy_lab.common.collect.union_find.SortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.SortedTreeSetUnionFind; import org.sosy_lab.common.collect.union_find.UnionFind; public final class FindOldToNewSingleSetBenchmark { @@ -29,6 +30,7 @@ public final class FindOldToNewSingleSetBenchmark { public static void main(String[] args) { + @Var boolean naive = false; @Var boolean immutable = false; @Var boolean persistent = false; @Var boolean sorted = false; @@ -42,13 +44,11 @@ public static void main(String[] args) { for (String string : args) { switch (string) { - case "-immutable" -> { - immutable = true; - } + case "-naive" -> naive = true; - case "-persistent" -> { - persistent = true; - } + case "-immutable" -> immutable = true; + + case "-persistent" -> persistent = true; case "-sorted" -> sorted = true; @@ -66,7 +66,9 @@ public static void main(String[] args) { } } - if (!immutable && !persistent && !sorted) { + if (naive) { + mutable(new SortedTreeSetUnionFind<>(), n); + } else if (!immutable && !persistent && !sorted) { if (unionByRank) { mutable(new ParentPointerTreeUnionFind<>(UnionType.UNION_BY_RANK), n); } else { diff --git a/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java b/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java index 2f70a6734..322a1ffc6 100644 --- a/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java +++ b/src/org/sosy_lab/common/collect/union_find/benchmarking/UnionSingleElementsIntoExistingSetBenchmark.java @@ -22,6 +22,7 @@ import org.sosy_lab.common.collect.union_find.PersistentSortedParentPointerTreeUnionFind; import org.sosy_lab.common.collect.union_find.PersistentUnionFind; import org.sosy_lab.common.collect.union_find.SortedParentPointerTreeUnionFind; +import org.sosy_lab.common.collect.union_find.SortedTreeSetUnionFind; import org.sosy_lab.common.collect.union_find.UnionFind; public final class UnionSingleElementsIntoExistingSetBenchmark { @@ -30,6 +31,7 @@ public final class UnionSingleElementsIntoExistingSetBenchmark { public static void main(String[] args) { + @Var boolean naive = false; @Var boolean immutable = false; @Var boolean persistent = false; @Var boolean sorted = false; @@ -43,13 +45,11 @@ public static void main(String[] args) { for (String string : args) { switch (string) { - case "-immutable" -> { - immutable = true; - } + case "-naive" -> naive = true; - case "-persistent" -> { - persistent = true; - } + case "-immutable" -> immutable = true; + + case "-persistent" -> persistent = true; case "-sorted" -> sorted = true; @@ -67,7 +67,9 @@ public static void main(String[] args) { } } - if (!immutable && !persistent && !sorted) { + if (naive) { + mutable(new SortedTreeSetUnionFind<>(), n); + } else if (!immutable && !persistent && !sorted) { if (unionByRank) { mutable(new ParentPointerTreeUnionFind<>(UnionType.UNION_BY_RANK), n); } else {