/* 
 * 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.bool;

import java.util.Iterator;
import java.util.Set;



/**
 * A non-constant boolean value with zero or more inputs. 
 * @specfield op: Operator
 * @specfield size: int
 * @specfield inputs: [0..size) -> one BooleanFormula
 * @invariant size >= 0
 * @author Emina Torlak
 */
public abstract class BooleanFormula extends BooleanValue implements Iterable<BooleanFormula> {
	private BooleanFormula negation;
	
	/**
	 * Constructs a boolean formula with the given negation.
	 */
	BooleanFormula(BooleanFormula negation) { 
		this.negation = negation;
	}
	/**
	 * Returns an integer hash of this formula, used
	 * to compute the hash of the composition of this and
	 * some other formula with the given operator.    
	 * @return an integer hash of this formula when acting
	 * as an input to a multigate with the given operator.
	 */
	abstract int hash(Operator op); 
	
	/**
	 * 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
	 */
	int contains(Operator op, int f, int k) {
		return f==label() ? 1 : -1;
	}
	
	/**
	 * 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']])
	 */
	void flatten(Operator op, Set<BooleanFormula> flat, int k) {
		assert k > 0;
		flat.add(this);
	}
	
	
	
	/**
	 * Returns the negation of this formula if it has already been computed.
	 * Otherwise, computes, caches and returns the negation of this formula.
	 * @return !this
	 * @see kodkod.engine.bool.BooleanValue#negation()
	 */
	final BooleanFormula negation() {
		if (negation==null) {
			negation = new NotGate(this);
		}
		return negation;
	}
	
	/**
	 * Returns true if the negation of this formula 
	 * has already been computed.
	 * @return true if the negation of this formula has already been computed.
	 */
	final boolean hasNegation()  { return negation != null; }

	/**
	 * Passes this value and the given
	 * argument value to the visitor, and returns the resulting value.
	 * @return the value produced by the visitor when visiting this node
	 * with the given argument.
	 */
	public abstract <T, A> T accept(BooleanVisitor<T, A> visitor, A arg);
	
	
	
	/**
	 * Returns an iterator over the inputs to this gate.
	 * @return an iterator over this.inputs.
	 */
	public abstract Iterator<BooleanFormula> iterator();
	
	
	/**
	 * Returns the number of inputs to this gate.
	 * @return #this.inputs
	 */
	public abstract int size();
	
	/**
	 * Returns the ith input to this gate.
	 * @return this.inputs[i]
	 * @requires 0 <= i < size
	 * @throws IndexOutOfBoundsException  i < 0 || i >= #this.inputs
	 */
	public abstract BooleanFormula input(int i);
	
}
