/*
* Kodkod -- Copyright (c) 2005-2007, 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;
import java.util.Collection;
import java.util.List;
import kodkod.engine.CapacityExceededException;
import kodkod.util.ints.IntSet;
import kodkod.util.ints.Ints;
/**
* A factory class that facilitates creation of tuples
* and tuple sets drawn from a given universe. Only one
* factory per universe exists.
*
* @specfield universe: Universe
* @invariant no f: TupleFactory - this | f.universe = this.universe
* @author Emina Torlak
*/
public final class TupleFactory {
private final Universe universe;
private final int base;
/**
* Constructs a factory for the given universe.
* @requires no (TupleFactory<:universe).universe
* @effects this.universe' = universe
* @throws NullPointerException - universe = null
*/
TupleFactory(Universe universe) {
this.universe = universe;
this.base = universe.size();
}
/**
* Returns the universe to which this factory belongs.
* @return this.universe
*/
public Universe universe() { return universe; }
/**
* Returns a tuple that contains the specified sequence of atoms,
* drawn from this.universe.
*
* @return {t: Tuple | t.universe = this.universe && t.atoms = atoms }
* @throws NullPointerException - atoms = null
* @throws IllegalArgumentException - atoms.length < 1
* @throws IllegalArgumentException - some a: atoms[int] | a !in this.universe.atoms[int]
*/
public Tuple tuple(Object... atoms) {
if (atoms.length<1) throw new IllegalArgumentException("atoms.length<1");
return new IntTuple(atoms);
}
/**
* Returns a tuple that contains the specified sequence of atoms,
* drawn from this.universe.
*
* @return {t: Tuple | t.universe = this.universe && t.atoms = atoms }
* @throws NullPointerException - atoms = null
* @throws IllegalArgumentException - atoms.size < 1
* @throws IllegalArgumentException - some a: atoms[int] | a !in this.universe.atoms[int]
*/
public Tuple tuple(List<?> atoms) {
if (atoms.size()<1) throw new IllegalArgumentException("atoms.size()<1");
return new IntTuple(atoms.toArray());
}
/**
* Returns a tuple with the specified arity whose index in an arity-dimensional
* space over this.universe is given by the index parameter.
*
* @return {t: Tuple | t.universe = this.universe && t.arity = arity &&
* index = sum({i : [0..arity) | universe.index(t.atoms[i]) * universe.size^(arity - 1 - i))}) }
* @throws IllegalArgumentException - arity < 1 || index < 0 || index >= universe.size^arity
*/
public Tuple tuple(final int arity, final int index) {
return new IntTuple(arity, index);
}
/**
* Returns a set of all tuples of the given arity, drawn from this.universe.
* @return { s: TupleSet | s.universe = this.universe && s.arity = arity &&
* s.tuples = {t: Tuple | t.universe = this.universe && t.arity = arity} }
* @throws IllegalArgumentException - arity < 1
*/
public TupleSet allOf(int arity) {
return new TupleSet(universe, arity,
0, ((int) Math.pow(base, arity)) - 1);
}
/**
* Returns a set of tuples of arity 1, each of which wraps one of the given objects.
* The method requires that the specified object be atoms in this.universe.
*
* @return {s: TupleSet | s.universe = this.universe && s.arity = 1 &&
* s.tuples = { t: Tuple | t.universe=this.universe &&
* t.arity=1 && t.atoms[0] in atoms[int]}}
* @throws NullPointerException - atoms = null
* @throws IllegalArgumentException - some atoms[int] - this.universe.atoms[int]
*/
public TupleSet setOf(Object... atoms) {
final TupleSet ret = new TupleSet(universe, 1);
for (Object atom: atoms) {
ret.add(new IntTuple(atom));
}
return ret;
}
/**
* Returns a tuple set consisting of specified tuples. The method requires that
* all given tuples have the same arity and be drawn from this.universe.
*
* @return {s: TupleSet | s.universe = this.universe && s.arity = first.arity &&
* s.tuples = first + rest[int] }
* @throws NullPointerException - first = null || rest = null
* @throws IllegalArgumentException - first.universe != this.universe
* @throws IllegalArgumentException - some t: rest[int] | t.universe != this.universe || t.arity != first.arity
*/
public TupleSet setOf(Tuple first, Tuple... rest) {
if (!first.universe().equals(universe))
throw new IllegalArgumentException("first.universe != this.universe");
final TupleSet ret = new TupleSet(universe, first.arity(), first.index(), first.index());
for(Tuple tuple: rest) {
ret.add(tuple);
}
return ret;
}
/**
* Returns a tuple set consisting of specified tuples. The method requires that
* all given tuples have the same arity and be drawn from this.universe.
*
* @return {s: TupleSet | s.universe = this.universe && s.arity = first.arity &&
* s.tuples = tuples }
* @throws NullPointerException - tuples = null
* @throws IllegalArgumentException - tuples.isEmpty()
* @throws IllegalArgumentException - tuples.universe != this.universe || #tuples.arity > 1
*/
public TupleSet setOf(Collection<Tuple> tuples) {
if (tuples.isEmpty())
throw new IllegalArgumentException("tuples.isEmpty()");
final TupleSet ret = new TupleSet(universe, tuples.iterator().next().arity());
for(Tuple t : tuples) {
ret.add(t);
}
return ret;
}
/**
* Returns a set of the given arity that contains all tuples whose indeces
* are contained in the given int set. Throws an IllegalArgumentException
* if the set contains an index that is either negative or greater than
* this.universe.size()^arity - 1. The returned TupleSet is backed by a clone
* of tupleIndices.
* @requires tupleIndices is cloneable
* @return {s: TupleSet | s.universe = this.universe && s.arity = arity &&
* s.tuples = {t: Tuple | t.index() in tupleIndices} }
* @throws NullPointerException - tupleIndices = null
* @throws IllegalArgumentException - tupleIndices is uncloneable
* @throws IllegalArgumentException - arity < 1
* @throws IllegalArgumentException - tupleIndices.min() < 0 || tupleIndices.max() >= this.universe.size()^arity
*/
public TupleSet setOf(int arity, IntSet tupleIndices) {
try {
return new TupleSet(universe,arity,tupleIndices.clone());
} catch (CloneNotSupportedException cne){
throw new IllegalArgumentException("uncloneable int set");
}
}
/**
* Returns an initially empty tuple set of the given arity, based on this.universe.
* @return { s: TupleSet | s.universe = this.universe && s.arity = arity && no s.tuples }
* @throws IllegalArgumentException - arity < 1
*/
public TupleSet noneOf(int arity) {
return new TupleSet(universe, arity);
}
/**
* Returns a tuple set that contains all tuples between <code>from</code>
* and <code>to</code>, inclusive. More formally, the returned set contains
* all tuples whose indeces are in the range [from.index()..to.index()].
* @return { s: TupleSet | s.universe = this.universe && s.arity = from.arity &&
* s.tuples = {t: Tuple | t.universe = this.universe &&
* t.arity = s.arity &&
* from.index()<=t.index()<=to.index() }}
* @throws NullPointerException - from = null || to = null
* @throws IllegalArgumentException - from.arity != to.arity
* @throws IllegalArgumentException - from.universe != this.universe || to.universe != this.universe
* @throws IllegalArgumentException - from.index > to.index
*/
public TupleSet range(Tuple from, Tuple to) {
if (from.arity()!=to.arity())
throw new IllegalArgumentException("from.arity!=to.arity");
if (!(from.universe().equals(universe)&&to.universe().equals(universe)))
throw new IllegalArgumentException("from.universe != this.universe || to.universe != this.universe");
return new TupleSet(universe, from.arity(), from.index(), to.index());
}
/**
* Returns a tuple set that contains all tuples in the specified area
* of the n-dimensional space, where n is the arity of the argument
* tuples. For example, suppose that this.universe consists of atoms
* {atom0, atom1, atom2, atom3}, where atom0 has index 0, atom1 has index 1, etc.
* Calling this method with tuples [atom0, atom2] and [atom1, atom3] as the
* first and second arguments would result in the set {[atom0, atom2],
* [atom0,atom3], [atom1,atom2], [atom1, atom3]}. That is, the returned set
* consists of all points in the rectangle whose upper left corner is the
* point [atom0, atom2] and whose lower right corner is at [atom1, atom3].
* @return {s: TupleSet | s.arity = upperLeft.arity &&
* s.universe = this.universe &&
* s.tuples = {t: Tuple | all i: [0..s.arity) |
* this.universe.index(upperLeft.atoms[i]) <=
* this.universe.index(t.atoms[i]) <=
* this.universe.index(lowerRight.atoms[i]}}
* @throws NullPointerException - upperLeft = null || lowerRight = null
* @throws IllegalArgumentException - upperLeft.arity != lowerRight.arity
* @throws IllegalArgumentException - lowerRight.universe != this.universe || upperLeft.universe != this.universe
* @throws IllegalArgumentException - some i: [0..upperLeft.arity) |
* this.universe.index(upperLeft.atoms[i]) >
* this.universe.index(lowerRight.atoms[i])
*/
public TupleSet area(Tuple upperLeft, Tuple lowerRight) {
if (!upperLeft.universe().equals(universe) || upperLeft.arity()!=lowerRight.arity())
throw new IllegalArgumentException();
TupleSet ret = new TupleSet(universe, 1, upperLeft.atomIndex(0),lowerRight.atomIndex(0));
for(int i = 1; i < upperLeft.arity(); i++) {
ret = ret.product(new TupleSet(universe, 1, upperLeft.atomIndex(i),lowerRight.atomIndex(i)));
}
return ret;
}
/**
* Throws a CapacityExceededException if all tuples of the given arity
* drawn from this.universe cannot be represented as an integer.
* @throws CapacityExceededException if all tuples of the given arity
* drawn from this.universe cannot be represented as an integer.
*/
void checkCapacity(int arity) {
if (StrictMath.pow(base,arity) > Integer.MAX_VALUE) {
throw new CapacityExceededException("Arity too large (" + arity + ") for a universe of size " + universe.size(), Ints.nCopies(arity, base));
}
}
/**
* Projects the tuple with the specified index and arity onto the
* specified column.
* @requires tupleIndex >= 0 && tupleIndex < this.universe.size() ^ arity
* @return this.universe.index(this.tuple(arity, tupleIndex).atoms[i])
*/
int project(int tupleIndex, int arity, int column) {
if (column < 0 || column >= arity) throw new IndexOutOfBoundsException(column+"");
return (tupleIndex / ((int) Math.pow(base, arity-1-column))) % base;
}
/**
* An implementation of the Tuple interface that stores
* only the tuple's arity and index, rather than the full
* sequence of atoms. Parts of the sequence are computed on
* demand, e.g. when the <code>get</code> method is invoked.
*
* @specfield universe: TupleFactory.this.universe
* @specfield arity: int
* @specfield index: int
* @invariant arity >= 1 && 0 <= index < TupleFactory.this.base^arity
* @invariant index = sum({i: [0..arity) | TupleFactory.this.universe.index(atoms[i]) * TupleFactory.this.base^(arity - 1 - i))
* @author Emina Torlak
*/
private final class IntTuple extends Tuple {
private final int arity, index;
/**
* Constructs a tuple with the specified arity and index, whose atoms
* are drawn from the factory's universe.
*
* @effects this.arity' = arity &&
* this.index' = index
* @throws IllegalArgumentException - arity < 1 || index < 0 || index >= TupleFactory.this.base^arity
*/
IntTuple(final int arity, final int index) {
checkCapacity(arity);
if (arity < 1 || index < 0 || index >= Math.pow(base, arity)) {
throw new IllegalArgumentException("arity < 1 || index < 0 || index >= universe.size^arity");
}
this.arity = arity;
this.index = index;
}
/**
* Constructs a tuple that contains the specified sequence of atoms, drawn from the
* enclosing factory's universe.
*
* @requires atoms.length > 0
* @effects this.atoms' = atoms
* @throws NullPointerException - atoms = null
* @throws IllegalArgumentException - some a: atoms[int] | a !in universe.atoms[int]
*/
IntTuple(final Object... atoms) {
this.arity = atoms.length;
checkCapacity(arity);
int tempIndex = 0, multiplier = 1;
for (int i = arity - 1; i >= 0; i--) {
tempIndex += universe.index(atoms[i]) * multiplier;
multiplier *= base;
}
this.index = tempIndex;
assert this.index >= 0;
}
/**
* Constructs a tuple with the specified arity, with the specified atom
* at each position.
* @effects this.arity' = arity && this.atoms = [0..arity)->atom
* @throws NullPointerException - atom = null
* @throws IllegalArgumentException - arity < 1 || atom !in this.universe.atoms[int]
*/
IntTuple(final int arity, final Object atom) {
checkCapacity(arity);
if (arity < 1) throw new IllegalArgumentException("arity < 1");
this.arity = arity;
int tempIndex = 1;
for (int i = 0; i < arity; i++) {
tempIndex = tempIndex*base + 1;
}
this.index = universe.index(atom) * tempIndex;
assert this.index >= 0;
}
/** {@inheritDoc} */
public Universe universe() { return universe; }
/** {@inheritDoc} */
public int arity() { return arity; }
/** {@inheritDoc} */
public int index() { return index; }
/** {@inheritDoc} */
public Object atom(int i) {
return universe.atom(atomIndex(i));
}
/** {@inheritDoc} */
public int atomIndex(int i) {
return project(index,arity,i);
// if (i < 0 || i >= arity) throw new IndexOutOfBoundsException("i < 0 || i >= this.arity");
// return (index / ((int) Math.pow(base, arity-1-i))) % base;
}
/** {@inheritDoc} */
public boolean contains(Object atom) {
for (int remainder = index, atomIndex = universe.index(atom);
remainder > 0; remainder = remainder / base) {
if (remainder % base == atomIndex) return true;
}
return false;
}
/** {@inheritDoc} */
public Tuple product(Tuple tuple) {
if (!universe.equals(tuple.universe())) throw new IllegalArgumentException("tuple.universe != this.universe");
return new IntTuple(arity + tuple.arity(),
index * ((int)Math.pow(base, tuple.arity())) + tuple.index());
}
}
}