/*
* 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];
}
}