/*
* 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.util.ints;
import java.util.Iterator;
/**
* A sparse sequence implementation based on an {@link kodkod.util.ints.IntSet IntSet}.
* This implementation can be used only when all entries in the sequence map to the same value.
*
* <p><b>Important Implementation Note</b>: As this implementation does not actually store any
* {@link kodkod.util.ints.IndexedEntry indexed entries},
* the methods {@link #first()}, {@link #last()}, {@link #ceil(int)}, and {@link #floor(int)}
* <b>may re-use the same IndexedEntry object</b>. For example, suppose that an entry <tt>e</tt> with <tt>e.index = 1</tt>
* is returned by a call to <tt>predecessor(2)</tt> on a homogenous sequence <tt>h = {<0,v>, <1,v>, <2,v>}</tt>.
* A subsequent call to <tt>h.predecessor(1)</tt> may return the same object <tt>e</tt>, with its index set to 0. Hence,
* the following assertion may fail: </p>
* <pre>
* IndexedEntry<V> e1 = h.predecessor(2);
* assert e1.index()==1; // this will work
* IndexedEntry<V> e2 = h.predecessor(1);
* assert e1.index()==1; // this may fail, as e1 may be == to e2
* </pre>
* <p>The entries returned by this implementation's {@link #iterator()} are unique
* to that iterator (but not necessarily independent of each other). For example, </p>
* <pre>
* // let s be a range sequence abstractly represented as { 0->v, 1->v, 2->v }
* Iterator<IndexedEntry<V>> iter1 = s.iterator();
* IndexedEntry<V> e1 = iter1.next();
* assert e1.index()==0; // this will work
* iter1.next();
* assert e1.index()==0; // this may fail, as the previous call may have changed the state of e1
* Iterator<IndexedEntry<V>> iter2 = s.iterator();
* IndexedEntry<V> e2 = iter2.next();
* iter1.next();
* assert e2.index()==0; // this will work
* </pre>
* @specfield indeces: set int
* @specfield entries: indeces -> one V
* @author Emina Torlak
*/
public final class HomogenousSequence<V> extends AbstractSparseSequence<V> {
private final IntSet indices;
private final V value;
private final EntryView<V> view;
/**
* Constructs a new homogenous sequence for the given value, backed
* by a {@link IntTreeSet IntTreeSet} instance.
* @ensures this.value' = value && no this.indices'
*/
public HomogenousSequence(V value) {
this.value = value;
this.indices = new IntTreeSet();
this.view = new EntryView<V>(Integer.MIN_VALUE, value);
}
/**
* Constructs a new homogenous sequence for the given value, backed
* by the specified intset. Any changes to the provided set will
* be reflected in this sequence, and vice versa. This sequence will
* be unmodifiable if the given index set is unmodifiable.
* @requires indices is cloneable
* @ensures this.value' = value && this.indices' = indices
*/
public HomogenousSequence(V value, IntSet indices) {
this.value = value;
this.indices = indices;
this.view = new EntryView<V>(Integer.MIN_VALUE, value);
}
/**
* Copy constructor
* @ensures constructs a deep copy of the original
*/
private HomogenousSequence(HomogenousSequence<V> original) {
this.value = original.value;
this.view = new EntryView<V>(Integer.MIN_VALUE, value);
try {
this.indices = original.indices.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(); // unreachable code.
}
}
/**
* Constructs a new homogeneous sequence from the provided sequence. The
* returned sequence is backed by a {@link IntTreeSet IntTreeSet} instance.
* @requires one seq.entries[int]
* @ensures this.value' = seq.entries[int] && this.indices' = seq.indices()
* @throws NullPointerException seq = null
* @throws IllegalArgumentException seq.isEmpty()
* @throws IllegalArgumentException #seq.entries[int] > 1
*/
public HomogenousSequence(SparseSequence<? extends V> seq) {
if (seq.isEmpty())
throw new IllegalArgumentException();
this.indices = new IntTreeSet();
this.value = seq.first().value();
this.view = new EntryView<V>(Integer.MIN_VALUE, value);
for(IndexedEntry<?> e : seq) {
if (!value.equals(e.value()))
throw new IllegalArgumentException();
indices.add(e.index());
}
}
/**
* Returns the set of all indices mapped by this sparse sequence.
* The returned set supports removal and addition iff this is not
* backed by an unmodifiable intset.
* @return {s: IntSet | s.ints = this.entries.V}
*/
public IntSet indices() {
return indices;
}
/**
* Returns an iterator over the entries in this sequence,
* whose indeces are between from and to. If from < to,
* the entries are returned in the ascending order of
* indeces. Otherwise, they are returned in the descending
* order of indeces.
* <p>While the returned iterator <i>i</i> re-uses the same IndexedEntry
* object, it is not shared with other iterators or other method calls.
* In particular, no other call except <tt>i.next()</tt> can change the
* value of the re-used object.</p>
* @return an iterator over the entries in this sequence
* whose indeces are between from and to. Formally, if
* from < to, then the first and last entries returned
* by the iterator are this.ceil(from) and this.floor(to).
* Otherwise, they are this.floor(from) and this.ceil(to).
* @see kodkod.util.ints.AbstractSparseSequence#iterator(int, int)
*/
public Iterator<IndexedEntry<V>> iterator(int from, int to) {
return new HomogenousIterator<V>(indices.iterator(from, to), value);
}
/**
* Returns the number of entries in this sequence.
* @return #this.entries
* @see kodkod.util.ints.SparseSequence#size()
*/
public int size() {
return indices.size();
}
/**
* Removes all entries from this sequences.
* @ensures no this.entries'
* @see kodkod.util.ints.SparseSequence#clear()
*/
public void clear() {
indices.clear();
}
/**
* Puts the given value at the specified index. If the
* sequence already mapped the index to a value, the
* previous value is replaced with the new one and returned.
*
* @requires this.value = value
* @ensures this.indices' = this.indices + index
* @return this.entries[index]
* @throws IllegalArgumentException this.value != value
* @see kodkod.util.ints.SparseSequence#put(int, Object)
*/
public V put(int index, V value) {
if (!equal(this.value, value))
throw new IllegalArgumentException();
return indices.add(index) ? null : value;
}
/**
* Returns the value to which this sequence maps the given
* index. If the index is not mapped, null is returned.
* @return this.entries[index]
* @see kodkod.util.ints.SparseSequence#get(int)
*/
public V get(int index) {
return indices.contains(index) ? value : null;
}
/**
* Removes the entry with the given index, if it exists, and
* returns the value previously stored at the index. If the
* sequence had no previous mapping for the index, null is returned.
* @ensures this.entries' = this.entries - index->E
* @return this.entries[index]
* @see kodkod.util.ints.SparseSequence#remove(int)
*/
public V remove(int index) {
return indices.remove(index) ? value : null;
}
/**
* Returns true if this sparse sequence has an entry for the
* given index; otherwise returns false.
* @return some this.entries[index]
* @see kodkod.util.ints.SparseSequence#containsIndex(int)
*/
public boolean containsIndex(int index) {
return indices.contains(index);
}
/**
* Returns true if this sequence has an entry with the given value;
* otherwise returns false.
* @return some this.indices && this.value = value
* @see kodkod.util.ints.SparseSequence#contains(java.lang.Object)
*/
public boolean contains(Object value) {
return !indices.isEmpty() && equal(this.value, value);
}
/**
* Returns the entry with the smallest index. If the sequence
* is empty, returns null.
* @return {e: IndexedEntry | e.index = min(this.entries.E) &&
* e.value = this.entries[e.index] }
* @see kodkod.util.ints.SparseSequence#first()
*/
public IndexedEntry<V> first() {
return indices.isEmpty() ? null : view.setIndexView(indices.min());
}
/**
* Returns the entry with the largest index. If the sequence
* is empty, returns null.
* @return {e: IndexedEntry | e.index = max(this.entries.E) &&
* e.value = this.entries[e.index] }
* @see kodkod.util.ints.SparseSequence#last()
*/
public IndexedEntry<V> last() {
return indices.isEmpty() ? null : view.setIndexView(indices.max());
}
/**
* If an entry for the given index exists, it is returned. Otherwise,
* successor(index) is returned.
* @return this.containsIndex(index) =>
* {e: IndexedEntry | e.index = index && e.value = this.entries[index] },
* successor(index)
* @see kodkod.util.ints.SparseSequence#ceil(int)
*/
public IndexedEntry<V> ceil(int index) {
if (indices.isEmpty() || index > indices.max())
return null;
else
return view.setIndexView(indices.ceil(index));
}
/**
* If an entry for the given index exists, it is returned. Otherwise,
* predecessor(index) is returned.
* @return this.containsIndex(index) =>
* {e: IndexedEntry | e.index = index && e.value = this.entries[index] },
* predecessor(index)
* @see kodkod.util.ints.SparseSequence#floor(int)
*/
public IndexedEntry<V> floor(int index) {
if (indices.isEmpty() || index < indices.min())
return null;
else
return view.setIndexView(indices.floor(index));
}
/**
* Returns a copy of this sparse sequence. The copy is independent of this
* sequence.
* @return a copy of this sparse sequence.
* @see kodkod.util.ints.SparseSequence#clone()
*/
public HomogenousSequence<V> clone() {
// ok to clone a final class using a copy constructor
return new HomogenousSequence<V>(this);
}
/**
* An iterator over a homogenous sequence.
* @author Emina Torlak
*/
private static final class HomogenousIterator<V> extends EntryView<V> implements Iterator<IndexedEntry<V>> {
private final IntIterator iter;
/**
* Constructs a wrapper for the given IntIterator and value
*/
HomogenousIterator(IntIterator iter, V value) {
super(Integer.MIN_VALUE, value);
this.iter = iter;
}
public boolean hasNext() {
return iter.hasNext();
}
public IndexedEntry<V> next() {
return setIndexView(iter.next());
}
public void remove() {
iter.remove();
}
}
}