/* * Kodkod -- Copyright (c) 2005-present, 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.bool; import static kodkod.engine.bool.Operator.AND; import static kodkod.engine.bool.Operator.OR; import java.util.Collection; import java.util.Iterator; import kodkod.engine.config.Options; import kodkod.engine.config.Options.IntEncoding; import kodkod.util.ints.IntSet; /** * A factory for creating {@link kodkod.engine.bool.BooleanValue boolean values}, * {@link kodkod.engine.bool.BooleanMatrix matrices}, and {@link kodkod.engine.bool.Int ints}. * * @specfield comparisonDepth: int // the depth to which circuits should be checked for equality * @specfield intEncoding: {@link IntEncoding} // the encoding used for generating integers ({@link #integer(int)} * @specfield bitwidth: int // the bitwidth used for integer computations * @specfield components: set {@link BooleanValue} * @invariant {@link BooleanConstant} in components * @invariant no f1, f2: BooleanFactory | f1 != f2 => f1.components & f2.components = {@link BooleanConstant} * @invariant let formulas = (components & {@link BooleanFormula}) - {@link NotGate} | * min(formulas.label) = 1 && max(formulas.label) = #formulas * @author Emina Torlak */ public abstract class BooleanFactory { /** * A circuit factory used internally to assemble circuits. */ private final CBCFactory circuits; private int numVars; /** The bitwidth used for integer computations */ final int bitwidth; /** * Constructs a boolean factory with the given number of input variables. Gates are * checked for semantic equality down to the given depth. Integers are represented * using the given number of bits. * @requires 0 <= numVars < Integer.MAX_VALUE * @requires checkToDepth >= 0 && bitwidth > 0 * @ensures #this.components' = numInputVariables && this.components' in BooleanVariable * @ensures this.bitwidth' = bitwidth * @ensures this.comparisonDepth' = comparisonDepth */ private BooleanFactory(int numVars, int comparisonDepth, int bitwidth) { this.circuits = new CBCFactory(numVars, 1<<comparisonDepth); this.bitwidth = bitwidth; this.numVars = numVars; } /** * Returns a boolean factory, initialized to contain the given number * of boolean variables. * <p>Gates are checked for semantic equality * down to the depth given by options.sharing when checking for cached values. In general, setting the * comparison depth to a higher value will result in more * subcomponents being shared. However, it will also slow down * gate construction. </p> * <p>Integers are created/manipulated according to the specifications in the given Options object.</p> * @return {f: BooleanFactory | #(f.components & BooleanVariable) = numVars && * BooleanConstant in f.components && f.components in BooleanVariable + BooleanConstant && * f.comparisonDepth = options.sharing && * f.bitwidth = options.bitwidth && f.intEncoding = options.intEncoding && * (all i: [1..numVars] | one f.components.label & i }} * @throws IllegalArgumentException numVars < 0 || numVars = Integer.MAX_VALUE * @throws NullPointerException options = null */ public static BooleanFactory factory(int numVars, Options options) { switch(options.intEncoding()) { case TWOSCOMPLEMENT : return new TwosComplementFactory(numVars, options.sharing(), options.bitwidth()); default : throw new IllegalArgumentException("unknown encoding: " + options.intEncoding()); } } /** * Returns a BooleanFactory with no variables; the returned factory * can manipulate only constants. * @return {f: BooleanFactory | f.components = BooleanConstant && * f.comparisonDepth = options.sharing && * f.bitwidth = options.bitwidth && f.intEncoding = options.intEncoding } * @throws NullPointerException options = null */ public static BooleanFactory constantFactory(Options options) { return factory(0, options); } /** * Returns the depth (from the root) to which components are checked for * semantic equality during gate construction. * @return this.comparisonDepth */ public final int comparisonDepth() { return Integer.numberOfTrailingZeros(circuits.cmpMax()); } /** * Sets the comparison depth to the given value. Setting the * comparison depth to a high value will result in more * subcomponents being shared. However, it will also slow down * gate construction. * @ensures this.comparisonDepth' = newDepth * @throws IllegalArgumentException newDepth < 1 */ public final void setComparisonDepth(int newDepth) { if (newDepth < 1) throw new IllegalArgumentException("newDepth < 1: " + newDepth); circuits.setCmpMax(1<<newDepth); } /** * Returns the bitwidth used for integer representation. * @return this.bitwidth */ public final int bitwidth() { return bitwidth; } /** * Returns the encoding used by this factory to represent integers. * @return this.intEncoding */ public abstract Options.IntEncoding intEncoding(); /** * Returns true if v is in this.components. * @return v in this.components * @throws NullPointerException v = null */ public final boolean contains(BooleanValue v) { return circuits.canAssemble(v); } /** * Returns the maximum label of a {@link BooleanVariable variable} in {@code this.components}. * @return max((this.components & BooleanVariable).label) */ public final int maxVariable() { return circuits.maxVariable(); } /** * Returns the maximum label of a {@link BooleanFormula formula} in {@code this.components}. * Note that {@link #maxFormula()} >= {@link #maxVariable()} since variables themselves are formulas. * @return max((this.components & BooleanFormula).label) */ public final int maxFormula() { return circuits.maxFormula(); } /** * Returns the variable with the given label. * @requires 0 < label <= numberOfVariables() * @return (this.components & BooleanVariable).label */ public final BooleanVariable variable(int label) { return circuits.variable(label); } /** * Adds the specified number of fresh variables to {@code this.components}. * @requires numVars >= 0 * @ensures let diff = this.components' - this.components | * diff in BooleanVariable && #diff = numVars && * diff.label = { i: int | this.maxFormula() < i <= this.maxFormula() + numVars } */ public final void addVariables(int numVars) { if (numVars < 0) { throw new IllegalArgumentException("Expected numVars >= 0, given numVars = " + numVars); } else if (numVars > 0) { circuits.addVariables(numVars); this.numVars += numVars; } // else do nothing } /** * Returns the number of variables in this factory. * @return #(this.components & BooleanVariable) */ public final int numberOfVariables() { return numVars; } /** * Returns the negation of the given boolean value. * @return {n: BooleanValue | n.label = -v.label && [[n]] = ![[v]] } * @ensures (components.v).components' = (components.v).components + n * @throws NullPointerException v = null */ public final BooleanValue not(BooleanValue v) { return v.negation(); } /** * Returns a boolean value whose meaning is the conjunction of the input components. * The behavior of this method is unspecified if v0 or v1 are not components of this factory. * @requires v0 + v1 in this.components * @return {v: BooleanValue | [[v]] = [[v0]] AND [[v1]] } * @ensures this.components' = this.components + v * @throws NullPointerException any of the arguments are null */ public final BooleanValue and(BooleanValue v0, BooleanValue v1) { return circuits.assemble(AND, v0, v1); } /** * Returns a boolean value whose meaning is the disjunction of the input components. * The behavior of this method is unspecified if v0 or v1 are not components of this factory. * @requires v0 + v1 in this.components * @return {v: BooleanValue | [[v]] = [[v0]] OR [[v1]] } * @ensures this.components' = this.components + v * @throws NullPointerException any of the arguments are null * @throws IllegalArgumentException v0 + v1 !in this.components */ public final BooleanValue or(BooleanValue v0, BooleanValue v1) { return circuits.assemble(OR, v0, v1); } /** * Returns a boolean value whose meaning is [[v0]] ^ [[v1]]. * The behavior of this method is unspecified if v0 or v1 are not components of this factory. * @requires v0 + v1 in this.components * @return { v: BooleanValue | [[v]] = [[v0]] xor [[v1]] } * @ensures this.components' = this.components + v * @throws NullPointerException any of the arguments are null */ public final BooleanValue xor(BooleanValue v0, BooleanValue v1) { return circuits.assemble(v0, v1.negation(), v1); } /** * Returns a boolean value whose meaning is [[v0]] => [[v1]]. * The behavior of this method is unspecified if v0 or v1 are not components of this factory. * @requires v0 + v1 in this.components * @return { v: BooleanValue | [[v]] = [[v0]] => [[v1]] } * @ensures this.components' = this.components + v * @throws NullPointerException any of the arguments are null */ public final BooleanValue implies(BooleanValue v0, BooleanValue v1) { return circuits.assemble(OR, v0.negation(), v1); } /** * Returns a boolean value whose meaning is [[v0]] <=> [[v1]]. * The behavior of this method is unspecified if v0 or v1 are not components of this factory. * @requires v0 + v1 in this.components * @return { v: BooleanValue | [[v]] = [[v0]] iff [[v1]] } * @ensures this.components' = this.components + v * @throws NullPointerException any of the arguments are null */ public final BooleanValue iff(BooleanValue v0, BooleanValue v1) { return circuits.assemble(v0, v1, v1.negation()); } /** * Returns a boolean value whose meaning is [[i]] ? [[t]] : [[e]]. * The behavior of this method is unspecified if i, t, or e are not components of this factory. * @requires i + t + e in this.components * @return { v: BooleanValue | [[v]] = [[i]] ? [[t]] : [[e]] } * @ensures this.components' = this.components + v * @throws NullPointerException any of the arguments are null */ public final BooleanValue ite(BooleanValue i, BooleanValue t, BooleanValue e) { return circuits.assemble(i, t, e); } /** * Returns a boolean value whose meaning is the sum bit of a full binary adder. * The behavior of this method is unspecified if v0, v1, or cin are not components of this factory. * @requires v0 + v1 + cin in this.components * @return { v: BooleanValue | [[v]] = [[cin]] xor [[v0]] xor [[v1]] } * @ensures this.components' = this.components + v * @throws NullPointerException any of the arguments are null */ public final BooleanValue sum(BooleanValue v0, BooleanValue v1, BooleanValue cin) { return xor(cin, xor(v0, v1)); } /** * Returns a boolean value whose meaning is the carry out bit of a full binary adder. * The behavior of this method is unspecified if v0, v1, or cin are not components of this factory. * @requires v0 + v1 + cin in this.components * @return { v: BooleanValue | [[v]] = ([[v0]] and [[v1]]) or ([[cin]] and ([[v0]] xor [[v1]])) } * @ensures this.components' = this.components + v * @throws NullPointerException any of the arguments are null */ public final BooleanValue carry(BooleanValue v0, BooleanValue v1, BooleanValue cin) { return or(and(v0, v1), and(cin, xor(v0, v1))); } /** * Converts the given accumulator into an immutable boolean value and adds it to this.components. * This method requires that all of g's inputs are in this.components. If g has no inputs, * its operator's identity constant is returned. If g has one input, that input is returned. * Otherwise, an immutable value that is semantically equivalent to g is returned. * The behavior of this method is unspecified if the components of g are not components of this factory. * @requires g.components in this.components * @return no g.inputs => g.op.identity(), * one g.inputs => g.inputs, * {g' : BooleanValue - BooleanAccumulator | [[g']] = [[g]] } * @ensures this.components' = this.components + g' */ public final BooleanValue accumulate(BooleanAccumulator g) { return circuits.assemble(g); } /** * Returns an Int that represents the given number using this.intEncoding. * @return { i: Int | [[i]] = number && i.encoding && this.intEncoding && i.factory = this} * @throws IllegalArgumentException the number cannot be represented using * the specified encoding */ public abstract Int integer(int number); /** * Returns an Int that represents 0 or the given number, depending on the value of the given bit. * The behavior of this method is unspecified if the bit is not a component of this factory. * @return { i: Int | [[bit]] => [[i]] = number, [[i]] = 0 && i.encoding = this.intEncoding && i.factory = this} */ public abstract Int integer(int number, BooleanValue bit); /** * Returns an Int that represents the sum of the elements returned by the iterator, * using this.intEncoding. * @param lo the first element of the current partial sum. Initial should be 0. * @param hi the last element of the current partial sum. Initial should be size-1, where size is the total * number of elements returned by the iterator. * @return an Int that represents the sum of the elements returned by the iterator, * using this.intEncoding. */ private Int sum(Iterator<BooleanValue> values, int low, int high) { if (low > high) return integer(0); else if (low == high) return integer(1, values.next()); else { final int mid = (low + high) / 2; final Int lsum = sum(values, low, mid); final Int hsum = sum(values, mid+1, high); return lsum.plus(hsum); } } /** * Returns an Int that represents the sum of all values in the given collection. * @return an Int that represents the sum of all values in the given collection. */ public final Int sum(Collection<BooleanValue> bits) { return sum(bits.iterator(), 0, bits.size()-1); } /** * Returns a BooleanMatrix with the given dimensions and this * as the factory for its non-FALSE components. The returned matrix * can store any value from this.components at all * indices between 0, inclusive, and d.capacity(), exclusive. * @throws NullPointerException d = null * @return { m: BooleanMatrix | m.factory = this && m.dimensions = d && m.elements = [0..d.capacity) -> one FALSE } */ public final BooleanMatrix matrix(Dimensions d) { if (d == null ) throw new NullPointerException(); return new BooleanMatrix(d, this); } /** * @throws IllegalArgumentException indices !in [0..d.capacity()) */ private static void validate(IntSet indices, Dimensions d) { if (!indices.isEmpty()) { if (!d.validate(indices.min()) || !d.validate(indices.max())) throw new IllegalArgumentException(); } } /** * Returns a BooleanMatrix <tt>m</tt> with the given dimensions, this * as its factory, and the indices from the set <tt>trueIndices</tt> initialized * to TRUE. An IndexOutOfBoundsException may be thrown * if {@link BooleanMatrix#set(int, BooleanValue)} is called on <tt>m</tt> with an index * not contained in <tt>allIndices</tt>. If <tt>allIndices.equals(trueIndices)</tt>, * <tt>m</tt> may be a constant matrix; that is, an IllegalArgumentException may be * thrown if {@link BooleanMatrix#set(int, BooleanValue)} is called on <tt>m</tt> with * a non-constant value. Finally, if cloning <tt>trueIndices</tt> results in an immutable * set, then {@link BooleanMatrix#set(int, BooleanValue) m.set(int, BooleanValue)} may throw * an UnsupportedOperationException when called with a member of <tt>trueIndices</tt>. * @requires allIndices.containsAll(trueIndices) * @return { m: BooleanMatrix | m.factory = this && m.dimensions = dims && * m.elements = [0..d.capacity()-1] ->one FALSE ++ indices->TRUE } * @throws IllegalArgumentException allIndices !in [0..d.capacity()) * @throws IllegalArgumentException one of the input sets is not cloneable * @throws NullPointerException d = null || allIndices = null || trueIndices = null */ public final BooleanMatrix matrix(Dimensions d, IntSet allIndices, IntSet trueIndices) { assert allIndices.size() >= trueIndices.size(); // sanity check validate(allIndices, d); validate(trueIndices, d); try { return new BooleanMatrix(d, this, allIndices, trueIndices.clone()); } catch (CloneNotSupportedException e) { throw new IllegalArgumentException(); } } /** * BooleanFactory that produces TwosComplementInts. * @invariant encoding = TwosComplement * @author Emina Torlak */ private static final class TwosComplementFactory extends BooleanFactory { /** * Constructs a boolean factory with the given number of input variables. Gates are * checked for semantic equality down to the given depth. Integers are represented * using the given number of bits. * @requires 0 <= numVars < Integer.MAX_VALUE * @requires checkToDepth >= 0 && bitwidth > 0 * @ensures #this.components' = numInputVariables && this.components' in BooleanVariable * @ensures this.bitwidth' = bitwidth * @ensures this.comparisonDepth' = comparisonDepth * @ensures this.intEncoding' = BINARY */ TwosComplementFactory(int numVars, int comparisonDepth, int bitwidth) { super(numVars, comparisonDepth, bitwidth); } /** * Returns TWOSCOMPLEMENT. * @return TWOSCOMPLEMENT * @see kodkod.engine.bool.BooleanFactory#intEncoding() */ @Override public IntEncoding intEncoding() { return IntEncoding.TWOSCOMPLEMENT; } /** * {@inheritDoc} * @see kodkod.engine.bool.BooleanFactory#integer(int) */ @Override public Int integer(int number) { return new TwosComplementInt(this, number, BooleanConstant.TRUE); } /** * {@inheritDoc} * @see kodkod.engine.bool.BooleanFactory#integer(int, kodkod.engine.bool.BooleanValue) */ @Override public Int integer(int number, BooleanValue bit) { return new TwosComplementInt(this, number, bit); } } }