/* 
 * Kodkod -- Copyright (c) 2005-2011, Emina Torlak
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package kodkod.engine.fol2sat;

import java.util.Map;

import kodkod.ast.Relation;
import kodkod.engine.satlab.SATSolver;
import kodkod.instance.Bounds;
import kodkod.instance.Instance;
import kodkod.instance.TupleFactory;
import kodkod.instance.TupleSet;
import kodkod.util.ints.IntIterator;
import kodkod.util.ints.IntSet;
import kodkod.util.ints.Ints;

/**
 * Stores the translation of a {@link kodkod.ast.Formula kodkod formula}
 * to CNF. 
 * 
 * @specfield formula: Formula // the formula that was translated
 * @specfield bounds: Bounds // the bounds used to obtain the CNF from the formula 
 * @specfield solver: SATSolver // a SATSolver containing the CNF representation of the formula
 * @specfield options: Options // the options object used to control translation parameters
 * @author Emina Torlak
 */
public final class Translation {
	private final Bounds bounds;
	private final SATSolver solver;
	/* maps relations to the literals that comprise their translations */
	private final Map<Relation, IntSet> primaryVarUsage;
	private final TranslationLog log;
	private final int maxPrimaryLit;
	
	/**
	 * Constructs a new Translation object for the given solver, bounds,  mapping
	 * from Relations to literals, and TranslationLog.
	 * @requires maxPrimaryLit = max(varUsage[Relation].max)
	 * @requires bounds.relations = varUsage.IntSet
	 * @ensures this.solver' = solver && this.bounds' = bounds
	 */
	Translation(SATSolver solver, Bounds bounds, Map<Relation, IntSet> varUsage, int maxPrimaryLit, TranslationLog log) {	
		this.solver = solver;
		this.bounds = bounds;
		this.primaryVarUsage = varUsage;
		this.maxPrimaryLit = maxPrimaryLit;
		this.log = log;
	}

	/**
	 * Returns a SATSolver object initialized with the CNF encoding of this.formula 
	 * and the timeout and random seed values specified by this.options.  Satisfiability
	 * of the formula can be checked by calling {@link kodkod.engine.satlab.SATSolver#solve()}.
	 * @return {s: SATSolver | [[s.clauses]] = [[this.formula]] && s.timeout() = this.options.timeout() && 
	 *                         s.seed() = this.options.seed() } 
	 */
	public SATSolver cnf() {
		return solver;
	}
	
	/**
	 * If this.solver.solve() is true, returns 
	 * an interpretation of the cnf solution as a 
	 * mapping from Relations to sets of Tuples.  The Relations
	 * mapped by the returned instance are either leaves
	 * of this.formula with different lower and upper
	 * bounds (i.e. {r: this.formula.*children & Relation | 
	 * this.bounds.upperBound[r] != this.bounds.lowerBound[r]}), 
	 * or skolem constants.
	 * @return an interpretation of the cnf solution as
	 * a mapping from (this.variableUsage().keySet() & Relation) to sets of Tuples.
	 * @throws IllegalStateException  this.solver.solve() has not been called or the 
	 * outcome of the last call was not <code>true</code>.
	 */
	public Instance interpret() {
		final TupleFactory f = bounds.universe().factory();
		final Instance instance = new Instance(bounds.universe());
//		System.out.println(varUsage);
		for(Relation r : bounds.relations()) {
			TupleSet lower = bounds.lowerBound(r);
			IntSet indeces = Ints.bestSet(lower.capacity());
			indeces.addAll(lower.indexView());
			IntSet vars = primaryVarUsage.get(r);
			if (vars!=null) {
				int lit = vars.min();
				for(IntIterator iter = bounds.upperBound(r).indexView().iterator(); iter.hasNext();) {
					final int index = iter.next();
					if (!indeces.contains(index) && solver.valueOf(lit++))
						indeces.add(index);
				}
			}
			instance.add(r, f.setOf(r.arity(), indeces));
		}
		return instance;
	}
	
	/**
	 * Returns the set of primary variable literals  that represent
	 * the tuples in the given relation.  If no literals were allocated
	 * to the given relation, null is returned.
	 * @return the set of primary variable literals that represent
	 * the tuples in the given relation. 
	 */
	public IntSet primaryVariables(Relation relation) {
		return primaryVarUsage.get(relation);
	}
	
	/**
	 * Returns the number of primary variables allocated 
	 * during translation.  Primary variables represent
	 * the tuples of Relations that are either leaves
	 * of this.formula with different lower and upper
	 * bounds (i.e. {r: this.formula.*children & Relation | 
	 * this.bounds.upperBound[r] != this.bounds.lowerBound[r]}), 
	 * or skolem constants.
	 * @return the number of primary variables allocated
	 * during translation.
	 */
	public int numPrimaryVariables() {
		return maxPrimaryLit;
	}
	
	/**
	 * If this.options.logTranslation was set to true, returns the log of the
	 * translation that produced this Translation object.  Otherwise returns null.
	 * @return the log of the translation that produced this Translation
	 * object, if this.options.logTranslation was enabled during translation, or null if not. 
	 */
	public TranslationLog log() {
		return log;
	}
	
}
