/* * 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.NoSuchElementException; /** * An implementation of the IntTreeSet interface based * on a balanced binary search tree. * * @specfield ints: set int * @author Emina Torlak */ public final class IntTreeSet extends AbstractIntSet implements Cloneable { /* The endpoints of the ranges in the tree do not touch, and they are * sorted by their right endpoints. * @invariant all n: tree.nodes | n.max = n.key && n.min <= n.max && * all n': tree.nodes - n | n'.max < n.min - 1 || n'.min > n.max + 1 */ private final IntTree<Range> tree; private int size; /** * Constructs an empty int set. * @ensures no this.ints' */ public IntTreeSet() { tree = new IntTree<Range>(); size = 0; } /** * Constructs a new int set containing the elements * in the specified set. * @ensures this.ints' = s.ints * @throws NullPointerException s = null */ public IntTreeSet(IntSet s) { this(); addAll(s); } /** * Copy constructor. * @ensures constructs a deep copy of the original set. */ private IntTreeSet(IntTreeSet original) { this.size = original.size; try { this.tree = original.tree.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(); // unreachable code } } // public String toString() { // for(Range next = tree.min(); next != null; next = tree.successor(next) ) { // System.out.print("[" + next.min + " .. " + next.key+ "] "); // } // System.out.println(""); // return ""; // } /** * {@inheritDoc} * @see kodkod.util.ints.IntSet#iterator(int,int) */ public IntIterator iterator(int from, int to) { return from <= to ? new AscendingIterator(from, to) : new DescendingIterator(from, to); } /** * {@inheritDoc} * @see kodkod.util.ints.IntSet#size() */ public int size() { return size; } /** * Returns true if i is in this set. * @return i in this.ints * @see kodkod.util.ints.IntSet#contains(int) */ @Override public boolean contains(int i) { final Range r = tree.searchGTE(i); return r != null && r.min <= i; } /** * Returns the smallest element in this set. * Throws a NoSuchElementException if this set is empty. * @return min(this.ints) * @throws java.util.NoSuchElementException no this.ints * @see kodkod.util.ints.IntSet#min() */ @Override public int min() { checkNonEmpty(); return tree.min().min; } /** * Returns the largest element in this set. * Throws a NoSuchElementException if this set is empty. * @return max(this.ints) * @throws java.util.NoSuchElementException no this.ints * @see kodkod.util.ints.IntSet#max() */ @Override public int max() { checkNonEmpty(); return tree.max().key; } /** * {@inheritDoc} * @see kodkod.util.ints.IntSet#floor(int) */ public int floor(int i) { checkNonEmpty(); Range r = tree.searchGTE(i); if (r==null || r.min > i) { r = tree.searchLTE(i); return r == null ? null : r.key; } else return i; } /** * {@inheritDoc} * @see kodkod.util.ints.IntSet#ceil(int) */ public int ceil(int i) { checkNonEmpty(); final Range r = tree.searchGTE(i); return r == null ? null : StrictMath.max(i, r.min); } /** * Adds the given integer to this set if not already present * and returns true. Otherwise does nothing and returns false. * @ensures this.ints' = this.ints + i * @return i in this.ints' * @see kodkod.util.ints.IntSet#add(int) */ @Override public boolean add(int i) { final Range ceil = tree.searchGTE(i); if (ceil==null || ceil.min > i) { final Range floor = tree.searchLTE(i); if (floor != null && floor.key==i-1) { if (ceil != null && ceil.min==i+1) { tree.delete(ceil); floor.key = ceil.key; } else { floor.key = i; } } else if (ceil != null && ceil.min==i+1) { ceil.min = i; } else { tree.insert(new Range(i,i)); } size++; return true; } return false; } /** * Removes the given integer from this set if already present and * returns true. Otherwise does nothing and returns false. * @ensures this.ints' = this.ints - i * @return i !in this.ints' * @see kodkod.util.ints.IntSet#remove(int) */ @Override public boolean remove(int i) { final Range ceil = tree.searchGTE(i); if (ceil != null && i >= ceil.min) { if (ceil.min==ceil.key) { tree.delete(ceil); } else if (i==ceil.min) { ceil.min++; } else if (i==ceil.key) { ceil.key = i-1; } else { // split the range in two tree.insert(new Range(ceil.min, i-1)); ceil.min = i+1; } size--; assert size >= 0; return true; } return false; } /** * {@inheritDoc} * @see kodkod.util.ints.IntSet#containsAll(kodkod.util.ints.IntCollection) */ @Override public boolean containsAll(IntCollection other) { if (other instanceof IntTreeSet) { IntTreeSet s = (IntTreeSet) other; if (size>=s.size) { for(Range r1 = s.tree.min(); r1 != null; r1 = s.tree.successor(r1)) { Range r0 = tree.searchGTE(r1.key); if (r0==null || r1.min < r0.min) return false; } return true; } return false; } return super.containsAll(other); } /** * Removes all elements from this set. * @ensures no this.ints' * @see kodkod.util.ints.IntCollection#clear() */ @Override public void clear() { tree.clear(); size = 0; } /** * Returns a copy of this int tree set. The copy is independent of this * IntSet. * @return a copy of this IntSet. * @see kodkod.util.ints.IntSet#clone() */ @Override public IntTreeSet clone() { // ok to use copy constructor to clone a final class return new IntTreeSet(this); } /** * A range of integers in an int set. * @specfield min: int * @specfield max: int * @invariant min <= max * @invariant max = key * @author Emina Torlak */ private static final class Range extends IntTree.Node<Range> implements Cloneable { private int min; Range(int min, int max) { super(max); this.min = min; } protected Range clone() throws CloneNotSupportedException { return (Range)super.clone(); } } /** * An iterator that traverses the ints in this set in the * ascending order. * @author Emina Torlak */ private final class AscendingIterator implements IntIterator { private Range next; private final int endpoint; private int currentMax, cursor, lastReturned; private boolean canRemove; /** * @requires from <= to */ AscendingIterator(int from, int to) { endpoint = to; lastReturned = Integer.MIN_VALUE; canRemove = false; next = tree.searchGTE(from); if (next==null) { cursor = 0; currentMax = -1; } else { cursor = StrictMath.max(next.min, from); currentMax = next.key; next = tree.successor(next); } } public boolean hasNext() { if (cursor > currentMax) { if (next==null) return false; this.cursor = next.min; this.currentMax = next.key; next = tree.successor(next); } return lastReturned<Integer.MAX_VALUE && cursor <= endpoint; } public int next() { if (!hasNext()) throw new NoSuchElementException(); canRemove = true; return lastReturned = cursor++; } public void remove() { if (!canRemove) throw new IllegalStateException(); IntTreeSet.this.remove((int)lastReturned); next = tree.searchGTE((int)cursor); canRemove = false; } } /** * An iterator that traverses the ints in this set in the * descending order. * @author Emina Torlak */ private final class DescendingIterator implements IntIterator { private Range next; private final int endpoint; private int currentMin, cursor, lastReturned; private boolean canRemove; /** * @requires from >= to */ DescendingIterator(int from, int to) { endpoint = to; lastReturned = Integer.MAX_VALUE; canRemove = false; next = tree.searchGTE(from); if (next==null || next.min > from) { next = tree.searchLTE(from); if (next==null) { cursor = -1; currentMin = 0; } else { cursor = StrictMath.min(next.key, from); currentMin = next.min; } } else { cursor = StrictMath.min(next.key, from); currentMin = next.min; } } public boolean hasNext() { if (cursor < currentMin) { if (next==null) return false; this.cursor = next.key; this.currentMin = next.min; next = tree.predecessor(next); } return lastReturned>Integer.MIN_VALUE && cursor >= endpoint; } public int next() { if (!hasNext()) throw new NoSuchElementException(); canRemove = true; return lastReturned = cursor--; } public void remove() { if (!canRemove) throw new IllegalStateException(); IntTreeSet.this.remove(lastReturned); next = tree.searchLTE(cursor); canRemove = false; } } }