package cmu.conditional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.sat4j.specs.IVec;
import org.sat4j.specs.IVecInt;
import org.sat4j.specs.IteratorInt;
import de.fosd.typechef.featureexpr.FeatureExpr;
import de.fosd.typechef.featureexpr.FeatureExprFactory;
import de.fosd.typechef.featureexpr.SingleFeatureExpr;
import de.fosd.typechef.featureexpr.bdd.BDDFeatureExpr;
import de.fosd.typechef.featureexpr.bdd.BDDFeatureModel;
import scala.Tuple2;
/**
* Representation of a values that depend on {@link FeatureExpr}.
*
* @author Jens Meinicke
*
*/
public abstract class Conditional<T> {
public static BDDFeatureModel fm = null;
public static BDDFeatureExpr bddFM;
public static final Map<String, SingleFeatureExpr> features = new HashMap<>();
private static Map<FeatureExpr, Boolean> cache = new HashMap<>();
public static void setFM(final String fmfile) {
cache.clear();
features.clear();
fm = (BDDFeatureModel) (fmfile.isEmpty() ? null : FeatureExprFactory.bdd().featureModelFactory().createFromDimacsFile(fmfile));
if (fm != null) {
createBDDFeatureModel();
} else {
bddFM = (BDDFeatureExpr) FeatureExprFactory.bdd().True();
}
}
/**
* Creates a BDD from the given feature model.
*/
private static void createBDDFeatureModel() {
@SuppressWarnings("rawtypes")//Gradle compiler bug
final IVec clauses = fm.clauses();
final scala.collection.immutable.Map<String, Object> vars = fm.variables();
java.util.Map<Integer, String> map = new HashMap<>();
for (Tuple2<String, Object> tuple : scala.collection.JavaConversions.asJavaCollection(vars)) {
map.put((Integer)tuple._2, tuple._1);
}
final int size = clauses.size();
FeatureExpr construction = FeatureExprFactory.True();
for (int i = 0; i < size; i++) {
IVecInt c = (IVecInt) clauses.get(i);
IteratorInt iterator = c.iterator();
FeatureExpr clause = FeatureExprFactory.False();
while (iterator.hasNext()) {
int value = iterator.next();
boolean selection2 = value > 0;
String feature = map.get(Math.abs(value));
SingleFeatureExpr featureExpr = features.get(feature);
if (featureExpr == null) {
featureExpr = FeatureExprFactory.createDefinedExternal(feature);
features.put(featureExpr.feature(), featureExpr);
}
if (selection2) {
clause = clause.or(featureExpr);
} else {
clause = clause.orNot(featureExpr);
}
}
construction = construction.and(clause);
}
bddFM = (BDDFeatureExpr) construction;
}
public static SingleFeatureExpr createFeature(String fname) {
final SingleFeatureExpr feature = FeatureExprFactory.createDefinedExternal("CONFIG_" + fname);
features.put(fname, feature);
return feature;
}
public static boolean isContradiction(final FeatureExpr f) {
if (!cache.containsKey(f)) {
if (f.isContradiction()) {
cache.put(f, Boolean.TRUE);
} else if (f.isTautology()) {
cache.put(f, Boolean.FALSE);
} else if (fm != null) {
cache.put(f, f.isContradiction(fm));
} else {
cache.put(f, Boolean.FALSE);
}
}
return cache.get(f);
}
public static boolean isTautology(final FeatureExpr f) {
return isContradiction(f.not());
}
public abstract T getValue();
public abstract T getValue(boolean ignore);
// protected static final FeatureExpr True = FeatureExprFactory.True();
// def map[U](f: T => U): Conditional[U] = mapr(x => One(f(x)))
@SuppressWarnings({ "unchecked", "rawtypes" })
public <U> Conditional<U> map(final Function<T, U> f) {
return mapfr(null, (FeatureExpr c, T x) -> new One(f.apply(x)));
}
// def mapr[U](f: T => Conditional[U]): Conditional[U] = mapfr(True, (c, x)
// => f(x))
public <U> Conditional<U> mapr(final Function<T, Conditional<U>> f) {
return mapfr(FeatureExprFactory.True(), (FeatureExpr c, T x) -> f.apply(x));
}
// def mapf[U](inFeature: FeatureExpr, f: (FeatureExpr, T) => U):
// Conditional[U] = mapfr(inFeature, (c, x) => One(f(c, x)))
public <U> Conditional<U> mapf(FeatureExpr inFeature, final BiFunction<FeatureExpr, T, Conditional<U>> f) {
return mapfr(inFeature, f::apply);
}
public void mapf(FeatureExpr inFeature, final BiConsumer<FeatureExpr, T> f) {
mapfr(inFeature, f::accept);
}
// def mapfr[U](inFeature: FeatureExpr, f: (FeatureExpr, T) =>
// Conditional[U]): Conditional[U]
public abstract <U> Conditional<U> mapfr(FeatureExpr inFeature, BiFunction<FeatureExpr, T, Conditional<U>> f);
public abstract void mapfr(FeatureExpr inFeature, BiConsumer<FeatureExpr, T> f);
public abstract Conditional<T> simplifyValues();
public Conditional<T> simplify() {
return simplify(FeatureExprFactory.True());
}
public abstract Conditional<T> simplify(FeatureExpr ctx);
public abstract List<T> toList();
public Map<T, FeatureExpr> toMap() {
Map<T, FeatureExpr> map = new HashMap<>();
toMap(FeatureExprFactory.True(), map);
return map;
}
protected abstract void toMap(FeatureExpr f, Map<T, FeatureExpr> map);
@Override
public abstract Conditional<T> clone() throws CloneNotSupportedException;
public static String getCTXString(FeatureExpr ctx) {
final FeatureExpr originalCTX = ctx;
ctx = simplifyCondition(ctx);
int start = ctx.toString().length();
ctx = simplifyCondition(ctx);
int end = ctx.toString().length();
if (start > end) {
System.out.println("---------------");
System.out.println("reduced by " + (start - end));
System.out.println(originalCTX);
System.out.println(ctx);
if (!ctx.equivalentTo(originalCTX, fm)) {
throw new RuntimeException();
}
System.out.println("---------------");
}
boolean oneSample = ctx instanceof BDDFeatureExpr && ((BDDFeatureExpr) ctx).bdd().pathCount() > 1000;
if (oneSample) {
ctx = new BDDFeatureExpr(((BDDFeatureExpr) ctx).bdd().satOne());
}
String context = ctx.toString().replaceAll("CONFIG_", "").replaceAll("__SELECTED_FEATURE_", "")
.replaceAll("def\\(", "").replaceAll("\\)", "").replaceAll("\\(", "");
if (oneSample) {
context = context + " | ...";
} else if ((context.length() > 300 && context.contains("|"))) {
context = (context.substring(0, context.indexOf('|')) + " | ...");
}
return context;
}
public static FeatureExpr simplifyCondition(FeatureExpr ctx) {
return ctx.simplify(bddFM);
}
public static FeatureExpr simplifyCondition(FeatureExpr ctx, FeatureExpr additionalConmstraint) {
return ctx.simplify(bddFM.and(additionalConmstraint));
}
public abstract int size();
public boolean isOne() {
return false;
}
}