/*******************************************************************************
 * SAT4J: a SATisfiability library for Java Copyright (C) 2004, 2012 Artois University and CNRS
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU Lesser General Public License Version 2.1 or later (the
 * "LGPL"), in which case the provisions of the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of the LGPL, and not to allow others to use your version of
 * this file under the terms of the EPL, indicate your decision by deleting
 * the provisions above and replace them with the notice and other provisions
 * required by the LGPL. If you do not delete the provisions above, a recipient
 * may use your version of this file under the terms of the EPL or the LGPL.
 *
 * Based on the original MiniSat specification from:
 *
 * An extensible SAT solver. Niklas Een and Niklas Sorensson. Proceedings of the
 * Sixth International Conference on Theory and Applications of Satisfiability
 * Testing, LNCS 2919, pp 502-518, 2003.
 *
 * See www.minisat.se for the original solver in C++.
 *
 * Contributors:
 *   CRIL - initial API and implementation
 *******************************************************************************/

package org.sat4j.pb.tools;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.sat4j.core.Vec;
import org.sat4j.core.VecInt;
import org.sat4j.pb.IPBSolver;
import org.sat4j.pb.ObjectiveFunction;
import org.sat4j.specs.ContradictionException;
import org.sat4j.specs.IConstr;
import org.sat4j.specs.IVec;
import org.sat4j.specs.IVecInt;
import org.sat4j.specs.IteratorInt;
import org.sat4j.specs.TimeoutException;
import org.sat4j.tools.Backbone;
import org.sat4j.tools.GateTranslator;

/**
 * Helper class intended to make life easier to people to feed a sat solver
 * programmatically.
 * 
 * @author daniel
 * 
 * @param <T>
 *            The class of the objects to map into boolean variables.
 * @param <C>
 *            The class of the object to map to each constraint.
 */
public class DependencyHelper<T, C> {

    public static final INegator NO_NEGATION = new INegator() {

        public boolean isNegated(Object thing) {
            return false;
        }

        public Object unNegate(Object thing) {
            return thing;
        }
    };

    public static final INegator BASIC_NEGATION = new INegator() {

        public boolean isNegated(Object thing) {
            return thing instanceof Negation;
        }

        public Object unNegate(Object thing) {
            return ((Negation) thing).getThing();
        }
    };

    private static final class Negation {
        private final Object thing;

        Negation(Object thing) {
            this.thing = thing;
        }

        Object getThing() {
            return this.thing;
        }

        @Override
        public String toString() {
            return "-" + thing;
        }
    }

    /**
	 * 
	 */
    private static final long serialVersionUID = 1L;

    private final Map<T, Integer> mapToDimacs = new HashMap<T, Integer>();
    private final Map<Integer, T> mapToDomain = new HashMap<Integer, T>();
    final Map<IConstr, C> descs = new HashMap<IConstr, C>();

    private final XplainPB xplain;
    private final GateTranslator gator;
    final IPBSolver solver;
    private INegator negator = BASIC_NEGATION;

    private ObjectiveFunction objFunction;
    private IVecInt objLiterals;
    private IVec<BigInteger> objCoefs;

    private final boolean explanationEnabled;
    private final boolean canonicalOptFunction;

    /**
     * 
     * @param solver
     *            the solver to be used to solve the problem.
     */
    public DependencyHelper(IPBSolver solver) {
        this(solver, true);
    }

    /**
     * 
     * @param solver
     *            the solver to be used to solve the problem.
     * @param explanationEnabled
     *            if true, will add one new variable per constraint to allow the
     *            solver to compute an explanation in case of failure. Default
     *            is true. Usually, this is set to false when one wants to check
     *            the encoding produced by the helper.
     */
    public DependencyHelper(IPBSolver solver, boolean explanationEnabled) {
        this(solver, explanationEnabled, true);
    }

    /**
     * 
     * @param solver
     *            the solver to be used to solve the problem.
     * @param explanationEnabled
     *            if true, will add one new variable per constraint to allow the
     *            solver to compute an explanation in case of failure. Default
     *            is true. Usually, this is set to false when one wants to check
     *            the encoding produced by the helper.
     * @param canonicalOptFunctionEnabled
     *            when set to true, the objective function sum up all the
     *            coefficients for a given literal. The default is true. It is
     *            useful to set it to false when checking the encoding produced
     *            by the helper.
     * @since 2.2
     */
    public DependencyHelper(IPBSolver solver, boolean explanationEnabled,
            boolean canonicalOptFunctionEnabled) {
        if (explanationEnabled) {
            this.xplain = new XplainPB(solver);
            this.solver = this.xplain;
        } else {
            this.xplain = null;
            this.solver = solver;
        }
        this.gator = new GateTranslator(this.solver);
        this.explanationEnabled = explanationEnabled;
        this.canonicalOptFunction = canonicalOptFunctionEnabled;
    }

