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


/** 
 * Represents a sequence of atoms drawn from a given {@link kodkod.instance.Universe universe}.  
 * Note that a Tuple of arity n whose atoms belong to a Universe u encodes an n-digit number in
 * base u.size.  The decimal representation of this number is taken to be the index of the tuple
 * in an n-dimensional space over the Universe u.  
 * 
 * @specfield arity: int
 * @specfield universe: Universe
 * @specfield atoms: [0..arity)->one Object
 * @invariant atoms[int] in universe.atoms[int]
 *
 * @author Emina Torlak                     
 */
public abstract class Tuple  {
	
    /**
     * Returns the universe from which the atoms in this
     * tuple are drawn.
     * @return this.universe
     */
    public abstract Universe universe();
    
    /**
     * Returns the arity of this tuple.
     * @return this.arity
     */
    public abstract int arity();
    
    /**
     * A Tuple encodes a number with this.arity digits in radix this.universe.size;  
     * a Tuple's index is the decimal representation of this number.  
     *  
     * @return sum({i: [0..arity) | universe.index(atoms[i]) * universe.size^(arity - 1 - i)})
     */
    public abstract int index();
    
    /**
     * Returns the atom at the specified index
     *
     * @return this.atoms[i]
     * @throws IndexOutOfBoundsException  i < 0 || i >= this.arity
     */
    public abstract Object atom(int i);
    
    /**
     * Returns the index of the ith atom as given by this.universe.
     * The effect of this method is the same as calling this.universe.index(this.atom(i)).
     * 
     * @return this.universe.index(this.atoms[i])
     * @throws IndexOutOfBoundsException  i < 0 || i >= this.arity
     */
    public abstract int atomIndex(int i);
    
    /**
     * Returns true if atom is in this tuple, otherwise returns false.
     *
     * @return atom in this.atoms[int]
     * @throws IllegalArgumentException  atom !in this.universe.atoms[int]
     */
    public abstract boolean contains(Object atom);
    
    /**
     * Returns the cross product of this and the specified tuple.
     *
     * @return {t : Tuple | t.atoms = this.atoms->tuple.atoms}
     * @throws NullPointerException  tuple = null
     * @throws IllegalArgumentException  tuple.universe != this.universe
     */
    public abstract Tuple product(Tuple tuple);
    
    /**
     * Returns true if o is a tuple with the same sequence of atoms as this,
     * drawn from the same universe as this.  Otherwise returns false.
     * 
     * @return o in Tuple && o.universe = this.universe && o.atoms = this.atoms
     */
    public boolean equals(Object o) {
    		if (this==o) return true;
    		else if (o instanceof Tuple) {
            final Tuple t = (Tuple) o;
            return universe().equals(t.universe()) && arity()==t.arity() && index()==t.index();
        }
        else return false;   
    }
    
    /**
     * Returns a hash code based on the tuple's arity, index, and the hash code
     * of its universe, so that the general contract of Object.hashCode is obeyed.
     * @return the hashcode for this tuple
     */
    public int hashCode() {
        return (arity() * 19 + index())^universe().hashCode();
    }
    
    /**
     * @see java.lang.Object#toString()
     */
    public String toString() {
        final StringBuilder ret = new StringBuilder("[");
        ret.append(atom(0));
        for (int i = 1; i < arity(); i++) {
            ret.append(", ");
            ret.append(atom(i));
        }
        ret.append("]");
        return ret.toString();
  
    }
}
