/* * 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.instance; import static java.util.Collections.unmodifiableMap; import static kodkod.util.ints.Ints.unmodifiableSequence; import java.util.AbstractSet; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import kodkod.ast.Relation; import kodkod.util.ints.IntSet; import kodkod.util.ints.SparseSequence; import kodkod.util.ints.TreeSequence; /** * <p>A Bounds object maps a {@link kodkod.ast.Relation relation} <i>r</i> to two * {@link kodkod.instance.TupleSet sets of tuples}, <i>rL</i> and <i>rU</i>, which represent the lower and upper * bounds on the {@link kodkod.instance.Tuple set of tuples} to which an {@link kodkod.instance.Instance instance} * based on these bounds may map <i>r</i>. The set <i>rL</i> represents all the tuples * that a given relation <i>must</i> contain. The set <i>rU</i> represents all the tuples * that a relation <i>may</i> contain. All bounding sets range over the same {@link kodkod.instance.Universe universe}. * </p> * <p>A Bounds object also maps integers to singleton tupleset that represent them. A tupleset may represent more than * one integer, but an integer is represented by at most one tupleset. </p> * @specfield universe: Universe * @specfield relations: set Relation * @specfield intBound: int -> lone TupleSet * @specfield lowerBound: relations -> one TupleSet * @specfield upperBound: relations -> one TupleSet * @invariant all i: intBound.TupleSet | intBound[i].size() = 1 && intBound[i].arity() = 1 * @invariant lowerBound[relations].universe = upperBound[relations].universe = universe * @invariant all r: relations | lowerBound[r].arity = upperBound[r].arity = r.arity * @invariant all r: relations | lowerBound[r].tuples in upperBound[r].tuples * @author Emina Torlak **/ public final class Bounds implements Cloneable { private final TupleFactory factory; private final Map<Relation, TupleSet> lowers, uppers; private final SparseSequence<TupleSet> intbounds; private final Set<Relation> relations; /** * Constructs a Bounds object with the given factory and mappings. */ private Bounds(TupleFactory factory, Map<Relation, TupleSet> lower, Map<Relation, TupleSet> upper, SparseSequence<TupleSet> intbounds) { this.factory = factory; this.lowers = lower; this.uppers = upper; this.intbounds = intbounds; this.relations = relations(lowers, uppers); } /** * Constructs new Bounds over the given universe. * @ensures this.universe' = universe && no this.relations' && no this.intBound' * @throws NullPointerException universe = null */ public Bounds(Universe universe) { this.factory = universe.factory(); this.lowers = new LinkedHashMap<Relation, TupleSet>(); this.uppers = new LinkedHashMap<Relation, TupleSet>(); this.intbounds = new TreeSequence<TupleSet>(); this.relations = relations(lowers, uppers); } /** * Returns a set view of the relations mapped by the given lower/upper bounds. * @requires lowers.keySet().equals(uppers.keySet()) * @return a set view of the relations mapped by the given lower/upper bounds */ private static Set<Relation> relations(final Map<Relation, TupleSet> lowers, final Map<Relation, TupleSet> uppers) { return new AbstractSet<Relation>() { public Iterator<Relation> iterator() { return new Iterator<Relation>() { final Iterator<Relation> itr = uppers.keySet().iterator(); Relation last = null; public boolean hasNext() { return itr.hasNext(); } public Relation next() { return last = itr.next(); } public void remove() { itr.remove(); lowers.remove(last); } }; } public int size() { return uppers.size(); } public boolean contains(Object key) { return uppers.containsKey(key); } public boolean remove(Object key) { return (uppers.remove(key) != null) && (lowers.remove(key) != null); } public boolean removeAll(Collection<?> c) { return uppers.keySet().removeAll(c) && lowers.keySet().removeAll(c); } public boolean retainAll(Collection<?> c) { return uppers.keySet().retainAll(c) && lowers.keySet().retainAll(c); } }; } /** * Returns this.universe. * @return this.universe */ public Universe universe() { return factory.universe(); } /** * Returns the set of all relations bound by this Bounds. * The returned set does not support the add operation. * It supports removal iff this is not an unmodifiable Bounds. * @return this.relations */ public Set<Relation> relations() { return relations; } /** * Returns the set of all integers bound by this Bounds. * The returned set does not support the add operation. * It supports removal iff this is not an unmodifiable Bounds. * @return this.intBounds.TupleSet */ public IntSet ints() { return intbounds.indices(); } /** * Returns the set of tuples that r must contain (the lower bound on r's contents). * If r is not mapped by this, null is returned. * @return r in this.relations => lowerBound[r], null */ public TupleSet lowerBound(Relation r) { return lowers.get(r); } /** * Returns a map view of this.lowerBound. The returned map is not modifiable. * @return a map view of this.lowerBound */ public Map<Relation, TupleSet> lowerBounds() { return unmodifiableMap(lowers); } /** * Returns the set of tuples that r may contain (the upper bound on r's contents). * If r is not mapped by this, null is returned. * @return r in this.relations => upperBound[r], null */ public TupleSet upperBound(Relation r) { return uppers.get(r); } /** * Returns a map view of this.upperBound. The returned map is not modifiable. * @return a map view of this.upperBound */ public Map<Relation, TupleSet> upperBounds() { return unmodifiableMap(uppers); } /** * Returns the set of tuples representing the given integer. If i is not * mapped by this, null is returned. * @return this.intBound[i] */ public TupleSet exactBound(int i) { return intbounds.get(i); } /** * Returns a sparse sequence view of this.intBound. The returned sequence * is not modifiable. * @return a sparse sequence view of this.intBound */ public SparseSequence<TupleSet> intBounds() { return unmodifiableSequence(intbounds); } /** * @throws IllegalArgumentException arity != bound.arity * @throws IllegalArgumentException bound.universe != this.universe */ private void checkBound(int arity, TupleSet bound) { if (arity != bound.arity()) throw new IllegalArgumentException("bound.arity != r.arity"); if (!bound.universe().equals(factory.universe())) throw new IllegalArgumentException("bound.universe != this.universe"); } /** * Sets both the lower and upper bounds of the given relation to * the given set of tuples. * * @requires tuples.arity = r.arity && tuples.universe = this.universe * @ensures this.relations' = this.relations + r * this.lowerBound' = this.lowerBound' ++ r->tuples && * this.upperBound' = this.lowerBound' ++ r->tuples * @throws NullPointerException r = null || tuples = null * @throws IllegalArgumentException tuples.arity != r.arity || tuples.universe != this.universe */ public void boundExactly(Relation r, TupleSet tuples) { checkBound(r.arity(), tuples); final TupleSet unmodifiableTuplesCopy = tuples.clone().unmodifiableView(); lowers.put(r, unmodifiableTuplesCopy); uppers.put(r, unmodifiableTuplesCopy); } /** * Sets the lower and upper bounds for the given relation. * * @requires lower.tuples in upper.tuples && lower.arity = upper.arity = r.arity && * lower.universe = upper.universe = this.universe * @ensures this.relations' = this.relations + r && * this.lowerBound' = this.lowerBound ++ r->lower && * this.upperBound' = this.upperBound ++ r->upper * @throws NullPointerException r = null || lower = null || upper = null * @throws IllegalArgumentException lower.arity != r.arity || upper.arity != r.arity * @throws IllegalArgumentException lower.universe != this.universe || upper.universe != this.universe * @throws IllegalArgumentException lower.tuples !in upper.tuples */ public void bound(Relation r, TupleSet lower, TupleSet upper) { if (!upper.containsAll(lower)) throw new IllegalArgumentException("lower.tuples !in upper.tuples"); if (upper.size()==lower.size()) { // upper.containsAll(lower) && upper.size()==lower.size() => upper.equals(lower) boundExactly(r, lower); } else { checkBound(r.arity(), lower); checkBound(r.arity(), upper); lowers.put(r, lower.clone().unmodifiableView()); uppers.put(r, upper.clone().unmodifiableView()); } } /** * Makes the specified tupleset the upper bound on the contents of the given relation. * The lower bound automatically becomen an empty tupleset with the same arity as * the relation. * * @requires upper.arity = r.arity && upper.universe = this.universe * @ensures this.relations' = this.relations + r * this.lowerBound' = this.lowerBound ++ r->{s: TupleSet | s.universe = this.universe && s.arity = r.arity && no s.tuples} && * this.upperBound' = this.upperBound ++ r->upper * @throws NullPointerException r = null || upper = null * @throws IllegalArgumentException upper.arity != r.arity || upper.universe != this.universe */ public void bound(Relation r, TupleSet upper) { checkBound(r.arity(), upper); lowers.put(r, factory.noneOf(r.arity()).unmodifiableView()); uppers.put(r, upper.clone().unmodifiableView()); } /** * Makes the specified tupleset an exact bound on the relational value * that corresponds to the given integer. * @requires ibound.arity = 1 && i.bound.size() = 1 * @ensures this.intBound' = this.intBound' ++ i -> ibound * @throws NullPointerException ibound = null * @throws IllegalArgumentException ibound.arity != 1 || ibound.size() != 1 * @throws IllegalArgumentException ibound.universe != this.universe */ public void boundExactly(int i, TupleSet ibound) { checkBound(1, ibound); if (ibound.size() != 1) throw new IllegalArgumentException("ibound.size != 1: " + ibound); intbounds.put(i, ibound.clone().unmodifiableView()); } /** * Returns an unmodifiable view of this Bounds object. * @return an unmodifiable view of his Bounds object. */ public Bounds unmodifiableView() { return new Bounds(factory, unmodifiableMap(lowers), unmodifiableMap(uppers), unmodifiableSequence(intbounds)); } /** * Returns a deep (modifiable) copy of this Bounds object. * @return a deep (modifiable) copy of this Bounds object. */ public Bounds clone() { try { return new Bounds(factory, new LinkedHashMap<Relation, TupleSet>(lowers), new LinkedHashMap<Relation, TupleSet>(uppers), intbounds.clone()); } catch (CloneNotSupportedException cnse) { throw new InternalError(); // should not be reached } } /** * @see java.lang.Object#toString() */ public String toString() { final StringBuilder str = new StringBuilder(); str.append("relation bounds:"); for(Map.Entry<Relation, TupleSet> entry: lowers.entrySet()) { str.append("\n "); str.append(entry.getKey()); str.append(": ["); str.append(entry.getValue()); TupleSet upper = uppers.get(entry.getKey()); if (!upper.equals(entry.getValue())) { str.append(", "); str.append(upper); } str.append("]"); } str.append("\nint bounds: "); str.append("\n "); str.append(intbounds); return str.toString(); } }