    public void setNegator(INegator negator) {
        this.negator = negator;
    }

    /**
     * Translate a domain object into a dimacs variable.
     * 
     * @param thing
     *            a domain object
     * @return the dimacs variable (an integer) representing that domain object.
     */
    protected int getIntValue(T thing) {
        return getIntValue(thing, true);
    }

    /**
     * Translate a domain object into a dimacs variable.
     * 
     * @param thing
     *            a domain object
     * @param create
     *            to allow or not the solver to create a new id if the object in
     *            unknown. If set to false, the method will throw an
     *            IllegalArgumentException if the object is unknown.
     * @return the dimacs variable (an integer) representing that domain object.
     */
    protected int getIntValue(T thing, boolean create) {
        T myThing;
        boolean negated = this.negator.isNegated(thing);
        if (negated) {
            myThing = (T) this.negator.unNegate(thing);
        } else {
            myThing = thing;
        }
        Integer intValue = this.mapToDimacs.get(myThing);
        if (intValue == null) {
            if (create) {
                intValue = this.solver.nextFreeVarId(true);
                this.mapToDomain.put(intValue, myThing);
                this.mapToDimacs.put(myThing, intValue);
            } else {
                throw new IllegalArgumentException("" + myThing
                        + " is unknown in the solver!");
            }
        }
        if (negated) {
            return -intValue;
        }
        return intValue;
    }

    /**
     * Retrieve the solution found.
     * 
     * Note that this method returns a Sat4j specific data structure (IVec).
     * People willing to retrieve a more common data structure should use
     * {@link #getASolution()} instead.
     * 
     * THAT METHOD IS EXPECTED TO BE CALLED IF hasASolution() RETURNS TRUE.
     * 
     * @return the domain object that must be satisfied to satisfy the
     *         constraints entered in the solver.
     * @see {@link #hasASolution()}
     * @see {@link #getASolution()}
     */
    public IVec<T> getSolution() {
        int[] model = this.solver.model();
        IVec<T> toInstall = new Vec<T>();
        if (model != null) {
            for (int i : model) {
                T obj = this.mapToDomain.get(i);
                if (obj != null) {
                    toInstall.push(obj);
                }
            }
        }
        return toInstall;
    }

    /**
     * Retrieve a collection of objects satisfying the constraints.
     * 
     * It behaves basically the same as {@link #getSolution()} but returns a
     * common Collection instead of a Sat4j specific IVec.
     * 
     * THAT METHOD IS EXPECTED TO BE CALLED IF hasASolution() RETURNS TRUE.
     * 
     * @return the domain object that must be satisfied to satisfy the
     *         constraints entered in the solver.
     * @see {@link #hasASolution()}
     * @see {@link #getSolution()}
     * @since 2.3.1
     */
    public Collection<T> getASolution() {
        int[] model = this.solver.model();
        Collection<T> toInstall = new ArrayList<T>();
        if (model != null) {
            for (int i : model) {
                if (i > 0) {
                    toInstall.add(this.mapToDomain.get(i));
                }
            }
        }
        return toInstall;
    }

    public BigInteger getSolutionCost() {
        return this.objFunction.calculateDegree(this.solver);
    }

    /**
     * Retrieve the boolean value associated with a domain object in the
     * solution found by the solver. THAT METHOD IS EXPECTED TO BE CALLED IF
     * hasASolution() RETURNS TRUE.
     * 
     * @param t
     *            a domain object
     * @return true iff the domain object has been set to true in the current
     *         solution.
     * @throws IllegalArgumentException
     *             If the argument of the method is unknown to the solver.
     */
    public boolean getBooleanValueFor(T t) {
        int dimacsValue = getIntValue(t, false);
        if (dimacsValue > 0) {
            return this.solver.model(dimacsValue);
        } else {
            return !this.solver.model(-dimacsValue);
        }
    }

