/* * 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 kodkod.util.ints.IndexedEntry; import kodkod.util.ints.SparseSequence; import kodkod.util.ints.TreeSequence; /** * An accumulator for easy construction of gates with multiple inputs. * An accumulator cannot be combined with other boolean values * using BooleanFactory methods. To use the circuit * represented by an accumulator, one must first convert it into a gate * by calling {@link BooleanFactory#accumulate(BooleanAccumulator)}. * * @specfield components: set BooleanValue * @specfield op: Operator.Nary * @author Emina Torlak */ public final class BooleanAccumulator extends BooleanValue implements Iterable<BooleanValue>{ final Operator.Nary op; private final SparseSequence<BooleanValue> inputs; // private final List<BooleanValue> inputs; /** * Constructs a new accumulator with the given * operator. * @requires op != null * @ensures this.op' = op && this.label' = label */ private BooleanAccumulator(Operator.Nary op) { this.op = op; inputs = new TreeSequence<BooleanValue>(); // inputs = new ArrayList<BooleanValue>(); } /** * Returns a tree based implementation of BooleanAccumulator. * The addInput operation executes in O(lg n) time where n is the number of gate inputs. * @return an empty tree based BooleanAccumulator with the given operator. * @throws NullPointerException op = null */ public static BooleanAccumulator treeGate(Operator.Nary op) { if (op==null) throw new NullPointerException(); return new BooleanAccumulator(op); } /** * Returns a tree based implementation of BooleanAccumulator, initialized with the given inputs. * The addInput operation executes in O(lg n) time where n is the number of gate inputs. * @return a tree based BooleanAccumulator with the given operator, initialized with the given inputs * @throws NullPointerException op = null || inputs = null */ public static BooleanAccumulator treeGate(Operator.Nary op, BooleanValue... inputs) { if (op==null) throw new NullPointerException(); final BooleanAccumulator ret = new BooleanAccumulator(op); for(BooleanValue v : inputs) { if (ret.add(v)!=ret) break; } return ret; } /** * Returns the operator for this accumulator. * @return this.op */ public Operator.Nary op() { return op; } /** * Adds the given value to this.components and returns the result. Specifically, * if the addition of the value causes the gate to evaluate to op.shortCircuit, * then this.inputs is set to op.shortCircuit. If the given value has already * been added or it is equal to this.op.identity, nothing changes. Otherwise, v * is added to this.input. The method returns this.op.shortCircuit if this.inputs * contains it after the addition, otherwise it returns the gate itself. * @ensures v = this.op.shortCircuit || v.negation in this.components => this.components' = this.op.shortCircuit, * v !in BooleanConstant => this.components' = this.components + v, * this.components' = this.components * @return this.components' = op.shortCircuit => op.shortCircuit, this */ public BooleanValue add(BooleanValue v) { if (isShortCircuited()) return op.shortCircuit(); else{ final int lit = v.label(); if (v==op.shortCircuit() || inputs.containsIndex(-lit)) { inputs.clear(); inputs.put(op.shortCircuit().label, op.shortCircuit()); return op.shortCircuit(); } if (v!=op.identity() && !inputs.containsIndex(lit)) { inputs.put(lit, (BooleanValue) v); } // if (v==op.shortCircuit()) { // inputs.clear(); // inputs.add(op.shortCircuit()); // return op.shortCircuit(); // } // if (v!=op.identity()) { inputs.add(v); } return this; } } /** * Returns true if this gate is short circuited; that is, * its inputs are reduced to this.op.shortCircuit. * @return this.inputs = this.op.shortCircuit */ public boolean isShortCircuited() { return inputs.size()==1 && inputs.first().value()==op.shortCircuit(); // return inputs.size()==1 && inputs.get(0)==op.shortCircuit(); } /** * Throws an IllegalArgumentException if op != this.op, * otherwise returns the sum of digests of this gate's * inputs with respect to the given operator. * @return op = this.op => sum(this.inputs.digest(op)) * @throws IllegalArgumentException op != this.op * @throws ClassCastException some this.inputs & BooleanConstant */ int digest(Operator op) { if (this.op != op) throw new IllegalArgumentException(); int d = 0; for(Iterator<BooleanValue> inputs = iterator(); inputs.hasNext();) { d += ((BooleanFormula)inputs.next()).hash(op); } return d; } /** * Returns the size of this accumulator. * @return #this.inputs */ public int size() { return inputs.size(); } /** * Returns an iterator over this.components, in * the increasing order of labels. The returned iterator * does not support removal. * @return an iterator over this.components, in the * increasing order of labels. */ public Iterator<BooleanValue> iterator() { return new Iterator<BooleanValue>() { final Iterator<IndexedEntry<BooleanValue>> iter = inputs.iterator(); public boolean hasNext() { return iter.hasNext(); } public BooleanValue next() { return iter.next().value(); } public void remove() { throw new UnsupportedOperationException(); } }; // return inputs.iterator(); } /** * Throws an unsupported operation exception. * @throws UnsupportedOperationException */ @Override BooleanValue negation() { throw new UnsupportedOperationException(); } /** * Returns 0. * @return 0. */ @Override public int label() { return 0; } public String toString() { return inputs.toString(); } }