/* * Copyright (C) 2017 Matthias Heizmann (heizmann@informatik.uni-freiburg.de) * Copyright (C) 2017 Daniel Dietsch (dietsch@informatik.uni-freiburg.de) * Copyright (C) 2017 University of Freiburg * * This file is part of the ULTIMATE Util Library. * * The ULTIMATE Util Library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The ULTIMATE Util Library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the ULTIMATE Util Library. If not, see . * * Additional permission under GNU GPL version 3 section 7: * If you modify the ULTIMATE Util Library, or any covered work, by linking * or combining it with Eclipse RCP (or a modified version of Eclipse RCP), * containing parts covered by the terms of the Eclipse Public License, the * licensors of the ULTIMATE Util Library grant you additional permission * to convey the resulting work. */ package de.uni_freiburg.informatik.ultimate.util.datastructures; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import de.uni_freiburg.informatik.ultimate.util.datastructures.relation.NestedMap2; import de.uni_freiburg.informatik.ultimate.util.datastructures.relation.Triple; /** * * @author Daniel Dietsch (dietsch@informatik.uni-freiburg.de) * @author Matthias Heizmann (heizmann@informatik.uni-freiburg.de) * * TODO: Merge {@link SetOperations} in this class. */ public class DataStructureUtils { private DataStructureUtils() { // do not instantiate } /** * Constructs a new {@link Set} that contains only elements that occur in set1 and that occur in set2. */ public static Set intersection(final Set set1, final Set set2) { final Set larger; final Set smaller; if (set1.size() > set2.size()) { larger = set1; smaller = set2; } else { larger = set2; smaller = set1; } return smaller.stream().filter(larger::contains).collect(Collectors.toSet()); } /** * Constructs a new {@link Set} that contains only elements that occur in all sets that are in the list inputSets. * Modifies the list inputSets but not the contained sets. */ public static Set intersection(final List> inputSets) { Collections.sort(inputSets, (o1, o2) -> o1.size() - o2.size()); Set result; final Iterator> it = inputSets.iterator(); result = new HashSet<>(it.next()); while (it.hasNext()) { result.retainAll(it.next()); } return result; } /** * @return an Optional that contains an element that is contained in both sets or is empty if both sets do not * have a common element. */ public static Optional getSomeCommonElement(final Set set1, final Set set2) { if (set1.isEmpty() || set2.isEmpty()) { // at least one is empty, there is no common element return Optional.empty(); } final Set larger; final Set smaller; if (set1.size() > set2.size()) { larger = set1; smaller = set2; } else { larger = set2; smaller = set1; } return smaller.stream().filter(larger::contains).findFirst(); } /** * Construct a {@link Set} that contains all elements of set1 that are not in set2. */ public static Set difference(final Set a, final Set b) { if (a.isEmpty()) { return Collections.emptySet(); } if (b.isEmpty()) { return new HashSet<>(a); } return a.stream().filter(elem -> !b.contains(elem)).collect(Collectors.toSet()); } @SafeVarargs public static Set toSet(final T... elems) { if (elems == null || elems.length == 0) { return Collections.emptySet(); } return new HashSet<>(Arrays.asList(elems)); } /** * Construct a {@link Set} that contains all elements of set1 and set2. */ public static Set union(final Set a, final Set b) { final Set rtr = DataStructureUtils.getFreshSet(a, a.size() + b.size()); rtr.addAll(b); return rtr; } @SafeVarargs public static Set union(final Set... a) { if (a.length == 0) { return Collections.emptySet(); } final int size = Arrays.stream(a).mapToInt(Set::size).sum(); final Set rtr = DataStructureUtils.getFreshSet(a[0], size); Arrays.stream(a).forEach(rtr::addAll); return rtr; } /** * Construct a {@link Set} that contains all elements of oldSet and has the capacity of oldSet. */ public static Set getFreshSet(final Set oldSet) { return DataStructureUtils.getFreshSet(oldSet, oldSet.size()); } /** * Construct a {@link Set} that contains all elements of oldSet and starts with an initial capacity. */ public static Set getFreshSet(final Set oldSet, final int capacity) { final Set rtr = new HashSet<>(capacity); rtr.addAll(oldSet); return rtr; } /** * Returns true, if the given sets have at least one common element. * * Should be quicker than first computing the intersection and the calling isEmpty() on it. * * @param set1 * @param set2 * @return */ public static boolean haveNonEmptyIntersection(final Set set1, final Set set2) { final Set larger; final Set smaller; if (set1.size() > set2.size()) { larger = set1; smaller = set2; } else { larger = set2; smaller = set1; } return smaller.stream().anyMatch(larger::contains); } /** * @return Both sets are disjoint */ public static boolean haveEmptyIntersection(final Set set1, final Set set2) { return !haveNonEmptyIntersection(set1, set2); } /** * Returns true, if the given collection and set have at least one common * element. */ public static boolean haveNonEmptyIntersection(final Collection collection, final Set set) { return collection.stream().anyMatch(set::contains); } /** * @return The collection and the set are disjoint */ public static boolean haveEmptyIntersection(final Collection collection, final Set set) { return !haveNonEmptyIntersection(collection, set); } public static String prettyPrint(final Set set) { final StringBuilder sb = new StringBuilder(); sb.append("Set: \n"); String sep = ""; for (final E e : set) { sb.append(sep); sb.append(String.format("\t%s", e)); sep = "\n"; } return sb.toString(); } public static String prettyPrint(final Map map) { // TODO: implement a width check for the keys, adapt column with, and/or cut their size at some length final StringBuilder sb = new StringBuilder(); sb.append("Map: \n"); String sep = ""; for (final Entry en : map.entrySet()) { sb.append(sep); sb.append(String.format("\t%-80s : \t %s", en.getKey(), en.getValue())); sep = "\n"; } return sb.toString(); } public static String prettyPrint(final NestedMap2 map) { final StringBuilder sb = new StringBuilder(); sb.append("NestedMap2: \n"); String sep = ""; for (final Triple en : map.entrySet()) { sb.append(sep); sb.append(String.format("\t%-80s : \t %-40s : \t %s", en.getFirst(), en.getSecond(), en.getThird())); sep = "\n"; } return sb.toString(); } @SuppressWarnings("unchecked") public static T[] concat(final T[] a1, final T[] a2) { if (a1 == null) { return a2; } if (a2 == null) { return a1; } if (a1.length == 0) { return a2; } if (a2.length == 0) { return a1; } final T[] dest = (T[]) Array.newInstance(a1.getClass().getComponentType(), a1.length + a2.length); System.arraycopy(a1, 0, dest, 0, a1.length); System.arraycopy(a2, 0, dest, a1.length, a2.length); return dest; } public static List concat(final List a1, final List a2) { if (a1 == null) { return a2; } if (a2 == null) { return a1; } if (a1.isEmpty()) { return a2; } if (a2.isEmpty()) { return a1; } final List rtr = new ArrayList<>(a1.size() + a2.size() + 1); rtr.addAll(a1); rtr.addAll(a2); return rtr; } /** * Rather naive powerset implementation * * @param baseSet * @return */ public static Collection> powerSet(final Set baseSet) { final Collection> result = new ArrayList<>(); final List baseList = new ArrayList<>(baseSet); final double pow = Math.pow(2, baseSet.size()); for (int bin = 0; bin < pow; bin++) { final Set subset = new HashSet<>(); // note that this char array always starts with a 1 (obviously) somehow, so we have to "fill up" leading 0s final char[] binS = Integer.toBinaryString(bin).toCharArray(); for (int pos = 0; pos < binS.length; pos++) { if (binS[pos] == '1') { subset.add(baseList.get(pos + baseSet.size() - binS.length)); } else { assert binS[pos] == '0'; } } result.add(subset); } return result; } /** * Construct Map that contains a pair (v,k) in the input contained the pair (k,v). If the input is not * injective the behavior is not specified. */ public static Map constructReverseMapping(final Map map) { return map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); } /** * * @return true iff both sets consist of the same elements or are empty, false otherwise. */ public static boolean isDifferent(final Set a, final Set b) { if (a.isEmpty() && b.isEmpty()) { return false; } if (a.size() != b.size()) { return true; } return a.stream().anyMatch(e -> !b.contains(e)); } public static boolean differenceIsEmpty(final T[] a, final T[] b) { if (a == null || a.length == 0) { return true; } if (b == null || b.length == 0) { return false; } final Set setB = new HashSet<>(Arrays.asList(b)); for (int i = 0; i < a.length; ++i) { if (!setB.contains(a[i])) { return false; } } return true; } /** * Adds all suffixes from suffix to the prefixes from prefix, i.e. * * * crossProduct([[A1,B1], [A2,B2]], [[C1,D1], [C2,D2]]) * returns * [[A1,B1,C1,D1],[A1,B1,C2,D2] [A2,B2,C1,D1],[A2,B2,C2,D2]] * * */ public static List> crossProduct(final List> prefix, final List> suffix) { if (prefix.isEmpty()) { return suffix; } if (suffix.isEmpty()) { return prefix; } final List> rtr = new ArrayList<>(prefix.size() * suffix.size()); for (final List p : prefix) { for (final List s : suffix) { rtr.add(concat(p, s)); } } return rtr; } /** * Gets an element from a given {@link Iterable}, and checks (with assertions) that this element is the only one. * * @param * The type of elements * * @param elements * The {@link Iterable} from which the first element is retrieved. * @param thing * A string describing the kind of element that is retrieved. Used in the assertion error messages * @return The first (and only) element */ public static E getOneAndOnly(final Iterable elements, final String thing) { final Iterator iterator = elements.iterator(); assert iterator.hasNext() : "Must have at least one " + thing; final E elem = iterator.next(); assert !iterator.hasNext() : "Only one " + thing + " allowed"; return elem; } /** * Gets an element from a given {@link Iterable}, if there is one. Also checks (with assertions) that there are not * more than one elements. * * @param * The type of elements * * @param elements * The {@link Iterable} from which the first element is retrieved. * @param errMsg * An error message used in the assertion that there are not more elements * @return The first (and only) element, if there is one */ public static Optional getOnly(final Iterable elements, final String errMsg) { final Iterator iterator = elements.iterator(); if (!iterator.hasNext()) { return Optional.empty(); } final E elem = iterator.next(); assert !iterator.hasNext() : errMsg; return Optional.of(elem); } public static Stream filteredCast(final Stream s, final Class c) { return s.filter(a -> c.isAssignableFrom(a.getClass())).map(c::cast); } /** * Converts a stream into an unmodifiable set (as returned by {@link Set#of()}. * * @param * The type of elements * @param stream * a stream of elements * @return the set * * @throws IllegalArgumentException * if the stream contains multiple occurrences of the same element */ public static Set asSet(final Stream stream) { final Object[] elements = stream.toArray(); return (Set) Set.of(elements); } /** * Return an unmodifiable view of the input set. Use * {@link Collections#emptySet} or {@link Collections#singleton} if possible to * get a memory-efficient representation. */ public static Set getUnmodifiable(final Set set) { if (set.isEmpty()) { return Collections.emptySet(); } else if (set.size() == 1) { return Collections.singleton(set.iterator().next()); } else { return Collections.unmodifiableSet(set); } } /** * Construct a new {@link ArrayList} that is a almost a copy of the input list * but where one given index is missing. * */ public static List copyAllButOne(final List list, final int indexToBeRemoved) { if (indexToBeRemoved < 0 || indexToBeRemoved >= list.size()) { throw new IllegalArgumentException("Index where we remove must be inside array"); } final List result = new ArrayList<>(list.size() -1); int i = 0; for (final E elem : list) { if (i != indexToBeRemoved) { result.add(elem); } i++; } assert i == list.size(); assert result.size() == list.size() - 1; return result; } }