    /**
     * 
     * @return true if the set of constraints entered inside the solver can be
     *         satisfied.
     * @throws TimeoutException
     */
    public boolean hasASolution() throws TimeoutException {
        return this.solver.isSatisfiable();
    }

    /**
     * 
     * @return true if the set of constraints entered inside the solver can be
     *         satisfied.
     * @throws TimeoutException
     */
    public boolean hasASolution(IVec<T> assumps) throws TimeoutException {
        IVecInt assumptions = new VecInt();
        for (Iterator<T> it = assumps.iterator(); it.hasNext();) {
            assumptions.push(getIntValue(it.next()));
        }
        return this.solver.isSatisfiable(assumptions);
    }

    /**
     * 
     * @return true if the set of constraints entered inside the solver can be
     *         satisfied.
     * @throws TimeoutException
     */
    public boolean hasASolution(Collection<T> assumps) throws TimeoutException {
        IVecInt assumptions = new VecInt();
        for (T t : assumps) {
            assumptions.push(getIntValue(t));
        }
        return this.solver.isSatisfiable(assumptions);
    }

    /**
     * Explain the reason of the inconsistency of the set of constraints.
     * 
     * THAT METHOD IS EXPECTED TO BE CALLED IF hasASolution() RETURNS FALSE.
     * 
     * @return a set of objects used to "name" each constraint entered in the
     *         solver.
     * @throws TimeoutException
     * @see {@link #hasASolution()}
     */
    public Set<C> why() throws TimeoutException {
        if (!this.explanationEnabled) {
            throw new UnsupportedOperationException("Explanation not enabled!");
        }
        Collection<IConstr> explanation = this.xplain.explain();
        Set<C> ezexplain = new TreeSet<C>();
        for (IConstr constr : explanation) {
            C desc = this.descs.get(constr);
            if (desc != null) {
                ezexplain.add(desc);
            }
        }
        return ezexplain;
    }

    /**
     * Explain a domain object has been set to true in a solution.
     * 
     * @return a set of objects used to "name" each constraint entered in the
     *         solver.
     * @throws TimeoutException
     * @see {@link #hasASolution()}
     */
    public Set<C> why(T thing) throws TimeoutException {
        IVecInt assumps = new VecInt();
        assumps.push(-getIntValue(thing));
        return why(assumps);
    }

    /**
     * Explain a domain object has been set to false in a solution.
     * 
     * @return a set of objects used to "name" each constraint entered in the
     *         solver.
     * @throws TimeoutException
     * @see {@link #hasASolution()}
     */
    public Set<C> whyNot(T thing) throws TimeoutException {
        IVecInt assumps = new VecInt();
        assumps.push(getIntValue(thing));
        return why(assumps);
    }

    private Set<C> why(IVecInt assumps) throws TimeoutException {
        if (this.xplain.isSatisfiable(assumps)) {
            return new TreeSet<C>();
        }
        return why();
    }

    /**
     * Add a constraint to set the value of a domain object to true.
     * 
     * @param thing
     *            the domain object
     * @param name
     *            the name of the constraint, to be used in an explanation if
     *            needed.
     * @throws ContradictionException
     *             if the set of constraints appears to be trivially
     *             inconsistent.
     */
    public void setTrue(T thing, C name) throws ContradictionException {
        IConstr constr = this.gator.gateTrue(getIntValue(thing));
        if (constr != null) {
            this.descs.put(constr, name);
        }
    }

    /**
     * Add a constraint to set the value of a domain object to false.
     * 
     * @param thing
     *            the domain object
     * @param name
     *            the name of the constraint, to be used in an explanation if
     *            needed.
     * @throws ContradictionException
     *             if the set of constraints appears to be trivially
     *             inconsistent.
     */
    public void setFalse(T thing, C name) throws ContradictionException {
        IConstr constr = this.gator.gateFalse(getIntValue(thing));
        // constraints duplication detection may end up with null constraint
        if (constr != null) {
            this.descs.put(constr, name);
        }

    }

    /**
     * Create a logical implication of the form lhs -> rhs
     * 
     * @param lhs
     *            some domain objects. They form a conjunction in the left hand
     *            side of the implication.
     * @return the right hand side of the implication.
     */
    public ImplicationRHS<T, C> implication(T... lhs) {
        IVecInt clause = new VecInt();
        for (T t : lhs) {
            clause.push(-getIntValue(t));
        }
        return new ImplicationRHS<T, C>(this, clause);
    }

