/*
* 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 a
* balanced binary search tree.
*
* @author Emina Torlak
*/
public final class TreeSequence<V> extends AbstractSparseSequence<V>
implements Cloneable {
private final IntTree<Entry<V>> tree;
private int size;
/**
* Constructs an empty tree sequence.
* @ensures no this.entries'
*/
public TreeSequence() {
tree = new IntTree<Entry<V>>();
size = 0;
}
/**
* Copy constructor.
* @ensures creatres a deep copy of the original
*/
private TreeSequence(TreeSequence<V> original) {
this.size = original.size;
try {
this.tree = original.tree.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(); // unreachable code;
}
}
/**
* {@inheritDoc}
* @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);
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#size()
*/
public int size() {
return size;
}
/**
* Removes all entries from this sequences.
* @ensures no this.entries'
* @see kodkod.util.ints.SparseSequence#clear()
*/
public void clear() {
tree.clear();
size = 0;
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#put(int, Object)
*/
public V put(int index, V value) {
final Entry<V> e = tree.search(index);
if (e==null) {
size++;
tree.insert(new Entry<V>(index, value));
return null;
} else {
return e.setValue(value);
}
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#get(int)
*/
public V get(int index) {
final Entry<V> e = tree.search(index);
return e==null ? null : e.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 Entry<V> e = tree.search(index);
if (e==null)
return null;
else {
size--;
tree.delete(e);
return e.value;
}
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#containsIndex(int)
*/
public boolean containsIndex(int index) {
return tree.search(index) != null;
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#first()
*/
public IndexedEntry<V> first() {
return tree.min();
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#last()
*/
public IndexedEntry<V> last() {
return tree.max();
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#ceil(int)
*/
public IndexedEntry<V> ceil(int index) {
return tree.searchGTE(index);
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#floor(int)
*/
public IndexedEntry<V> floor(int index) {
return tree.searchLTE(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 TreeSequence<V> clone() {
// ok to use copy constructor to clone a final class
return new TreeSequence<V>(this);
}
// public String toString() {
// return tree.toString();
// }
private static final class Entry<V> extends IntTree.Node<Entry<V>> implements IndexedEntry<V>, Cloneable {
V value;
Entry(int key, V value) {
super(key);
this.value = value;
}
public int index() {
return key;
}
public V value() {
return value;
}
/**
* Sets this.value to the given value and returns the previous value.
* @ensures this.value' = value
* @requires this.value
*/
V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
public boolean equals(Object o) {
if (o==this) return true;
if (!(o instanceof IndexedEntry)) return false;
return AbstractSparseSequence.equal(this, (IndexedEntry<?>)o);
}
public int hashCode() {
return AbstractSparseSequence.hashCode(this);
}
public String toString() {
return key + "=" + value;
}
protected Entry<V> clone() throws CloneNotSupportedException {
return (Entry<V>)super.clone();
}
}
private abstract class EntryIterator implements Iterator<IndexedEntry<V>> {
final int endIndex;
Entry<V> lastReturned ;
Entry<V> next;
/**
* Constructs a tree iterator which traverses the tree starting at
* the given Entry in either descending or ascending order, depending
* on whether start.index is greater than endIndex.
*/
EntryIterator(Entry<V> start, int endIndex) {
this.next = start;
this.lastReturned = null;
this.endIndex = endIndex;
}
/**
* Advances the next pointer to its successor,
* if this is an ascending iterator or to
* its predecessor, if this is a descending iterator.
* @requires next != NIL
*/
abstract void advance();
public abstract boolean hasNext();
public IndexedEntry<V> next() {
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
advance();
return lastReturned;
}
public final void remove() {
if (lastReturned == null)
throw new IllegalStateException();
if (next==null) {
TreeSequence.this.remove(lastReturned.key);
} else {
final int nextIndex = next.key;
TreeSequence.this.remove(lastReturned.key);
// necessary since the structural modifications made by the delete
// procedure may affect the next pointer
next = tree.search(nextIndex);
}
lastReturned = null;
}
}
private final class AscendingIterator extends EntryIterator {
/**
* Constructs an ascending iterator over the entries with
* indeces between from and to.
* @requires from <= to
*/
AscendingIterator(int from, int to) {
super(tree.searchGTE(from),to);
}
/**
* Sets next to its successor.
*/
final void advance() {
next = tree.successor(next);
}
/**
* Returns true if next != NIL and its index is less
* than or equal to the ending index.
*/
public boolean hasNext() {
return next != null && next.key<=endIndex;
}
}
private final class DescendingIterator extends EntryIterator {
/**
* Constructs a descending iterator over the entries with
* indeces between from and to.
* @requires from >= to
*/
DescendingIterator(int from, int to) {
super(tree.searchLTE(from),to);
}
/**
* Sets next to its predecessor.
*/
final void advance() {
next = tree.predecessor(next);
}
/**
* Returns true if next != NIL and its index is greater
* than or equal to the ending index.
*/
public boolean hasNext() {
return next != null && next.key>=endIndex;
}
}
}