package com.juliasoft.beedeedee.factories;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.juliasoft.beedeedee.bdd.Assignment;
import com.juliasoft.beedeedee.bdd.BDD;
import com.juliasoft.beedeedee.bdd.ReplacementWithExistingVarException;
import com.juliasoft.beedeedee.bdd.UnsatException;
import com.juliasoft.beedeedee.factories.EquivalenceRelation.Filter;
/**
* A BDD factory using the ER representation.
*/
public class ERFactory extends Factory {
public ERFactory(int utSize, int cacheSize) {
super(utSize, cacheSize);
}
@Override
public BDD makeZero() {
try (GCLock lock = new GCLock()) {
return new BDDER(ZERO, EquivalenceRelation.empty, false);
}
}
@Override
public BDD makeOne() {
try (GCLock lock = new GCLock()) {
return new BDDER(ONE, EquivalenceRelation.empty, false);
}
}
@Override
public BDD makeVar(int v) {
try (GCLock lock = new GCLock()) {
return new BDDER(innerMakeVar(v), EquivalenceRelation.empty, false);
}
}
@Override
public BDD makeNotVar(int v) {
try (GCLock lock = new GCLock()) {
return new BDDER(innerMakeNotVar(v), EquivalenceRelation.empty, false);
}
}
@Override
public int nodeCount(Collection<BDD> bdds) {
throw new RuntimeException("Not yet implemented"); //TODO
}
private class UsefulLeaders implements Filter {
private final int bdd;
private UsefulLeaders(int bdd) {
this.bdd = bdd;
}
@Override
public boolean accept(BitSet eqClass) {
return accept(eqClass, bdd);
}
private boolean accept(BitSet eqClass, int bdd) {
if (bdd < FIRST_NODE_NUM)
return false;
else {
int var = ut.var(bdd);
return (eqClass.nextSetBit(0) != var && eqClass.get(var))
|| accept(eqClass, ut.low(bdd)) || accept(eqClass, ut.high(bdd));
}
}
}
private class RenamerWithLeader {
private final EquivalenceRelation equivalenceRelations;
private final int resultId;
private final int maxVar;
private final RenameWithLeaderInternalCache rwlic;
private RenamerWithLeader(int id, EquivalenceRelation equivalenceRelations) {
this.equivalenceRelations = equivalenceRelations;
this.maxVar = equivalenceRelations.maxVar();
this.rwlic = new RenameWithLeaderInternalCache(20);
this.resultId = renameWithLeader(id, 0, new BitSet());
}
private int renameWithLeader(final int bdd, final int level, final BitSet t) {
int var;
if (bdd < FIRST_NODE_NUM || (var = ut.var(bdd)) > maxVar)
return bdd;
int cached = rwlic.get(bdd, level, t);
if (cached >= 0)
return cached;
Filter filter = new UsefulLeaders(bdd);
// we further filter here, since some equivalence class might be irrelevant
// from the residual bdd, but not for the whole bdd
int minLeader = equivalenceRelations.getMinLeaderGreaterOrEqualtTo(level, var, filter), result, leader;
if (minLeader >= 0) {
BitSet augmented = (BitSet) t.clone();
augmented.set(minLeader);
result = MK(minLeader++, renameWithLeader(bdd, minLeader, t), renameWithLeader(bdd, minLeader, augmented));
}
else if ((leader = equivalenceRelations.getLeader(var, filter)) < 0)
result = MK(var++, renameWithLeader(ut.low(bdd), var, t), renameWithLeader(ut.high(bdd), var, t));
else if (leader == var) {
BitSet augmented = (BitSet) t.clone();
augmented.set(var);
result = MK(var++, renameWithLeader(ut.low(bdd), var, t), renameWithLeader(ut.high(bdd), var, augmented));
}
else if (t.get(leader))
result = renameWithLeader(ut.high(bdd), var + 1, t);
else
result = renameWithLeader(ut.low(bdd), var + 1, t);
rwlic.put(bdd, level, t, result);
return result;
}
private class RenameWithLeaderInternalCache {
private final int size;
private final int[] cache;
private final int[] levels;
private final BitSet[] ts;
private final int[] results;
private RenameWithLeaderInternalCache(int size) {
this.size = size;
this.cache = new int[size];
this.levels = new int[size];
this.ts = new BitSet[size];
Arrays.fill(cache, -1);
this.results = new int[size];
}
private int get(int f, int level, BitSet t) {
int pos = hash(f, level, t);
if (cache[pos] == f && levels[pos] == level && ts[pos].equals(t))
return results[pos];
return -1;
}
private void put(int f, int level, BitSet t, int res) {
int pos = hash(f, level, t);
cache[pos] = f;
levels[pos] = level;
ts[pos] = (BitSet) t.clone();
results[pos] = res;
}
private int hash(int f, int level, BitSet t) {
return Math.abs((f ^ (level << 24) ^ t.hashCode()) % size);
}
}
}
public static class EquivResult {
private BitSet entailed;
private BitSet disentailed;
private ArrayList<Pair> equiv;
private final static EquivResult emptyEquivResult = new EquivResult();
private EquivResult() {
this(new BitSet(), new BitSet(), new ArrayList<Pair>());
}
private EquivResult(EquivResult parent) {
this(parent.entailed, parent.disentailed, parent.equiv);
}
private EquivResult(BitSet entailed, BitSet disentailed, ArrayList<Pair> equiv) {
this.entailed = entailed;
this.disentailed = disentailed;
this.equiv = equiv;
}
}
private class EquivVarsCalculator {
private final EquivCache equivCache = ut.getEquivCache();
private final Iterable<Pair> result;
private EquivVarsCalculator(int id) {
this.result = equivVars(id).equiv;
}
@SuppressWarnings("unchecked")
private EquivResult equivVars(int bdd) {
if (bdd < FIRST_NODE_NUM)
return EquivResult.emptyEquivResult;
EquivResult result = equivCache.get(bdd);
if (result != null)
return result;
int var = ut.var(bdd);
int low = ut.low(bdd);
int high = ut.high(bdd);
if (high == ZERO) {
if (low != ONE) {
result = new EquivResult(equivVars(low));
result.disentailed = (BitSet) result.disentailed.clone();
result.disentailed.set(var);
int maxd = result.disentailed.length() - 1;
if (var != maxd) {
result.equiv = (ArrayList<Pair>) result.equiv.clone();
result.equiv.add(new Pair(var, maxd));
}
}
else {
result = new EquivResult();
result.disentailed.set(var);
}
}
else if (low == ZERO) {
if (high != ONE) {
result = new EquivResult(equivVars(high));
result.entailed = (BitSet) result.entailed.clone();
result.entailed.set(var);
int maxe = result.entailed.length() - 1;
if (var != maxe) {
result.equiv = (ArrayList<Pair>) result.equiv.clone();
result.equiv.add(new Pair(var, maxe));
}
}
else {
result = new EquivResult();
result.entailed.set(var);
}
}
else if (high != ONE && low != ONE) {
EquivResult resultTrue = equivVars(high);
EquivResult resultFalse = equivVars(low);
result = new EquivResult(resultTrue);
result.entailed = (BitSet) result.entailed.clone();
result.entailed.and(resultFalse.entailed);
result.disentailed = (BitSet) result.disentailed.clone();
result.disentailed.and(resultFalse.disentailed);
// size does matter for efficiency
if (resultFalse.equiv.size() < result.equiv.size()) {
ArrayList<Pair> temp = result.equiv;
result.equiv = (ArrayList<Pair>) resultFalse.equiv.clone();
result.equiv.retainAll(temp);
}
else {
result.equiv = (ArrayList<Pair>) result.equiv.clone();
result.equiv.retainAll(resultFalse.equiv);
}
BitSet intersection = (BitSet) resultTrue.entailed.clone();
intersection.and(resultFalse.disentailed);
int length = intersection.length();
if (length > 0)
result.equiv.add(new Pair(var, length - 1));
}
else
result = EquivResult.emptyEquivResult;
equivCache.put(bdd, result);
return result;
}
}
/**
* A {@link BDD} implementation that separates information on equivalent
* variables from a robdd.
*/
public class BDDER extends BDDImpl {
private EquivalenceRelation l;
/**
* Constructs a BDDER by normalizing the given bdd.
*
* @param bdd the starting BDD, which is immediately freed if not equal to
* (same as) normalized
*/
BDDER(int id) {
this(id, EquivalenceRelation.empty, true);
}
/**
* Copy constructor.
*/
private BDDER(BDDER parent) {
super(parent.id);
this.l = parent.l;
}
/**
* Normalizing constructor.
*
* @param id the BDD id
* @param l the equivalence relation
* @param shouldNormalize true if and only if normalization must be applied
*/
private BDDER(int id, EquivalenceRelation l, boolean shouldNormalize) {
super(id);
if (id == ZERO)
l = EquivalenceRelation.empty;
this.l = l;
if (shouldNormalize)
normalize();
}
private void normalize() {
if (id >= FIRST_NODE_NUM && (id >= NUMBER_OF_PREALLOCATED_NODES || !l.isEmpty())) {
EquivalenceRelation eNew = l, eOld;
int newId = id, oldId;
do {
eNew = (eOld = eNew).addPairs(new EquivVarsCalculator(newId).result);
newId = renameWithLeader(oldId = newId, eNew);
}
while (newId != oldId || eNew != eOld);
setId(newId);
this.l = eNew;
if (newId == ZERO) { // && !l.isEmpty()) {
l = EquivalenceRelation.empty;
//TODO throw new RuntimeException("normalizzazione di false con l = " + l);
}
}
}
@Override
public void free() {
super.free();
l = null;
}
@Override
public boolean isOne() {
return super.isOne() && l.isEmpty();
}
@Override
public boolean isVar() {
return super.isVar() && l.isEmpty();
}
@Override
public boolean isNotVar() {
return super.isNotVar() && l.isEmpty();
}
private BDDER or(BDDER other, boolean intoThis) {
int or;
EquivalenceRelation newL;
if (isZero()) {
or = other.id;
newL = other.l;
} else if (other.isZero()) {
or = id;
newL = l;
} else {
or = orOfPairsInDifference(other);
newL = l.intersection(other.l);
}
if (intoThis) {
setId(or);
l = newL;
return this;
} else
return new BDDER(or, newL, false);
}
/**
* Subtracts the pairs in the other set from this set.
*
* @param other the other set
*/
private int orOfPairsInDifference(BDDER other) {
Collection<Pair> myPairs = l.pairs();
Collection<Pair> otherPairs = other.l.pairs();
BitSet toRemoveFromThis = new BitSet();
for (Pair pair: myPairs)
if (otherPairs.contains(pair))
toRemoveFromThis.set(pair.second);
BitSet toRemoveFromOther = new BitSet();
for (Pair pair: otherPairs)
if (myPairs.contains(pair))
toRemoveFromOther.set(pair.second);
int squeezedBDD1 = new EquivalenceSqueezer().squeezedId;
for (Pair pair: myPairs)
if (!toRemoveFromThis.get(pair.first) && !toRemoveFromThis.get(pair.second))
squeezedBDD1 = innerAnd(squeezedBDD1, innerBiimp(innerMakeVar(pair.first), innerMakeVar(pair.second)));
int squeezedBDD2 = other.new EquivalenceSqueezer().squeezedId;
for (Pair pair: otherPairs)
if (!toRemoveFromOther.get(pair.first) && !toRemoveFromOther.get(pair.second))
squeezedBDD2 = innerAnd(squeezedBDD2, innerBiimp(innerMakeVar(pair.first), innerMakeVar(pair.second)));
return innerOr(squeezedBDD1, squeezedBDD2);
}
@Override
public BDD or(BDD other) {
try (GCLock lock = new GCLock()) {
return or((BDDER) other, false);
}
}
@Override
public BDD orWith(BDD other) {
try (GCLock lock = new GCLock()) {
or((BDDER) other, true);
}
other.free();
return this;
}
private class EquivalenceSqueezer {
private final SqueezeEquivCache cache = ut.getSqueezeEquivCache();
private final int squeezedId;
private EquivalenceSqueezer() {
this.squeezedId = squeezeEquiv(id);
}
private int squeezeEquiv(int bdd) {
if (bdd < FIRST_NODE_NUM)
return bdd;
int cached = cache.get(bdd, l);
if (cached >= 0)
return cached;
int var = ut.var(bdd), result;
if (l.getLeader(var) == var)
result = MK(var, squeezeEquiv(ut.low(bdd)), squeezeEquiv(ut.high(bdd)));
else if (ut.high(bdd) == ZERO)
result = squeezeEquiv(ut.low(bdd));
else
result = squeezeEquiv(ut.high(bdd));
cache.put(bdd, l, result);
return result;
}
}
/**
* Computes conjunction of this BDDER with another. The resulting BDDER is the
* normalized version of that having as l the union of the two l's, and as n
* the conjunction of the two bdds.
* The result is normalized.
*
* @param other the other ER
* @return the conjunction
*/
private BDDER and(BDDER other, boolean intoThis) {
EquivalenceRelation newL = l.addClasses(other.l);
int newId;
boolean normalize = true;
if (id == ONE && other.id == ONE) {
newId = id;
l = newL;
normalize = false;
}
newId = innerAnd(id, other.id);
if (intoThis) {
setId(newId);
l = newL;
if (normalize) {
normalize();
}
return this;
}
else
return new BDDER(newId, newL, normalize);
}
private BDDER andNoNormalization(BDDER other, boolean intoThis) {
if (intoThis) {
setId(innerAnd(id, other.id));
l = l.addClasses(other.l);
return this;
}
else
return new BDDER(innerAnd(id, other.id), l.addClasses(other.l), false);
}
@Override
public BDD and(BDD other) {
try (GCLock lock = new GCLock()) {
return and((BDDER) other, false);
}
}
@Override
public BDD andWith(BDD other) {
try (GCLock lock = new GCLock()) {
and((BDDER) other, true);
}
other.free();
return this;
}
/**
* Computes XOR of this BDDER with another. It uses the identity g1 x g2 = (g1
* | g2) & !(g1 & g2). The result is normalized.
*
* TODO use another identity?
*
* @param other the other BDDER
* @return the xor
*/
private BDDER xor(BDDER other, boolean intoThis) {
if (intoThis) {
BDDER and = andNoNormalization(other, false);
or(other, true);
BDDER notAnd = and.notNoNormalization(true);
and(notAnd, true);
notAnd.free();
return this;
}
else {
BDDER and = andNoNormalization(other, false);
BDDER or = or(other, false);
BDDER notAnd = and.notNoNormalization(true);
or.and(notAnd, true);
notAnd.free();
return or;
}
}
@Override
public BDD xor(BDD other) {
try (GCLock lock = new GCLock()) {
return xor((BDDER) other, false);
}
}
@Override
public BDD xorWith(BDD other) {
try (GCLock lock = new GCLock()) {
xor((BDDER) other, true);
}
other.free();
return this;
}
@Override
public BDD nand(BDD other) {
try (GCLock lock = new GCLock()) {
return andNoNormalization((BDDER) other, false).not(true);
}
}
@Override
public BDD nandWith(BDD other) {
try (GCLock lock = new GCLock()) {
andNoNormalization((BDDER) other, true);
not(true);
}
other.free();
return this;
}
/**
* Computes negation of this ER. It uses the identity !(L & n) = !L | !n =
* !p1 | !p2 | ... | !pn | !n. The result is normalized.
*
* @return the negation
*/
private BDDER not(boolean intoThis) {
int not = innerNot(id);
for (BitSet bs: l)
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
for (int j = bs.nextSetBit(i + 1); j >= 0; j = bs.nextSetBit(j + 1))
not = innerOr(not, innerNot(innerBiimp(innerMakeVar(i), innerMakeVar(j))));
if (intoThis) {
setId(not);
l = EquivalenceRelation.empty;
if (!isVar() && !isNotVar())
normalize();
return this;
}
else
return new BDDER(not, EquivalenceRelation.empty, !isVar() && !isNotVar());
}
private BDDER notNoNormalization(boolean intoThis) {
int not = innerNot(id);
for (BitSet bs: l)
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
for (int j = bs.nextSetBit(i + 1); j >= 0; j = bs.nextSetBit(j + 1))
not = innerOr(not, innerNot(innerBiimp(innerMakeVar(i), innerMakeVar(j))));
if (intoThis) {
setId(not);
l = EquivalenceRelation.empty;
return this;
}
else
return new BDDER(not, EquivalenceRelation.empty, false);
}
@Override
public BDD not() {
try (GCLock lock = new GCLock()) {
return not(false);
}
}
@Override
public BDD notWith() {
try (GCLock lock = new GCLock()) {
return not(true);
}
}
/**
* Computes the implication of this BDDER with another. It uses the identity g1 -> g2 = !g1 | g2.
*
* @param other the other BDDER
* @return the implication
*/
private BDDER imp(BDDER other, boolean intoThis) {
if (intoThis)
return notNoNormalization(true).or(other, true);
else
return notNoNormalization(false).or(other, true);
}
@Override
public BDD imp(BDD other) {
try (GCLock lock = new GCLock()) {
return imp((BDDER) other, false);
}
}
@Override
public BDD impWith(BDD other) {
try (GCLock lock = new GCLock()) {
imp((BDDER) other, true);
}
other.free();
return this;
}
/**
* Computes the biimplication of this BDDER with another. It uses the identity
* g1 <-> g2 = (!g1 | g2) & (!g2 | g1).
*
* TODO use another identity?
*
* @param other the other BDDER
* @return the biimplication
*/
private BDDER biimp(BDDER other, boolean intoThis) {
if ((isVar() && other.isVar()) || (isNotVar() && other.isNotVar())) {
int var1 = ut.var(id);
int var2 = ut.var(other.id);
if (var1 > var2) {
int temp = var1;
var1 = var2;
var2 = temp;
}
if (intoThis) {
setId(ONE);
l = new EquivalenceRelation(new int[][] {{var1, var2}});
return this;
}
else
return new BDDER(ONE, new EquivalenceRelation(new int[][] {{var1, var2}}), false);
}
BDDER notG2 = other.not(false);
BDDER or2 = notG2.or(this, true);
if (intoThis) {
notNoNormalization(true);
or(other, true);
and(or2, true);
or2.free();
return this;
}
else {
BDDER notG1 = notNoNormalization(false);
BDDER or1 = notG1.or(other, true);
BDDER and = or1.and(or2, true);
or2.free();
return and;
}
}
@Override
public BDD biimp(BDD other) {
try (GCLock lock = new GCLock()) {
return biimp((BDDER) other, false);
}
}
@Override
public BDD biimpWith(BDD other) {
try (GCLock lock = new GCLock()) {
biimp((BDDER) other, true);
}
other.free();
return this;
}
@Override
public BDD copy() {
try (GCLock lock = new GCLock()) {
return new BDDER(this);
}
}
@Override
public Assignment anySat() throws UnsatException {
Assignment anySat = super.anySat();
l.updateAssignment(anySat);
return anySat;
}
@Override
public List<Assignment> allSat() {
throw new UnsupportedOperationException();
}
@Override
public long satCount() {
return satCount_();
}
private long satCount_() {
BitSet vars = super.vars();
int c = 1;
for (BitSet eqClass: l) {
int leader = eqClass.nextSetBit(0);
if (!vars.get(leader))
c *= 2;
}
return c * super.satCount(vars.cardinality() - 1);
}
@Override
public long satCount(int maxVar) {
// TODO FIXME check this
return satCount_();
}
@Override
public BDD restrict(int var, boolean value) {
throw new UnsupportedOperationException();
}
@Override
public BDD restrict(BDD var) {
throw new UnsupportedOperationException();
}
@Override
public BDD restrictWith(BDD var) {
throw new UnsupportedOperationException();
}
@Override
public BDD exist(int var) {
try (GCLock lock = new GCLock()) {
return exist_(var);
}
}
@Override
public BDD exist(BDD vars) {
try (GCLock lock = new GCLock()) {
return exist_(vars.vars());
}
}
@Override
public BDD exist(BitSet vars) {
try (GCLock lock = new GCLock()) {
return exist_(vars);
}
}
private BDDER exist_(int var) {
if (l.containsVar(var)) {
Map<Integer, Integer> renaming = new HashMap<>();
renaming.put(var, l.nextLeader(var));
int exist = innerReplace(id, renaming, renaming.hashCode()); // requires normalized representation
return new BDDER(exist, l.removeVar(var), true);
}
else
return new BDDER(innerExist(id, var), l, true);
}
private BDDER exist_(BitSet vars) {
EquivalenceRelation lNew = l;
BitSet quantifiedVars = new BitSet();
Map<Integer, Integer> renaming = new HashMap<>();
for (int i = vars.nextSetBit(0); i >= 0; i = vars.nextSetBit(i + 1))
if (l.containsVar(i)) {
int nextLeader = l.nextLeader(i, vars);
if (nextLeader < 0)
quantifiedVars.set(i);
else
renaming.put(i, nextLeader);
lNew = lNew.removeVar(i);
}
else
quantifiedVars.set(i);
int exist;
if (!renaming.isEmpty()) {
exist = innerReplace(id, renaming, renaming.hashCode()); // requires normalized representation
if (quantifiedVars.isEmpty())
// no need to normalize in this case
return new BDDER(exist, lNew, false);
else
exist = innerQuantify(exist, quantifiedVars, true, quantifiedVars.hashCode());
}
else if (quantifiedVars.isEmpty())
return new BDDER(id, l, false);
else
exist = innerQuantify(id, quantifiedVars, true, quantifiedVars.hashCode());
return new BDDER(exist, lNew, true);
}
@Override
public BDD forAll(int var) {
throw new UnsupportedOperationException();
}
@Override
public BDD forAll(BDD var) {
throw new UnsupportedOperationException();
}
@Override
public BDD simplify(BDD d) {
throw new UnsupportedOperationException();
}
@Override
public int[] varProfile() {
throw new UnsupportedOperationException();
}
@Override
public BDD replace(Map<Integer, Integer> renaming) {
BitSet nVars = super.vars();
for (Integer v: renaming.values())
if ((l.containsVar(v) || nVars.get(v)) && !renaming.containsKey(v))
throw new ReplacementWithExistingVarException(v);
try (GCLock lock = new GCLock()) {
int nNew = innerReplace(id, renaming, renaming.hashCode());
// perform "simultaneous" substitution
renaming = new HashMap<>(renaming);
Map<Integer, Integer> varsOnTheRighSide = splitRenaming(renaming);
EquivalenceRelation eNew = l.replace(varsOnTheRighSide); // these renamings need to be performed first
eNew = eNew.replace(renaming);
return new BDDER(renameWithLeader(nNew, eNew), eNew, true);
}
}
@Override
public BDD replaceWith(Map<Integer, Integer> renaming) {
BitSet nVars = super.vars();
for (Integer v: renaming.values())
if ((l.containsVar(v) || nVars.get(v)) && !renaming.containsKey(v))
throw new ReplacementWithExistingVarException(v);
// perform "simultaneous" substitution
renaming = new HashMap<>(renaming);
Map<Integer, Integer> varsOnTheRighSide = splitRenaming(renaming);
EquivalenceRelation eNew = l.replace(varsOnTheRighSide); // these renamings need to be performed first
eNew = eNew.replace(renaming);
try (GCLock lock = new GCLock()) {
int nNew = innerReplace(id, renaming, renaming.hashCode());
setId(renameWithLeader(nNew, eNew));
l = eNew;
normalize();
}
return this;
}
/**
* Separates renamings affecting variables on the right side of some renaming.
* The original renaming map is modified.
*
* @param renaming the original renaming map. After the execution of this method
* it does not contain mappings present in the returned map
* @return a map containing mappings renaming variables present on the right side
* of some other mapping
*/
private Map<Integer, Integer> splitRenaming(Map<Integer, Integer> renaming) {
Map<Integer, Integer> varsOnTheRighSide = new HashMap<>();
ArrayList<Integer> values = new ArrayList<>(renaming.values());
for (Integer i: values)
if (renaming.containsKey(i)) {
varsOnTheRighSide.put(i, renaming.get(i));
renaming.remove(i);
}
return varsOnTheRighSide;
}
private int renameWithLeader(int id, EquivalenceRelation equivalenceRelations) {
RenameWithLeaderCache cache = ut.getRWLCache();
equivalenceRelations = equivalenceRelations.filter(new UsefulLeaders(id));
int result = cache.get(id, equivalenceRelations);
if (result >= 0)
return result;
else
return new RenamerWithLeader(id, equivalenceRelations).resultId;
}
@Override
public long pathCount() {
throw new UnsupportedOperationException();
}
@Override
public BDD ite(BDD thenBDD, BDD elseBDD) {
throw new UnsupportedOperationException();
}
@Override
public BDD relProd(BDD other, BDD var) {
throw new UnsupportedOperationException();
}
@Override
public BDD compose(BDD other, int var) {
throw new UnsupportedOperationException();
}
@Override
public boolean isEquivalentTo(BDD other) {
if (other instanceof BDDER) {
BDDER o = (BDDER) other;
return l.equals(o.l) && super.isEquivalentTo(o);
}
else
try (GCLock lock = new GCLock()) {
return ((BDDImpl) other).getId() == getFullBDD();
}
}
/**
* @return the full BDD, containing also equivalence constraints in l.
*/
private int getFullBDD() {
int full = id;
for (Pair pair: l.pairs())
full = innerAnd(full, innerBiimp(innerMakeVar(pair.first), innerMakeVar(pair.second)));
return full;
}
@Override
public int hashCodeAux() {
return super.hashCodeAux() ^ l.hashCode();
}
@Override
public int var() {
try (GCLock lock = new GCLock()) {
return ut.var(getFullBDD());
}
}
@Override
public BDDER high() {
try (GCLock lock = new GCLock()) {
return new BDDER(ut.high(getFullBDD()));
}
}
@Override
public BDDER low() {
try (GCLock lock = new GCLock()) {
return new BDDER(ut.low(getFullBDD()));
}
}
@Override
public Factory getFactory() {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
return l + System.lineSeparator() + super.toString();
}
@Override
public BitSet vars() {
BitSet res = super.vars();
for (BitSet eqClass: l)
res.or(eqClass);
return res;
}
@Override
public int maxVar() {
return Math.max(l.maxVar(), super.maxVar());
}
boolean isNormalized() {
try (GCLock lock = new GCLock()) {
BDDER norm = new BDDER(getId(), l, true);
boolean result = isEquivalentTo(norm);
norm.free();
return result;
}
}
EquivalenceRelation getEquiv() {
return l;
}
}
}