    public DisjunctionRHS<T, C> disjunction(T... lhs) {
        IVecInt literals = new VecInt();
        for (T t : lhs) {
            literals.push(-getIntValue(t));
        }
        return new DisjunctionRHS<T, C>(this, literals);
    }

    /**
     * Create a constraint stating that at most i domain object should be set to
     * true.
     * 
     * @param i
     *            the maximum number of domain object to set to true.
     * @param things
     *            the domain objects.
     * @return an object used to name the constraint. The constraint MUST BE
     *         NAMED.
     * @throws ContradictionException
     */
    public void atLeast(C name, int i, T... things)
            throws ContradictionException {
        IVecInt literals = new VecInt();
        for (T t : things) {
            literals.push(getIntValue(t));
        }
        this.descs.put(this.solver.addAtLeast(literals, i), name);
    }

    /**
     * Create a constraint stating that at most i domain object should be set to
     * true.
     * 
     * @param i
     *            the maximum number of domain object to set to true.
     * @param things
     *            the domain objects.
     * @return an object used to name the constraint. The constraint MUST BE
     *         NAMED.
     * @throws ContradictionException
     */
    public ImplicationNamer<T, C> atMost(int i, T... things)
            throws ContradictionException {
        IVec<IConstr> toName = new Vec<IConstr>();
        IVecInt literals = new VecInt();
        for (T t : things) {
            literals.push(getIntValue(t));
        }
        toName.push(this.solver.addAtMost(literals, i));
        return new ImplicationNamer<T, C>(this, toName);
    }

    /**
     * Create a constraint stating that at most i domain object should be set to
     * true.
     * 
     * @param i
     *            the maximum number of domain object to set to true.
     * @param things
     *            the domain objects.
     * @return an object used to name the constraint. The constraint MUST BE
     *         NAMED.
     * @throws ContradictionException
     */
    public void atMost(C name, int i, T... things)
            throws ContradictionException {
        IVecInt literals = new VecInt();
        for (T t : things) {
            literals.push(getIntValue(t));
        }
        this.descs.put(this.solver.addAtMost(literals, i), name);
    }

    /**
     * Create a clause (thing1 or thing 2 ... or thingn)
     * 
     * @param name
     * @param things
     * 
     * @throws ContradictionException
     */
    public void clause(C name, T... things) throws ContradictionException {
        IVecInt literals = new VecInt(things.length);
        for (T t : things) {
            literals.push(getIntValue(t));
        }
        IConstr constr = this.gator.addClause(literals);
        // constr can be null if duplicated clauses are detected.
        if (constr != null) {
            this.descs.put(constr, name);
        }

    }

    /**
     * Create a constraint using equivalency chains thing <=> (thing1 <=> thing2
     * <=> ... <=> thingn)
     * 
     * @param thing
     *            a domain object
     * @param things
     *            other domain objects.
     * @throws ContradictionException
     */
    public void iff(C name, T thing, T... otherThings)
            throws ContradictionException {
        IVecInt literals = new VecInt(otherThings.length);
        for (T t : otherThings) {
            literals.push(getIntValue(t));
        }
        IConstr[] constrs = this.gator.iff(getIntValue(thing), literals);
        for (IConstr constr : constrs) {
            if (constr != null) {
                this.descs.put(constr, name);
            }
        }
    }

    /**
     * Create a constraint of the form thing <=> (thing1 and thing 2 ... and
     * thingn)
     * 
     * @param name
     * @param thing
     * @param otherThings
     * @throws ContradictionException
     */
    public void and(C name, T thing, T... otherThings)
            throws ContradictionException {
        IVecInt literals = new VecInt(otherThings.length);
        for (T t : otherThings) {
            literals.push(getIntValue(t));
        }
        IConstr[] constrs = this.gator.and(getIntValue(thing), literals);
        for (IConstr constr : constrs) {
            if (constr != null) {
                this.descs.put(constr, name);
            }
        }
    }

    /**
     * Create a constraint of the form thing <=> (thing1 or thing 2 ... or
     * thingn)
     * 
     * @param name
     * @param thing
     * @param otherThings
     * @throws ContradictionException
     */
    public void or(C name, T thing, T... otherThings)
            throws ContradictionException {
        IVecInt literals = new VecInt(otherThings.length);
        for (T t : otherThings) {
            literals.push(getIntValue(t));
        }
        IConstr[] constrs = this.gator.or(getIntValue(thing), literals);
        for (IConstr constr : constrs) {
            if (constr != null) {
                this.descs.put(constr, name);
            }
        }
    }

