/* * 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 java.util.Iterator; import java.util.Set; import kodkod.util.collections.Containers; /** * Represents a multi gate with more than two inputs. * @invariant #this.inputs > 2 * @invariant digest = sum(inputs.digest(this.op)) */ final class NaryGate extends MultiGate { private final BooleanFormula[] inputs; /** * Constructs a new n-ary gate with the given label, from the given mutable multi gate. * @requires g != null && #g.inputs > 2 * @ensures this.op' = g.op && this.inputs' = g.inputs && this.label' = label */ NaryGate(BooleanAccumulator g, int label, int hashcode) { super(g.op, label, hashcode); this.inputs = new BooleanFormula[g.size()]; int index = 0; for(Iterator<BooleanValue> i = g.iterator(); i.hasNext(); ) { inputs[index] = (BooleanFormula) i.next(); index++; } } /** * Returns the number of inputs to this gate. * @return #this.inputs */ @Override public int size() { return inputs.length; } /** * Returns an iterator over the inputs to this gate, in the ascending * label order. * @return an iterator over this.inputs */ @Override public Iterator<BooleanFormula> iterator() { return Containers.iterate(inputs); } /** * Returns an integer k' such that 0 < |k'| < k and |k'| is the number of flattening * steps that need to be taken to determine that this circuit has (or does not have) * an input with the given label. * A positive k' indicates that f is found to be an input to this circuit in k' steps. * A negative k' indicates that f is not an input to this circuit, when it is flattened * using at most k steps. * @requires k > 0 * @return the number of flattening * steps that need to be taken to determine that f is (not) an input to this circuit */ @Override int contains(Operator op, int f, int k) { assert k > 0; if (f==label()) return 1; else if (this.op != op || f>label() || -f>label()) return -1; else { int low = 0, high = inputs.length-1, step = 1; while (low <= high && step <= k) { int mid = (low + high) >>> 1; int midVal = inputs[mid].label(); if (midVal < f) low = mid + 1; else if (midVal > f) high = mid - 1; else return step; // key found in the given number of steps step++; } return 1-step; // key not found. } } /** * Flattens this circuit with respect to the given operator into * the provided set. * Specifically, the method modifies the set so that it contains * the elements f_0, ..., f_k' where k' <= k elements and * [[this]] = op(f_0, ..., f_k'). * The default implementation simply adds this to the set. * @requires k > 0 * @ensures 1 <= k' <= k && some f_0,..., f_k' : flat.elts' | * [[this]] = op([[f_0]], ..., [[f_k']]) */ @Override void flatten(Operator op, Set<BooleanFormula> flat, int k) { assert k > 0; if (this.op == op && k >= inputs.length) { int diff = k - inputs.length; for(BooleanFormula f: inputs) { int oldsize = flat.size(); f.flatten(op, flat, StrictMath.max(1, diff)); diff -= (flat.size() - oldsize); } } else { flat.add(this); } } /** * Returns true if the given iterator and this.iterator * return the same elements, in the same order. * @return true if values and this.iterator return the same elements, * in the same order. */ boolean sameInputs(Iterator<? extends BooleanValue> values) { for(BooleanFormula f : inputs) { if (!(values.hasNext() && f == values.next())) return false; } return !values.hasNext(); } /** * Returns the ith input to this gate. * @return this.inputs[i] * @requires 0 <= i < size * @throws IndexOutOfBoundsException i < 0 || i >= #this.inputs */ @Override public BooleanFormula input(int i) { if (i < 0 || i > inputs.length) throw new IndexOutOfBoundsException(); return inputs[i]; } }