/*
* 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;
import java.util.NoSuchElementException;
/**
* An implementation of a sparse sequence based on an array.
* This implementation can be used only when the indices
* of the sequence are known in advance. The indices with
* which an ArraySequence is construct remain fixed throughout.
* The put operation fails whenever its index argument is not one
* of the sequence's pre-set indices, and iteration time is proportional
* to the number of pre-set indices. This sequence does not
* allow null values. The lookup and put operations are logarithmic in
* the number of pre-set indices.
*
* @specfield indeces: set int
* @specfield entries: indeces -> lone (V - null)
* @author Emina Torlak
*/
public final class ArraySequence<V> extends AbstractSparseSequence<V> implements Cloneable {
private final EntryView<V>[] entries;
private int size;
/**
* Constructs an array sequence that contains
* the given indeces.
* @ensures this.indeces' = indeces && no this.entries'
* @throws NullPointerException indeces = null
*/
@SuppressWarnings("unchecked")
public ArraySequence(IntSet indices) {
this.entries = new EntryView[indices.size()];
this.size = indices.size();
final IntIterator indexIter = indices.iterator();
for(int i = 0; indexIter.hasNext(); i++) {
entries[i] = new EntryView<V>(indexIter.next(), null);
}
}
/**
* Constructs a new array sequence with the same index/value mappings
* as the given sequence.
* @ensures this.entries' = s.entries
* @throws NullPointerException s = null || null in s
*/
@SuppressWarnings("unchecked")
public ArraySequence(SparseSequence<? extends V> s) {
this.entries = new EntryView[s.size()];
this.size = s.size();
int i = 0;
for(IndexedEntry<?> entry : s) {
if (entry.value()==null)
throw new NullPointerException();
entries[i++] = new EntryView<V>(entry.index(), (V)entry.value());
}
}
/**
* Copy constructor.
* @ensures constructs a deep copy of the original array sequence.
*/
@SuppressWarnings("unchecked")
private ArraySequence(ArraySequence<V> original) {
this.size = original.size;
this.entries = new EntryView[original.entries.length];
int i = 0;
for(EntryView<V> e : original.entries)
this.entries[i++] = new EntryView<V>(e.index(), e.value());
}
/**
* Returns the number of entries in this sequence.
* @return #this.entries
* @see kodkod.util.ints.SparseSequence#size()
*/
public int size() {
return size;
}
/**
* Returns true if this sequence is empty; otherwise returns false.
* @return no this.entries
* @see kodkod.util.ints.SparseSequence#isEmpty()
*/
public boolean isEmpty() {
return size==0;
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#clear()
*/
public void clear() {
for(EntryView<V> e : entries) {
e.setValue(null);
}
}
/**
* Searches this.entries for the specified index using the
* binary search algorithm. If the index is not found, then
* -insertionPoint - 1 is returned, where insertionPoint is
* the point at which the given index would be inserted into
* this.entries.
* @return the position in this.entries where the entry with
* the given index is located, or -insertionPoint - 1 if
* the index is not in this.indeces
*/
private final int search(int index) {
int low = 0;
int high = entries.length-1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midIndex = entries[mid].index();
if (midIndex < index)
low = mid + 1;
else if (midIndex > index)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found.
}
/**
* 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.
*
* @ensures this.entries' = this.entries + index->value
* @return this.entries[index]
* @throws IndexOutOfBoundsException index !in this.indeces
* @throws NullPointerException value = null
* @see kodkod.util.ints.SparseSequence#put(int, Object)
*/
public V put(int index, V value) {
if (value==null)
throw new NullPointerException();
final int position = search(index);
if (position < 0)
throw new IndexOutOfBoundsException(""+index);
if (entries[position]==null) size++;
return entries[position].setValue(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) {
final int position = search(index);
return position < 0 ? null : entries[position].value();
}
/**
* 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) {
final int position = search(index);
if (position < 0)
return null;
else {
if (entries[position].value()!=null) size--;
return entries[position].setValue(null);
}
}
/**
* Returns true if this sparse sequence has an entry for the
* given index; otherwise returns false.
* @return index in this.indeces
* @see kodkod.util.ints.SparseSequence#containsIndex(int)
*/
public boolean containsIndex(int index) {
final int position = search(index);
return position >= 0 && entries[position].value()!=null;
}
/**
* 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.
* @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.SparseSequence#iterator(int, int)
*/
public Iterator<IndexedEntry<V>> iterator(int from, int to) {
return from <= to ? new AscendingIterator(from, to) : new DescendingIterator(from, to);
}
/**
* 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() {
if (size==0)
return null;
for(EntryView<V> e : entries) {
if (e.value()!=null)
return e;
}
throw new InternalError(); // unreachable code
}
/**
* 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() {
if (size==0)
return null;
for(int i = entries.length-1; i>=0; i--) {
if (entries[i].value()!=null)
return entries[i];
}
throw new InternalError(); // unreachable code
}
/**
* 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) {
final int position = search(index);
for(int i = position < 0 ? -position-1 : position; i < entries.length; i++) {
if (entries[i].value()!=null)
return entries[i];
}
return null;
}
/**
* 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) {
final int position = search(index);
for(int i = position < -1 ? -position-2 : position; i >=0 ; i--) {
if (entries[i].value()!=null)
return entries[i];
}
return null;
}
/**
* 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 ArraySequence<V> clone() {
return new ArraySequence<V>(this);
}
/**
* An iterator that traverses this sequence in the ascending order.
*
* @author Emina Torlak
*/
private final class AscendingIterator implements Iterator<IndexedEntry<V>> {
final int endIndex;
IndexedEntry<V> lastReturned = null;
int cursor;
/**
* @requires from <= to
*/
AscendingIterator(int from, int to) {
final int fromPos = search(from);
final int toPos = search(to);
cursor = fromPos < 0 ? -fromPos-1 : fromPos;
endIndex = toPos < -1 ? -toPos-2 : toPos;
}
public boolean hasNext() {
while (cursor < entries.length && entries[cursor].value()==null)
cursor++;
return cursor<=endIndex;
}
public IndexedEntry<V> next() {
if (!hasNext())
throw new NoSuchElementException();
return lastReturned=entries[cursor++];
}
public void remove() {
if (lastReturned==null)
throw new IllegalStateException();
entries[lastReturned.index()].setValue(null);
lastReturned = null;
}
}
/**
* An iterator that traverses this sequence in the descending order.
*
* @author Emina Torlak
*/
private final class DescendingIterator implements Iterator<IndexedEntry<V>> {
final int endIndex;
IndexedEntry<V> lastReturned = null;
int cursor;
/**
* @requires from >= to
*/
DescendingIterator(int from , int to) {
final int fromPos = search(from);
final int toPos = search(to);
cursor = fromPos < -1 ? -fromPos-2 : fromPos;
endIndex = toPos < 0 ? -toPos-1 : toPos;
}
public boolean hasNext() {
while (cursor >= 0 && entries[cursor].value()==null)
cursor--;
return cursor>=endIndex;
}
public IndexedEntry<V> next() {
if (!hasNext())
throw new NoSuchElementException();
return lastReturned=entries[cursor--];
}
public void remove() {
if (lastReturned==null)
throw new IllegalStateException();
entries[lastReturned.index()].setValue(null);
lastReturned = null;
}
}
}