    /**
     * Create a constraint of the form thing <= (thing1 or thing 2 ... or
     * thingn)
     * 
     * @param name
     * @param thing
     * @param otherThings
     * @throws ContradictionException
     */
    public void halfOr(C name, T thing, T... otherThings)
            throws ContradictionException {
        IVecInt literals = new VecInt(otherThings.length);
        for (T t : otherThings) {
            literals.push(getIntValue(t));
        }
        IConstr[] constrs = this.gator.halfOr(getIntValue(thing), literals);
        for (IConstr constr : constrs) {
            if (constr != null) {
                this.descs.put(constr, name);
            }
        }
    }

    /**
     * Create a constraint of the form thing <=> (if conditionThing then
     * thenThing else elseThing)
     * 
     * @param name
     * @param thing
     * @param otherThings
     * @throws ContradictionException
     */
    public void ifThenElse(C name, T thing, T conditionThing, T thenThing,
            T elseThing) throws ContradictionException {
        IConstr[] constrs = this.gator.ite(getIntValue(thing),
                getIntValue(conditionThing), getIntValue(thenThing),
                getIntValue(elseThing));
        for (IConstr constr : constrs) {
            if (constr != null) {
                this.descs.put(constr, name);
            }
        }
    }

    /**
     * Add an objective function to ask for a solution that minimize the
     * objective function.
     * 
     * @param wobj
     *            a set of weighted objects (pairs of domain object and
     *            BigInteger).
     */
    public void setObjectiveFunction(WeightedObject<T>... wobj) {
        createObjectivetiveFunctionIfNeeded(wobj.length);
        for (WeightedObject<T> wo : wobj) {
            addProperly(wo.thing, wo.getWeight());
        }

    }

    private void addProperly(T thing, BigInteger weight) {
        int lit = getIntValue(thing);
        int index;
        if (this.canonicalOptFunction
                && (index = this.objLiterals.indexOf(lit)) != -1) {
            this.objCoefs.set(index, this.objCoefs.get(index).add(weight));
            if (this.objCoefs.get(index).equals(BigInteger.ZERO)) {
                this.objLiterals.delete(index);
                this.objCoefs.delete(index);
            }
        } else {
            this.objLiterals.push(lit);
            this.objCoefs.push(weight);
        }
    }

    private void createObjectivetiveFunctionIfNeeded(int n) {
        if (this.objFunction == null) {
            this.objLiterals = new VecInt(n);
            this.objCoefs = new Vec<BigInteger>(n);
            this.objFunction = new ObjectiveFunction(this.objLiterals,
                    this.objCoefs);
            this.solver.setObjectiveFunction(this.objFunction);
        }
    }

    /**
     * Add a weighted literal to the objective function.
     * 
     * @param thing
     * @param weight
     */
    public void addToObjectiveFunction(T thing, int weight) {
        addToObjectiveFunction(thing, BigInteger.valueOf(weight));
    }

    /**
     * Add a weighted literal to the objective function.
     * 
     * @param thing
     * @param weight
     */
    public void addToObjectiveFunction(T thing, BigInteger weight) {
        createObjectivetiveFunctionIfNeeded(20);
        addProperly(thing, weight);
    }

    /**
     * Create a PB constraint of the form <code>
     * w1.l1 + w2.l2 + ... wn.ln >= degree
     * </code> where wi are position integers and li are domain objects.
     * 
     * @param degree
     * @param wobj
     * @throws ContradictionException
     */
    public void atLeast(C name, BigInteger degree, WeightedObject<T>... wobj)
            throws ContradictionException {
        IVecInt literals = new VecInt(wobj.length);
        IVec<BigInteger> coeffs = new Vec<BigInteger>(wobj.length);
        for (WeightedObject<T> wo : wobj) {
            literals.push(getIntValue(wo.thing));
            coeffs.push(wo.getWeight());
        }
        this.descs.put(
                this.solver.addPseudoBoolean(literals, coeffs, true, degree),
                name);
    }

