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

/**
 * ExprOperator associated with a {@link kodkod.engine.bool.BooleanValue boolean value}.
 * 
 * @specfield ordinal: [0..5]
 * @invariant AND.ordinal = 0 && OR.ordinal = 1 && ITE.ordinal = 2 &&
 * NOT.ordinal = 2 && VAR.ordinal = 4  && CONST.ordinal = 5
 * @author Emina Torlak
 */
public abstract class Operator implements Comparable<Operator>{
	final int ordinal;
	
	private Operator(int ordinal) {	
		this.ordinal = ordinal;
	}

	/**
	 * Returns the ordinal of this operator constant.
	 * @return the ordinal of this operator constant.
	 */
	public final int ordinal() {
		return ordinal;
	}
	
	/**
	 * Returns an integer i such that i < 0 if this.ordinal < op.ordinal,
	 * i = 0 when this.ordinal = op.ordinal, and i > 0 when this.ordinal > op.ordinal.
	 * @return i: int | this.ordinal < op.ordinal => i < 0,
	 *         this.ordinal = op.ordinal => i = 0, i > 0
	 * @throws NullPointerException  op = null
	 */
	public int compareTo(Operator op) {
		return ordinal() - op.ordinal();
	}
	
	/**
	 * N-ary {@link MultiGate AND} operator.
	 */
	public static final Nary AND = new Nary(0) {
		public String toString() { return "&"; }
		/** @return true */
		public BooleanConstant identity() { return BooleanConstant.TRUE; }
		/** @return false */
		public BooleanConstant shortCircuit() { return BooleanConstant.FALSE; }
		/** @return OR */
		public Nary complement() {  return OR; }
	};
	
	/**
	 * N-ary {@link MultiGate OR} operator.
	 */
	public static final Nary OR = new Nary(1) {
		public String toString() { return "|"; }
		/** @return false */
		public BooleanConstant identity() { return BooleanConstant.FALSE; }
		/** @return true */
		public BooleanConstant shortCircuit() { return BooleanConstant.TRUE; }
		/** @return AND */
		public Nary complement() {  return AND; }
	};
	
	/**
	 * Ternary {@link ITEGate if-then-else} operator.
	 */
	public static final Ternary ITE = new Ternary(2) {
		public String toString() { return "?"; }
	};
	
	/**
	 * Unary {@link NotGate negation} operator.
	 */
	public static final Operator NOT = new Operator(3) {
		public String toString() { return "!"; }
	};
	
	/**
	 * Zero-arity {@link BooleanVariable variable} operator.  
	 */
	public static final Operator VAR = new Operator(4) {
		public String toString() { return "var"; }
	};
	
	/**
	 * Zero-arity {@link BooleanConstant constant} operator.  
	 */
	public static final Operator CONST = new Operator(5) {
		public String toString() { return "const"; }
	};
	
	/**
	 * An n-ary operator, where n>=2
	 */
	public static abstract class Nary extends Operator {
		
		private Nary(int ordinal) {
			super(ordinal);
		}
		
		/**
		 * Returns the hashcode for a gate v such that
		 * v.op = this && v.inputs[int] = f0 + f1
		 * @return f0.hash(this) + f1.hash(this)
		 */
		int hash(BooleanFormula f0, BooleanFormula f1) {
			return f0.hash(this) + f1.hash(this);
		}
		
		/**
		 * Returns the hashcode for a gate v such that
		 * v.op = this && v.iterator() = formulas.
		 * @return sum(formulas.hash(this))
		 */
		int hash(Iterator<BooleanFormula> formulas) {
			int sum = 0;
			while(formulas.hasNext())
				sum += formulas.next().hash(this);
			return sum;
		}
		
		/**
		 * Returns the boolean constant <i>c</i> such that 
		 * for all logical values <i>x</i>, <i>c</i> composed
		 * with <i>x</i> using this operator will result in <i>x</i>.
		 * @return the identity value of this binary operator
		 */
		public abstract BooleanConstant identity();
		/**
		 * Returns the boolean constant <i>c</i> such that 
		 * for all logical values <i>x</i>, <i>c</i> composed
		 * with <i>x</i> using this operator will result in <i>c</i>.
		 * @return the short circuiting value of this binary operator
		 */
		public abstract BooleanConstant shortCircuit();
		/**
		 * Returns the binary operator whose identity and short circuit
		 * values are the negation of this operator's identity and
		 * short circuit.
		 * @return the complement of this binary operator
		 */
		public abstract Operator.Nary complement();
	}
	
	static abstract class Ternary extends Operator {
		
		private Ternary(int ordinal) {
			super(ordinal);
		}
		
		/**
		 * Returns the hashcode for a gate v such that
		 * v = (i ? t : e)
		 * @return 3*i.hash(this) + 5*t.hash(this) + 7*e.hash(this)
		 */
		int hash(BooleanFormula i, BooleanFormula t, BooleanFormula e) {
			return 3*i.hash(this) + 5*t.hash(this) + 7*e.hash(this);
		}
	}
	
}
