/*
* 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;
/**
* <p>A tree-based sparse sequence implementation. Unlike {@link kodkod.util.ints.TreeSequence},
* <b>this is not a general-purpose sparse sequence implementation</b>. In particular,
* the entries with consecutive indices and the same value are not stored explicitly. As a result,
* methods
* that return an {@link kodkod.util.ints.IndexedEntry} may re-use the same object.
* Specifically, the last assertion in the following code snippet may fail.</p>
* <pre>
* // let s be a range sequence abstractly represented as { 0->v, 1->v, 2->v }
* IndexedEntry<V> e1 = s.predecessor(2);
* assert e1.index()==1; // this will work
* IndexedEntry<V> e2 = s.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>
* <p>This implementation is a good choice when the client expects the usage pattern with many consecutive
* indices mapped to the same value, and when there is no need for entry uniqueness. </p>
* @author Emina Torlak
*/
public final class RangeSequence<V> extends AbstractSparseSequence<V> implements Cloneable {
/* The ranges are sorted by their right endpoints. All consecutive indices
* that map to the same value are represented by a single range node.
* @invariant (all n: tree.nodes | n.max = n.key && n.min <= n.max) &&
* (no disj n, n': tree.nodes | n.value=n'.value && n.max = n'.min-1)
*/
private final IntTree<Entry<V>> tree;
private final EntryView<V> view;
private int size;
/**
* Constructs an empty RangeSequence.
* @ensures no this.entries'
*/
public RangeSequence() {
view = new EntryView<V>(Integer.MIN_VALUE,null);
tree = new IntTree<Entry<V>>();
size = 0;
}
/**
* Copy constructor.
* @ensures creatres a deep copy of the original
*/
private RangeSequence(RangeSequence<V> original) {
this.size = original.size;
try {
this.tree = original.tree.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(); // unreachable code;
}
view = new EntryView<V>(Integer.MIN_VALUE,null);
}
/**
* {@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;
}
/**
* Returns true if e is the head of a contiguous homogenous
* sequence starting with the mapping e.min->e.value and
* ending with the mapping index->value.
* @return e!=null index-1 = e.key && equal(value, e.value)
*/
private boolean isHeadOf(Entry<V> e, int index, V value){
return e!=null && e.key==index-1 && equal(e.value, value);
}
/**
* Returns true if e is the tail of a contiguous homogenous
* sequence starting with the mapping index->value and
* ending with the mapping e.max->e.value.
* @return e!=null && index+1 = e.min && equal(value, e.value)
*/
private boolean isTailOf(Entry<V> e, int index, V value) {
return e!=null && e.min()==index+1 && equal(e.value, value);
}
/**
* Merges the mapping index->value into its floor or ceiling, if
* possible. Otherwise creates a new node for index->value and
* inserts into the tree.
* @requires index !in this.nodes.key
* @requires f = searchLTE(index) && c = searchGTE(index)
* @ensures this.entries' = this.entries + index->value
*/
private void merge(int index, V value, Entry<V> f, Entry<V> c) {
if (isHeadOf(f, index, value)) {
if (f.isPoint()) {
if (isTailOf(c, index, value)) {
if (c.isPoint()) { // [f: 0->a][i: 1->a][c: 2->a] ---> [{0,1,2}->a]
tree.delete(c);
tree.replace(f, new Range<V>(f.key, c.key, value));
} else { // [f: 0->a][i: 1->a][c: {2,3}->a] ---> [c: {0,1,2,3}->a]
tree.delete(f);
((Range<V>)c).min = f.key;
}
} else { // [f: 0->a][i: 1->a] ---> [{0,1}->a]
tree.replace(f, new Range<V>(f.key, index, value));
}
} else {
if (isTailOf(c, index, value)) { // [f: {-1,0}->a][i: 1->a][c: 2->a] ---> [f: {-1,0,1,2}->a]
tree.delete(c);
f.key = c.key;
} else { // [f: {-1,0}->a][i: 1->a] ---> [f: {0,1,2}->a]
f.key = index;
}
}
} else if (isTailOf(c, index, value)) {
if (c.isPoint()) { // [i: 1->a][c: 2->a] ---> [{1,2}->a]
tree.replace(c, new Range<V>(index, c.key, value));
} else { // [i: 1->a][c: {2,3}->a] ---> [c: {1,2,3}->a]
((Range<V>)c).min = index;
}
} else { // can't merge anything
tree.insert(new Point<V>(index,value));
}
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#put(int, Object)
*/
public V put(int index, V value) {
Entry<V> c = tree.searchGTE(index);
if (c==null || c.min() > index) { // we are adding a new index
size++;
merge(index, value, tree.searchLTE(index), c);
return null;
} else { // the index already exists
if (equal(value, c.value)) {
return value; // nothing to do
} else if (c.isPoint()) {
if (isHeadOf(tree.predecessor(c), index, value) ||
isTailOf(tree.successor(c), index, value)) {
final V oldVal = remove(index);
put(index, value);
return oldVal;
}
return c.setValue(value);
} else { // split the range
final V oldVal = split(index, c);
tree.insert(new Point<V>(index,value));
return oldVal;
}
}
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#get(int)
*/
public V get(int index) {
final Entry<V> e = tree.searchGTE(index);
return e==null || e.min() > index ? null : e.value;
}
/**
* Splits the z entry into the least number of entries
* that do not contain the given index.
* @requires z.min() <= index <= z.max()
* @requires z != NIL
* @ensures this.entries' = this.entries' - index->V
* @return z.value
*/
private V split(int index, Entry<V> z) {
final V val = z.value;
if (z.isPoint())
tree.delete(z);
else { // z contains a range of keys
final Range<V> r = (Range<V>) z;
final int min = r.min, max = r.key;
if (min==index) {
if (min+1<max)
r.min++;
else
tree.replace(r, new Point<V>(max, val));
} else if (max==index) {
if (max-1>min)
r.key--;
else
tree.replace(r, new Point<V>(min, val));
} else { // index is between min and max
if (min==index-1) {
tree.replace(r, new Point<V>(index-1, val));
if (max==index+1) {
tree.insert(new Point<V>(max, val));
} else {
r.min = index+1;
tree.insert(r);
}
} else {
r.key = index-1;
if (max==index+1) {
tree.insert(new Point<V>(max, val));
} else {
tree.insert(new Range<V>(index+1, max, val));
}
}
}
}
return val;
}
/**
* 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> z = tree.searchGTE(index);
if (z==null || index < z.min()) return null;
size--;
return split(index,z);
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#containsIndex(int)
*/
public boolean containsIndex(int index) {
final Entry<V> e = tree.searchGTE(index);
return e != null && e.min() <= index;
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#first()
*/
public IndexedEntry<V> first() {
final Entry<V> e = tree.min();
return e==null ? null : view.setView(e.min(), e.value);
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#last()
*/
public IndexedEntry<V> last() {
final Entry<V> e = tree.max();
return e==null ? null : view.setView(e.max(), e.value);
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#ceil(int)
*/
public IndexedEntry<V> ceil(int index) {
final Entry<V> e = tree.searchGTE(index);
return e == null ? null : view.setView(StrictMath.max(index, e.min()), e.value);
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#floor(int)
*/
public IndexedEntry<V> floor(int index) {
Entry<V> e = tree.searchGTE(index);
if (e==null || e.min() > index) {
e = tree.searchLTE(index);
return e == null ? null : view.setView(e.max(), e.value);
} else
return view.setView(index, e.value);
}
/**
* 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 RangeSequence<V> clone() {
// ok to use copy constructor to clone a final class
return new RangeSequence<V>(this);
}
/**
* A mapping from range of integers in to a value
* @specfield min: int
* @specfield max: int
* @specfield value: V
* @invariant min <= max
* @invariant max = key
* @author Emina Torlak
*/
private static abstract class Entry<V> extends IntTree.Node<Entry<V>> implements Cloneable {
V value;
Entry(int max, V value) {
super(max);
this.value = value;
}
V setValue(V newValue) {
final V oldValue = value;
value = newValue;
return oldValue;
}
/**
* Returns the minimum of this.
* @return this.min
*/
abstract int min();
/**
* Returns the maximum of this.
* @return this.max
*/
final int max() { return key; }
/**
* Return true if this.min=this.max
* @return this.min = this.max
*/
abstract boolean isPoint();
/**
* {@inheritDoc}
* @throws CloneNotSupportedException
* @see java.lang.Object#clone()
*/
protected Entry<V> clone() throws CloneNotSupportedException {
return (Entry<V>) super.clone();
}
}
/**
* A point entry in a range sequence.
* @specfield min: int
* @specfield max: int
* @specfield value: V
* @specfield index: [min..max]
* @invariant min = max
*/
private static final class Point<V> extends Entry<V> {
/**
* Constructs an entry with the given index and value.
* @ensures this.index' = index && this.value' = value
* @ensures this.min' = this.max' = index
*/
Point(int index, V value) {
super(index, value);
}
@Override
int min() { return key; }
@Override
boolean isPoint() { return true; }
protected Point<V> clone() throws CloneNotSupportedException {
return (Point<V>) super.clone();
}
}
/**
* A range entry in a range sequence.
* @specfield min: int
* @specfield max: int
* @specfield value: V
* @invariant min < max
*/
private static final class Range<V> extends Entry<V> {
int min;
/**
* Constructs an entry with the given min/max and value.
* @ensures this.index' = min && this.value' = value
* @ensures this.min' = min && this.max' = max
*/
Range(int min, int max, V value) {
super(max, value);
this.min = min;
}
@Override
int min() { return min; }
@Override
boolean isPoint() { return false; }
protected Range<V> clone() throws CloneNotSupportedException {
return (Range<V>) super.clone();
}
}
/**
* An iterator over the entries in this sequence.
*/
private abstract class EntryIterator implements Iterator<IndexedEntry<V>>, IndexedEntry<V> {
final int endIndex;
int endpoint, cursor, index;
boolean canRemove;
Entry<V> next;
V value;
/**
* @ensures this.endIndex' = endIndex && canRemove = false;
*/
EntryIterator(int endIndex) {
this.endIndex = endIndex;
this.canRemove = false;
}
public final int index() { return index; }
public final V value() { return value; }
public final boolean equals(Object o) {
if (o==this) return true;
if (!(o instanceof IndexedEntry)) return false;
return AbstractSparseSequence.equal(this, (IndexedEntry<?>)o);
}
public final int hashCode() {
return AbstractSparseSequence.hashCode(this);
}
public final String toString() {
return index + "=" + value;
}
}
/**
* An iterator that returns the entries in this sequence
* in the ascending order of indices.
*
* @author Emina Torlak
*/
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(to);
next = tree.searchGTE(from);
index = Integer.MIN_VALUE;
if (next==null) {
cursor = 0;
endpoint = -1;
value = null;
} else {
cursor = StrictMath.max(next.min(), from);
endpoint = next.key;
value = next.value;
next = tree.successor(next);
}
}
public boolean hasNext() {
if (cursor > endpoint) {
if (next==null) return false;
this.cursor = next.min();
this.endpoint = next.key;
this.value = next.value;
next = tree.successor(next);
}
return index<Integer.MAX_VALUE && cursor <= endIndex;
}
public IndexedEntry<V> next() {
if (!hasNext())
throw new NoSuchElementException();
index = cursor++;
canRemove = true;
return this;
}
public void remove() {
if (!canRemove)
throw new IllegalStateException();
RangeSequence.this.remove(index);
next = tree.searchGTE(cursor);
canRemove = false;
}
}
/**
* An iterator that returns the entries in this sequence
* in the descending order of indices.
*
* @author Emina Torlak
*/
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(to);
next = tree.searchGTE(from);
index = Integer.MAX_VALUE;
if (next==null || next.min() > from) {
next = tree.searchLTE(from);
if (next==null) {
cursor = -1;
endpoint = 0;
value = null;
} else {
cursor = next.key;
endpoint = next.min();
value = next.value;
}
} else {
cursor = StrictMath.min(next.key, from);
endpoint = next.min();
value = next.value;
next = tree.predecessor(next);
}
}
public boolean hasNext() {
if (cursor < endpoint) {
if (next==null) return false;
this.cursor = next.key;
this.endpoint = next.min();
this.value = next.value;
next = tree.predecessor(next);
}
return index>Integer.MIN_VALUE && cursor >= endIndex;
}
public IndexedEntry<V> next() {
if (!hasNext())
throw new NoSuchElementException();
index = cursor--;
canRemove = true;
return this;
}
public void remove() {
if (!canRemove)
throw new IllegalStateException();
RangeSequence.this.remove((int) index);
next = tree.searchLTE((int)cursor);
canRemove = false;
}
}
}