    /**
     * Create a PB constraint of the form <code>
     * w1.l1 + w2.l2 + ... wn.ln <= degree
     * </code> where wi are position integers and li are domain objects.
     * 
     * @param degree
     * @param wobj
     * @throws ContradictionException
     */
    public void atMost(C name, BigInteger degree, WeightedObject<T>... wobj)
            throws ContradictionException {
        IVecInt literals = new VecInt(wobj.length);
        IVec<BigInteger> coeffs = new Vec<BigInteger>(wobj.length);
        for (WeightedObject<T> wo : wobj) {
            literals.push(getIntValue(wo.thing));
            coeffs.push(wo.getWeight());
        }
        this.descs.put(
                this.solver.addPseudoBoolean(literals, coeffs, false, degree),
                name);
    }

    public void atMost(C name, int degree, WeightedObject<T>... wobj)
            throws ContradictionException {
        atMost(name, BigInteger.valueOf(degree), wobj);
    }

    /**
     * Stop the SAT solver that is looking for a solution. The solver will throw
     * a TimeoutException.
     */
    public void stopSolver() {
        this.solver.expireTimeout();
    }

    /**
     * Stop the explanation computation. A TimeoutException will be thrown by
     * the explanation algorithm.
     */
    public void stopExplanation() {
        if (!this.explanationEnabled) {
            throw new UnsupportedOperationException("Explanation not enabled!");
        }
        this.xplain.cancelExplanation();
    }

    public void discard(IVec<T> things) throws ContradictionException {
        IVecInt literals = new VecInt(things.size());
        for (Iterator<T> it = things.iterator(); it.hasNext();) {
            literals.push(-getIntValue(it.next()));
        }
        this.solver.addBlockingClause(literals);
    }

    public void discardSolutionsWithObjectiveValueGreaterThan(long value)
            throws ContradictionException {
        ObjectiveFunction obj = this.solver.getObjectiveFunction();
        IVecInt literals = new VecInt(obj.getVars().size());
        obj.getVars().copyTo(literals);
        IVec<BigInteger> coeffs = new Vec<BigInteger>(obj.getCoeffs().size());
        obj.getCoeffs().copyTo(coeffs);
        this.solver.addPseudoBoolean(literals, coeffs, false,
                BigInteger.valueOf(value));
    }

    public String getObjectiveFunction() {
        ObjectiveFunction obj = this.solver.getObjectiveFunction();
        StringBuilder stb = new StringBuilder();
        for (int i = 0; i < obj.getVars().size(); i++) {
            stb.append(obj.getCoeffs().get(i)
                    + (obj.getVars().get(i) > 0 ? " " : "~")
                    + this.mapToDomain.get(Math.abs(obj.getVars().get(i)))
                    + " ");
        }
        return stb.toString();
    }

    public int getNumberOfVariables() {
        return this.mapToDimacs.size();
    }

    public int getNumberOfConstraints() {
        return this.descs.size();
    }

    public Map<Integer, T> getMappingToDomain() {
        return this.mapToDomain;
    }

    public Object not(T thing) {
        return new Negation(thing);
    }

    /**
     * @since 2.2
     * @return the IPBSolver enclosed in the helper.
     */
    public IPBSolver getSolver() {
        if (this.explanationEnabled) {
            return this.xplain.decorated();
        }
        return this.solver;
    }

    /**
     * Reset the state of the helper (mapping, objective function, etc).
     * 
     * @since 2.3.1
     */
    public void reset() {
        this.mapToDimacs.clear();
        this.mapToDomain.clear();
        this.descs.clear();
        this.solver.reset();
        if (this.objLiterals != null) {
            this.objLiterals.clear();
            this.objCoefs.clear();
        }
    }

    /**
     * Compute the objects implied by the assumptions.
     * 
     * @param assumptions
     *            already satisfied objects (e.g. user choices)
     * @param satisfied
     *            the objects that are always satisfied under assumptions
     *            (includes the objects from assumptions)
     * @param falsified
     *            the objects that are always falsified under assumptions.
     * @return the set of objects fixed
     * @throws TimeoutException
     * @since 2.3.3
     */
    public void impliedBy(Collection<T> assumptions, Collection<T> satisfied,
            Collection<T> falsified) throws TimeoutException {
        IVecInt assump = new VecInt(assumptions.size());
        for (T thing : assumptions) {
            assump.push(getIntValue(thing));
        }
        IVecInt implied = Backbone.instance().compute(solver, assump);
        int p;
        for (IteratorInt it = implied.iterator(); it.hasNext();) {
            p = it.next();
            if (p > 0) {
                satisfied.add(mapToDomain.get(p));
            } else {
                falsified.add(mapToDomain.get(-p));
            }
        }
    }
}
