/*
* Copyright (C) 2022 Dominik Klumpp (klumpp@informatik.uni-freiburg.de)
* Copyright (C) 2022 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.util.AbstractCollection;
import java.util.BitSet;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import de.uni_freiburg.informatik.ultimate.util.LazyInt;
import de.uni_freiburg.informatik.ultimate.util.datastructures.poset.ILattice;
/**
* A data structure that efficiently represents (immutable) subsets of a given set, using bit vectors ({@link BitSet}).
*
* @author Dominik Klumpp (klumpp@informatik.uni-freiburg.de)
*
* @param
* The type of elements in the set.
*/
public final class BitSubSet extends AbstractCollection {
private final Factory mFactory;
private final BitSet mBitSet;
private final LazyInt mHash;
private BitSubSet(final Factory factory, final BitSet bitset) {
mFactory = factory;
mBitSet = bitset;
mHash = new LazyInt(mBitSet::hashCode);
}
@Override
public boolean contains(final Object o) {
final Integer index = mFactory.getIndex(o);
return index != null && mBitSet.get(index);
}
public boolean containsAll(final BitSubSet> c) {
assert c.mFactory == mFactory : "Cannot compare sets from different universes";
final BitSet diff = BitSet.valueOf(c.mBitSet.toLongArray());
diff.andNot(mBitSet);
return diff.isEmpty();
}
@Override
public Iterator iterator() {
return new BitSubSetIterator<>(mFactory.mElements, mBitSet);
}
@Override
public int size() {
return mBitSet.cardinality();
}
@Override
public boolean isEmpty() {
return mBitSet.isEmpty();
}
@Override
public int hashCode() {
return mHash.get();
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o instanceof BitSubSet>) {
final BitSubSet> b = (BitSubSet>) o;
if (b.mFactory == mFactory) {
return b.mBitSet.equals(mBitSet);
}
}
return super.equals(o);
}
private static class BitSubSetIterator implements Iterator {
private final Object[] mElements;
private final BitSet mBitSet;
private int mIndex;
public BitSubSetIterator(final Object[] elements, final BitSet bitset) {
mElements = elements;
mBitSet = bitset;
mIndex = mBitSet.nextSetBit(0);
}
@Override
public boolean hasNext() {
return mIndex >= 0;
}
@Override
public E next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
final E elem = (E) mElements[mIndex];
mIndex = mBitSet.nextSetBit(mIndex + 1);
return elem;
}
}
/**
* A factory to create {@link BitSubSet} instances representing subsets of a shared universe.
*
* This class also implements an {@link ILattice} for the created instances.
*
* @author Dominik Klumpp (klumpp@informatik.uni-freiburg.de)
*
* @param
* The type of elements in the universe
*/
public static class Factory implements ILattice> {
private static final String LEFT_NOT_CREATED_BY_FACTORY = "Operand 'left' not created by this factory";
private static final String RIGHT_NOT_CREATED_BY_FACTORY = "Operand 'right' not created by this factory";
private final Object[] mElements;
private final Map mIndexMap;
private BitSubSet mUniverse;
private BitSubSet mEmpty;
/**
* Create a new factory capable of creating subsets of the given universe set.
*
* @param universe
* The set of all elements that can appear in the created subsets.
*/
public Factory(final Set universe) {
mElements = universe.stream().sorted(Comparator.comparing(Objects::hashCode)).toArray();
mIndexMap = new HashMap<>(universe.size());
for (int i = 0; i < mElements.length; ++i) {
mIndexMap.put((E) mElements[i], i);
}
}
/**
* Turn an ordinary {@link Set} instance into a {@link BitSubSet}.
*
* This is only allowed if the set contains only elements in this instance's {@link #universe()}. Otherwise the
* operation will fail.
*
* @param set
* The set to be converted into a {@link BitSubSet}
* @return an equivalent {@link BitSubSet}, i.e., a set such that {@link Set#equals(Object)} will return true
* when given the original set
*/
public BitSubSet valueOf(final Set set) {
final BitSet bitset = new BitSet(mElements.length);
for (final E elem : set) {
final int index = getIndex(elem);
bitset.set(index);
}
return new BitSubSet<>(this, bitset);
}
/**
* @return The set of all elements in this instance's universe
*/
public BitSubSet universe() {
if (mUniverse == null) {
final BitSet bitset = new BitSet(mElements.length);
flip(bitset);
mUniverse = new BitSubSet<>(this, bitset);
}
return mUniverse;
}
/**
* @return a representation of the empty set
*/
public BitSubSet empty() {
if (mEmpty == null) {
mEmpty = new BitSubSet<>(this, new BitSet());
}
return mEmpty;
}
/**
* @param operand
* A set created by this instance
* @return The given set's complement relative to the {@link #universe()}.
*/
public BitSubSet complement(final BitSubSet operand) {
assert operand.mFactory == this : "operand not created by this factory";
final BitSet bitset = copy(operand.mBitSet);
flip(bitset);
return new BitSubSet<>(this, bitset);
}
/**
* @param left
* A set created by this instance
* @param right
* A set created by this instance
* @return A set representing the union of the given sets
*/
public BitSubSet union(final BitSubSet left, final BitSubSet right) {
assert left.mFactory == this : LEFT_NOT_CREATED_BY_FACTORY;
assert right.mFactory == this : RIGHT_NOT_CREATED_BY_FACTORY;
if (left == right) {
return left;
}
if (right.isEmpty()) {
return left;
}
if (left.isEmpty()) {
return right;
}
final BitSet union = copy(left.mBitSet);
union.or(right.mBitSet);
return new BitSubSet<>(this, union);
}
/**
* @param left
* A set created by this instance
* @param right
* A set created by this instance
* @return A set representing the intersection of the given sets
*/
public BitSubSet intersection(final BitSubSet left, final BitSubSet right) {
assert left.mFactory == this : LEFT_NOT_CREATED_BY_FACTORY;
assert right.mFactory == this : RIGHT_NOT_CREATED_BY_FACTORY;
if (left == right) {
return left;
}
if (right.isEmpty()) {
return empty();
}
if (left.isEmpty()) {
return empty();
}
final BitSet inter = copy(left.mBitSet);
inter.and(right.mBitSet);
return new BitSubSet<>(this, inter);
}
/**
* @param left
* A set created by this instance
* @param right
* A set created by this instance
* @return A set representing the difference of the given sets
*/
public BitSubSet difference(final BitSubSet left, final BitSubSet right) {
assert left.mFactory == this : LEFT_NOT_CREATED_BY_FACTORY;
assert right.mFactory == this : RIGHT_NOT_CREATED_BY_FACTORY;
if (left.isEmpty() || right.isEmpty()) {
return left;
}
final BitSet diff = copy(left.mBitSet);
diff.andNot(right.mBitSet);
return new BitSubSet<>(this, diff);
}
private void flip(final BitSet bitset) {
if (mElements.length == 0) {
return;
}
bitset.flip(0, mElements.length);
}
private static BitSet copy(final BitSet bitset) {
return (BitSet) bitset.clone();
}
private final Integer getIndex(final Object element) {
return mIndexMap.get(element);
}
@Override
public ComparisonResult compare(final BitSubSet o1, final BitSubSet o2) {
assert o1.mFactory == this && o2.mFactory == this : "operand not created by this factory";
if (o1.equals(o2)) {
return ComparisonResult.EQUAL;
}
if (o2.containsAll(o1)) {
return ComparisonResult.STRICTLY_SMALLER;
}
if (o1.containsAll(o2)) {
return ComparisonResult.STRICTLY_GREATER;
}
return ComparisonResult.INCOMPARABLE;
}
@Override
public BitSubSet getBottom() {
return empty();
}
@Override
public BitSubSet getTop() {
return universe();
}
@Override
public BitSubSet supremum(final BitSubSet h1, final BitSubSet h2) {
return union(h1, h2);
}
@Override
public BitSubSet infimum(final BitSubSet h1, final BitSubSet h2) {
return intersection(h1, h2);
}
}
}